Skip to content

Commit

Permalink
Refactor to use the udev library from crates.io.
Browse files Browse the repository at this point in the history
Signed-off-by: Jean Pierre Dudey <jeandudey@hotmail.com>
  • Loading branch information
jeandudey committed Apr 26, 2018
1 parent fe36f28 commit 35e032e
Show file tree
Hide file tree
Showing 14 changed files with 250 additions and 589 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
[workspace]
members = ["mio-udev", "tokio-udev", "udev-sys", "udev"]
members = ["mio-udev", "tokio-udev"]
4 changes: 3 additions & 1 deletion mio-udev/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
name = "mio-udev"
version = "0.1.0"
authors = ["Jean Pierre Dudey <jeandudey@hotmail.com>"]
license = "Apache-2.0/MIT"
description = "MIO support for udev device events."

[dependencies]
udev = { path = "../udev", version = "0.1.0" }
udev = "0.2"
mio = "0.6"
libc = "0.2"
114 changes: 98 additions & 16 deletions mio-udev/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,31 +1,109 @@
#![cfg(target_os = "linux")]

//! # mio-udev
//!
//! This library implements abstractions around `udev` to make it usable
//! with `mio` event loop.
//!
//! # Usage
//!
//! First put the dependency on your crate's `Cargo.toml`. For example:
//!
//! ```toml
//! [dependencies]
//! mio-udev = "0.1"
//! ```
//!
//! Then import it in your crate root as:
//!
//! ```rust
//! extern crate mio_udev;
//! ```
extern crate udev;
extern crate mio;
extern crate libc;

pub use udev::{Attribute, Attributes, Context, Device, Event, Property,
Properties, Error as UdevError};

mod util;

use std::io;
use std::os::unix::io::{AsRawFd, RawFd};
use std::ffi::OsStr;

use mio::{Ready, Poll, PollOpt, Token};
use mio::event::Evented;
use mio::unix::EventedFd;

#[derive(Debug)]
pub struct MonitorIo {
monitor: udev::Monitor,
/// 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 MonitorBuilder {
builder: udev::MonitorBuilder,
}

