From 6afd0b8e74fbc344231437599d977854014868cc Mon Sep 17 00:00:00 2001 From: John Whittington Date: Sun, 9 Jun 2024 14:24:11 +0200 Subject: [PATCH 1/7] #18 port libudev to native udevrs --- Cargo.lock | 131 +++++++++++++++++++++++++++++++++++++++++++++------- Cargo.toml | 16 +++---- src/udev.rs | 43 ++++++++--------- 3 files changed, 140 insertions(+), 50 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c645e23..1edffba 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -75,6 +75,15 @@ dependencies = [ "serde_json", ] +[[package]] +name = "atomic-polyfill" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4" +dependencies = [ + "critical-section", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -105,6 +114,12 @@ version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "cc" version = "1.0.83" @@ -216,6 +231,12 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +[[package]] +name = "critical-section" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" + [[package]] name = "cyme" version = "1.6.0" @@ -240,7 +261,7 @@ dependencies = [ "strum", "strum_macros", "terminal_size 0.2.6", - "udev", + "udevrs", "usb-ids", ] @@ -348,12 +369,40 @@ dependencies = [ "wasi", ] +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "hash32" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" +dependencies = [ + "byteorder", +] + [[package]] name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +[[package]] +name = "heapless" +version = "0.7.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f" +dependencies = [ + "atomic-polyfill", + "hash32", + "rustc_version", + "spin", + "stable_deref_trait", +] + [[package]] name = "heck" version = "0.4.1" @@ -481,15 +530,6 @@ dependencies = [ "redox_syscall", ] -[[package]] -name = "libudev-sys" -version = "0.1.4" -source = "git+https://github.com/Emilgardis/libudev-sys/?branch=fix-cross-compilation#808a604d27d0afc1305bbcc5d27c6c083c99dfa4" -dependencies = [ - "libc", - "pkg-config", -] - [[package]] name = "libusb1-sys" version = "0.7.0" @@ -514,6 +554,16 @@ version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829" +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + [[package]] name = "log" version = "0.4.20" @@ -532,6 +582,17 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +[[package]] +name = "nix" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" +dependencies = [ + "bitflags 2.4.1", + "cfg-if", + "libc", +] + [[package]] name = "nom" version = "7.1.3" @@ -706,6 +767,15 @@ dependencies = [ "libusb1-sys", ] +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + [[package]] name = "rustix" version = "0.37.27" @@ -745,6 +815,18 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" + [[package]] name = "serde" version = "1.0.193" @@ -822,6 +904,21 @@ version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "strsim" version = "0.10.0" @@ -941,15 +1038,15 @@ dependencies = [ ] [[package]] -name = "udev" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50051c6e22be28ee6f217d50014f3bc29e81c20dc66ff7ca0d5c5226e1dcc5a1" +name = "udevrs" +version = "0.2.0" dependencies = [ - "io-lifetimes", + "bitflags 2.4.1", + "glob", + "heapless", "libc", - "libudev-sys", - "pkg-config", + "log", + "nix", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 2d0fbda..4da266a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,32 +32,32 @@ terminal_size = "0.2.5" strum = "0.24.1" strum_macros = "0.24.3" -[patch.crates-io] -libudev-sys = { git = "https://github.com/Emilgardis/libudev-sys/", branch = "fix-cross-compilation" } +# [patch.crates-io] +# libudev-sys = { git = "https://github.com/Emilgardis/libudev-sys/", branch = "fix-cross-compilation" } [dev-dependencies] diff = "0.1" assert-json-diff = "2.0.2" [target.x86_64-unknown-linux-gnu.dependencies] -udev = { version = "^0.8.0", optional = true } +udevrs = { path = "/home/john/devel/udev", version = "^0.2.0", optional = true } rusb = "0.9.4" [target.arm-unknown-linux-gnueabihf.dependencies] -udev = { version = "^0.8.0", optional = true } +udevrs = { path = "/home/john/devel/udev", version = "^0.2.0", optional = true } rusb = "0.9.4" [target.aarch64-unknown-linux-gnu.dependencies] -udev = { version = "^0.8.0", optional = true } +udevrs = { path = "/home/john/devel/udev", version = "^0.2.0", optional = true } rusb = "0.9.4" [features] libusb = ["dep:rusb"] -udev = ["dep:udev"] -udev_hwdb = ["udev/hwdb"] +udev = ["dep:udevrs"] +udev_hwdb = ["dep:udevrs"] usb_test = [] cli_generate = ["dep:clap_complete", "dep:clap_mangen"] # for generating man and completions -default = ["libusb"] +default = ["libusb", "udev"] [[bin]] name = "cyme" diff --git a/src/udev.rs b/src/udev.rs index 2fdfe84..87d7eb3 100644 --- a/src/udev.rs +++ b/src/udev.rs @@ -1,6 +1,5 @@ //! Utilities to get device information using udev - only supported on Linux. Requires 'udev' feature. -use std::path::Path; -use udev as udevlib; +use udevrs::{udev_new, UdevDevice, UdevHwdb}; use crate::error::{Error, ErrorKind}; @@ -24,7 +23,7 @@ pub struct UdevInfo { /// ``` pub fn get_udev_info(port_path: &str) -> Result { let path: String = format!("/sys/bus/usb/devices/{}", port_path); - let device = udevlib::Device::from_syspath(Path::new(&path)).map_err(|e| { + let device = UdevDevice::new_from_syspath(udev_new(), &path).map_err(|e| { Error::new( ErrorKind::Udev, &format!( @@ -36,10 +35,8 @@ pub fn get_udev_info(port_path: &str) -> Result { Ok({ UdevInfo { - driver: device - .driver() - .map(|s| s.to_str().unwrap_or("").to_string()), - syspath: device.syspath().to_str().map(|s| s.to_string()), + driver: Some(device.driver().to_string()), + syspath: Some(device.syspath().to_string()), } }) } @@ -53,7 +50,7 @@ pub fn get_udev_info(port_path: &str) -> Result { /// ``` pub fn get_udev_driver_name(port_path: &str) -> Result, Error> { let path: String = format!("/sys/bus/usb/devices/{}", port_path); - let device = udevlib::Device::from_syspath(Path::new(&path)).map_err(|e| { + let device = UdevDevice::new_from_syspath(udev_new(), &path).map_err(|e| { Error::new( ErrorKind::Udev, &format!( @@ -63,9 +60,7 @@ pub fn get_udev_driver_name(port_path: &str) -> Result, Error> { ) })?; - Ok(device - .driver() - .map(|s| s.to_str().unwrap_or("").to_string())) + Ok(Some(device.driver().to_owned())) } /// Lookup the syspath for a device given the `port_path`. @@ -77,7 +72,7 @@ pub fn get_udev_driver_name(port_path: &str) -> Result, Error> { /// ``` pub fn get_udev_syspath(port_path: &str) -> Result, Error> { let path: String = format!("/sys/bus/usb/devices/{}", port_path); - let device = udevlib::Device::from_syspath(Path::new(&path)).map_err(|e| { + let device = UdevDevice::new_from_syspath(udev_new(), &path).map_err(|e| { Error::new( ErrorKind::Udev, &format!( @@ -87,7 +82,7 @@ pub fn get_udev_syspath(port_path: &str) -> Result, Error> { ) })?; - Ok(device.syspath().to_str().map(|s| s.to_string())) + Ok(Some(device.syspath().to_owned())) } /// Lookup a udev attribute given the `port_path` and `attribute`. @@ -104,12 +99,12 @@ pub fn get_udev_syspath(port_path: &str) -> Result, Error> { /// let interface_class = get_udev_attribute("1-0:1.0", "bInterfaceClass").unwrap(); /// assert_eq!(interface_class, Some("09".into())); /// ``` -pub fn get_udev_attribute + std::fmt::Display>( +pub fn get_udev_attribute + std::fmt::Display + Into>( port_path: &str, attribute: T, ) -> Result, Error> { let path: String = format!("/sys/bus/usb/devices/{}", port_path); - let device = udevlib::Device::from_syspath(Path::new(&path)).map_err(|e| { + let mut device = UdevDevice::new_from_syspath(udev_new(), &path).map_err(|e| { Error::new( ErrorKind::Udev, &format!( @@ -119,15 +114,13 @@ pub fn get_udev_attribute + std::fmt::Display>( ) })?; - Ok(device - .attribute_value(attribute) - .map(|s| s.to_str().unwrap_or("").to_string())) + Ok(device.get_sysattr_value(&attribute.into())) } /// udev hwdb lookup functions /// /// Protected by the `udev_hwdb` feature because 'libudev-sys' excludes hwdb ffi bindings if native udev does not support hwdb -#[cfg(feature = "udev_hwdb")] +//#[cfg(feature = "udev_hwdb")] pub mod hwdb { use super::*; /// Lookup an entry in the udev hwdb given the `modalias` and `key`. @@ -138,17 +131,17 @@ pub mod hwdb { /// use cyme::udev; /// /// let modalias = "usb:v1D6Bp0001"; - /// let vendor = hwdb::get(&modalias, "ID_VENDOR_FROM_DATABASE").unwrap(); + /// let vendor = udev::hwdb::get(&modalias, "ID_VENDOR_FROM_DATABASE").unwrap(); /// /// assert_eq!(vendor, Some("Linux Foundation".into())); /// /// let modalias = "usb:v*p*d*dc03dsc01dp01*"; - /// let vendor = hwdb::get(&modalias, "ID_USB_PROTOCOL_FROM_DATABASE").unwrap(); + /// let vendor = udev::hwdb::get(&modalias, "ID_USB_PROTOCOL_FROM_DATABASE").unwrap(); /// /// assert_eq!(vendor, Some("Keyboard".into())); /// ``` - pub fn get(modalias: &str, key: &'static str) -> Result, Error> { - let hwdb = udevlib::Hwdb::new().map_err(|e| { + pub fn get(modalias: &str, _key: &'static str) -> Result, Error> { + let mut hwdb = UdevHwdb::new(udev_new()).map_err(|e| { Error::new( ErrorKind::Udev, &format!("Failed to get hwdb: Error({})", e), @@ -156,8 +149,8 @@ pub mod hwdb { })?; Ok(hwdb - .query_one(&modalias.to_string(), &key.to_string()) - .map(|s| s.to_str().unwrap_or("").to_string())) + .get_properties_list_entry(&modalias.to_string(), 0) + .map(|entry| entry.value().to_owned())) } } From 238618a95b74da5fe53ecb74982d924020ee9728 Mon Sep 17 00:00:00 2001 From: John Whittington Date: Thu, 13 Jun 2024 11:29:56 +0200 Subject: [PATCH 2/7] keep libudev create as optional feature for fallback --- Cargo.lock | 24 +++++++ Cargo.toml | 19 +++-- README.md | 8 ++- src/lib.rs | 5 +- src/udev.rs | 3 +- src/udev_ffi.rs | 184 ++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 230 insertions(+), 13 deletions(-) create mode 100644 src/udev_ffi.rs diff --git a/Cargo.lock b/Cargo.lock index 1edffba..25cdde1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -261,6 +261,7 @@ dependencies = [ "strum", "strum_macros", "terminal_size 0.2.6", + "udev", "udevrs", "usb-ids", ] @@ -530,6 +531,16 @@ dependencies = [ "redox_syscall", ] +[[package]] +name = "libudev-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c8469b4a23b962c1396b9b451dda50ef5b283e8dd309d69033475fa9b334324" +dependencies = [ + "libc", + "pkg-config", +] + [[package]] name = "libusb1-sys" version = "0.7.0" @@ -1037,9 +1048,22 @@ dependencies = [ "time-core", ] +[[package]] +name = "udev" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50051c6e22be28ee6f217d50014f3bc29e81c20dc66ff7ca0d5c5226e1dcc5a1" +dependencies = [ + "io-lifetimes", + "libc", + "libudev-sys", + "pkg-config", +] + [[package]] name = "udevrs" version = "0.2.0" +source = "git+https://github.com/tuna-f1sh/udev/?branch=main#2f43f007ed9cb0f6b813b4b2a1d089e0d09745f5" dependencies = [ "bitflags 2.4.1", "glob", diff --git a/Cargo.toml b/Cargo.toml index 4da266a..1beb268 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,29 +32,34 @@ terminal_size = "0.2.5" strum = "0.24.1" strum_macros = "0.24.3" -# [patch.crates-io] -# libudev-sys = { git = "https://github.com/Emilgardis/libudev-sys/", branch = "fix-cross-compilation" } +[patch.crates-io] +udevrs = { git = "https://github.com/tuna-f1sh/udev/", branch = "main" } [dev-dependencies] diff = "0.1" assert-json-diff = "2.0.2" [target.x86_64-unknown-linux-gnu.dependencies] -udevrs = { path = "/home/john/devel/udev", version = "^0.2.0", optional = true } +udevrs = { version = "^0.2.0", optional = true } +udevlib = { package = "udev", version = "^0.8.0", optional = true } rusb = "0.9.4" [target.arm-unknown-linux-gnueabihf.dependencies] -udevrs = { path = "/home/john/devel/udev", version = "^0.2.0", optional = true } +udevrs = { version = "^0.2.0", optional = true } +udevlib = { package = "udev", version = "^0.8.0", optional = true } rusb = "0.9.4" [target.aarch64-unknown-linux-gnu.dependencies] -udevrs = { path = "/home/john/devel/udev", version = "^0.2.0", optional = true } +udevrs = { version = "^0.2.0", optional = true } +udevlib = { package = "udev", version = "^0.8.0", optional = true } rusb = "0.9.4" [features] libusb = ["dep:rusb"] -udev = ["dep:udevrs"] -udev_hwdb = ["dep:udevrs"] +udev = ["libusb", "dep:udevrs"] +udev_hwdb = ["libusb", "udevlib?/hwdb"] +# libudev C binding +udevlib = ["libusb", "dep:udevlib"] usb_test = [] cli_generate = ["dep:clap_complete", "dep:clap_mangen"] # for generating man and completions default = ["libusb", "udev"] diff --git a/README.md b/README.md index b856151..9afa5fa 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,6 @@ The name comes from the technical term for the type of blossom on a Apple tree: ## Requirements * Linux/Windows and pre-compiled targets require [libusb 1.0.0](https://libusb.info): `brew install libusb`, `sudo apt install libusb-1.0-0-dev` or one's package manager of choice. -* Linux pre-compiled and `--features udev`/`--features udev_hwdb` requires 'libudev-dev': `sudo apt install libudev-dev` or one's package manager of choice. For pre-compiled binaries, see the [releases](https://github.com/tuna-f1sh/cyme/releases). @@ -70,9 +69,12 @@ More package managers to come/package distribution, please feel free to create a ## Linux udev -To obtain device and interface drivers being used on Linux like `lsusb`, one must install 'libudev-dev' via a package manager and the `--features udev` feature when building. To lookup USB IDs from the udev hwdb as well (like `lsusb`) use `--features udev_hwdb`. Without hwdb, `cyme` will use the 'usb-ids' crate, which is the same source as the hwdb binary data but the bundled hwdb may differ due to customisations or last update ('usb-ids' will be most up to date). +> [!NOTE] +> Only supported on Linux targets. -Only supported on Linux targets. +To obtain device and interface drivers being used on Linux like `lsusb`, one can use the `--features udev` feature when building - it's a default feature. The feature uses the Rust crate [udevrs](https://crates.io/crates/udevrs) to obtain the information. To use the C FFI libudev library, use `--no-default-features --features udevlib` which will use the 'libudev' crate. Note that this will require 'libudev-dev' to be installed on the host machine. + +To lookup USB IDs from the udev hwdb as well (like `lsusb`) use `--features udev_hwdb`. Without hwdb, `cyme` will use the 'usb-ids' crate, which is the same source as the hwdb binary data but the bundled hwdb may differ due to customisations or last update ('usb-ids' will be most up to date). ## Alias `lsusb` diff --git a/src/lib.rs b/src/lib.rs index 0c0851a..ce62aec 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -45,9 +45,12 @@ pub mod icon; pub mod lsusb; pub mod system_profiler; pub mod types; +pub mod usb; #[cfg(all(target_os = "linux", feature = "udev"))] pub mod udev; -pub mod usb; +#[cfg(all(all(target_os = "linux", feature = "udevlib"), not(feature = "udev")))] +#[path = "udev_ffi.rs"] +pub mod udev; /// Set cyme module and binary log level pub fn set_log_level(debug: u8) -> crate::error::Result<()> { diff --git a/src/udev.rs b/src/udev.rs index 87d7eb3..37b52a8 100644 --- a/src/udev.rs +++ b/src/udev.rs @@ -119,8 +119,7 @@ pub fn get_udev_attribute + std::fmt::Display + Into, + /// The syspath for the device + pub syspath: Option, +} + +/// Lookup the driver and syspath for a device given the `port_path`. Returns [`UdevInfo`] containing both. +/// +/// ```no_run +/// use cyme::udev::get_udev_info; +/// +/// let udevi = get_udev_info("1-0:1.0").unwrap(); +/// assert_eq!(udevi.driver, Some("hub".into())); +/// assert_eq!(udevi.syspath.unwrap().contains("usb1/1-0:1.0"), true); +/// ``` +pub fn get_udev_info(port_path: &str) -> Result { + let path: String = format!("/sys/bus/usb/devices/{}", port_path); + let device = udevlib::Device::from_syspath(Path::new(&path)).map_err(|e| { + Error::new( + ErrorKind::Udev, + &format!( + "Failed to get udev info for device at {}: Error({})", + path, e + ), + ) + })?; + + Ok({ + UdevInfo { + driver: device + .driver() + .map(|s| s.to_str().unwrap_or("").to_string()), + syspath: device.syspath().to_str().map(|s| s.to_string()), + } + }) +} + +/// Lookup the driver name for a device given the `port_path`. +/// +/// ```no_run +/// use cyme::udev::get_udev_driver_name; +/// let driver = get_udev_driver_name("1-0:1.0").unwrap(); +/// assert_eq!(driver, Some("hub".into())); +/// ``` +pub fn get_udev_driver_name(port_path: &str) -> Result, Error> { + let path: String = format!("/sys/bus/usb/devices/{}", port_path); + let device = udevlib::Device::from_syspath(Path::new(&path)).map_err(|e| { + Error::new( + ErrorKind::Udev, + &format!( + "Failed to get udev info for device at {}: Error({})", + path, e + ), + ) + })?; + + Ok(device + .driver() + .map(|s| s.to_str().unwrap_or("").to_string())) +} + +/// Lookup the syspath for a device given the `port_path`. +/// +/// ```no_run +/// use cyme::udev::get_udev_syspath; +/// let syspath = get_udev_syspath("1-0:1.0").unwrap(); +/// assert_eq!(syspath.unwrap().contains("usb1/1-0:1.0"), true); +/// ``` +pub fn get_udev_syspath(port_path: &str) -> Result, Error> { + let path: String = format!("/sys/bus/usb/devices/{}", port_path); + let device = udevlib::Device::from_syspath(Path::new(&path)).map_err(|e| { + Error::new( + ErrorKind::Udev, + &format!( + "Failed to get udev info for device at {}: Error({})", + path, e + ), + ) + })?; + + Ok(device.syspath().to_str().map(|s| s.to_string())) +} + +/// Lookup a udev attribute given the `port_path` and `attribute`. +/// +/// This only works on Linux and not all devices have all attributes. +/// These attributes are generally readable by all users. +/// +/// NOTE: In general you should read from sysfs directly as it does not +/// depend on the udev feature. See `get_sysfs_string()` in lsusb.rs +/// +/// ```no_run +/// use cyme::udev::get_udev_attribute; +/// +/// let interface_class = get_udev_attribute("1-0:1.0", "bInterfaceClass").unwrap(); +/// assert_eq!(interface_class, Some("09".into())); +/// ``` +pub fn get_udev_attribute + std::fmt::Display>( + port_path: &str, + attribute: T, +) -> Result, Error> { + let path: String = format!("/sys/bus/usb/devices/{}", port_path); + let device = udevlib::Device::from_syspath(Path::new(&path)).map_err(|e| { + Error::new( + ErrorKind::Udev, + &format!( + "Failed to get udev attribute {} for device at {}: Error({})", + attribute, path, e + ), + ) + })?; + + Ok(device + .attribute_value(attribute) + .map(|s| s.to_str().unwrap_or("").to_string())) +} + +/// udev hwdb lookup functions +/// +/// Protected by the `udev_hwdb` feature because 'libudev-sys' excludes hwdb ffi bindings if native udev does not support hwdb +#[cfg(feature = "udev_hwdb")] +pub mod hwdb { + use super::*; + /// Lookup an entry in the udev hwdb given the `modalias` and `key`. + /// + /// Should act like https://github.com/gregkh/usbutils/blob/master/names.c#L115 + /// + /// ``` + /// use cyme::udev; + /// + /// let modalias = "usb:v1D6Bp0001"; + /// let vendor = udev::hwdb::get(&modalias, "ID_VENDOR_FROM_DATABASE").unwrap(); + /// + /// assert_eq!(vendor, Some("Linux Foundation".into())); + /// + /// let modalias = "usb:v*p*d*dc03dsc01dp01*"; + /// let vendor = udev::hwdb::get(&modalias, "ID_USB_PROTOCOL_FROM_DATABASE").unwrap(); + /// + /// assert_eq!(vendor, Some("Keyboard".into())); + /// ``` + pub fn get(modalias: &str, key: &'static str) -> Result, Error> { + let hwdb = udevlib::Hwdb::new().map_err(|e| { + Error::new( + ErrorKind::Udev, + &format!("Failed to get hwdb: Error({})", e), + ) + })?; + + Ok(hwdb + .query_one(&modalias.to_string(), &key.to_string()) + .map(|s| s.to_str().unwrap_or("").to_string())) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + /// Tests can obtain driver and syspath for root_hub on bus 1 - only do if we have USB + #[cfg_attr(not(feature = "usb_test"), ignore)] + #[test] + fn test_udev_info() { + let udevi = get_udev_info("1-0:1.0").unwrap(); + assert_eq!(udevi.driver, Some("hub".into())); + assert!(udevi.syspath.unwrap().contains("usb1/1-0:1.0")); + } + + /// Tests can lookup bInterfaceClass of the root hub, which is always 09 + #[cfg_attr(not(feature = "usb_test"), ignore)] + #[test] + fn test_udev_attribute() { + let interface_class = get_udev_attribute("1-0:1.0", "bInterfaceClass").unwrap(); + assert_eq!(interface_class, Some("09".into())); + } +} From 99af03487c82bdc907fcd125b174eb9025038843 Mon Sep 17 00:00:00 2001 From: John Whittington Date: Thu, 13 Jun 2024 11:41:27 +0200 Subject: [PATCH 3/7] fixes with -F=usb_test --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/udev.rs | 42 ++++++++++++++++++++++++------------------ 3 files changed, 26 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 25cdde1..807112d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1063,7 +1063,7 @@ dependencies = [ [[package]] name = "udevrs" version = "0.2.0" -source = "git+https://github.com/tuna-f1sh/udev/?branch=main#2f43f007ed9cb0f6b813b4b2a1d089e0d09745f5" +source = "git+https://github.com/tuna-f1sh/udev/?branch=query#d27ba56a4c229aa4b89c016f392f0966012a6969" dependencies = [ "bitflags 2.4.1", "glob", diff --git a/Cargo.toml b/Cargo.toml index 1beb268..62388d9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,7 +33,7 @@ strum = "0.24.1" strum_macros = "0.24.3" [patch.crates-io] -udevrs = { git = "https://github.com/tuna-f1sh/udev/", branch = "main" } +udevrs = { git = "https://github.com/tuna-f1sh/udev/", branch = "query" } [dev-dependencies] diff = "0.1" diff --git a/src/udev.rs b/src/udev.rs index 37b52a8..a120013 100644 --- a/src/udev.rs +++ b/src/udev.rs @@ -23,7 +23,7 @@ pub struct UdevInfo { /// ``` pub fn get_udev_info(port_path: &str) -> Result { let path: String = format!("/sys/bus/usb/devices/{}", port_path); - let device = UdevDevice::new_from_syspath(udev_new(), &path).map_err(|e| { + let mut device = UdevDevice::new_from_syspath(udev_new(), &path).map_err(|e| { Error::new( ErrorKind::Udev, &format!( @@ -35,8 +35,8 @@ pub fn get_udev_info(port_path: &str) -> Result { Ok({ UdevInfo { - driver: Some(device.driver().to_string()), - syspath: Some(device.syspath().to_string()), + driver: device.get_driver().map(|s| s.trim().to_string()), + syspath: Some(device.syspath().trim().to_string()), } }) } @@ -50,7 +50,7 @@ pub fn get_udev_info(port_path: &str) -> Result { /// ``` pub fn get_udev_driver_name(port_path: &str) -> Result, Error> { let path: String = format!("/sys/bus/usb/devices/{}", port_path); - let device = UdevDevice::new_from_syspath(udev_new(), &path).map_err(|e| { + let mut device = UdevDevice::new_from_syspath(udev_new(), &path).map_err(|e| { Error::new( ErrorKind::Udev, &format!( @@ -60,7 +60,7 @@ pub fn get_udev_driver_name(port_path: &str) -> Result, Error> { ) })?; - Ok(Some(device.driver().to_owned())) + Ok(device.get_driver().map(|s| s.trim().to_string())) } /// Lookup the syspath for a device given the `port_path`. @@ -82,7 +82,7 @@ pub fn get_udev_syspath(port_path: &str) -> Result, Error> { ) })?; - Ok(Some(device.syspath().to_owned())) + Ok(Some(device.syspath().trim().to_string())) } /// Lookup a udev attribute given the `port_path` and `attribute`. @@ -114,14 +114,13 @@ pub fn get_udev_attribute + std::fmt::Display + Into Result, Error> { + pub fn get(modalias: &str, key: &'static str) -> Result, Error> { let mut hwdb = UdevHwdb::new(udev_new()).map_err(|e| { Error::new( ErrorKind::Udev, @@ -147,9 +146,16 @@ pub mod hwdb { ) })?; - Ok(hwdb - .get_properties_list_entry(&modalias.to_string(), 0) - .map(|entry| entry.value().to_owned())) + hwdb.get_properties_list_entry(&modalias.to_string(), 0) + .map(|mut entry| entry + .find(|entry| entry.name() == key) + .map(|entry| entry.value().to_owned()) + ).map_err(|e| { + Error::new( + ErrorKind::Udev, + &format!("Failed to get hwdb modalias/key {}/{}: Error({})", modalias, key, e), + ) + }) } } @@ -163,7 +169,7 @@ mod tests { fn test_udev_info() { let udevi = get_udev_info("1-0:1.0").unwrap(); assert_eq!(udevi.driver, Some("hub".into())); - assert!(udevi.syspath.unwrap().contains("usb1/1-0:1.0")); + assert!(udevi.syspath.unwrap().contains("1-0:1.0")); } /// Tests can lookup bInterfaceClass of the root hub, which is always 09 From 26c6c230e23f3fd1169a8d12ea10fe9b27287078 Mon Sep 17 00:00:00 2001 From: John Whittington Date: Fri, 21 Jun 2024 11:06:01 +0200 Subject: [PATCH 4/7] udevrs fixes integrated; hwdb lookup working --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/lib.rs | 2 +- src/udev.rs | 15 ++++----------- 4 files changed, 7 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 807112d..1177185 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -239,7 +239,7 @@ checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" [[package]] name = "cyme" -version = "1.6.0" +version = "1.7.0" dependencies = [ "assert-json-diff", "clap", diff --git a/Cargo.toml b/Cargo.toml index 62388d9..5ee3366 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ description = "List system USB buses and devices; a modern cross-platform `lsusb repository = "https://github.com/tuna-f1sh/cyme" readme = "README.md" license = "GPL-3.0-or-later" -version = "1.6.1" +version = "1.7.0" edition = "2021" keywords = ["usb", "lsusb", "system_profiler", "macos", "libusb"] categories = ["command-line-utilities"] diff --git a/src/lib.rs b/src/lib.rs index ce62aec..e3704ea 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -45,12 +45,12 @@ pub mod icon; pub mod lsusb; pub mod system_profiler; pub mod types; -pub mod usb; #[cfg(all(target_os = "linux", feature = "udev"))] pub mod udev; #[cfg(all(all(target_os = "linux", feature = "udevlib"), not(feature = "udev")))] #[path = "udev_ffi.rs"] pub mod udev; +pub mod usb; /// Set cyme module and binary log level pub fn set_log_level(debug: u8) -> crate::error::Result<()> { diff --git a/src/udev.rs b/src/udev.rs index a120013..cfd704c 100644 --- a/src/udev.rs +++ b/src/udev.rs @@ -114,7 +114,9 @@ pub fn get_udev_attribute + std::fmt::Display + Into Date: Fri, 21 Jun 2024 11:17:24 +0200 Subject: [PATCH 5/7] CHANGELOG update for udev native --- CHANGELOG.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2502008..3048e71 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,13 @@ ## [Unreleased] -- Working on full dumps of device descriptors ([#15](https://github.com/tuna-f1sh/cyme/issues/15)) +### Addded + +- Full dumps of device descriptors for matching `--lsusb --verbose` ([#15](https://github.com/tuna-f1sh/cyme/issues/15)) + +### Changed + +- Replace [udev-rs](https://github.com/Smithay/udev-rs) and indirectly libudev-sys with Rust native [udev](https://github.com/cr8t/udev); libudev dependency (and system requirement) is now optional but can be used with `--no-default-features -F=udevlib`. ([#19](https://github.com/tuna-f1sh/cyme/pull/19)) ## [1.6.1] - 2024-13-06 From 78b292380de60037a8b0f8da53b5d996544afef3 Mon Sep 17 00:00:00 2001 From: John Whittington Date: Fri, 21 Jun 2024 11:24:55 +0200 Subject: [PATCH 6/7] build uses udev native, no hwdb bin (favor usb-id) --- .github/workflows/build.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c17be3d..f99c21e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -21,9 +21,13 @@ jobs: fail-fast: false matrix: job: - - { os: ubuntu-latest, target: aarch64-unknown-linux-gnu, use-cross: true, feature-flags: "--all-features" } + # only default as don't generate docs or hwdb + - { os: ubuntu-latest, target: aarch64-unknown-linux-gnu, use-cross: true, feature-flags: "" } + # all ok as udev ignored on non-linux - { os: ubuntu-latest, target: x86_64-pc-windows-gnu, use-cross: true, feature-flags: "--all-features" } - - { os: ubuntu-latest, target: x86_64-unknown-linux-gnu, use-cross: false, feature-flags: "--all-features" } + # specificy to avoid udevlib + - { os: ubuntu-latest, target: x86_64-unknown-linux-gnu, use-cross: false, feature-flags: "-F=cli_generate" } + # all ok as udev ignored on non-linux - { os: macos-latest, target: universal-apple-darwin, use-cross: false, feature-flags: "--all-features" } steps: - uses: actions/checkout@v4 From 7e995555e23844a67e8b8027aa436291232be50a Mon Sep 17 00:00:00 2001 From: John Whittington Date: Tue, 25 Jun 2024 07:07:11 +0200 Subject: [PATCH 7/7] udevrs from patched to release version --- Cargo.lock | 5 +++-- Cargo.toml | 9 +++------ 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1177185..d32dad2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1062,8 +1062,9 @@ dependencies = [ [[package]] name = "udevrs" -version = "0.2.0" -source = "git+https://github.com/tuna-f1sh/udev/?branch=query#d27ba56a4c229aa4b89c016f392f0966012a6969" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdc959ba33f1deec76a55dd1fc7f6265b404dfb6155dc3c14f21551fa60e1a09" dependencies = [ "bitflags 2.4.1", "glob", diff --git a/Cargo.toml b/Cargo.toml index 5ee3366..22305ad 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,25 +32,22 @@ terminal_size = "0.2.5" strum = "0.24.1" strum_macros = "0.24.3" -[patch.crates-io] -udevrs = { git = "https://github.com/tuna-f1sh/udev/", branch = "query" } - [dev-dependencies] diff = "0.1" assert-json-diff = "2.0.2" [target.x86_64-unknown-linux-gnu.dependencies] -udevrs = { version = "^0.2.0", optional = true } +udevrs = { version = "^0.3.0", optional = true } udevlib = { package = "udev", version = "^0.8.0", optional = true } rusb = "0.9.4" [target.arm-unknown-linux-gnueabihf.dependencies] -udevrs = { version = "^0.2.0", optional = true } +udevrs = { version = "^0.3.0", optional = true } udevlib = { package = "udev", version = "^0.8.0", optional = true } rusb = "0.9.4" [target.aarch64-unknown-linux-gnu.dependencies] -udevrs = { version = "^0.2.0", optional = true } +udevrs = { version = "^0.3.0", optional = true } udevlib = { package = "udev", version = "^0.8.0", optional = true } rusb = "0.9.4"