libudev-0.3.0/.cargo_vcs_info.json0000644000000001121400113530400125050ustar { "git": { "sha1": "54d4de489d2908f9b0a4c217e239446cc51ee3e7" } } libudev-0.3.0/.gitignore010064400017500001750000000000241400113446600133100ustar 00000000000000/target /Cargo.lock libudev-0.3.0/.mailmap010064400017500001750000000000611400113446600127420ustar 00000000000000 libudev-0.3.0/.travis.yml010064400017500001750000000006521400113446600134400ustar 00000000000000language: rust rust: - 1.4.0 - stable - beta - nightly before_install: - sudo rm /etc/apt/sources.list.d/*.list - sudo apt-get update -q - sudo apt-get install -y build-essential libudev-dev - pkg-config --list-all - pkg-config --libs libudev - pkg-config --modversion libudev - if [ -d "ci/$TRAVIS_RUST_VERSION" ]; then find "ci/$TRAVIS_RUST_VERSION" -mindepth 1 -maxdepth 1 -exec ln -s '{}' . \; ; fi libudev-0.3.0/CHANGELOG.md010064400017500001750000000053321400113446600131400ustar 00000000000000# Change Log ## 0.3.0 (2020-01-17) This release changes the resource management strategy. Tracking lifetimes of dependent resources makes for a less-ergonomic API. That has been replaced with reference-counting. There are no known backwards-compatibility issues with this change, so existing code should not have to be updated for compatibility with reference-counting. There are, however, some breaking changes to the API that are unrelated to the changes in resource management: 1. `Context::device_from_syspath()` has been moved to `Device::from_syspath()`. Upgrading involves a search-and-replace. Anywhere you have the following code: ```rust context.device_from_syspath(path); ``` should be replaced with: ```rust Device::from_syspath(&context, path); ``` 2. Several methods on `Device` changed their return type to account for the possibility of the C library returning `NULL` pointers: * `Device::syspath()` returns `Option<&Path>` instead of `&Path`. * `Device::devpath()` returns `Option<&OsStr>` instead of `&OsStr`. * `Device::subsystem()` returns `Option<&OsStr>` instead of `&OsStr`. * `Device::sysname()` returns `Option<&OsStr>` instead of `&OsStr`. Client code will have to decide how to handle a `None` return value. ### Added * Implemented `Clone` for `Context`. ### Fixed * Fixed several issues related to lifetimes. * Handled possible `NULL` return from `udev_device_get_syspath()`, `udev_device_get_devpath()`, `udev_device_get_subsystem()`, and `udev_device_get_sysname()`. * Reduced memory footprint of several types to a single pointer. * Reduced unnecessary memory allocations when iterating devices and setting up a monitor. ### Changed * Replaced explicit lifetimes with reference counting. * Moved `Context::device_from_syspath()` to `Device::from_syspath()`. * Changed return type of `Device::syspath()`, `Device::devpath()`, `Device::subsystem()`, and `Device::sysname()` to `Option<...>`. ## 0.2.0 (2016-04-16) ### Added * Added `Device::parent()`. ### Changed * Replaced `c_int` error code with `std::io::ErrorKind`. * Minimum supported version of Rust is now 1.4.0. ## 0.1.2 (2015-11-04) ### Changed * Upgraded libc dependency to v0.2. ## 0.1.1 (2015-10-02) ### Added * Added `Monitor`. * Wrote documentation. ## 0.1.0 (2015-05-07) ### Changed * Bumped `libudev-sys` dependency to v0.1.1, which allows `libudev` to compile on 1.0.0-beta and presumably the imminent 1.0.0 release. ## 0.0.2 (2015-04-14) ### Changed * Removed dependencies on unstable features: * `#![feature(libc)]`: replaced with `libc` crate * `#1[feature(std_misc)]`: replaced `AsOsStr` with `AsRef` * `#![feature(unsafe_destructor)]`: stabilized ## 0.0.1 (2015-04-04) Initial release libudev-0.3.0/CONTRIBUTORS010064400017500001750000000004111400113446600132000ustar 00000000000000Bryant Mairs David Cuddeback Harlan Lieberman-Berg mulhern mulkieran Roope Salmi Wojciech Kluczka libudev-0.3.0/Cargo.lock0000644000000014441400113530400104710ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. [[package]] name = "libc" version = "0.2.82" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89203f3fba0a3795506acaad8ebce3c80c0af93f994d5a1d7a0b1eeb23271929" [[package]] name = "libudev" version = "0.3.0" dependencies = [ "libc", "libudev-sys", ] [[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 = "pkg-config" version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" libudev-0.3.0/Cargo.toml0000644000000020041400113530400105050ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] name = "libudev" version = "0.3.0" authors = ["David Cuddeback "] description = "Rust wrapper for libudev" homepage = "https://github.com/dcuddeback/libudev-rs" documentation = "http://dcuddeback.github.io/libudev-rs/libudev/" readme = "README.md" keywords = ["udev", "hardware", "bindings", "sysfs", "systemd"] license = "MIT" repository = "https://github.com/dcuddeback/libudev-rs" [dependencies.libc] version = "0.2" [dependencies.libudev-sys] version = "0.1.3" libudev-0.3.0/Cargo.toml.orig010064400017500001750000000007301400113446600142130ustar 00000000000000[package] name = "libudev" version = "0.3.0" authors = ["David Cuddeback "] 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" libudev-0.3.0/LICENSE010064400017500001750000000020431325626224400123370ustar 00000000000000Copyright (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. libudev-0.3.0/README.md010064400017500001750000000031571400113446600126110ustar 00000000000000# Libudev This crate provides a safe wrapper around the native `libudev` library. * [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.3" ``` 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.3" ``` 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()); } } ``` ## License Copyright © 2015 David Cuddeback Distributed under the [MIT License](LICENSE). libudev-0.3.0/ci/1.4.0/Cargo.lock010064400017500001750000000013241400113446600142640ustar 00000000000000[root] name = "libudev" version = "0.3.0" dependencies = [ "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", "libudev-sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "libc" version = "0.2.40" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "libudev-sys" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "pkg-config" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" libudev-0.3.0/examples/list_devices.rs010064400017500001750000000027001377721445300162020ustar 00000000000000extern 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(()) } libudev-0.3.0/examples/monitor.rs010064400017500001750000000035301400113446600152000ustar 00000000000000extern 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 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().map_or("", |s| { s.to_str().unwrap_or("") }), event.subsystem().map_or("", |s| { s.to_str().unwrap_or("") }), event.sysname().map_or("", |s| { s.to_str().unwrap_or("") }), event.devtype().map_or("", |s| { s.to_str().unwrap_or("") })); } } libudev-0.3.0/src/context.rs010064400017500001750000000033211400113446600141440ustar 00000000000000use ::handle::Handle; /// A libudev context. Contexts may not be sent or shared between threads. The `libudev(3)` manpage /// says: /// /// > All functions require a libudev context to operate. This context can be create via /// > udev_new(3). It is used to track library state and link objects together. No global state is /// > used by libudev, everything is always linked to a udev context. Furthermore, multiple /// > different udev contexts can be used in parallel by multiple threads. However, a single /// > context must not be accessed by multiple threads in parallel. /// /// In Rust, that means that `Context` is `!Send` and `!Sync`. This means a `Context` must be /// created in the thread where it will be used. Several contexts can exist in separate threads, /// but they can not be sent between threads. /// /// Other types in this library (`Device`, `Enumerator`, `Monitor`, etc.) share a reference to a /// context, which means that these types must also be `!Send` and `!Sync`. pub struct Context { udev: *mut ::ffi::udev, } impl Clone for Context { /// Increments reference count of `libudev` context. fn clone(&self) -> Self { Context { udev: unsafe { ::ffi::udev_ref(self.udev) }, } } } impl Drop for Context { /// Decrements reference count of `libudev` 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 { Ok(Context { udev: try_alloc!(unsafe { ::ffi::udev_new() }), }) } } libudev-0.3.0/src/device.rs010064400017500001750000000252341400113446600137260ustar 00000000000000use std::str; use std::ffi::{CStr, OsStr}; use std::marker::PhantomData; use std::path::Path; use std::str::FromStr; use libc::{c_char, dev_t}; use ::context::Context; use ::handle::Handle; pub unsafe fn from_raw(device: *mut ::ffi::udev_device) -> Device { ::ffi::udev_ref(::ffi::udev_device_get_udev(device)); Device { device: device } } /// A structure that provides access to sysfs/kernel devices. pub struct Device { device: *mut ::ffi::udev_device, } impl Drop for Device { fn drop(&mut self) { unsafe { let udev = ::ffi::udev_device_get_udev(self.device); ::ffi::udev_device_unref(self.device); ::ffi::udev_unref(udev); } } } #[doc(hidden)] impl Handle<::ffi::udev_device> for Device { fn as_ptr(&self) -> *mut ::ffi::udev_device { self.device } } impl Device { /// 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 from_syspath(context: &Context, syspath: &Path) -> ::Result { let syspath = try!(::util::os_str_to_cstring(syspath)); Ok(unsafe { from_raw(try_alloc!( ::ffi::udev_device_new_from_syspath(context.as_ptr(), syspath.as_ptr()) )) }) } /// 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 { 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) -> Option<&Path> { ::util::ptr_to_path(unsafe { ::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) -> Option<&OsStr> { ::util::ptr_to_os_str(unsafe { ::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_path(unsafe { ::ffi::udev_device_get_devnode(self.device) }) } /// Returns the parent of the device. pub fn parent(&self) -> Option { let ptr = unsafe { ::ffi::udev_device_get_parent(self.device) }; if !ptr.is_null() { unsafe { ::ffi::udev_device_ref(ptr); Some(from_raw(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) -> Option<&OsStr> { ::util::ptr_to_os_str(unsafe { ::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) -> Option<&OsStr> { ::util::ptr_to_os_str(unsafe { ::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 { 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()) { Ok(s) => FromStr::from_str(s).ok(), Err(_) => None, } } 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) }) } /// Retrieves the value of a device property. pub fn property_value>(&self, property: T) -> Option<&OsStr> { match ::util::os_str_to_cstring(property) { Ok(prop) => { ::util::ptr_to_os_str(unsafe { ::ffi::udev_device_get_property_value(self.device, prop.as_ptr()) }) }, Err(_) => None, } } /// Retrieves the value of a device attribute. pub fn attribute_value>(&self, attribute: T) -> Option<&OsStr> { match ::util::os_str_to_cstring(attribute) { Ok(attr) => { ::util::ptr_to_os_str(unsafe { ::ffi::udev_device_get_sysattr_value(self.device, attr.as_ptr()) }) }, Err(_) => None, } } /// Sets the value of a device attribute. pub fn set_attribute_value, U: AsRef>(&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 = libudev::Device::from_syspath(&context, 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: PhantomData, 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 = libudev::Device::from_syspath(&context, 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: PhantomData<&'a Device>, entry: *mut ::ffi::udev_list_entry, } impl<'a> Iterator for Properties<'a> { type Item = Property<'a>; fn next(&mut self) -> Option> { if !self.entry.is_null() { unsafe { let name = ::util::ptr_to_os_str_unchecked(::ffi::udev_list_entry_get_name(self.entry)); let value = ::util::ptr_to_os_str_unchecked(::ffi::udev_list_entry_get_value(self.entry)); self.entry = ::ffi::udev_list_entry_get_next(self.entry); Some(Property { name: name, value: value, }) } } else { None } } fn size_hint(&self) -> (usize, Option) { (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, entry: *mut ::ffi::udev_list_entry, } impl<'a> Iterator for Attributes<'a> { type Item = Attribute<'a>; fn next(&mut self) -> Option> { 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) { (0, None) } } /// A device attribute. pub struct Attribute<'a> { device: &'a Device, 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) } } libudev-0.3.0/src/enumerator.rs010064400017500001750000000144621400113446600146510ustar 00000000000000use std::ffi::OsStr; use std::marker::PhantomData; use std::path::Path; use ::context::Context; use ::device::Device; use ::handle::Handle; /// 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 { enumerator: *mut ::ffi::udev_enumerate, } impl Drop for Enumerator { fn drop(&mut self) { unsafe { let udev = ::ffi::udev_enumerate_get_udev(self.enumerator); ::ffi::udev_enumerate_unref(self.enumerator); ::ffi::udev_unref(udev); }; } } impl Enumerator { /// Creates a new Enumerator. pub fn new(context: &Context) -> ::Result { unsafe { let ptr = try_alloc!( ::ffi::udev_enumerate_new(context.as_ptr()) ); ::ffi::udev_ref(context.as_ptr()); Ok(Enumerator { 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>(&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, U: AsRef>(&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>(&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, U: AsRef>(&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>(&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>(&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, U: AsRef>(&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 { try!(::util::errno_to_result(unsafe { ::ffi::udev_enumerate_scan_devices(self.enumerator) })); unsafe { Ok(Devices { _enumerator: PhantomData, udev: ::ffi::udev_enumerate_get_udev(self.enumerator), entry: ::ffi::udev_enumerate_get_list_entry(self.enumerator), }) } } } /// Iterator over devices. pub struct Devices<'a> { _enumerator: PhantomData<&'a Enumerator>, udev: *mut ::ffi::udev, entry: *mut ::ffi::udev_list_entry, } impl<'a> Iterator for Devices<'a> { type Item = Device; fn next(&mut self) -> Option { while !self.entry.is_null() { unsafe { let syspath = ::ffi::udev_list_entry_get_name(self.entry); self.entry = ::ffi::udev_list_entry_get_next(self.entry); let device = ::ffi::udev_device_new_from_syspath(self.udev, syspath); if !device.is_null() { return Some(::device::from_raw(device)); } else { continue; } }; } None } fn size_hint(&self) -> (usize, Option) { (0, None) } } libudev-0.3.0/src/error.rs010064400017500001750000000034111400113446600136110ustar 00000000000000use 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 = StdResult; /// 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 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 } } libudev-0.3.0/src/handle.rs010064400017500001750000000000701400113446600137110ustar 00000000000000pub trait Handle { fn as_ptr(&self) -> *mut T; } libudev-0.3.0/src/lib.rs010064400017500001750000000011031400113446600132220ustar 00000000000000extern 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; libudev-0.3.0/src/monitor.rs010064400017500001750000000135671400113446600141640ustar 00000000000000use std::fmt; use std::ptr; use std::ffi::OsStr; use std::ops::Deref; use std::os::unix::io::{RawFd, AsRawFd}; use ::context::Context; use ::device::Device; use ::handle::Handle; /// 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 { monitor: *mut ::ffi::udev_monitor, } impl Drop for Monitor { fn drop(&mut self) { unsafe { let udev = ::ffi::udev_monitor_get_udev(self.monitor); ::ffi::udev_monitor_unref(self.monitor); ::ffi::udev_unref(udev); } } } impl Monitor { /// Creates a new `Monitor`. pub fn new(context: &Context) -> ::Result { unsafe { let ptr = try_alloc!( ::ffi::udev_monitor_new_from_netlink(context.as_ptr(), b"udev\0".as_ptr() as *mut _) ); ::ffi::udev_ref(context.as_ptr()); Ok(Monitor { monitor: ptr }) } } /// Adds a filter that matches events for devices with the given subsystem. pub fn match_subsystem>(&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, U: AsRef>(&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>(&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 { 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 { inner: Monitor, } /// Provides raw access to the monitor's socket. impl AsRawFd for MonitorSocket { /// 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 MonitorSocket { /// 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(&mut self) -> Option { let device = unsafe { ::ffi::udev_monitor_receive_device(self.inner.monitor) }; if !device.is_null() { Some(Event { device: unsafe { ::device::from_raw(device) }, }) } else { None } } } /// 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 { device: Device, } /// Provides access to the device associated with the event. impl Deref for Event { type Target = Device; fn deref(&self) -> &Device { &self.device } } impl Event { /// 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 } } libudev-0.3.0/src/util.rs010064400017500001750000000021311400113446600134330ustar 00000000000000use std::slice; use std::ffi::{CString, OsStr}; use std::path::Path; use libc::{c_int, c_char}; use std::os::unix::prelude::*; #[inline(always)] pub fn ptr_to_path<'a>(ptr: *const c_char) -> Option<&'a Path> { match ptr_to_os_str(ptr) { Some(path) => Some(Path::new(path)), None => None, } } #[inline(always)] 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 } } #[inline(always)] 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)) } #[inline(always)] pub fn os_str_to_cstring>(s: T) -> ::Result { match CString::new(s.as_ref().as_bytes()) { Ok(s) => Ok(s), Err(_) => return Err(::error::from_errno(::libc::EINVAL)), } } #[inline(always)] pub fn errno_to_result(errno: c_int) -> ::Result<()> { match errno { 0 => Ok(()), e => Err(::error::from_errno(e)), } }