impl MonitorIo {
/// Creates a new monitor io object from an existing udev monitor.
///
/// # Notes
impl MonitorBuilder {
/// Creates a new `MonitorSocket`.
#[inline(always)]
pub fn new(context: &Context) -> io::Result<Self> {
Ok(MonitorBuilder { builder: udev::MonitorBuilder::new(context)? })
}

/// Adds a filter that matches events for devices with the given subsystem.
#[inline(always)]
pub fn match_subsystem<T>(&mut self, subsystem: T) -> io::Result<()>
where T: AsRef<OsStr>,
{
Ok(self.builder.match_subsystem::<T>(subsystem)?)
}

/// Adds a filter that matches events for devices with the given subsystem
/// and device type.
#[inline(always)]
pub fn match_subsystem_devtype<T, U>(&mut self,
subsystem: T,
devtype: U) -> io::Result<()>
where T: AsRef<OsStr>,
U: AsRef<OsStr>,
{
Ok(self.builder.match_subsystem_devtype::<T, U>(subsystem, devtype)?)
}

/// Adds a filter that matches events for devices with the given tag.
#[inline(always)]
pub fn match_tag<T>(&mut self, tag: T) -> io::Result<()>
where T: AsRef<OsStr>,
{
Ok(self.builder.match_tag::<T>(tag)?)
}

/// Removes all filters currently set on the monitor.
#[inline(always)]
pub fn clear_filters(&mut self) -> io::Result<()> {
Ok(self.builder.clear_filters()?)
}

/// Listens for events matching the current filters.
///
/// It marks the file descriptor as `FD_CLOEXEC` and sets the `O_NONBLOCK`
/// flag.
pub fn from_monitor(monitor: udev::Monitor) -> io::Result<MonitorIo> {
/// This method consumes the `MonitorBuilder`.
pub fn listen(self) -> io::Result<MonitorSocket> {
Ok(MonitorSocket::new(self.builder.listen()?)?)
}
}

/// A wrapper around an `udev::MonitorSocket` that adds the required `mio`
/// functionality.
pub struct MonitorSocket {
monitor: udev::MonitorSocket,
}

impl MonitorSocket {
fn new(monitor: udev::MonitorSocket) -> io::Result<MonitorSocket> {
use libc::{fcntl, F_GETFD, FD_CLOEXEC, F_SETFD, F_GETFL, F_SETFL, O_NONBLOCK};
use util::cvt;

Expand All @@ -46,11 +124,7 @@ impl MonitorIo {
unsafe { cvt(fcntl(fd, F_SETFL, r | O_NONBLOCK))? };
}

Ok(MonitorIo { monitor })
}

pub fn receive_device(&self) -> io::Result<udev::Device> {
self.monitor.receive_device()
Ok(MonitorSocket { monitor })
}

#[inline(always)]
Expand All @@ -59,7 +133,7 @@ impl MonitorIo {
}
}

impl Evented for MonitorIo {
impl Evented for MonitorSocket {
fn register(&self, poll: &Poll, token: Token, interest: Ready, opts: PollOpt)
-> io::Result<()>
{
Expand All @@ -76,3 +150,11 @@ impl Evented for MonitorIo {
EventedFd(&self.fd()).deregister(poll)
}
}

impl Iterator for MonitorSocket {
type Item = Event;

fn next(&mut self) -> Option<Self::Item> {
self.monitor.next()
}
}
5 changes: 3 additions & 2 deletions tokio-udev/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@
name = "tokio-udev"
version = "0.1.0"
authors = ["Jean Pierre Dudey <jeandudey@hotmail.com>"]
license = "Apache-2.0/MIT"
description = "MIO support for udev device events."

[dependencies]
libc = "0.2"

mio-udev = { path = "../mio-udev", version = "0.1.0" }
mio = "0.6"
udev = { path = "../udev", version = "0.1.0" }
mio-udev = { path = "../mio-udev", version = "0.1" }

futures = "0.1"
tokio-reactor = "0.1"
Expand Down
19 changes: 7 additions & 12 deletions tokio-udev/examples/usb_hotplug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,16 @@ extern crate futures;

use futures::{Future, stream::Stream};

use tokio_udev::{USB_SUBSYSTEM, USB_DEVICE, UDEV_MONITOR};
use tokio_udev::{Context, MonitorBuilder};

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()?);
let context = Context::new().unwrap();
let mut builder = MonitorBuilder::new(&context).unwrap();
builder.match_subsystem_devtype("usb", "usb_device").unwrap();
let monitor = builder.listen().unwrap();

let hotplug_stream = monitor.for_each(|_device| {
println!("Hotplug event!");
Ok(())
})
.map_err(|e| {
Expand Down
144 changes: 138 additions & 6 deletions tokio-udev/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,150 @@
#![cfg(target_os = "linux")]

//! # tokio udev
//! # tokio-udev
//!
//! This library implements an stream of device events from `udev`
//! asynchronously.
//!
//! # Usage
//!
//! First put the dependency on your crate's `Cargo.toml`. For example:
//!
//! ```toml
//! [dependencies]
//! tokio-udev = "0.1"
//! ```
//!
//! Then import it in your crate root as:
//!
//! ```rust
//! extern crate tokio_udev;
//! ```
extern crate libc;

extern crate udev;
extern crate mio_udev;
extern crate mio;
extern crate mio_udev;

extern crate futures;
extern crate tokio_reactor;

mod monitor;
pub use mio_udev::{Attribute, Attributes, Context, Device, Event, Property,
Properties, UdevError};

use std::io;
use std::ffi::OsStr;
use std::sync::Mutex;

use tokio_reactor::PollEvented;
use futures::{Async, Poll, stream::Stream};

/// 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 MonitorBuilder {
builder: mio_udev::MonitorBuilder,
}

impl MonitorBuilder {
/// Creates a new `MonitorSocket`.
#[inline(always)]
pub fn new(context: &mio_udev::Context) -> io::Result<Self> {
Ok(MonitorBuilder { builder: mio_udev::MonitorBuilder::new(context)? })
}

/// Adds a filter that matches events for devices with the given subsystem.
#[inline(always)]
pub fn match_subsystem<T>(&mut self, subsystem: T) -> io::Result<()>
where T: AsRef<OsStr>,
{
Ok(self.builder.match_subsystem::<T>(subsystem)?)
}

/// Adds a filter that matches events for devices with the given subsystem
/// and device type.
#[inline(always)]
pub fn match_subsystem_devtype<T, U>(&mut self,
subsystem: T,
devtype: U) -> io::Result<()>
where T: AsRef<OsStr>,
U: AsRef<OsStr>,
{
Ok(self.builder.match_subsystem_devtype::<T, U>(subsystem, devtype)?)
}

/// Adds a filter that matches events for devices with the given tag.
#[inline(always)]
pub fn match_tag<T>(&mut self, tag: T) -> io::Result<()>
where T: AsRef<OsStr>,
{
Ok(self.builder.match_tag::<T>(tag)?)
}

/// Removes all filters currently set on the monitor.
#[inline(always)]
pub fn clear_filters(&mut self) -> io::Result<()> {
Ok(self.builder.clear_filters()?)
}

/// Listens for events matching the current filters.
///
/// This method consumes the `MonitorBuilder`.
pub fn listen(self) -> io::Result<MonitorSocket> {
Ok(MonitorSocket::new(self.builder.listen()?))
}
}

/// Asynchronous stream of device events.
pub struct MonitorSocket {
inner: Mutex<Inner>,
}

impl MonitorSocket {
fn new(monitor: mio_udev::MonitorSocket) -> MonitorSocket {
MonitorSocket { inner: Mutex::new(Inner::new(monitor)), }
}

fn poll_receive(&mut self) -> Poll<Option<mio_udev::Event>, io::Error> {
self.inner.lock().unwrap().poll_receive()
}
}

unsafe impl Send for MonitorSocket {}
unsafe impl Sync for MonitorSocket {}

struct Inner {
io: PollEvented<mio_udev::MonitorSocket>,
}

impl Inner {
fn new(monitor: mio_udev::MonitorSocket) -> Inner {
Inner { io: PollEvented::new(monitor), }
}

fn poll_receive(&mut self) -> Poll<Option<mio_udev::Event>, io::Error> {
if let Async::NotReady = self.io.poll_read_ready(mio::Ready::readable())? {
return Ok(Async::NotReady);
}

match self.io.get_mut().next() {
Some(device) => Ok(Async::Ready(Some(device))),
None => {
self.io.clear_read_ready(mio::Ready::readable())?;
Ok(Async::NotReady)
},
}

}
}

pub use udev::{Subsystem, DeviceType, MonitorName, USB_SUBSYSTEM, USB_DEVICE, UDEV_MONITOR};
impl Stream for MonitorSocket {
type Item = mio_udev::Event;
type Error = io::Error;

pub use monitor::{Builder, Monitor};
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
self.poll_receive()
}
}
Loading

0 comments on commit 35e032e

Please sign in to comment.