use libc;
use libc::{
c_char,
c_int,
};
use std::ffi::{OsString,OsStr,CStr};
use std::os::unix::ffi::OsStrExt;
use std::mem::size_of;
use std::os::unix::io::{RawFd,AsRawFd,FromRawFd};
use unistd::read;
use Result;
use NixPath;
use errno::Errno;
libc_bitflags! {
pub struct AddWatchFlags: u32 {
IN_ACCESS;
IN_MODIFY;
IN_ATTRIB;
IN_CLOSE_WRITE;
IN_CLOSE_NOWRITE;
IN_OPEN;
IN_MOVED_FROM;
IN_MOVED_TO;
IN_CREATE;
IN_DELETE;
IN_DELETE_SELF;
IN_MOVE_SELF;
IN_UNMOUNT;
IN_Q_OVERFLOW;
IN_IGNORED;
IN_CLOSE;
IN_MOVE;
IN_ONLYDIR;
IN_DONT_FOLLOW;
IN_ISDIR;
IN_ONESHOT;
IN_ALL_EVENTS;
}
}
libc_bitflags! {
pub struct InitFlags: c_int {
IN_CLOEXEC;
IN_NONBLOCK;
}
}
#[derive(Debug, Clone, Copy)]
pub struct Inotify {
fd: RawFd
}
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub struct WatchDescriptor {
wd: i32
}
#[derive(Debug)]
pub struct InotifyEvent {
pub wd: WatchDescriptor,
pub mask: AddWatchFlags,
pub cookie: u32,
pub name: Option<OsString>
}
impl Inotify {
pub fn init(flags: InitFlags) -> Result<Inotify> {
let res = Errno::result(unsafe {
libc::inotify_init1(flags.bits())
});
res.map(|fd| Inotify { fd })
}
pub fn add_watch<P: ?Sized + NixPath>(&self,
path: &P,
mask: AddWatchFlags)
-> Result<WatchDescriptor>
{
let res = path.with_nix_path(|cstr| {
unsafe {
libc::inotify_add_watch(self.fd, cstr.as_ptr(), mask.bits())
}
})?;
Errno::result(res).map(|wd| WatchDescriptor { wd })
}
#[cfg(target_os = "linux")]
pub fn rm_watch(&self, wd: WatchDescriptor) -> Result<()> {
let res = unsafe { libc::inotify_rm_watch(self.fd, wd.wd) };
Errno::result(res).map(drop)
}
#[cfg(target_os = "android")]
pub fn rm_watch(&self, wd: WatchDescriptor) -> Result<()> {
let res = unsafe { libc::inotify_rm_watch(self.fd, wd.wd as u32) };
Errno::result(res).map(drop)
}
pub fn read_events(&self) -> Result<Vec<InotifyEvent>> {
let header_size = size_of::<libc::inotify_event>();
let mut buffer = [0u8; 4096];
let mut events = Vec::new();
let mut offset = 0;
let nread = read(self.fd, &mut buffer)?;
while (nread - offset) >= header_size {
let event = unsafe {
&*(
buffer
.as_ptr()
.offset(offset as isize) as *const libc::inotify_event
)
};
let name = match event.len {
0 => None,
_ => {
let ptr = unsafe {
buffer
.as_ptr()
.offset(offset as isize + header_size as isize)
as *const c_char
};
let cstr = unsafe { CStr::from_ptr(ptr) };
Some(OsStr::from_bytes(cstr.to_bytes()).to_owned())
}
};
events.push(InotifyEvent {
wd: WatchDescriptor { wd: event.wd },
mask: AddWatchFlags::from_bits_truncate(event.mask),
cookie: event.cookie,
name
});
offset += header_size + event.len as usize;
}
Ok(events)
}
}
impl AsRawFd for Inotify {
fn as_raw_fd(&self) -> RawFd {
self.fd
}
}
impl FromRawFd for Inotify {
unsafe fn from_raw_fd(fd: RawFd) -> Self {
Inotify { fd }
}
}