diff options
Diffstat (limited to 'third_party/rust/libudev')
-rw-r--r-- | third_party/rust/libudev/.cargo-checksum.json | 1 | ||||
-rw-r--r-- | third_party/rust/libudev/Cargo.toml | 15 | ||||
-rw-r--r-- | third_party/rust/libudev/LICENSE | 20 | ||||
-rw-r--r-- | third_party/rust/libudev/README.md | 67 | ||||
-rw-r--r-- | third_party/rust/libudev/examples/list_devices.rs | 45 | ||||
-rw-r--r-- | third_party/rust/libudev/examples/monitor.rs | 70 | ||||
-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 |
14 files changed, 1132 insertions, 0 deletions
diff --git a/third_party/rust/libudev/.cargo-checksum.json b/third_party/rust/libudev/.cargo-checksum.json new file mode 100644 index 0000000000..3507f01577 --- /dev/null +++ b/third_party/rust/libudev/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"Cargo.toml":"a3144a56dea1604c3ad249ff68c7b343c589df72b9de0bde4e1656c7c56edf7e","LICENSE":"871afd9d691846de71e0b83812ba9c7ff00bc7b3ad102dedcaa109f2246d52ad","README.md":"e0585b8675215bc6990055477104786f567dee8ce31a59f0c11421b5ad9529a7","examples/list_devices.rs":"9cb549127f2cad350223646332dc39a674ca765095553cdd2ef47cfd0b108535","examples/monitor.rs":"5631686fe2957f1c8236da9620fea3d3360bb15408827996a1b86d40cf80ba0e","src/context.rs":"26f22d5335aadeb4daa4abfb76363f95651662542c889e2ed5352a1dcebca225","src/device.rs":"01ae029537f8734cfc0e4b33b4f3890595caa707925d339c2914d80f70c8f938","src/enumerator.rs":"1f0a12e2b47fc293aaa5b2a5318aa6a0859772af80ba2195e2d83f465b606988","src/error.rs":"5c219d200b2c6dc0aabb4d916190581922c8ede8361235502eeb1d07b2dcb4a0","src/handle.rs":"86f7cfe4ed8eea20b26322dafb17054b9f42e117ae39725659254808b10b12eb","src/lib.rs":"6556d2be078163881321c27dde73c491b6ecb5fcba4b639942bd111f979fa10f","src/monitor.rs":"f271d92e6e723e0346733c33293119f1df43c46913456e9dcf7a6765d1d3ab78","src/util.rs":"f0e1bc2bdbb68047ed8710ed33d80685e4d3acdb20ccf79b0f45932060ac4bd8"},"package":"ea626d3bdf40a1c5aee3bcd4f40826970cae8d80a8fec934c82a63840094dcfe"}
\ No newline at end of file diff --git a/third_party/rust/libudev/Cargo.toml b/third_party/rust/libudev/Cargo.toml new file mode 100644 index 0000000000..4e3ff1b8b6 --- /dev/null +++ b/third_party/rust/libudev/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "libudev" +version = "0.2.0" +authors = ["David Cuddeback <david.cuddeback@gmail.com>"] +description = "Rust wrapper for libudev" +license = "MIT" +homepage = "https://github.com/dcuddeback/libudev-rs" +repository = "https://github.com/dcuddeback/libudev-rs" +documentation = "http://dcuddeback.github.io/libudev-rs/libudev/" +keywords = ["udev", "hardware", "bindings", "sysfs", "systemd"] +readme = "README.md" + +[dependencies] +libudev-sys = "0.1.3" +libc = "0.2" diff --git a/third_party/rust/libudev/LICENSE b/third_party/rust/libudev/LICENSE new file mode 100644 index 0000000000..19f28b12c2 --- /dev/null +++ b/third_party/rust/libudev/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2015 David Cuddeback + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/third_party/rust/libudev/README.md b/third_party/rust/libudev/README.md new file mode 100644 index 0000000000..a4e9984352 --- /dev/null +++ b/third_party/rust/libudev/README.md @@ -0,0 +1,67 @@ +# Libudev +This crate provides a safe wrapper around the native `libudev` library. It applies the RAII pattern +and Rust lifetimes to ensure safe usage of all `libudev` functionality. The RAII pattern ensures +that all acquired resources are released when they're no longer needed, and Rust lifetimes ensure +that resources are released in a proper order. + +* [Documentation](http://dcuddeback.github.io/libudev-rs/libudev/) + +## Dependencies +In order to use the `libudev` crate, you must have a Linux system with the `libudev` library +installed where it can be found by `pkg-config`. To install `libudev` on Debian-based Linux +distributions, execute the following command: + +``` +sudo apt-get install libudev-dev +``` + +`libudev` is a Linux-specific package. It is not available for Windows, OS X, or other operating +systems. + +### Cross-Compiling +The `libudev` crate can be used when cross-compiling to a foreign target. Details on how to +cross-compile `libudev` are explained in the [`libudev-sys` crate's +README](https://github.com/dcuddeback/libudev-sys#cross-compiling). + +## Usage +Add `libudev` as a dependency in `Cargo.toml`: + +```toml +[dependencies] +libudev = "0.2" +``` + +If you plan to support operating systems other than Linux, you'll need to add `libudev` as a +target-specific dependency: + +```toml +[target.x86_64-unknown-linux-gnu.dependencies] +libudev = "0.2" +``` + +Import the `libudev` crate. The starting point for nearly all `libudev` functionality is to create a +context object. + +```rust +extern crate libudev; + +fn main() { + let context = libudev::Context::new().unwrap(); + let mut enumerator = libudev::Enumerator::new(&context).unwrap(); + + enumerator.match_subsystem("tty").unwrap(); + + for device in enumerator.scan_devices().unwrap() { + println!("found device: {:?}", device.syspath()); + } +} +``` + +## Contributors +* [dcuddeback](https://github.com/dcuddeback) +* [Susurrus](https://github.com/Susurrus) + +## License +Copyright © 2015 David Cuddeback + +Distributed under the [MIT License](LICENSE). diff --git a/third_party/rust/libudev/examples/list_devices.rs b/third_party/rust/libudev/examples/list_devices.rs new file mode 100644 index 0000000000..5ef71765de --- /dev/null +++ b/third_party/rust/libudev/examples/list_devices.rs @@ -0,0 +1,45 @@ +extern crate libudev; + +use std::io; + +fn main() { + let context = libudev::Context::new().unwrap(); + list_devices(&context).unwrap(); +} + +fn list_devices(context: &libudev::Context) -> io::Result<()> { + let mut enumerator = try!(libudev::Enumerator::new(&context)); + + for device in try!(enumerator.scan_devices()) { + println!(""); + println!("initialized: {:?}", device.is_initialized()); + println!(" devnum: {:?}", device.devnum()); + println!(" syspath: {:?}", device.syspath()); + println!(" devpath: {:?}", device.devpath()); + println!(" subsystem: {:?}", device.subsystem()); + println!(" sysname: {:?}", device.sysname()); + println!(" sysnum: {:?}", device.sysnum()); + println!(" devtype: {:?}", device.devtype()); + println!(" driver: {:?}", device.driver()); + println!(" devnode: {:?}", device.devnode()); + + if let Some(parent) = device.parent() { + println!(" parent: {:?}", parent.syspath()); + } + else { + println!(" parent: None"); + } + + println!(" [properties]"); + for property in device.properties() { + println!(" - {:?} {:?}", property.name(), property.value()); + } + + println!(" [attributes]"); + for attribute in device.attributes() { + println!(" - {:?} {:?}", attribute.name(), attribute.value()); + } + } + + Ok(()) +} diff --git a/third_party/rust/libudev/examples/monitor.rs b/third_party/rust/libudev/examples/monitor.rs new file mode 100644 index 0000000000..ad9dedcbd0 --- /dev/null +++ b/third_party/rust/libudev/examples/monitor.rs @@ -0,0 +1,70 @@ +extern crate libudev; +extern crate libc; + +use std::io; +use std::ptr; +use std::thread; +use std::time::Duration; + +use std::os::unix::io::{AsRawFd}; + +use libc::{c_void,c_int,c_short,c_ulong,timespec}; + +#[repr(C)] +struct pollfd { + fd: c_int, + events: c_short, + revents: c_short, +} + +#[repr(C)] +struct sigset_t { + __private: c_void +} + +#[allow(non_camel_case_types)] +type nfds_t = c_ulong; + +const POLLIN: c_short = 0x0001; + +extern "C" { + fn ppoll(fds: *mut pollfd, nfds: nfds_t, timeout_ts: *mut libc::timespec, sigmask: *const sigset_t) -> c_int; +} + +fn main() { + let context = libudev::Context::new().unwrap(); + monitor(&context).unwrap(); +} + +fn monitor(context: &libudev::Context) -> io::Result<()> { + let mut monitor = try!(libudev::Monitor::new(&context)); + + try!(monitor.match_subsystem_devtype("usb", "usb_device")); + let mut socket = try!(monitor.listen()); + + let mut fds = vec!(pollfd { fd: socket.as_raw_fd(), events: POLLIN, revents: 0 }); + + loop { + let result = unsafe { ppoll((&mut fds[..]).as_mut_ptr(), fds.len() as nfds_t, ptr::null_mut(), ptr::null()) }; + + if result < 0 { + return Err(io::Error::last_os_error()); + } + + let event = match socket.receive_event() { + Some(evt) => evt, + None => { + thread::sleep(Duration::from_millis(10)); + continue; + } + }; + + println!("{}: {} {} (subsystem={}, sysname={}, devtype={})", + event.sequence_number(), + event.event_type(), + event.syspath().to_str().unwrap_or("---"), + event.subsystem().to_str().unwrap_or(""), + event.sysname().to_str().unwrap_or(""), + event.devtype().map_or("", |s| { s.to_str().unwrap_or("") })); + } +} 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)) + } +} |