use std::os::fd;
use std::os::fd::{AsFd, AsRawFd};
use std::{ffi, io, path};
pub struct Monitor {
inotify: inotify::Inotify,
filename: ffi::OsString,
invalidated: bool,
}
impl Monitor {
pub fn new(path: impl AsRef<path::Path>) -> io::Result<Self> {
let path = path.as_ref();
let filename = path.file_name().ok_or_else(|| {
io::Error::new(io::ErrorKind::InvalidInput, "state path needs a filename")
})?;
let parent = path.parent().filter(|p| !p.as_os_str().is_empty());
let inotify = inotify::Inotify::init()?;
inotify.watches().add(
parent.unwrap_or_else(|| path::Path::new(".")),
inotify::WatchMask::CREATE
| inotify::WatchMask::MODIFY
| inotify::WatchMask::CLOSE_WRITE
| inotify::WatchMask::MOVED_TO
| inotify::WatchMask::MOVED_FROM
| inotify::WatchMask::DELETE
| inotify::WatchMask::DELETE_SELF
| inotify::WatchMask::MOVE_SELF
| inotify::WatchMask::ONLYDIR,
)?;
Ok(Self {
inotify,
filename: filename.to_owned(),
invalidated: false,
})
}
pub fn has_changed(&mut self) -> io::Result<bool> {
if self.invalidated {
return Err(io::Error::new(
io::ErrorKind::NotFound,
"state directory watch invalidated; recreate the monitor",
));
}
let mut changed = false;
let mut buffer = [0_u8; 4096];
loop {
match self.inotify.read_events(&mut buffer) {
Ok(events) => {
for event in events {
if event.mask.intersects(
inotify::EventMask::DELETE_SELF
| inotify::EventMask::MOVE_SELF
| inotify::EventMask::IGNORED
| inotify::EventMask::UNMOUNT,
) {
self.invalidated = true;
}
changed |= event.mask.contains(inotify::EventMask::Q_OVERFLOW)
|| event.name == Some(self.filename.as_os_str());
}
}
Err(error) if error.kind() == io::ErrorKind::Interrupted => {}
Err(error) if error.kind() == io::ErrorKind::WouldBlock => break,
Err(error) => return Err(error),
}
}
if self.invalidated {
return Err(io::Error::new(
io::ErrorKind::NotFound,
"state directory watch invalidated; recreate the monitor",
));
}
Ok(changed)
}
}
impl AsRawFd for Monitor {
fn as_raw_fd(&self) -> fd::RawFd {
self.inotify.as_raw_fd()
}
}
impl AsFd for Monitor {
fn as_fd(&self) -> fd::BorrowedFd<'_> {
self.inotify.as_fd()
}
}