From fe36f28683c4f76dcabc62726180a09fa320adde Mon Sep 17 00:00:00 2001 From: Jean Pierre Dudey Date: Mon, 23 Apr 2018 15:40:23 -0400 Subject: [PATCH] Initial commit. Signed-off-by: Jean Pierre Dudey --- .gitignore | 4 + Cargo.toml | 2 + mio-udev/Cargo.toml | 9 ++ mio-udev/src/lib.rs | 78 +++++++++++++ mio-udev/src/util.rs | 23 ++++ tokio-udev/Cargo.toml | 17 +++ tokio-udev/examples/usb_hotplug.rs | 29 +++++ tokio-udev/src/lib.rs | 18 +++ tokio-udev/src/monitor.rs | 78 +++++++++++++ udev-sys/Cargo.toml | 7 ++ udev-sys/src/lib.rs | 127 ++++++++++++++++++++++ udev/Cargo.toml | 9 ++ udev/src/context.rs | 64 +++++++++++ udev/src/device.rs | 82 ++++++++++++++ udev/src/lib.rs | 15 +++ udev/src/monitor.rs | 169 +++++++++++++++++++++++++++++ 16 files changed, 731 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 mio-udev/Cargo.toml create mode 100644 mio-udev/src/lib.rs create mode 100644 mio-udev/src/util.rs create mode 100644 tokio-udev/Cargo.toml create mode 100644 tokio-udev/examples/usb_hotplug.rs create mode 100644 tokio-udev/src/lib.rs create mode 100644 tokio-udev/src/monitor.rs create mode 100644 udev-sys/Cargo.toml create mode 100644 udev-sys/src/lib.rs create mode 100644 udev/Cargo.toml create mode 100644 udev/src/context.rs create mode 100644 udev/src/device.rs create mode 100644 udev/src/lib.rs create mode 100644 udev/src/monitor.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a821aa9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ + +/target +**/*.rs.bk +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..040d893 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,2 @@ +[workspace] +members = ["mio-udev", "tokio-udev", "udev-sys", "udev"] diff --git a/mio-udev/Cargo.toml b/mio-udev/Cargo.toml new file mode 100644 index 0000000..1506dbf --- /dev/null +++ b/mio-udev/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "mio-udev" +version = "0.1.0" +authors = ["Jean Pierre Dudey "] + +[dependencies] +udev = { path = "../udev", version = "0.1.0" } +mio = "0.6" +libc = "0.2" diff --git a/mio-udev/src/lib.rs b/mio-udev/src/lib.rs new file mode 100644 index 0000000..c8efc80 --- /dev/null +++ b/mio-udev/src/lib.rs @@ -0,0 +1,78 @@ +#![cfg(target_os = "linux")] + +extern crate udev; +extern crate mio; +extern crate libc; + +mod util; + +use std::io; +use std::os::unix::io::{AsRawFd, RawFd}; + +use mio::{Ready, Poll, PollOpt, Token}; +use mio::event::Evented; +use mio::unix::EventedFd; + +#[derive(Debug)] +pub struct MonitorIo { + monitor: udev::Monitor, +} + +impl MonitorIo { + /// Creates a new monitor io object from an existing udev monitor. + /// + /// # Notes + /// + /// It marks the file descriptor as `FD_CLOEXEC` and sets the `O_NONBLOCK` + /// flag. + pub fn from_monitor(monitor: udev::Monitor) -> io::Result { + use libc::{fcntl, F_GETFD, FD_CLOEXEC, F_SETFD, F_GETFL, F_SETFL, O_NONBLOCK}; + use util::cvt; + + let fd = monitor.as_raw_fd(); + + // Make sure the udev file descriptor is marked as CLOEXEC. + let r = unsafe { cvt(fcntl(fd, F_GETFD))? }; + + if !((r & FD_CLOEXEC) == FD_CLOEXEC) { + unsafe { cvt(fcntl(fd, F_SETFD, r | FD_CLOEXEC))? }; + } + + // Some older versions of udev are not non-blocking by default, + // so make sure this is set + let r = unsafe { cvt(fcntl(fd, F_GETFL))? }; + + if !((r & O_NONBLOCK) == O_NONBLOCK) { + unsafe { cvt(fcntl(fd, F_SETFL, r | O_NONBLOCK))? }; + } + + Ok(MonitorIo { monitor }) + } + + pub fn receive_device(&self) -> io::Result { + self.monitor.receive_device() + } + + #[inline(always)] + fn fd(&self) -> RawFd { + self.monitor.as_raw_fd() + } +} + +impl Evented for MonitorIo { + fn register(&self, poll: &Poll, token: Token, interest: Ready, opts: PollOpt) + -> io::Result<()> + { + EventedFd(&self.fd()).register(poll, token, interest, opts) + } + + fn reregister(&self, poll: &Poll, token: Token, interest: Ready, opts: PollOpt) + -> io::Result<()> + { + EventedFd(&self.fd()).reregister(poll, token, interest, opts) + } + + fn deregister(&self, poll: &Poll) -> io::Result<()> { + EventedFd(&self.fd()).deregister(poll) + } +} diff --git a/mio-udev/src/util.rs b/mio-udev/src/util.rs new file mode 100644 index 0000000..47fb1d1 --- /dev/null +++ b/mio-udev/src/util.rs @@ -0,0 +1,23 @@ +use std::{io, ops::Neg}; + +#[doc(hidden)] +pub trait One { + fn one() -> Self; +} + +macro_rules! one { + ($($t:ident)*) => ($( + impl One for $t { fn one() -> $t { 1 } } + )*) +} + +one! { i8 i16 i32 i64 isize u8 u16 u32 u64 usize } + +pub fn cvt>(t: T) -> io::Result { + let one: T = T::one(); + if t == -one { + Err(io::Error::last_os_error()) + } else { + Ok(t) + } +} diff --git a/tokio-udev/Cargo.toml b/tokio-udev/Cargo.toml new file mode 100644 index 0000000..358f5d5 --- /dev/null +++ b/tokio-udev/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "tokio-udev" +version = "0.1.0" +authors = ["Jean Pierre Dudey "] + +[dependencies] +libc = "0.2" + +mio-udev = { path = "../mio-udev", version = "0.1.0" } +mio = "0.6" +udev = { path = "../udev", version = "0.1.0" } + +futures = "0.1" +tokio-reactor = "0.1" + +[dev-dependencies] +tokio = "0.1" diff --git a/tokio-udev/examples/usb_hotplug.rs b/tokio-udev/examples/usb_hotplug.rs new file mode 100644 index 0000000..10f2bea --- /dev/null +++ b/tokio-udev/examples/usb_hotplug.rs @@ -0,0 +1,29 @@ +extern crate tokio_udev; +extern crate tokio; +extern crate futures; + +use futures::{Future, stream::Stream}; + +use tokio_udev::{USB_SUBSYSTEM, USB_DEVICE, UDEV_MONITOR}; + +fn main() { + let monitor = tokio_udev::Builder::new() + .add_match(USB_SUBSYSTEM, USB_DEVICE) + .build(UDEV_MONITOR) + .expect("couldn't create monitor"); + + let hotplug_stream = monitor.for_each(|device| { + println!("====================="); + println!(" Usb HotPlug Event "); + println!("====================="); + println!("devpath: \"{:?}\"", device.get_devpath()?); + println!("action: \"{}\"", device.get_action()?); + + Ok(()) + }) + .map_err(|e| { + println!("error: {}", e); + }); + + tokio::run(hotplug_stream); +} diff --git a/tokio-udev/src/lib.rs b/tokio-udev/src/lib.rs new file mode 100644 index 0000000..f7f3bbe --- /dev/null +++ b/tokio-udev/src/lib.rs @@ -0,0 +1,18 @@ +#![cfg(target_os = "linux")] + +//! # tokio udev + +extern crate libc; + +extern crate udev; +extern crate mio_udev; +extern crate mio; + +extern crate futures; +extern crate tokio_reactor; + +mod monitor; + +pub use udev::{Subsystem, DeviceType, MonitorName, USB_SUBSYSTEM, USB_DEVICE, UDEV_MONITOR}; + +pub use monitor::{Builder, Monitor}; diff --git a/tokio-udev/src/monitor.rs b/tokio-udev/src/monitor.rs new file mode 100644 index 0000000..08bb0f0 --- /dev/null +++ b/tokio-udev/src/monitor.rs @@ -0,0 +1,78 @@ +use std::io; + +use {mio, udev, mio_udev}; +use tokio_reactor::PollEvented; +use futures::{Async, Poll, stream::Stream}; + +#[derive(Debug)] +pub struct Builder { + match_filters: Vec<(udev::Subsystem, udev::DeviceType)>, +} + +impl Builder { + pub fn new() -> Builder { + Builder { + match_filters: Vec::new(), + } + } + + pub fn add_match(mut self, + subsystem: udev::Subsystem, + devtype: udev::DeviceType) -> Builder { + self.match_filters.push((subsystem, devtype)); + self + } + + pub fn build(self, name: udev::MonitorName) -> io::Result { + let context = udev::Context::new(); + let monitor = udev::Monitor::new_from_netlink(&context, name); + + for filter in self.match_filters { + monitor.filter_add_match_subsystem_devtype(filter.0, filter.1)?; + } + + monitor.enable_receiving()?; + + Monitor::new(monitor) + } +} + +#[derive(Debug)] +pub struct Monitor { + io: PollEvented, +} + +impl Monitor { + pub fn new(monitor: udev::Monitor) -> io::Result { + let io = PollEvented::new(mio_udev::MonitorIo::from_monitor(monitor)?); + Ok(Monitor { io }) + } + + pub fn poll_receive(&self) -> Poll, io::Error> { + if let Async::NotReady = self.io.poll_read_ready(mio::Ready::readable())? { + return Ok(Async::NotReady); + } + + match self.io.get_ref().receive_device() { + Ok(device) => Ok(Async::Ready(Some(device))), + Err(e) => { + if e.kind() == io::ErrorKind::WouldBlock { + self.io.clear_read_ready(mio::Ready::readable())?; + Ok(Async::NotReady) + } else { + Err(e) + } + }, + } + + } +} + +impl Stream for Monitor { + type Item = udev::Device; + type Error = io::Error; + + fn poll(&mut self) -> Poll, Self::Error> { + self.poll_receive() + } +} diff --git a/udev-sys/Cargo.toml b/udev-sys/Cargo.toml new file mode 100644 index 0000000..a24aba8 --- /dev/null +++ b/udev-sys/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "udev-sys" +version = "0.1.0" +authors = ["Jean Pierre Dudey "] + +[dependencies] +libc = "0.2" diff --git a/udev-sys/src/lib.rs b/udev-sys/src/lib.rs new file mode 100644 index 0000000..bf28f8b --- /dev/null +++ b/udev-sys/src/lib.rs @@ -0,0 +1,127 @@ +#![cfg(target_os = "linux")] +#![allow(non_camel_case_types)] + +extern crate libc; + +use libc::{c_void, c_char, c_int, c_ulonglong, dev_t}; + +pub type udev = c_void; + +#[link(name = "udev")] +extern "C" { + pub fn udev_new() -> *mut udev; + pub fn udev_ref(udev: *mut udev) -> *mut udev; + pub fn udev_unref(udev: *mut udev) -> *mut udev; +} + +pub type udev_monitor = c_void; + +#[link(name = "udev")] +extern "C" { + pub fn udev_monitor_new_from_netlink( + udev: *mut udev, + name: *const c_char, + ) -> *mut udev_monitor; + pub fn udev_monitor_ref(udev_monitor: *mut udev_monitor) -> *mut udev_monitor; + pub fn udev_monitor_unref(udev_monitor: *mut udev_monitor) -> *mut udev_monitor; +} + +#[link(name = "udev")] +extern "C" { + pub fn udev_monitor_filter_update(udev_monitor: *mut udev_monitor) -> c_int; + pub fn udev_monitor_filter_remove(udev_monitor: *mut udev_monitor) -> c_int; + pub fn udev_monitor_filter_add_match_subsystem_devtype( + udev_monitor: *mut udev_monitor, + subsytem: *const c_char, + devtype: *const c_char, + ) -> c_int; + pub fn udev_monitor_filter_add_match_tag( + udev_monitor: *mut udev_monitor, + tag: *const c_char + ) -> c_int; +} + +pub type udev_device = c_void; + +#[link(name = "udev")] +extern "C" { + pub fn udev_monitor_receive_device(udev_monitor: *mut udev_monitor) -> *mut udev_device; + pub fn udev_monitor_enable_receiving(udev_monitor: *mut udev_monitor) -> c_int; + pub fn udev_monitor_set_receive_buffer_size( + udev_monitor: *mut udev_monitor, + size: c_int, + ) -> c_int; + pub fn udev_monitor_get_fd(udev_monitor: *mut udev_monitor) -> c_int; +} + +#[link(name = "udev")] +extern "C" { + pub fn udev_device_new_from_syspath( + udev: *mut udev, + syspath: *const c_char, + ) -> *mut udev_device; + pub fn udev_device_new_from_devnum( + udev: *mut udev, + type_: c_char, + devnum: dev_t, + ) -> *mut udev_device; + pub fn udev_device_new_from_subsystem_sysname( + udev: *mut udev, + subsystem: *const c_char, + sysname: *const c_char, + ) -> *mut udev_device; + pub fn udev_device_new_from_device_id( + udev: *mut udev, + id: *const c_char, + ) -> *mut udev_device; + pub fn udev_device_new_from_environment(udev: *mut udev) -> *mut udev_device; + pub fn udev_device_ref(udev_device: *mut udev_device) -> *mut udev_device; + pub fn udev_device_unref(udev_device: *mut udev_device) -> *mut udev_device; +} + +pub type udev_list_entry = c_void; + +#[link(name = "udev")] +extern "C" { + pub fn udev_device_get_devpath(udev_device: *mut udev_device) -> *const c_char; + pub fn udev_device_get_subsystem(udev_device: *mut udev_device) -> *const c_char; + pub fn udev_device_get_devtype(udev_device: *mut udev_device) -> *const c_char; + pub fn udev_device_get_syspath(udev_device: *mut udev_device) -> *const c_char; + pub fn udev_device_get_sysname(udev_device: *mut udev_device) -> *const c_char; + pub fn udev_device_get_sysnum(udev_device: *mut udev_device) -> *const c_char; + pub fn udev_device_get_devnode(udev_device: *mut udev_device) -> *const c_char; + pub fn udev_device_get_is_initialized(udev_device: *mut udev_device) -> c_int; + pub fn udev_device_get_devlinks_list_entry( + udev_device: *mut udev_device + ) -> *mut udev_list_entry; + pub fn udev_device_get_properties_list_entry( + udev_device: *mut udev_device + ) -> *mut udev_list_entry; + pub fn udev_device_get_tags_list_entry( + udev_device: *mut udev_device + ) -> *mut udev_list_entry; + pub fn udev_device_get_sysattr_list_entry( + udev_device: *mut udev_device + ) -> *mut udev_list_entry; + pub fn udev_device_get_property_value( + udev_device: *mut udev_device, + key: *const c_char + ) -> *const c_char; + pub fn udev_device_get_driver(udev_device: *mut udev_device) -> *const c_char; + pub fn udev_device_get_devnum(udev_device: *mut udev_device) -> dev_t; + pub fn udev_device_get_action(udev_device: *mut udev_device) -> *const c_char; + pub fn udev_device_get_seqnum(udev_device: *mut udev_device) -> c_ulonglong; + pub fn udev_device_get_usec_since_initialized( + udev_device: *mut udev_device + ) -> c_ulonglong; + pub fn udev_device_get_sysattr_value( + udev_device: *mut udev_device, + sysattr: *const c_char + ) -> *const c_char; + pub fn udev_device_set_sysattr_value( + udev_device: *mut udev_device, + sysattr: *const c_char, + value: *mut c_char + ) -> c_int; + pub fn udev_device_has_tag(udev_device: *mut udev_device, tag: *const c_char) -> c_int; +} diff --git a/udev/Cargo.toml b/udev/Cargo.toml new file mode 100644 index 0000000..9011708 --- /dev/null +++ b/udev/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "udev" +version = "0.1.0" +authors = ["Jean Pierre Dudey "] + +[dependencies] +udev-sys = { path = "../udev-sys", version = "0.1" } +libc = "0.2" +log = "0.4" diff --git a/udev/src/context.rs b/udev/src/context.rs new file mode 100644 index 0000000..6850b3e --- /dev/null +++ b/udev/src/context.rs @@ -0,0 +1,64 @@ +use {std::ptr, udev_sys}; + +/// Udev context. +/// +/// This is a ptr to the **udev** library context. This context is reference +/// counted internally so a call to `Context::clone` points to the same +/// `Context`. +#[derive(Debug)] +pub struct Context { + ptr: *mut udev_sys::udev, +} + +unsafe impl Send for Context {} +unsafe impl Sync for Context {} + +impl Context { + /// Creates a new udev context. + /// + /// # Panics + /// + /// This function panics if the returned context ptr is invalid. + pub fn new() -> Context { + trace!("creating new udev context, calling `udev_new`"); + let ptr = unsafe { udev_sys::udev_new() }; + if ptr == ptr::null_mut() { + panic!("udev_new returned `std::ptr::null_mut()`."); + } + + Context { + ptr, + } + } + + pub(crate) fn as_ptr(&self) -> *mut udev_sys::udev { + assert!(self.ptr != ptr::null_mut()); + self.ptr + } +} + +impl Clone for Context { + /// Increments the reference count. + fn clone(&self) -> Context { + trace!("incrementing udev context refence count, calling `udev_ref`"); + + assert!(self.ptr != ptr::null_mut()); + let ptr = unsafe { udev_sys::udev_ref(self.ptr) }; + assert!(ptr != ptr::null_mut()); + + Context { + ptr, + } + } +} + +impl Drop for Context { + /// Decrements the reference count, once it reaches 0 it's dropped. + fn drop(&mut self) { + trace!("dropping udev context, calling `udev_unref`"); + + if self.ptr != ptr::null_mut() { + unsafe { udev_sys::udev_unref(self.ptr) }; + } + } +} diff --git a/udev/src/device.rs b/udev/src/device.rs new file mode 100644 index 0000000..e3c28c0 --- /dev/null +++ b/udev/src/device.rs @@ -0,0 +1,82 @@ +use std::{io, ptr, path::PathBuf, str::FromStr}; + +use udev_sys; + +macro_rules! call_cstring { + ($call:expr) => { + { + let r = unsafe { $call }; + if r == $crate::std::ptr::null_mut() { + Err($crate::std::io::Error::new($crate::std::io::ErrorKind::Other, + concat!("call to `", stringify!($fn), "` failed"))) + } else { + Ok(unsafe { $crate::std::ffi::CStr::from_ptr(r).to_owned() }) + } + } + } +} + +/// An Udev device. +#[derive(Debug)] +pub struct Device { + ptr: *mut udev_sys::udev_device, +} + +unsafe impl Send for Device {} +unsafe impl Sync for Device {} + +impl Device { + #[doc(hidden)] + pub fn from_raw_part(ptr: *mut udev_sys::udev_device) -> Device { + assert!(ptr != ptr::null_mut()); + + Device { ptr } + } + + pub fn get_devpath(&self) -> io::Result { + assert!(self.ptr != ptr::null_mut()); + + call_cstring!(udev_sys::udev_device_get_devpath(self.ptr))? + .to_str() + .map_err(|e| { + io::Error::new(io::ErrorKind::Other, Box::new(e)) + }) + .and_then(|devpath_str| { + PathBuf::from_str(devpath_str) + .map_err(|_| io::Error::new(io::ErrorKind::Other, "couldn't parse path")) + }) + } + + pub fn get_action(&self) -> io::Result { + assert!(self.ptr != ptr::null_mut()); + + call_cstring!(udev_sys::udev_device_get_devpath(self.ptr))? + .to_str() + .map_err(|e| { + io::Error::new(io::ErrorKind::Other, Box::new(e)) + }) + .map(|action| { + action.to_string() + }) + } +} + +impl Clone for Device { + fn clone(&self) -> Device { + assert!(self.ptr != ptr::null_mut()); + let ptr = unsafe { udev_sys::udev_device_ref(self.ptr) }; + assert!(ptr != ptr::null_mut()); + + Device { + ptr, + } + } +} + +impl Drop for Device { + fn drop(&mut self) { + if self.ptr != ptr::null_mut() { + unsafe { udev_sys::udev_device_unref(self.ptr) }; + } + } +} diff --git a/udev/src/lib.rs b/udev/src/lib.rs new file mode 100644 index 0000000..cab6c2a --- /dev/null +++ b/udev/src/lib.rs @@ -0,0 +1,15 @@ +#![cfg(target_os = "linux")] + +#[macro_use] +extern crate log; + +extern crate udev_sys; +extern crate libc; + +mod context; +mod device; +mod monitor; + +pub use context::Context; +pub use device::Device; +pub use monitor::{Monitor, MonitorName, Subsystem, DeviceType, UDEV_MONITOR, USB_SUBSYSTEM, USB_DEVICE}; diff --git a/udev/src/monitor.rs b/udev/src/monitor.rs new file mode 100644 index 0000000..c16707c --- /dev/null +++ b/udev/src/monitor.rs @@ -0,0 +1,169 @@ +use std::{io, ptr}; +use std::os::unix::io::{AsRawFd, RawFd}; + +use {context::Context, device::Device, udev_sys}; +use libc::c_char; + +/// Name of an udev monitor. +#[derive(Debug)] +pub struct MonitorName(&'static [u8]); + +impl MonitorName { + /// The name as a nul terminated pointer to C string. + fn as_ptr(&self) -> *const u8 { + self.0.as_ptr() + } +} + +/// Udev monitor name (a.k.a `"udev"`). +pub const UDEV_MONITOR: MonitorName = MonitorName(b"udev\0"); + +/// Udev subsystem. +#[derive(Debug)] +pub struct Subsystem(&'static [u8]); + +impl Subsystem { + /// The name as a nul terminated pointer to C string. + fn as_ptr(&self) -> *const u8 { + self.0.as_ptr() + } +} + +/// USB udev subsytem (a.k.a `"usb"`). +pub const USB_SUBSYSTEM: Subsystem = Subsystem(b"usb\0"); + +/// Udev device type. +#[derive(Debug)] +pub struct DeviceType(&'static [u8]); + +impl DeviceType { + /// The name as a nul terminated pointer to C string. + fn as_ptr(&self) -> *const u8 { + self.0.as_ptr() + } +} + +/// USB device type (a.k.a `"usb_device"`) +pub const USB_DEVICE: DeviceType = DeviceType(b"usb_device\0"); + +/// An udev monitor. +#[derive(Debug)] +pub struct Monitor { + ptr: *mut udev_sys::udev_monitor, +} + +unsafe impl Send for Monitor {} +unsafe impl Sync for Monitor {} + +impl Monitor { + /// Create a new monitor. + /// + /// # Notes + /// + /// This is equivalent to the udev C function + /// `udev_monitor_new_from_netlink`. + pub fn new_from_netlink(context: &Context, name: MonitorName) -> Monitor { + trace!("creating new monitor, calling `udev_monitor_new_from_netlink`."); + + let name_ptr = name.as_ptr() as *const c_char; + let ptr = unsafe { + udev_sys::udev_monitor_new_from_netlink(context.as_ptr(), name_ptr) + }; + + if ptr == ptr::null_mut() { + panic!("`udev_monitor_new_from_netlink` returned `std::ptr::null_mut()`."); + } + + Monitor { + ptr, + } + } + + // TODO: documentation. + pub fn filter_add_match_subsystem_devtype(&self, + subsystem: Subsystem, + devtype: DeviceType) -> io::Result<()> { + assert!(self.ptr != ptr::null_mut()); + + let subsystem_ptr = subsystem.as_ptr() as *const c_char; + let devtype_ptr = devtype.as_ptr() as *const c_char; + + let r = unsafe { + udev_sys::udev_monitor_filter_add_match_subsystem_devtype(self.ptr, + subsystem_ptr, + devtype_ptr) + }; + + if r < 0 { + return Err(io::Error::new(io::ErrorKind::Other, + "couldn't add new subsystem-devtype match filter")); + } + + Ok(()) + } + + pub fn enable_receiving(&self) -> io::Result<()> { + assert!(self.ptr != ptr::null_mut()); + + let r = unsafe { + udev_sys::udev_monitor_enable_receiving(self.ptr) + }; + + if r < 0 { + return Err(io::Error::new(io::ErrorKind::Other, + "couldn't enable receiving on udev monitor")); + } + + Ok(()) + } + + pub fn receive_device(&self) -> io::Result { + assert!(self.ptr != ptr::null_mut()); + + let r = unsafe { + udev_sys::udev_monitor_receive_device(self.ptr) + }; + + if r == ptr::null_mut() { + return Err(io::Error::new(io::ErrorKind::WouldBlock, + "couldn't receive device from monitor")); + } + + Ok(Device::from_raw_part(r)) + } +} + +impl Clone for Monitor { + /// Increments the reference count of the `Monitor`. + fn clone(&self) -> Monitor { + trace!("incrementing reference count."); + assert!(self.ptr != ptr::null_mut()); + let ptr = unsafe { udev_sys::udev_monitor_ref(self.ptr) }; + assert!(ptr != ptr::null_mut()); + + Monitor { + ptr, + } + } +} + +impl Drop for Monitor { + /// Decrements the reference count, once it reaches 0 it's dropped. + fn drop(&mut self) { + if self.ptr != ptr::null_mut() { + unsafe { udev_sys::udev_monitor_unref(self.ptr) }; + } else { + trace!("monitor is already null."); + } + } +} + +impl AsRawFd for Monitor { + fn as_raw_fd(&self) -> RawFd { + assert!(self.ptr != ptr::null_mut()); + + unsafe { + udev_sys::udev_monitor_get_fd(self.ptr) as RawFd + } + } +}