hannobraun / inotify-rs

Idiomatic inotify wrapper for the Rust programming language
ISC License
261 stars 65 forks source link

Invalid argument when trying to monitor /dev/snd #208

Closed naftulikay closed 1 year ago

naftulikay commented 1 year ago

I'm using the current stable version of inotify, 0.10.2, on Ubuntu LTS Focal, kernel 5.15, amd64. I'm attempting to port this C code to Rust to make management of a certain hardware audio device easier and more idiomatic. The C code mentioned essentially does the following:

#include <sys/inotify.h>

void alsa_inotify_init(void) {
  GIOChannel *io_channel;

  // static variables :facepalm:
  inotify_fd = inotify_init();
  inotify_wd = inotify_add_watch(inotify_fd, "/dev/snd", IN_CREATE);
  io_channel = g_io_channel_unix_new(inotify_fd);
  g_io_add_watch_full(
    io_channel, 0,
    G_IO_IN | G_IO_ERR | G_IO_HUP,
    inotify_callback, NULL, NULL
  );
}

Essentially, this is done to setup callbacks for when devices are added/removed from /dev/snd to detect new devices and remove ones that were disconnected.

I'm essentially trying to do the same thing in Rust, but all I get is errors:

  2023-07-28T22:58:18.243280Z  WARN test_inotify: Received error when attempting to listen for events: Os { code: 22, kind: InvalidInput, message: "Invalid argument" }
    at test_inotify/src/main.rs:133

Here is my Rust code, which should simply log out all events that are received:

use futures::StreamExt;
use inotify::{EventMask, Inotify, WatchMask};
use std::path::PathBuf;
use std::str::FromStr;
use tracing::{debug, info, warn};
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::Registry;

#[cfg(target_os = "linux")]
#[tokio::main]
async fn main() {
    tracing::subscriber::set_global_default(
        Registry::default().with(tracing_subscriber::fmt::layer().pretty()),
    )
    .expect("unable to configure logging");

    let notifier = Inotify::init().expect("unable to initialize inotify");

    debug!("inotify initialized");

    let watch = notifier
        .watches()
        .add(
            PathBuf::from_str("/dev/snd/").unwrap(),
            WatchMask::ALL_EVENTS,
        )
        .expect("unable to add a watch to /dev/snd");

    debug!(wd = watch.get_watch_descriptor_id(), "watch created");

    info!("Listening for events...");

    let mut event_stream = notifier
        // 16MiB of buffer space, no idea what the right size should be
        .into_event_stream(Vec::with_capacity(1024usize.pow(2) * 16))
        .expect("unable to convert notifier into event stream");

    while let Some(event) = event_stream.next().await {
        let event = match event {
            Ok(e) => {
                debug!("Received valid event");
                e
            }
            Err(e) => {
                // FIXME here is where the error occurs
                warn!("Received error when attempting to listen for events: {:?}", e);
                continue;
            }
        };

        let path = if let Some(name) = event.name {
            name.to_string_lossy().to_string()
        } else {
            "(none)".to_string()
        };

        let flags = format!("{:b}", event.mask.bits());

        let cookie = if event.cookie > 0 {
            format!("{:X}", event.cookie)
        } else {
            "(none)".to_string()
        };

        // should log like: wd=1 path=/dev/snd/hw:00 flags=01010101 cookie=(none) Event Received
        info!(
            wd = event.wd.get_watch_descriptor_id(),
            path, flags, cookie, "Event received"
        );
    }
}

Note that in this example code, I'm allowing any event to come across just to see if it's working at all. When I unplug my USB audio device, I get flooded with thousands of errors, so much so that my IDE begins to freeze up. Other than the errors, I receive no other events whatsoever.

I am admittedly not as familiar with inotify as it relates to kernel device files, but simply using:

inotifywatch -e create,delete /dev/snd/

does report changes as they occur, so it seems to be related to either how I'm using this inotify crate or some bug within the crate.

What am I doing incorrectly here?

naftulikay commented 1 year ago

This is my bad, I was passing an uninitialized Vec<u8> as a buffer. The problem was here:

let mut event_stream = Inotify::init()?.into_event_stream(Vec::with_capacity(4096))?;

Vec::with_capacity does not initialize the memory. Either of the following will fix the issue:

// with a stack array
let mut event_stream = Inotify::init()?.into_event_stream([0; 4096])?;
// with an *initialized* vector: the vec! macro does initialize memory
let mut event_stream = Inotify::init()?.into_event_stream(vec![0; 4096])?;
hannobraun commented 1 year ago

Glad you figured it out!