diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /third_party/rust/libudev/src | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/libudev/src')
-rw-r--r-- | third_party/rust/libudev/src/context.rs | 47 | ||||
-rw-r--r-- | third_party/rust/libudev/src/device.rs | 340 | ||||
-rw-r--r-- | third_party/rust/libudev/src/enumerator.rs | 171 | ||||
-rw-r--r-- | third_party/rust/libudev/src/error.rs | 76 | ||||
-rw-r--r-- | third_party/rust/libudev/src/handle.rs | 7 | ||||
-rw-r--r-- | third_party/rust/libudev/src/lib.rs | 29 | ||||
-rw-r--r-- | third_party/rust/libudev/src/monitor.rs | 211 | ||||
-rw-r--r-- | third_party/rust/libudev/src/util.rs | 33 |
8 files changed, 914 insertions, 0 deletions
diff --git a/third_party/rust/libudev/src/context.rs b/third_party/rust/libudev/src/context.rs new file mode 100644 index 0000000000..95252611e4 --- /dev/null +++ b/third_party/rust/libudev/src/context.rs @@ -0,0 +1,47 @@ +use std::path::Path; + +use ::device::{Device}; +use ::handle::{Handle}; + +/// A libudev context. +pub struct Context { + udev: *mut ::ffi::udev +} + +impl Drop for Context { + fn drop(&mut self) { + unsafe { + ::ffi::udev_unref(self.udev); + } + } +} + +#[doc(hidden)] +impl Handle<::ffi::udev> for Context { + fn as_ptr(&self) -> *mut ::ffi::udev { + self.udev + } +} + +impl Context { + /// Creates a new context. + pub fn new() -> ::Result<Self> { + let ptr = try_alloc!(unsafe { ::ffi::udev_new() }); + + Ok(Context { udev: ptr }) + } + + /// Creates a device for a given syspath. + /// + /// The `syspath` parameter should be a path to the device file within the `sysfs` file system, + /// e.g., `/sys/devices/virtual/tty/tty0`. + pub fn device_from_syspath(&self, syspath: &Path) -> ::Result<Device> { + let syspath = try!(::util::os_str_to_cstring(syspath)); + + let ptr = try_alloc!(unsafe { + ::ffi::udev_device_new_from_syspath(self.udev, syspath.as_ptr()) + }); + + Ok(::device::new(self, ptr)) + } +} diff --git a/third_party/rust/libudev/src/device.rs b/third_party/rust/libudev/src/device.rs new file mode 100644 index 0000000000..736e7b58ff --- /dev/null +++ b/third_party/rust/libudev/src/device.rs @@ -0,0 +1,340 @@ +use std::str; + +use std::ffi::{CStr,OsStr}; +use std::path::Path; +use std::str::FromStr; + +use libc::{c_char,dev_t}; + +use ::context::Context; +use ::handle::*; + +pub fn new(context: &Context, device: *mut ::ffi::udev_device) -> Device { + Device { + _context: context, + device: device, + } +} + +/// A structure that provides access to sysfs/kernel devices. +pub struct Device<'a> { + _context: &'a Context, + device: *mut ::ffi::udev_device, +} + +impl<'a> Drop for Device<'a> { + fn drop(&mut self) { + unsafe { + ::ffi::udev_device_unref(self.device); + } + } +} + +#[doc(hidden)] +impl<'a> Handle<::ffi::udev_device> for Device<'a> { + fn as_ptr(&self) -> *mut ::ffi::udev_device { + self.device + } +} + +impl<'a> Device<'a> { + /// Checks whether the device has already been handled by udev. + /// + /// When a new device is connected to the system, udev initializes the device by setting + /// permissions, renaming network devices, and possibly other initialization routines. This + /// method returns `true` if udev has performed all of its work to initialize this device. + /// + /// This method only applies to devices with device nodes or network interfaces. All other + /// devices return `true` by default. + pub fn is_initialized(&self) -> bool { + unsafe { + ::ffi::udev_device_get_is_initialized(self.device) > 0 + } + } + + /// Gets the device's major/minor number. + pub fn devnum(&self) -> Option<dev_t> { + match unsafe { ::ffi::udev_device_get_devnum(self.device) } { + 0 => None, + n => Some(n) + } + } + + /// Returns the syspath of the device. + /// + /// The path is an absolute path and includes the sys mount point. For example, the syspath for + /// `tty0` could be `/sys/devices/virtual/tty/tty0`, which includes the sys mount point, + /// `/sys`. + pub fn syspath(&self) -> &Path { + Path::new(unsafe { + ::util::ptr_to_os_str_unchecked(::ffi::udev_device_get_syspath(self.device)) + }) + } + + /// Returns the kernel devpath value of the device. + /// + /// The path does not contain the sys mount point, but does start with a `/`. For example, the + /// devpath for `tty0` could be `/devices/virtual/tty/tty0`. + pub fn devpath(&self) -> &OsStr { + unsafe { + ::util::ptr_to_os_str_unchecked(::ffi::udev_device_get_devpath(self.device)) + } + } + + /// Returns the path to the device node belonging to the device. + /// + /// The path is an absolute path and starts with the device directory. For example, the device + /// node for `tty0` could be `/dev/tty0`. + pub fn devnode(&self) -> Option<&Path> { + ::util::ptr_to_os_str(unsafe { ::ffi::udev_device_get_devnode(self.device) }).map(|path| { + Path::new(path) + }) + } + + /// Returns the parent of the device. + pub fn parent(&self) -> Option<Device> { + let ptr = unsafe { ::ffi::udev_device_get_parent(self.device) }; + + if !ptr.is_null() { + unsafe { + ::ffi::udev_device_ref(ptr); + } + + Some(Device { + _context: self._context, + device: ptr, + }) + } + else { + None + } + } + + /// Returns the subsystem name of the device. + /// + /// The subsystem name is a string that indicates which kernel subsystem the device belongs to. + /// Examples of subsystem names are `tty`, `vtconsole`, `block`, `scsi`, and `net`. + pub fn subsystem(&self) -> &OsStr { + unsafe { + ::util::ptr_to_os_str_unchecked(::ffi::udev_device_get_subsystem(self.device)) + } + } + + /// Returns the kernel device name for the device. + /// + /// The sysname is a string that differentiates the device from others in the same subsystem. + /// For example, `tty0` is the sysname for a TTY device that differentiates it from others, + /// such as `tty1`. + pub fn sysname(&self) -> &OsStr { + unsafe { + ::util::ptr_to_os_str_unchecked(::ffi::udev_device_get_sysname(self.device)) + } + } + + /// Returns the instance number of the device. + /// + /// The instance number is used to differentiate many devices of the same type. For example, + /// `/dev/tty0` and `/dev/tty1` are both TTY devices but have instance numbers of 0 and 1, + /// respectively. + /// + /// Some devices don't have instance numbers, such as `/dev/console`, in which case the method + /// returns `None`. + pub fn sysnum(&self) -> Option<usize> { + let ptr = unsafe { ::ffi::udev_device_get_sysnum(self.device) }; + + if !ptr.is_null() { + match str::from_utf8(unsafe { CStr::from_ptr(ptr) }.to_bytes()) { + Err(_) => None, + Ok(s) => FromStr::from_str(s).ok() + } + } + else { + None + } + } + + /// Returns the devtype name of the device. + pub fn devtype(&self) -> Option<&OsStr> { + ::util::ptr_to_os_str(unsafe { ::ffi::udev_device_get_devtype(self.device) }) + } + + /// Returns the name of the kernel driver attached to the device. + pub fn driver(&self) -> Option<&OsStr> { + ::util::ptr_to_os_str(unsafe { ::ffi::udev_device_get_driver(self.device) }) + } + + /// Retreives the value of a device property. + pub fn property_value<T: AsRef<OsStr>>(&self, property: T) -> Option<&OsStr> { + let prop = match ::util::os_str_to_cstring(property) { + Ok(s) => s, + Err(_) => return None + }; + + ::util::ptr_to_os_str(unsafe { + ::ffi::udev_device_get_property_value(self.device, prop.as_ptr()) + }) + } + + /// Retreives the value of a device attribute. + pub fn attribute_value<T: AsRef<OsStr>>(&self, attribute: T) -> Option<&OsStr> { + let attr = match ::util::os_str_to_cstring(attribute) { + Ok(s) => s, + Err(_) => return None + }; + + ::util::ptr_to_os_str(unsafe { + ::ffi::udev_device_get_sysattr_value(self.device, attr.as_ptr()) + }) + } + + /// Sets the value of a device attribute. + pub fn set_attribute_value<T: AsRef<OsStr>, U: AsRef<OsStr>>(&mut self, attribute: T, value: U) -> ::Result<()> { + let attribute = try!(::util::os_str_to_cstring(attribute)); + let value = try!(::util::os_str_to_cstring(value)); + + ::util::errno_to_result(unsafe { + ::ffi::udev_device_set_sysattr_value(self.device, attribute.as_ptr(), value.as_ptr() as *mut c_char) + }) + } + + /// Returns an iterator over the device's properties. + /// + /// ## Example + /// + /// This example prints out all of a device's properties: + /// + /// ```no_run + /// # use std::path::Path; + /// # let mut context = libudev::Context::new().unwrap(); + /// # let device = context.device_from_syspath(Path::new("/sys/devices/virtual/tty/tty0")).unwrap(); + /// for property in device.properties() { + /// println!("{:?} = {:?}", property.name(), property.value()); + /// } + /// ``` + pub fn properties(&self) -> Properties { + Properties { + _device: self, + entry: unsafe { ::ffi::udev_device_get_properties_list_entry(self.device) } + } + } + + /// Returns an iterator over the device's attributes. + /// + /// ## Example + /// + /// This example prints out all of a device's attributes: + /// + /// ```no_run + /// # use std::path::Path; + /// # let mut context = libudev::Context::new().unwrap(); + /// # let device = context.device_from_syspath(Path::new("/sys/devices/virtual/tty/tty0")).unwrap(); + /// for attribute in device.attributes() { + /// println!("{:?} = {:?}", attribute.name(), attribute.value()); + /// } + /// ``` + pub fn attributes(&self) -> Attributes { + Attributes { + device: self, + entry: unsafe { ::ffi::udev_device_get_sysattr_list_entry(self.device) } + } + } +} + + +/// Iterator over a device's properties. +pub struct Properties<'a> { + _device: &'a Device<'a>, + entry: *mut ::ffi::udev_list_entry +} + +impl<'a> Iterator for Properties<'a> { + type Item = Property<'a>; + + fn next(&mut self) -> Option<Property<'a>> { + if self.entry.is_null() { + None + } + else { + let name = unsafe { ::util::ptr_to_os_str_unchecked(::ffi::udev_list_entry_get_name(self.entry)) }; + let value = unsafe { ::util::ptr_to_os_str_unchecked(::ffi::udev_list_entry_get_value(self.entry)) }; + + self.entry = unsafe { ::ffi::udev_list_entry_get_next(self.entry) }; + + Some(Property { + name: name, + value: value + }) + } + } + + fn size_hint(&self) -> (usize, Option<usize>) { + (0, None) + } +} + +/// A device property. +pub struct Property<'a> { + name: &'a OsStr, + value: &'a OsStr +} + +impl<'a> Property<'a> { + /// Returns the property name. + pub fn name(&self) -> &OsStr { + self.name + } + + /// Returns the property value. + pub fn value(&self) -> &OsStr { + self.value + } +} + + +/// Iterator over a device's attributes. +pub struct Attributes<'a> { + device: &'a Device<'a>, + entry: *mut ::ffi::udev_list_entry +} + +impl<'a> Iterator for Attributes<'a> { + type Item = Attribute<'a>; + + fn next(&mut self) -> Option<Attribute<'a>> { + if !self.entry.is_null() { + let name = unsafe { ::util::ptr_to_os_str_unchecked(::ffi::udev_list_entry_get_name(self.entry)) }; + + self.entry = unsafe { ::ffi::udev_list_entry_get_next(self.entry) }; + + Some(Attribute { + device: self.device, + name: name + }) + } + else { + None + } + } + + fn size_hint(&self) -> (usize, Option<usize>) { + (0, None) + } +} + +/// A device attribute. +pub struct Attribute<'a> { + device: &'a Device<'a>, + name: &'a OsStr +} + +impl<'a> Attribute<'a> { + /// Returns the attribute name. + pub fn name(&self) -> &OsStr { + self.name + } + + /// Returns the attribute value. + pub fn value(&self) -> Option<&OsStr> { + self.device.attribute_value(self.name) + } +} diff --git a/third_party/rust/libudev/src/enumerator.rs b/third_party/rust/libudev/src/enumerator.rs new file mode 100644 index 0000000000..641d68a2c1 --- /dev/null +++ b/third_party/rust/libudev/src/enumerator.rs @@ -0,0 +1,171 @@ +use std::ffi::{OsStr}; +use std::path::Path; + +use ::handle::prelude::*; + +pub use context::{Context}; +pub use device::{Device}; + + +/// An enumeration context. +/// +/// An Enumerator scans `/sys` for devices matching its filters. Filters are added to an Enumerator +/// by calling its `match_*` and `nomatch_*` methods. After the filters are setup, the +/// `scan_devices()` method finds devices in `/sys` that match the filters. +pub struct Enumerator<'a> { + context: &'a Context, + enumerator: *mut ::ffi::udev_enumerate +} + +impl<'a> Drop for Enumerator<'a> { + fn drop(&mut self) { + unsafe { ::ffi::udev_enumerate_unref(self.enumerator) }; + } +} + +impl<'a> Enumerator<'a> { + /// Creates a new Enumerator. + pub fn new(context: &'a Context) -> ::Result<Self> { + let ptr = try_alloc!(unsafe { ::ffi::udev_enumerate_new(context.as_ptr()) }); + + Ok(Enumerator { + context: context, + enumerator: ptr + }) + } + + /// Adds a filter that matches only initialized devices. + pub fn match_is_initialized(&mut self) -> ::Result<()> { + ::util::errno_to_result(unsafe { + ::ffi::udev_enumerate_add_match_is_initialized(self.enumerator) + }) + } + + /// Adds a filter that matches only devices that belong to the given kernel subsystem. + pub fn match_subsystem<T: AsRef<OsStr>>(&mut self, subsystem: T) -> ::Result<()> { + let subsystem = try!(::util::os_str_to_cstring(subsystem)); + + ::util::errno_to_result(unsafe { + ::ffi::udev_enumerate_add_match_subsystem(self.enumerator, subsystem.as_ptr()) + }) + } + + /// Adds a filter that matches only devices with the given attribute value. + pub fn match_attribute<T: AsRef<OsStr>, U: AsRef<OsStr>>(&mut self, attribute: T, value: U) -> ::Result<()> { + let attribute = try!(::util::os_str_to_cstring(attribute)); + let value = try!(::util::os_str_to_cstring(value)); + + ::util::errno_to_result(unsafe { + ::ffi::udev_enumerate_add_match_sysattr(self.enumerator, attribute.as_ptr(), value.as_ptr()) + }) + } + + /// Adds a filter that matches only devices with the given kernel device name. + pub fn match_sysname<T: AsRef<OsStr>>(&mut self, sysname: T) -> ::Result<()> { + let sysname = try!(::util::os_str_to_cstring(sysname)); + + ::util::errno_to_result(unsafe { + ::ffi::udev_enumerate_add_match_sysname(self.enumerator, sysname.as_ptr()) + }) + } + + /// Adds a filter that matches only devices with the given property value. + pub fn match_property<T: AsRef<OsStr>, U: AsRef<OsStr>>(&mut self, property: T, value: U) -> ::Result<()> { + let property = try!(::util::os_str_to_cstring(property)); + let value = try!(::util::os_str_to_cstring(value)); + + ::util::errno_to_result(unsafe { + ::ffi::udev_enumerate_add_match_property(self.enumerator, property.as_ptr(), value.as_ptr()) + }) + } + + /// Adds a filter that matches only devices with the given tag. + pub fn match_tag<T: AsRef<OsStr>>(&mut self, tag: T) -> ::Result<()> { + let tag = try!(::util::os_str_to_cstring(tag)); + + ::util::errno_to_result(unsafe { + ::ffi::udev_enumerate_add_match_tag(self.enumerator, tag.as_ptr()) + }) + } + + /// Includes the parent device and all devices in the subtree of the parent device. + pub fn match_parent(&mut self, parent: &Device) -> ::Result<()> { + ::util::errno_to_result(unsafe { + ::ffi::udev_enumerate_add_match_parent(self.enumerator, parent.as_ptr()) + }) + } + + /// Adds a filter that matches only devices that don't belong to the given kernel subsystem. + pub fn nomatch_subsystem<T: AsRef<OsStr>>(&mut self, subsystem: T) -> ::Result<()> { + let subsystem = try!(::util::os_str_to_cstring(subsystem)); + + ::util::errno_to_result(unsafe { + ::ffi::udev_enumerate_add_nomatch_subsystem(self.enumerator, subsystem.as_ptr()) + }) + } + + /// Adds a filter that matches only devices that don't have the the given attribute value. + pub fn nomatch_attribute<T: AsRef<OsStr>, U: AsRef<OsStr>>(&mut self, attribute: T, value: U) -> ::Result<()> { + let attribute = try!(::util::os_str_to_cstring(attribute)); + let value = try!(::util::os_str_to_cstring(value)); + + ::util::errno_to_result(unsafe { + ::ffi::udev_enumerate_add_nomatch_sysattr(self.enumerator, attribute.as_ptr(), value.as_ptr()) + }) + } + + /// Includes the device with the given syspath. + pub fn add_syspath(&mut self, syspath: &Path) -> ::Result<()> { + let syspath = try!(::util::os_str_to_cstring(syspath)); + + ::util::errno_to_result(unsafe { + ::ffi::udev_enumerate_add_syspath(self.enumerator, syspath.as_ptr()) + }) + } + + /// Scans `/sys` for devices matching the attached filters. + /// + /// The devices will be sorted in dependency order. + pub fn scan_devices(&mut self) -> ::Result<Devices> { + try!(::util::errno_to_result(unsafe { + ::ffi::udev_enumerate_scan_devices(self.enumerator) + })); + + Ok(Devices { + enumerator: self, + entry: unsafe { ::ffi::udev_enumerate_get_list_entry(self.enumerator) } + }) + } +} + + +/// Iterator over devices. +pub struct Devices<'a> { + enumerator: &'a Enumerator<'a>, + entry: *mut ::ffi::udev_list_entry +} + +impl<'a> Iterator for Devices<'a> { + type Item = Device<'a>; + + fn next(&mut self) -> Option<Device<'a>> { + while !self.entry.is_null() { + let syspath = Path::new(unsafe { + ::util::ptr_to_os_str_unchecked(::ffi::udev_list_entry_get_name(self.entry)) + }); + + self.entry = unsafe { ::ffi::udev_list_entry_get_next(self.entry) }; + + match self.enumerator.context.device_from_syspath(syspath) { + Ok(d) => return Some(d), + Err(_) => continue + }; + } + + None + } + + fn size_hint(&self) -> (usize, Option<usize>) { + (0, None) + } +} diff --git a/third_party/rust/libudev/src/error.rs b/third_party/rust/libudev/src/error.rs new file mode 100644 index 0000000000..2c503149c8 --- /dev/null +++ b/third_party/rust/libudev/src/error.rs @@ -0,0 +1,76 @@ +use std::ffi::CStr; +use std::fmt; +use std::io; +use std::str; + +use std::error::Error as StdError; +use std::result::Result as StdResult; + +use ::libc::c_int; + +/// A `Result` type for libudev operations. +pub type Result<T> = StdResult<T,Error>; + +/// Types of errors that occur in libudev. +#[derive(Debug,Clone,Copy,PartialEq,Eq)] +pub enum ErrorKind { + NoMem, + InvalidInput, + Io(io::ErrorKind) +} + +/// The error type for libudev operations. +#[derive(Debug)] +pub struct Error { + errno: c_int, +} + +impl Error { + fn strerror(&self) -> &str { + unsafe { + str::from_utf8_unchecked(CStr::from_ptr(::libc::strerror(self.errno)).to_bytes()) + } + } + + /// Returns the corresponding `ErrorKind` for this error. + pub fn kind(&self) -> ErrorKind { + match self.errno { + ::libc::ENOMEM => ErrorKind::NoMem, + ::libc::EINVAL => ErrorKind::InvalidInput, + errno => ErrorKind::Io(io::Error::from_raw_os_error(errno).kind()), + } + } + + /// Returns a description of the error. + pub fn description(&self) -> &str { + self.strerror() + } +} + +impl fmt::Display for Error { + fn fmt(&self, fmt: &mut fmt::Formatter) -> StdResult<(),fmt::Error> { + fmt.write_str(self.strerror()) + } +} + +impl StdError for Error { + fn description(&self) -> &str { + self.strerror() + } +} + +impl From<Error> for io::Error { + fn from(error: Error) -> io::Error { + let io_error_kind = match error.kind() { + ErrorKind::Io(kind) => kind, + ErrorKind::InvalidInput => io::ErrorKind::InvalidInput, + ErrorKind::NoMem => io::ErrorKind::Other, + }; + + io::Error::new(io_error_kind, error.strerror()) + } +} + +pub fn from_errno(errno: c_int) -> Error { + Error { errno: -errno } +} diff --git a/third_party/rust/libudev/src/handle.rs b/third_party/rust/libudev/src/handle.rs new file mode 100644 index 0000000000..3cfd943a00 --- /dev/null +++ b/third_party/rust/libudev/src/handle.rs @@ -0,0 +1,7 @@ +pub mod prelude { + pub use super::Handle; +} + +pub trait Handle<T> { + fn as_ptr(&self) -> *mut T; +} diff --git a/third_party/rust/libudev/src/lib.rs b/third_party/rust/libudev/src/lib.rs new file mode 100644 index 0000000000..ac6be5c169 --- /dev/null +++ b/third_party/rust/libudev/src/lib.rs @@ -0,0 +1,29 @@ +extern crate libudev_sys as ffi; +extern crate libc; + +pub use context::{Context}; +pub use device::{Device,Properties,Property,Attributes,Attribute}; +pub use enumerator::{Enumerator,Devices}; +pub use error::{Result,Error,ErrorKind}; +pub use monitor::{Monitor,MonitorSocket,EventType,Event}; + +macro_rules! try_alloc { + ($exp:expr) => {{ + let ptr = $exp; + + if ptr.is_null() { + return Err(::error::from_errno(::libc::ENOMEM)); + } + + ptr + }} +} + +mod context; +mod device; +mod enumerator; +mod error; +mod monitor; + +mod handle; +mod util; diff --git a/third_party/rust/libudev/src/monitor.rs b/third_party/rust/libudev/src/monitor.rs new file mode 100644 index 0000000000..836a8736ce --- /dev/null +++ b/third_party/rust/libudev/src/monitor.rs @@ -0,0 +1,211 @@ +use std::fmt; +use std::ptr; + +use std::ffi::{CString,OsStr}; +use std::ops::Deref; +use std::os::unix::io::{RawFd,AsRawFd}; + +use ::context::{Context}; +use ::device::{Device}; +use ::handle::prelude::*; + + +/// Monitors for device events. +/// +/// A monitor communicates with the kernel over a socket. Filtering events is performed efficiently +/// in the kernel, and only events that match the filters are received by the socket. Filters must +/// be setup before listening for events. +pub struct Monitor<'a> { + context: &'a Context, + monitor: *mut ::ffi::udev_monitor +} + +impl<'a> Drop for Monitor<'a> { + fn drop(&mut self) { + unsafe { + ::ffi::udev_monitor_unref(self.monitor); + } + } +} + +impl<'a> Monitor<'a> { + /// Creates a new `Monitor`. + pub fn new(context: &'a Context) -> ::Result<Self> { + let name = CString::new("udev").unwrap(); + + let ptr = try_alloc!(unsafe { + ::ffi::udev_monitor_new_from_netlink(context.as_ptr(), name.as_ptr()) + }); + + Ok(Monitor { + context: context, + monitor: ptr + }) + } + + /// Adds a filter that matches events for devices with the given subsystem. + pub fn match_subsystem<T: AsRef<OsStr>>(&mut self, subsystem: T) -> ::Result<()> { + let subsystem = try!(::util::os_str_to_cstring(subsystem)); + + ::util::errno_to_result(unsafe { + ::ffi::udev_monitor_filter_add_match_subsystem_devtype(self.monitor, subsystem.as_ptr(), ptr::null()) + }) + } + + /// Adds a filter that matches events for devices with the given subsystem and device type. + pub fn match_subsystem_devtype<T: AsRef<OsStr>, U: AsRef<OsStr>>(&mut self, subsystem: T, devtype: U) -> ::Result<()> { + let subsystem = try!(::util::os_str_to_cstring(subsystem)); + let devtype = try!(::util::os_str_to_cstring(devtype)); + + ::util::errno_to_result(unsafe { + ::ffi::udev_monitor_filter_add_match_subsystem_devtype(self.monitor, subsystem.as_ptr(), devtype.as_ptr()) + }) + } + + /// Adds a filter that matches events for devices with the given tag. + pub fn match_tag<T: AsRef<OsStr>>(&mut self, tag: T) -> ::Result<()> { + let tag = try!(::util::os_str_to_cstring(tag)); + + ::util::errno_to_result(unsafe { + ::ffi::udev_monitor_filter_add_match_tag(self.monitor, tag.as_ptr()) + }) + } + + /// Removes all filters currently set on the monitor. + pub fn clear_filters(&mut self) -> ::Result<()> { + ::util::errno_to_result(unsafe { + ::ffi::udev_monitor_filter_remove(self.monitor) + }) + } + + /// Listens for events matching the current filters. + /// + /// This method consumes the `Monitor`. + pub fn listen(self) -> ::Result<MonitorSocket<'a>> { + try!(::util::errno_to_result(unsafe { + ::ffi::udev_monitor_enable_receiving(self.monitor) + })); + + Ok(MonitorSocket { inner: self }) + } +} + + +/// An active monitor that can receive events. +/// +/// The events received by a `MonitorSocket` match the filters setup by the `Monitor` that created +/// the socket. +/// +/// Monitors are initially setup to receive events from the kernel via a nonblocking socket. A +/// variant of `poll()` should be used on the file descriptor returned by the `AsRawFd` trait to +/// wait for new events. +pub struct MonitorSocket<'a> { + inner: Monitor<'a> +} + +/// Provides raw access to the monitor's socket. +impl<'a> AsRawFd for MonitorSocket<'a> { + /// Returns the file descriptor of the monitor's socket. + fn as_raw_fd(&self) -> RawFd { + unsafe { + ::ffi::udev_monitor_get_fd(self.inner.monitor) + } + } +} + +impl<'a> MonitorSocket<'a> { + /// Receives the next available event from the monitor. + /// + /// This method does not block. If no events are available, it returns `None` immediately. + pub fn receive_event<'b>(&'b mut self) -> Option<Event<'a>> { + let device = unsafe { + ::ffi::udev_monitor_receive_device(self.inner.monitor) + }; + + if device.is_null() { + None + } + else { + let device = ::device::new(self.inner.context, device); + + Some(Event { device: device }) + } + } +} + +/// Types of events that can be received from udev. +#[derive(Debug,Clone,Copy,PartialEq,Eq)] +pub enum EventType { + /// A device was added. + Add, + + /// A device changed. + Change, + + /// A device was removed. + Remove, + + /// An unknown event occurred. + Unknown, +} + +impl Default for EventType { + fn default() -> EventType { + EventType::Unknown + } +} + +impl fmt::Display for EventType { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str(match self { + &EventType::Add => "add", + &EventType::Change => "change", + &EventType::Remove => "remove", + &EventType::Unknown => "unknown", + }) + } +} + + +/// An event that indicates a change in device state. +pub struct Event<'a> { + device: Device<'a> +} + +/// Provides access to the device associated with the event. +impl<'a> Deref for Event<'a> { + type Target = Device<'a>; + + fn deref(&self) -> &Device<'a> { + &self.device + } +} + +impl<'a> Event<'a> { + /// Returns the `EventType` corresponding to this event. + pub fn event_type(&self) -> EventType { + let value = match self.device.property_value("ACTION") { + Some(s) => s.to_str(), + None => None + }; + + match value { + Some("add") => EventType::Add, + Some("change") => EventType::Change, + Some("remove") => EventType::Remove, + _ => EventType::Unknown + } + } + + /// Returns the event's sequence number. + pub fn sequence_number(&self) -> u64 { + unsafe { + ::ffi::udev_device_get_seqnum(self.device.as_ptr()) as u64 + } + } + + /// Returns the device associated with this event. + pub fn device(&self) -> &Device { + &self.device + } +} diff --git a/third_party/rust/libudev/src/util.rs b/third_party/rust/libudev/src/util.rs new file mode 100644 index 0000000000..6831d2e14a --- /dev/null +++ b/third_party/rust/libudev/src/util.rs @@ -0,0 +1,33 @@ +use std::slice; +use std::ffi::{CString,OsStr}; + +use ::libc::{c_int,c_char}; + +use std::os::unix::prelude::*; + +pub fn ptr_to_os_str<'a>(ptr: *const c_char) -> Option<&'a OsStr> { + if !ptr.is_null() { + Some(unsafe { ptr_to_os_str_unchecked(ptr) }) + } + else { + None + } +} + +pub unsafe fn ptr_to_os_str_unchecked<'a>(ptr: *const c_char) -> &'a OsStr { + OsStr::from_bytes(slice::from_raw_parts(ptr as *const u8, ::libc::strlen(ptr) as usize)) +} + +pub fn os_str_to_cstring<T: AsRef<OsStr>>(s: T) -> ::Result<CString> { + match CString::new(s.as_ref().as_bytes()) { + Ok(s) => Ok(s), + Err(_) => return Err(::error::from_errno(::libc::EINVAL)) + } +} + +pub fn errno_to_result(errno: c_int) -> ::Result<()> { + match errno { + 0 => Ok(()), + e => Err(::error::from_errno(e)) + } +} |