|
| 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 | +} |
0 commit comments