Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Monitor example? #15

Closed
CertainLach opened this issue Jan 26, 2024 · 2 comments · Fixed by #17
Closed

Monitor example? #15

CertainLach opened this issue Jan 26, 2024 · 2 comments · Fixed by #17

Comments

@CertainLach
Copy link

Either I misunderstand how to use this library, or it doesn't setup something properly?

Using version 0.1.1, removing filter/connecting to kernel doesn't change anything, socket is never ready.

use std::os::fd::RawFd;
use std::result;
use std::sync::Arc;
use std::time::Duration;

use tokio::io::unix::AsyncFd;
use tokio::time::sleep;
use udevrs::Udev;
use udevrs::UdevMonitor;

#[derive(thiserror::Error, Debug)]
pub enum Error {
	#[error("udev: {0}")]
	Udev(udevrs::Error),
	#[error("io: {0}")]
	Io(#[from] std::io::Error),
}
impl From<udevrs::Error> for Error {
	fn from(value: udevrs::Error) -> Self {
		Self::Udev(value)
	}
}

type Result<T, E = Error> = result::Result<T, E>;

// TODO(windows): https://github.com/kevinmehall/nusb/pull/20
async fn device_events() -> Result<()> {
	let mut udev = Arc::new(Udev::new());

	let mut monitor = UdevMonitor::new_from_netlink(udev, "udev")?;
	monitor.filter_add_match_subsystem_devtype("usb", "usb_interface")?;
	monitor.enable_receiving()?;

	let fd: RawFd = monitor.sock();
	let fd = AsyncFd::new(fd)?;

	loop {
		let mut ready = fd.readable().await?;
		let Ok(v) = dbg!(monitor.receive_device()) else {
			continue;
		};
		dbg!(&v);
		ready.retain_ready();
		sleep(Duration::from_millis(5)).await;
	}
}

#[tokio::test]
async fn test() {
	tracing_subscriber::fmt::init();
	device_events().await.expect("no error");
}
@kevinmehall
Copy link

I also ran into problems trying to integrate UdevMonitor. Here's what I found so far:

I'm using Ubuntu 22.04 (kernel 6.2.0, udev from systemd 249.11), and I tried using UdevMonitor with the following:

use std::{sync::Arc, thread, time::Duration};

use udevrs::{Udev, UdevMonitor};

fn main() {
    env_logger::init();
    let udev = Arc::new(Udev::new());
    let mut monitor = UdevMonitor::new_from_netlink(udev, "udev").unwrap();

    monitor.enable_receiving().unwrap();

    loop {
        println!("{:?}", monitor.receive_device());

        // for lack of an easy way to set it to blocking mode or poll
        thread::sleep(Duration::from_secs(1));
    }
}

This prints Err(UdevMonitor("unable to receive message: Resource temporarily unavailable (os error 11)")) (expected, because the socket is in nonblocking mode), but never receives any useful events.

I compared the strace output between this program and udevadm monitor -u, and found that udevadm passes nl_groups=2 when calling bind:

bind(4, {sa_family=AF_NETLINK, nl_pid=0, nl_groups=0x000002}, 12) = 0

but this library is passing 0:

bind(3, {sa_family=AF_NETLINK, nl_pid=0, nl_groups=00000000}, 12) = 0

which from what I can tell, means not to subscribe to any multicast events.

Looking at the code, this value comes from self.snl, which doesn't appear to be written when configuring the group. There's a call to set_snl_group, but this writes self.snl_group, which doesn't seem to actually be used anywhere.

So I tried forcing nl_groups to 2:

--- a/src/monitor.rs
+++ b/src/monitor.rs
@@ -652,10 +652,11 @@ impl UdevMonitor {
 
         let mut err = if !self.bound {
             // SAFETY: all arguments are valid, and pointers reference valid memory.
+            let snl = UdevSocket::new_nl(libc::AF_NETLINK, 0, 2);
             unsafe {
                 libc::bind(
                     self.sock,
-                    self.snl.as_nl_ptr()? as *const _,
+                    snl.as_nl_ptr()? as *const _,
                     mem::size_of::<libc::sockaddr_nl>() as u32,
                 )
             }

Now it receives what looks like plausible events in strace:

recvmsg(3, {msg_name={sa_family=AF_NETLINK, nl_pid=623, nl_groups=0x000002}, msg_namelen=12, msg_iov=[{iov_base=[{prefix="libudev", magic=htonl(0xfeedcafe), header_size=40, properties_off=40, properties_len=324, filter_subsystem_hash=htonl(0x577c5e5), filter_devtype_hash=htonl(0x27f8f50c), filter_tag_bloom_hi=htonl(0), filter_tag_bloom_lo=htonl(0)}, "ACTION=remove\0DEVPATH=/devices/p"...], iov_len=8192}], msg_iovlen=1, msg_controllen=0, msg_flags=MSG_CTRUNC}, 0) = 364

but fails to parse them and returns Err(UdevMonitor("msg_controllen (0) is too small for a cmsghdr")).

cr8t added a commit that referenced this issue Apr 4, 2024
Sets the default `nl_groups` value to `2` for `Monitor::snl`. This
ensures that if users do not call a method that sets `snl`, the default
will be valid to begin receiving `Netlink` events.

Part of resolving: #15
@cr8t
Copy link
Owner

cr8t commented Apr 4, 2024

So I tried forcing nl_groups to 2

I've set this as the default value when creating a new Monitor.

Looking at the code, this value comes from self.snl, which doesn't appear to be written when configuring the group.

There is the Monitor::set_snl function which allows to set self.snl:

monitor.set_snl(UdevSocket::new_nl(libc::AF_NETLINK, 0, 2));

Most of this code was ported from the library portion of eudev, so there may be code in the binary portion that sets this value.

fails to parse them and returns Err(UdevMonitor("msg_controllen (0) is too small for a cmsghdr"))

I'll continue debugging this error, thank you both for the detailed error reports.

cr8t added a commit that referenced this issue May 9, 2024
Fixes the polling mechanism for UDEV events by correcting `ucred`
parsing.

Adds an example, credit to @kevinmehall:
<#15 (comment)>

Resolves: #15
cr8t added a commit that referenced this issue May 9, 2024
Fixes the polling mechanism for UDEV events by correcting `ucred`
parsing.

Adds an example, credit to @kevinmehall:
<#15 (comment)>

Resolves: #15
@cr8t cr8t closed this as completed in #17 May 14, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants