use std::path::PathBuf;
use std::time::Duration;
pub(super) struct InotifyWatcher {
inotify: inotify::Inotify,
buffer: Vec<u8>,
}
impl InotifyWatcher {
pub(super) fn new(watch_paths: &[PathBuf]) -> Option<Self> {
use inotify::WatchMask;
let inotify = match inotify::Inotify::init() {
Ok(v) => v,
Err(_) => return None,
};
let mask = WatchMask::MODIFY
| WatchMask::CLOSE_WRITE
| WatchMask::ATTRIB
| WatchMask::CREATE
| WatchMask::DELETE
| WatchMask::MOVED_FROM
| WatchMask::MOVED_TO
| WatchMask::MOVE_SELF
| WatchMask::DELETE_SELF;
let mut added = 0usize;
for p in watch_paths {
if inotify.watches().add(p, mask).is_ok() {
added = added.saturating_add(1);
}
}
if added == 0 {
return None;
}
Some(Self {
inotify,
buffer: vec![0u8; 4096],
})
}
pub(super) fn wait(&mut self, timeout: Duration) -> bool {
use std::os::unix::io::AsRawFd as _;
let timeout_ms = i32::try_from(timeout.as_millis()).unwrap_or(i32::MAX);
let fd = self.inotify.as_raw_fd();
let mut pfd = libc::pollfd {
fd,
events: libc::POLLIN,
revents: 0,
};
loop {
let r = unsafe { libc::poll(&mut pfd, 1, timeout_ms) };
if r > 0 {
let _ = self.inotify.read_events(&mut self.buffer);
return true;
}
if r == 0 {
return false;
}
let err = std::io::Error::last_os_error();
if err.kind() == std::io::ErrorKind::Interrupted {
continue;
}
return false;
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn watcher_new_returns_none_without_paths() {
assert!(InotifyWatcher::new(&[]).is_none());
}
}