| 1 | use std::str; |
| 2 | |
| 3 | use std::ffi::{CStr, CString, OsStr}; |
| 4 | use std::io::Result; |
| 5 | use std::marker::PhantomData; |
| 6 | use std::path::Path; |
| 7 | use std::ptr; |
| 8 | use std::str::FromStr; |
| 9 | |
| 10 | use libc::{c_char, dev_t}; |
| 11 | |
| 12 | use list::{Entry, EntryList}; |
| 13 | use Udev; |
| 14 | use {ffi, util}; |
| 15 | |
| 16 | use {AsRaw, FromRaw}; |
| 17 | |
| 18 | /// A structure that provides access to sysfs/kernel devices. |
| 19 | pub struct Device { |
| 20 | udev: Udev, |
| 21 | device: *mut ffi::udev_device, |
| 22 | } |
| 23 | |
| 24 | /// Permissible types of UNIX file I/O API device special file. |
| 25 | /// |
| 26 | /// See also [`from_devnum`][crate::Device::from_devnum]. |
| 27 | #[repr (u8)] |
| 28 | pub enum DeviceType { |
| 29 | /// UNIX character-style file IO semantics. |
| 30 | Character = b'c' , |
| 31 | /// UNIX block-style file IO semantics. |
| 32 | Block = b'b' , |
| 33 | } |
| 34 | |
| 35 | impl std::fmt::Debug for Device { |
| 36 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| 37 | f&mut DebugStruct<'_, '_>.debug_struct("Device" ) |
| 38 | .field("initialized" , &self.is_initialized()) |
| 39 | .field("device_major_minor_number" , &self.devnum()) |
| 40 | .field("system_path" , &self.syspath()) |
| 41 | .field("device_path" , &self.devpath()) |
| 42 | .field("device_node" , &self.devnode()) |
| 43 | .field("subsystem_name" , &self.subsystem()) |
| 44 | .field("system_name" , &self.sysname()) |
| 45 | .field("instance_number" , &self.sysnum()) |
| 46 | .field("device_type" , &self.devtype()) |
| 47 | .field("driver" , &self.driver()) |
| 48 | .field("action" , &self.action()) |
| 49 | .field(name:"parent" , &self.parent()) |
| 50 | .finish() |
| 51 | } |
| 52 | } |
| 53 | |
| 54 | impl Clone for Device { |
| 55 | fn clone(&self) -> Self { |
| 56 | Self { |
| 57 | udev: self.udev.clone(), |
| 58 | device: unsafe { ffi::udev_device_ref(self.device) }, |
| 59 | } |
| 60 | } |
| 61 | } |
| 62 | |
| 63 | impl Drop for Device { |
| 64 | fn drop(&mut self) { |
| 65 | unsafe { |
| 66 | ffi::udev_device_unref(self.device); |
| 67 | } |
| 68 | } |
| 69 | } |
| 70 | |
| 71 | #[cfg (feature = "send" )] |
| 72 | unsafe impl Send for Device {} |
| 73 | #[cfg (feature = "sync" )] |
| 74 | unsafe impl Sync for Device {} |
| 75 | |
| 76 | as_ffi_with_context!(Device, device, ffi::udev_device, ffi::udev_device_ref); |
| 77 | |
| 78 | /// A convenience alias for a list of properties, bound to a device. |
| 79 | pub type Properties<'a> = EntryList<'a, Device>; |
| 80 | |
| 81 | /// A convenience alias for a list of attributes, bound to a device. |
| 82 | pub struct Attributes<'a> { |
| 83 | entries: EntryList<'a, Device>, |
| 84 | device: &'a Device, |
| 85 | } |
| 86 | |
| 87 | impl Device { |
| 88 | /// Creates a device for a given syspath. |
| 89 | /// |
| 90 | /// The `syspath` parameter should be a path to the device file within the `sysfs` file system, |
| 91 | /// e.g., `/sys/devices/virtual/tty/tty0`. |
| 92 | pub fn from_syspath(syspath: &Path) -> Result<Self> { |
| 93 | // Create a new Udev context for this device |
| 94 | // It would be more efficient to allow callers to create just one context and use multiple |
| 95 | // devices, however that would be an API-breaking change. |
| 96 | // |
| 97 | // When devices are enumerated using an `Enumerator`, it will use |
| 98 | // `from_syspath_with_context` which can reuse the existing `Udev` context to avoid this |
| 99 | // extra overhead. |
| 100 | let udev = Udev::new()?; |
| 101 | |
| 102 | Self::from_syspath_with_context(udev, syspath) |
| 103 | } |
| 104 | |
| 105 | /// Creates a device for a given syspath, using an existing `Udev` instance rather than |
| 106 | /// creating one automatically. |
| 107 | /// |
| 108 | /// The `syspath` parameter should be a path to the device file within the `sysfs` file system, |
| 109 | /// e.g., `/sys/devices/virtual/tty/tty0`. |
| 110 | pub fn from_syspath_with_context(udev: Udev, syspath: &Path) -> Result<Self> { |
| 111 | let syspath = util::os_str_to_cstring(syspath)?; |
| 112 | |
| 113 | let ptr = try_alloc!(unsafe { |
| 114 | ffi::udev_device_new_from_syspath(udev.as_raw(), syspath.as_ptr()) |
| 115 | }); |
| 116 | |
| 117 | Ok(Self::from_raw(udev, ptr)) |
| 118 | } |
| 119 | |
| 120 | /// Create new udev device, and fill in information from the sys device |
| 121 | /// and the udev database entry. |
| 122 | /// |
| 123 | /// The device is looked up by the `subsystem` and `sysname` string of the device, like "mem" / "zero", or "block" / "sda". |
| 124 | pub fn from_subsystem_sysname(subsystem: String, sysname: String) -> Result<Self> { |
| 125 | let subsystem = CString::new(subsystem.as_bytes()) |
| 126 | .ok() |
| 127 | .ok_or(std::io::Error::from_raw_os_error(libc::EINVAL))?; |
| 128 | |
| 129 | let sysname = CString::new(sysname.as_bytes()) |
| 130 | .ok() |
| 131 | .ok_or(std::io::Error::from_raw_os_error(libc::EINVAL))?; |
| 132 | |
| 133 | let udev = Udev::new()?; |
| 134 | |
| 135 | let ptr = try_alloc!(unsafe { |
| 136 | ffi::udev_device_new_from_subsystem_sysname( |
| 137 | udev.as_raw(), |
| 138 | subsystem.as_ptr(), |
| 139 | sysname.as_ptr(), |
| 140 | ) |
| 141 | }); |
| 142 | |
| 143 | Ok(Self::from_raw(udev, ptr)) |
| 144 | } |
| 145 | |
| 146 | /// Create new udev device, and fill in information from the sys device |
| 147 | /// and the udev database entry, using an existing `Udev` instance rather than |
| 148 | /// creating a new one. |
| 149 | /// |
| 150 | /// The device is looked up by the `subsystem` and `sysname` string of the device, like "mem" / "zero", or "block" / "sda". |
| 151 | pub fn from_subsystem_sysname_with_context( |
| 152 | udev: Udev, |
| 153 | subsystem: String, |
| 154 | sysname: String, |
| 155 | ) -> Result<Self> { |
| 156 | let subsystem = CString::new(subsystem.as_bytes()) |
| 157 | .ok() |
| 158 | .ok_or(std::io::Error::from_raw_os_error(libc::EINVAL))?; |
| 159 | |
| 160 | let sysname = CString::new(sysname.as_bytes()) |
| 161 | .ok() |
| 162 | .ok_or(std::io::Error::from_raw_os_error(libc::EINVAL))?; |
| 163 | |
| 164 | let ptr = try_alloc!(unsafe { |
| 165 | ffi::udev_device_new_from_subsystem_sysname( |
| 166 | udev.as_raw(), |
| 167 | subsystem.as_ptr(), |
| 168 | sysname.as_ptr(), |
| 169 | ) |
| 170 | }); |
| 171 | |
| 172 | Ok(Self::from_raw(udev, ptr)) |
| 173 | } |
| 174 | |
| 175 | /// Creates a rust udev `Device` for a given UNIX device "special file" type and number. |
| 176 | /// |
| 177 | /// The `dev_type` parameter indicates which of the historical UNIX file-like I/O paradigms the |
| 178 | /// device permits, and is either [`DeviceType::Character`] or [`DeviceType::Block`]. |
| 179 | /// |
| 180 | /// n.b. This function follows the naming used by the underlying `libudev` function. As with |
| 181 | /// the underlying function, there is **no** **direct** **correspondence** between this |
| 182 | /// function's `dev_type` parameter and string values returned by [`devtype`][Self::devtype]. |
| 183 | /// i.e. They represent different underlying concepts within the OS kernel. |
| 184 | /// |
| 185 | /// The `devnum` parameter is of type [`libc::dev_t`][libc::dev_t] which encodes the historical |
| 186 | /// UNIX major and minor device numbers (see below). |
| 187 | /// |
| 188 | /// Typically both parameters would be determined at run-time by calling one of the `stat` |
| 189 | /// family of system calls (or Rust std library functions which utilise them) on a filesystem |
| 190 | /// "special file" inode (e.g. `/dev/null`) or (more commonly) on a symbolic link to such a |
| 191 | /// file which was created by the `udevd` system daemon such as those under `/dev/disk/`. |
| 192 | /// |
| 193 | /// ``` |
| 194 | /// use std::{env, fs, os::linux::fs::MetadataExt}; |
| 195 | /// use udev::DeviceType; |
| 196 | /// |
| 197 | /// fn main() -> std::io::Result<()> { |
| 198 | /// let args: Vec<String> = env::args().collect(); |
| 199 | /// # // Examples are automatically run as tests: provide dummy args for cargo test. |
| 200 | /// # let args: Vec<String> = vec!("testname" .into(), "/dev/null" .into()); |
| 201 | /// let path = args.get(1).expect("No filename given" ); |
| 202 | /// let metadata = fs::metadata(path).unwrap_or_else(|_| panic!("Can't open file: {}" , path)); |
| 203 | /// let devtype = match metadata.st_mode() & libc::S_IFMT { |
| 204 | /// libc::S_IFCHR => Some(DeviceType::Character), |
| 205 | /// libc::S_IFBLK => Some(DeviceType::Block), |
| 206 | /// _ => None, |
| 207 | /// }.expect("Not a character or block special file" ); |
| 208 | /// let ud = udev::Device::from_devnum(devtype, metadata.st_rdev()) |
| 209 | /// .expect("Couldn't construct udev from supplied path" ); |
| 210 | /// println!("syspath of {} is {:?}" , path, ud.syspath()); |
| 211 | /// let dn = ud.devnum(); |
| 212 | /// println!("devnum: {}" , dn.unwrap()); |
| 213 | /// Ok(()) |
| 214 | /// } |
| 215 | /// ``` |
| 216 | /// The user should be aware that a given device may change its major and/or minor number |
| 217 | /// across reboots, when the hardware attached to the device is subject to hot-plug events, or |
| 218 | /// for a variety of other reasons. |
| 219 | /// |
| 220 | /// The `udevd` system daemon (or equivalent) is configured to dynamically create filesystem |
| 221 | /// symbolic links (examples of which can be seen under e.g. `/dev/disk/by-id/` on most Linux |
| 222 | /// systems), the purpose of which is to provide a predictable and persistent means of |
| 223 | /// identifying devices which themselves have a persistent state or identity. |
| 224 | /// |
| 225 | /// Code similar to the sample presented above may be used to obtain a [`udev::Device`][Self] |
| 226 | /// corresponding to the filesystem path of the UNIX file I/O style device node or symbolic |
| 227 | /// link. |
| 228 | /// |
| 229 | /// Historical UNIX systems statically allocated their internal data structures which were |
| 230 | /// associated with devices that exposed a "file-like" user-space API (e.g. `/dev/null`). A |
| 231 | /// device could be uniquely and persistently identified by combining its type (either |
| 232 | /// "character" or "block"), with its major and minor device numbers. |
| 233 | /// |
| 234 | /// In the underlying OS kernel, a major number might be allocated to a single device driver |
| 235 | /// such as a SCSI disk controller, and that device driver would allocate the minor device |
| 236 | /// number (e.g. `4` might have represented the 4th SCSI device addressable by a particular |
| 237 | /// SCSI host adapter). The `mknod` system utility would be used to create friendly filesystem |
| 238 | /// paths in the filesystem, which corresponded with these attributes, and file permissions |
| 239 | /// would be managed with utilities such as `chown` and `chmod` etc. and the numbers would not |
| 240 | /// change between system reboots. |
| 241 | /// |
| 242 | /// As has been noted, modern UNIX-like operating systems dynamically allocate devices. To |
| 243 | /// provide backward compatibility with existing user-space APIs, the concept of major/minor |
| 244 | /// devices being associated with file system "special file" inodes has been retained. |
| 245 | /// |
| 246 | /// For udev devices which present a UNIX file I/O style interface (i.e. via `/dev/` paths), |
| 247 | /// the Linux `udevadm` utility currently reports devices belonging to the `"block"` subsystem |
| 248 | /// to be of type "block", and all other file I/O style udev devices to be of type "character". |
| 249 | /// |
| 250 | /// Those needing to compose or decompose values of type `dev_t` should refer to |
| 251 | /// [`libc::major`], [`libc::minor`], [`libc::makedev`] and equivalent functionality from |
| 252 | /// higher-level rust crates. |
| 253 | pub fn from_devnum(dev_type: self::DeviceType, devnum: dev_t) -> Result<Self> { |
| 254 | let udev = Udev::new()?; |
| 255 | |
| 256 | Self::from_devnum_with_context(udev, dev_type, devnum) |
| 257 | } |
| 258 | |
| 259 | /// Creates a rust udev `Device` for a given UNIX device "special file" type and number. Uses |
| 260 | /// an existing [`Udev`] instance rather than creating one automatically. |
| 261 | /// |
| 262 | /// See [`from_devnum`][Self::from_devnum] for detailed usage. |
| 263 | pub fn from_devnum_with_context( |
| 264 | udev: Udev, |
| 265 | dev_type: self::DeviceType, |
| 266 | devnum: dev_t, |
| 267 | ) -> Result<Self> { |
| 268 | let ptr = try_alloc!(unsafe { |
| 269 | ffi::udev_device_new_from_devnum(udev.as_raw(), dev_type as c_char, devnum) |
| 270 | }); |
| 271 | |
| 272 | Ok(Self::from_raw(udev, ptr)) |
| 273 | } |
| 274 | |
| 275 | /// Creates a rust `Device` given an already created libudev `ffi::udev_device*` and a |
| 276 | /// corresponding `Udev` instance from which the device was created. |
| 277 | /// |
| 278 | /// This guarantees that the `Udev` will live longer than the corresponding `Device` |
| 279 | pub(crate) fn from_raw(udev: Udev, ptr: *mut ffi::udev_device) -> Self { |
| 280 | Self { udev, device: ptr } |
| 281 | } |
| 282 | |
| 283 | /// Checks whether the device has already been handled by udev. |
| 284 | /// |
| 285 | /// When a new device is connected to the system, udev initializes the device by setting |
| 286 | /// permissions, renaming network devices, and possibly other initialization routines. This |
| 287 | /// method returns `true` if udev has performed all of its work to initialize this device. |
| 288 | /// |
| 289 | /// This method only applies to devices with device nodes or network interfaces. All other |
| 290 | /// devices return `true` by default. |
| 291 | pub fn is_initialized(&self) -> bool { |
| 292 | unsafe { ffi::udev_device_get_is_initialized(self.device) > 0 } |
| 293 | } |
| 294 | |
| 295 | /// Gets the device's major/minor number. |
| 296 | pub fn devnum(&self) -> Option<dev_t> { |
| 297 | match unsafe { ffi::udev_device_get_devnum(self.device) } { |
| 298 | 0 => None, |
| 299 | n => Some(n), |
| 300 | } |
| 301 | } |
| 302 | |
| 303 | /// Returns the syspath of the device. |
| 304 | /// |
| 305 | /// The path is an absolute path and includes the sys mount point. For example, the syspath for |
| 306 | /// `tty0` could be `/sys/devices/virtual/tty/tty0`, which includes the sys mount point, |
| 307 | /// `/sys`. |
| 308 | pub fn syspath(&self) -> &Path { |
| 309 | Path::new(unsafe { |
| 310 | util::ptr_to_os_str_unchecked(ffi::udev_device_get_syspath(self.device)) |
| 311 | }) |
| 312 | } |
| 313 | |
| 314 | /// Returns the kernel devpath value of the device. |
| 315 | /// |
| 316 | /// The path does not contain the sys mount point, but does start with a `/`. For example, the |
| 317 | /// devpath for `tty0` could be `/devices/virtual/tty/tty0`. |
| 318 | pub fn devpath(&self) -> &OsStr { |
| 319 | unsafe { util::ptr_to_os_str_unchecked(ffi::udev_device_get_devpath(self.device)) } |
| 320 | } |
| 321 | |
| 322 | /// Returns the path to the device node belonging to the device. |
| 323 | /// |
| 324 | /// The path is an absolute path and starts with the device directory. For example, the device |
| 325 | /// node for `tty0` could be `/dev/tty0`. |
| 326 | pub fn devnode(&self) -> Option<&Path> { |
| 327 | unsafe { util::ptr_to_os_str(ffi::udev_device_get_devnode(self.device)) } |
| 328 | .map(|path| Path::new(path)) |
| 329 | } |
| 330 | |
| 331 | /// Returns the parent of the device. |
| 332 | pub fn parent(&self) -> Option<Self> { |
| 333 | let ptr = unsafe { ffi::udev_device_get_parent(self.device) }; |
| 334 | |
| 335 | if ptr.is_null() { |
| 336 | return None; |
| 337 | } |
| 338 | |
| 339 | Some(Self::from_raw(self.udev.clone(), unsafe { |
| 340 | ffi::udev_device_ref(ptr) |
| 341 | })) |
| 342 | } |
| 343 | |
| 344 | /// Returns the parent of the device with the matching subsystem and devtype if any. |
| 345 | pub fn parent_with_subsystem<T: AsRef<OsStr>>(&self, subsystem: T) -> Result<Option<Self>> { |
| 346 | let subsystem = util::os_str_to_cstring(subsystem)?; |
| 347 | let ptr = unsafe { |
| 348 | ffi::udev_device_get_parent_with_subsystem_devtype( |
| 349 | self.device, |
| 350 | subsystem.as_ptr(), |
| 351 | ptr::null(), |
| 352 | ) |
| 353 | }; |
| 354 | |
| 355 | if ptr.is_null() { |
| 356 | return Ok(None); |
| 357 | } |
| 358 | |
| 359 | Ok(Some(Self::from_raw(self.udev.clone(), unsafe { |
| 360 | ffi::udev_device_ref(ptr) |
| 361 | }))) |
| 362 | } |
| 363 | |
| 364 | /// Returns the parent of the device with the matching subsystem and devtype if any. |
| 365 | pub fn parent_with_subsystem_devtype<T: AsRef<OsStr>, U: AsRef<OsStr>>( |
| 366 | &self, |
| 367 | subsystem: T, |
| 368 | devtype: U, |
| 369 | ) -> Result<Option<Self>> { |
| 370 | let subsystem = util::os_str_to_cstring(subsystem)?; |
| 371 | let devtype = util::os_str_to_cstring(devtype)?; |
| 372 | let ptr = unsafe { |
| 373 | ffi::udev_device_get_parent_with_subsystem_devtype( |
| 374 | self.device, |
| 375 | subsystem.as_ptr(), |
| 376 | devtype.as_ptr(), |
| 377 | ) |
| 378 | }; |
| 379 | |
| 380 | if ptr.is_null() { |
| 381 | return Ok(None); |
| 382 | } |
| 383 | |
| 384 | Ok(Some(Self::from_raw(self.udev.clone(), unsafe { |
| 385 | ffi::udev_device_ref(ptr) |
| 386 | }))) |
| 387 | } |
| 388 | |
| 389 | /// Returns the subsystem name of the device. |
| 390 | /// |
| 391 | /// The subsystem name is a string that indicates which kernel subsystem the device belongs to. |
| 392 | /// Examples of subsystem names are `tty`, `vtconsole`, `block`, `scsi`, and `net`. |
| 393 | pub fn subsystem(&self) -> Option<&OsStr> { |
| 394 | unsafe { util::ptr_to_os_str(ffi::udev_device_get_subsystem(self.device)) } |
| 395 | } |
| 396 | |
| 397 | /// Returns the kernel device name for the device. |
| 398 | /// |
| 399 | /// The sysname is a string that differentiates the device from others in the same subsystem. |
| 400 | /// For example, `tty0` is the sysname for a TTY device that differentiates it from others, |
| 401 | /// such as `tty1`. |
| 402 | pub fn sysname(&self) -> &OsStr { |
| 403 | unsafe { util::ptr_to_os_str_unchecked(ffi::udev_device_get_sysname(self.device)) } |
| 404 | } |
| 405 | |
| 406 | /// Returns the instance number of the device. |
| 407 | /// |
| 408 | /// The instance number is used to differentiate many devices of the same type. For example, |
| 409 | /// `/dev/tty0` and `/dev/tty1` are both TTY devices but have instance numbers of 0 and 1, |
| 410 | /// respectively. |
| 411 | /// |
| 412 | /// Some devices don't have instance numbers, such as `/dev/console`, in which case the method |
| 413 | /// returns `None`. |
| 414 | pub fn sysnum(&self) -> Option<usize> { |
| 415 | let ptr = unsafe { ffi::udev_device_get_sysnum(self.device) }; |
| 416 | |
| 417 | if ptr.is_null() { |
| 418 | return None; |
| 419 | } |
| 420 | |
| 421 | match str::from_utf8(unsafe { CStr::from_ptr(ptr) }.to_bytes()) { |
| 422 | Err(_) => None, |
| 423 | Ok(s) => FromStr::from_str(s).ok(), |
| 424 | } |
| 425 | } |
| 426 | |
| 427 | /// Returns the devtype name of the device (if any), for example "disk". |
| 428 | pub fn devtype(&self) -> Option<&OsStr> { |
| 429 | unsafe { util::ptr_to_os_str(ffi::udev_device_get_devtype(self.device)) } |
| 430 | } |
| 431 | |
| 432 | /// Returns the name of the kernel driver attached to the device. |
| 433 | pub fn driver(&self) -> Option<&OsStr> { |
| 434 | unsafe { util::ptr_to_os_str(ffi::udev_device_get_driver(self.device)) } |
| 435 | } |
| 436 | |
| 437 | /// Retrieves the value of a device property. |
| 438 | pub fn property_value<T: AsRef<OsStr>>(&self, property: T) -> Option<&OsStr> { |
| 439 | let prop = match util::os_str_to_cstring(property) { |
| 440 | Ok(s) => s, |
| 441 | Err(_) => return None, |
| 442 | }; |
| 443 | |
| 444 | unsafe { |
| 445 | util::ptr_to_os_str(ffi::udev_device_get_property_value( |
| 446 | self.device, |
| 447 | prop.as_ptr(), |
| 448 | )) |
| 449 | } |
| 450 | } |
| 451 | |
| 452 | /// Retrieves the value of a device attribute. |
| 453 | pub fn attribute_value<T: AsRef<OsStr>>(&self, attribute: T) -> Option<&OsStr> { |
| 454 | let attr = match util::os_str_to_cstring(attribute) { |
| 455 | Ok(s) => s, |
| 456 | Err(_) => return None, |
| 457 | }; |
| 458 | |
| 459 | unsafe { |
| 460 | util::ptr_to_os_str(ffi::udev_device_get_sysattr_value( |
| 461 | self.device, |
| 462 | attr.as_ptr(), |
| 463 | )) |
| 464 | } |
| 465 | } |
| 466 | |
| 467 | /// Sets the value of a device attribute. |
| 468 | pub fn set_attribute_value<T: AsRef<OsStr>, U: AsRef<OsStr>>( |
| 469 | &mut self, |
| 470 | attribute: T, |
| 471 | value: U, |
| 472 | ) -> Result<()> { |
| 473 | let attribute = util::os_str_to_cstring(attribute)?; |
| 474 | let value = util::os_str_to_cstring(value)?; |
| 475 | |
| 476 | util::errno_to_result(unsafe { |
| 477 | ffi::udev_device_set_sysattr_value( |
| 478 | self.device, |
| 479 | attribute.as_ptr(), |
| 480 | value.as_ptr() as *mut c_char, |
| 481 | ) |
| 482 | }) |
| 483 | } |
| 484 | |
| 485 | /// Returns an iterator over the device's properties. |
| 486 | /// |
| 487 | /// ## Example |
| 488 | /// |
| 489 | /// This example prints out all of a device's properties: |
| 490 | /// |
| 491 | /// ```no_run |
| 492 | /// # use std::path::Path; |
| 493 | /// # let device = udev::Device::from_syspath(Path::new("/sys/devices/virtual/tty/tty0" )).unwrap(); |
| 494 | /// for property in device.properties() { |
| 495 | /// println!("{:?} = {:?}" , property.name(), property.value()); |
| 496 | /// } |
| 497 | /// ``` |
| 498 | pub fn properties(&self) -> Properties { |
| 499 | Properties { |
| 500 | entry: unsafe { ffi::udev_device_get_properties_list_entry(self.device) }, |
| 501 | phantom: PhantomData, |
| 502 | } |
| 503 | } |
| 504 | |
| 505 | /// Returns an iterator over the device's attributes. |
| 506 | /// |
| 507 | /// ## Example |
| 508 | /// |
| 509 | /// This example prints out all of a device's attributes: |
| 510 | /// |
| 511 | /// ```no_run |
| 512 | /// # use std::path::Path; |
| 513 | /// # let device = udev::Device::from_syspath(Path::new("/sys/devices/virtual/tty/tty0" )).unwrap(); |
| 514 | /// for attribute in device.attributes() { |
| 515 | /// println!("{:?} = {:?}" , attribute.name(), attribute.value()); |
| 516 | /// } |
| 517 | /// ``` |
| 518 | pub fn attributes(&self) -> Attributes { |
| 519 | Attributes { |
| 520 | entries: EntryList { |
| 521 | entry: unsafe { ffi::udev_device_get_sysattr_list_entry(self.device) }, |
| 522 | phantom: PhantomData, |
| 523 | }, |
| 524 | device: self, |
| 525 | } |
| 526 | } |
| 527 | |
| 528 | /// Returns the device action for the device. |
| 529 | pub fn action(&self) -> Option<&OsStr> { |
| 530 | unsafe { util::ptr_to_os_str(ffi::udev_device_get_action(self.device)) } |
| 531 | } |
| 532 | } |
| 533 | |
| 534 | impl<'a> Iterator for Attributes<'a> { |
| 535 | type Item = Entry<'a>; |
| 536 | |
| 537 | // The list of sysattr entries only contains the attribute names, with |
| 538 | // the values being empty. To get the value, each has to be queried. |
| 539 | fn next(&mut self) -> Option<Entry<'a>> { |
| 540 | match self.entries.next() { |
| 541 | Some(Entry { name: &OsStr, value: _ }) => Some(Entry { |
| 542 | name, |
| 543 | value: self.device.attribute_value(attribute:name), |
| 544 | }), |
| 545 | None => None, |
| 546 | } |
| 547 | } |
| 548 | |
| 549 | fn size_hint(&self) -> (usize, Option<usize>) { |
| 550 | (0, None) |
| 551 | } |
| 552 | } |
| 553 | |