Skip to content

Commit ea46f92

Browse files
committed
FreeBSD: support hotplug using devd
1 parent 88cf88e commit ea46f92

3 files changed

Lines changed: 137 additions & 2 deletions

File tree

src/hotplug.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,13 @@ mod linux;
1313
#[cfg(target_os = "linux")]
1414
use linux::Impl;
1515

16+
#[cfg(target_os = "freebsd")]
17+
mod freebsd;
18+
#[cfg(target_os = "freebsd")]
19+
use freebsd::Impl;
20+
1621
mod fallback;
17-
#[cfg(not(target_os = "linux"))]
22+
#[cfg(not(any(target_os = "linux", target_os = "freebsd")))]
1823
use fallback::Impl;
1924

2025
use std::{

src/hotplug/freebsd.rs

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
use std::{
2+
ffi::{OsStr, c_int},
3+
io,
4+
mem::{self, offset_of},
5+
os::{
6+
fd::{AsRawFd, FromRawFd, OwnedFd, RawFd},
7+
unix::ffi::OsStrExt,
8+
},
9+
path::PathBuf,
10+
};
11+
12+
use libc::{AF_UNIX, SOCK_CLOEXEC, SOCK_SEQPACKET, connect, recv, sockaddr_un, socket, ssize_t};
13+
14+
use crate::Evdev;
15+
16+
fn cvt(ret: c_int) -> io::Result<c_int /* never -1 */> {
17+
if ret == -1 {
18+
Err(io::Error::last_os_error())
19+
} else {
20+
Ok(ret)
21+
}
22+
}
23+
24+
fn cvt_r(mut f: impl FnMut() -> ssize_t) -> io::Result<ssize_t> {
25+
loop {
26+
let ret = f();
27+
if ret == -1 {
28+
let err = io::Error::last_os_error();
29+
if err.kind() != io::ErrorKind::Interrupted {
30+
return Err(err);
31+
}
32+
} else {
33+
return Ok(ret);
34+
}
35+
}
36+
}
37+
38+
pub(crate) struct Impl {
39+
fd: OwnedFd,
40+
}
41+
42+
impl AsRawFd for Impl {
43+
#[inline]
44+
fn as_raw_fd(&self) -> RawFd {
45+
self.fd.as_raw_fd()
46+
}
47+
}
48+
49+
impl super::HotplugImpl for Impl {
50+
fn open() -> io::Result<Self> {
51+
const PATH: &[u8] = b"/var/run/devd.seqpacket.pipe";
52+
53+
unsafe {
54+
let fd = OwnedFd::from_raw_fd(cvt(socket(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0))?);
55+
let mut addr: sockaddr_un = mem::zeroed();
56+
addr.sun_path
57+
.as_mut_ptr()
58+
.cast::<u8>()
59+
.copy_from_nonoverlapping(PATH.as_ptr(), PATH.len());
60+
addr.sun_len = (offset_of!(sockaddr_un, sun_path) + PATH.len())
61+
.try_into()
62+
.unwrap();
63+
addr.sun_family = AF_UNIX as _;
64+
65+
cvt(connect(
66+
fd.as_raw_fd(),
67+
(&raw const addr).cast(),
68+
mem::size_of_val(&addr) as _,
69+
))?;
70+
71+
Ok(Self { fd })
72+
}
73+
}
74+
75+
fn read(&self) -> io::Result<Evdev> {
76+
let mut buf = [0u8; 8192];
77+
loop {
78+
unsafe {
79+
let len =
80+
cvt_r(|| recv(self.as_raw_fd(), buf.as_mut_ptr().cast(), buf.len() as _, 0))?;
81+
let msg = &buf[..len as usize];
82+
83+
// The messages we're looking for are newline-terminated and look like this:
84+
// !system=DEVFS subsystem=CDEV type=CREATE cdev=input/eventN
85+
let Some(msg) = msg.strip_prefix(b"!") else {
86+
continue;
87+
};
88+
let Some(msg) = msg.strip_suffix(b"\n") else {
89+
continue;
90+
};
91+
92+
log::trace!("incoming devd message: {}", msg.escape_ascii());
93+
94+
let mut system_devfs = false;
95+
let mut subsys_cdev = false;
96+
let mut type_create = false;
97+
let mut cdev = None;
98+
for part in msg.split(|b| *b == b' ') {
99+
let mut split = part.splitn(2, |b| *b == b'=');
100+
let Some(key) = split.next() else {
101+
continue;
102+
};
103+
let Some(value) = split.next() else {
104+
continue;
105+
};
106+
107+
match key {
108+
b"system" if value == b"DEVFS" => system_devfs = true,
109+
b"subsystem" if value == b"CDEV" => subsys_cdev = true,
110+
b"type" if value == b"CREATE" => type_create = true,
111+
b"cdev" => cdev = Some(value),
112+
_ => {}
113+
}
114+
}
115+
116+
if system_devfs && subsys_cdev && type_create {
117+
if let Some(cdev) = cdev {
118+
let mut path = PathBuf::from("/dev/");
119+
path.push(OsStr::from_bytes(cdev));
120+
121+
log::debug!("match! trying to open: {}", path.display());
122+
return Evdev::open(path);
123+
}
124+
}
125+
126+
log::trace!("no match");
127+
}
128+
}
129+
}
130+
}

src/hotplug/linux.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ impl Impl {
8383
addr.nl_groups = group as _;
8484
cvt(bind(
8585
fd.as_raw_fd(),
86-
&addr as *const _ as *const sockaddr,
86+
(&raw const addr).cast(),
8787
size_of_val(&addr) as socklen_t,
8888
))?;
8989

0 commit comments

Comments
 (0)