mio_udev/
lib.rs

1// Copyright 2020 Jean Pierre Dudey. See the LICENSE-MIT and
2// LICENSE-APACHE files at the top-level directory of this
3// distribution.
4//
5// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8// option. This file may not be copied, modified, or distributed
9// except according to those terms.
10
11//! # mio-udev
12//!
13//! This library implements abstractions around `udev` to make it usable
14//! with `mio` event loop.
15//!
16//! # Usage
17//!
18//! First put the dependency on your crate's `Cargo.toml`. For example:
19//!
20//! ```toml
21//! [dependencies]
22//! mio-udev = "0.1"
23//! ```
24//!
25//! Then import it in your crate root as:
26//!
27//! ```rust
28//! use mio_udev;
29//! ```
30
31#![cfg(target_os = "linux")]
32
33pub use udev::{
34    Attribute, Attributes, Device, Enumerator, Event, EventType, Properties,
35    Property,
36};
37
38mod util;
39
40use std::ffi::OsStr;
41use std::io;
42use std::os::unix::io::{AsRawFd, RawFd};
43
44use mio::event::Source;
45use mio::unix::SourceFd;
46use mio::{Interest, Registry, Token};
47
48/// Monitors for device events.
49///
50/// A monitor communicates with the kernel over a socket. Filtering events is
51/// performed efficiently in the kernel, and only events that match the filters
52/// are received by the socket. Filters must be setup before listening for
53/// events.
54pub struct MonitorBuilder {
55    builder: udev::MonitorBuilder,
56}
57
58impl MonitorBuilder {
59    /// Creates a new `MonitorSocket`.
60    #[inline(always)]
61    pub fn new() -> io::Result<Self> {
62        Ok(MonitorBuilder {
63            builder: udev::MonitorBuilder::new()?,
64        })
65    }
66
67    #[inline(always)]
68    fn map(builder: udev::MonitorBuilder) -> Self {
69        MonitorBuilder { builder }
70    }
71
72    /// Adds a filter that matches events for devices with the given subsystem.
73    #[inline(always)]
74    pub fn match_subsystem<T>(self, subsystem: T) -> io::Result<Self>
75    where
76        T: AsRef<OsStr>,
77    {
78        self.builder.match_subsystem::<T>(subsystem).map(Self::map)
79    }
80
81    /// Adds a filter that matches events for devices with the given subsystem
82    /// and device type.
83    #[inline(always)]
84    pub fn match_subsystem_devtype<T, U>(
85        self,
86        subsystem: T,
87        devtype: U,
88    ) -> io::Result<Self>
89    where
90        T: AsRef<OsStr>,
91        U: AsRef<OsStr>,
92    {
93        self.builder
94            .match_subsystem_devtype::<T, U>(subsystem, devtype)
95            .map(Self::map)
96    }
97
98    /// Adds a filter that matches events for devices with the given tag.
99    #[inline(always)]
100    pub fn match_tag<T>(self, tag: T) -> io::Result<Self>
101    where
102        T: AsRef<OsStr>,
103    {
104        self.builder.match_tag::<T>(tag).map(Self::map)
105    }
106
107    /// Removes all filters currently set on the monitor.
108    #[inline(always)]
109    pub fn clear_filters(self) -> io::Result<Self> {
110        self.builder.clear_filters().map(Self::map)
111    }
112
113    /// Listens for events matching the current filters.
114    ///
115    /// This method consumes the `MonitorBuilder`.
116    pub fn listen(self) -> io::Result<MonitorSocket> {
117        MonitorSocket::new(self.builder.listen()?)
118    }
119}
120
121/// A wrapper around an `udev::MonitorSocket` that adds the required `mio`
122/// functionality.
123pub struct MonitorSocket {
124    monitor: udev::MonitorSocket,
125}
126
127impl MonitorSocket {
128    fn new(monitor: udev::MonitorSocket) -> io::Result<MonitorSocket> {
129        use crate::util::cvt;
130        use libc::{
131            fcntl, FD_CLOEXEC, F_GETFD, F_GETFL, F_SETFD, F_SETFL, O_NONBLOCK,
132        };
133
134        let fd = monitor.as_raw_fd();
135
136        // Make sure the udev file descriptor is marked as CLOEXEC.
137        let r = unsafe { cvt(fcntl(fd, F_GETFD))? };
138
139        if (r & FD_CLOEXEC) != FD_CLOEXEC {
140            unsafe { cvt(fcntl(fd, F_SETFD, r | FD_CLOEXEC))? };
141        }
142
143        // Some older versions of udev are not non-blocking by default,
144        // so make sure this is set
145        let r = unsafe { cvt(fcntl(fd, F_GETFL))? };
146
147        if (r & O_NONBLOCK) != O_NONBLOCK {
148            unsafe { cvt(fcntl(fd, F_SETFL, r | O_NONBLOCK))? };
149        }
150
151        Ok(MonitorSocket { monitor })
152    }
153
154    #[inline(always)]
155    fn fd(&self) -> RawFd {
156        self.monitor.as_raw_fd()
157    }
158}
159
160impl AsRawFd for MonitorSocket {
161    fn as_raw_fd(&self) -> RawFd {
162        self.fd()
163    }
164}
165
166impl Source for MonitorSocket {
167    fn register(
168        &mut self,
169        registry: &Registry,
170        token: Token,
171        interest: Interest,
172    ) -> io::Result<()> {
173        SourceFd(&self.fd()).register(registry, token, interest)
174    }
175
176    fn reregister(
177        &mut self,
178        registry: &Registry,
179        token: Token,
180        interest: Interest,
181    ) -> io::Result<()> {
182        SourceFd(&self.fd()).reregister(registry, token, interest)
183    }
184
185    fn deregister(&mut self, registry: &Registry) -> io::Result<()> {
186        SourceFd(&self.fd()).deregister(registry)
187    }
188}
189
190impl Iterator for MonitorSocket {
191    type Item = Event;
192
193    fn next(&mut self) -> Option<Self::Item> {
194        self.monitor.next()
195    }
196}