summaryrefslogtreecommitdiff
path: root/src/sys/inotify.rs
diff options
context:
space:
mode:
authorVincent Dagonneau <vincentdagonneau@gmail.com>2019-01-20 09:11:15 +0100
committerVincent Dagonneau <vincentdagonneau@gmail.com>2019-02-21 09:23:23 +0100
commit3966d0bd195580e8262084a99faa2d940817d219 (patch)
tree191e7e2306ec4747c320f89398796cf33d87a04f /src/sys/inotify.rs
parenta2fa2828e0663de420ce02a063c9ea66e7f00c36 (diff)
downloadnix-3966d0bd195580e8262084a99faa2d940817d219.zip
Added inotify bindings for Linux and Android.
Diffstat (limited to 'src/sys/inotify.rs')
-rw-r--r--src/sys/inotify.rs231
1 files changed, 231 insertions, 0 deletions
diff --git a/src/sys/inotify.rs b/src/sys/inotify.rs
new file mode 100644
index 00000000..91cc48d3
--- /dev/null
+++ b/src/sys/inotify.rs
@@ -0,0 +1,231 @@
+//! Monitoring API for filesystem events.
+//!
+//! Inotify is a Linux-only API to monitor filesystems events.
+//!
+//! For more documentation, please read [inotify(7)](http://man7.org/linux/man-pages/man7/inotify.7.html).
+//!
+//! # Examples
+//!
+//! Monitor all events happening in directory "test":
+//! ```no_run
+//! # use nix::sys::inotify::{AddWatchFlags,InitFlags,Inotify};
+//! #
+//! // We create a new inotify instance.
+//! let instance = Inotify::init(InitFlags::empty()).unwrap();
+//!
+//! // We add a new watch on directory "test" for all events.
+//! let wd = instance.add_watch("test", AddWatchFlags::IN_ALL_EVENTS).unwrap();
+//!
+//! loop {
+//! // We read from our inotify instance for events.
+//! let events = instance.read_events().unwrap();
+//! println!("Events: {:?}", events);
+//! }
+//! ```
+
+use libc;
+use libc::{
+ c_char,
+ c_int,
+ uint32_t
+};
+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! {
+ /// Configuration options for [`inotify_add_watch`](fn.inotify_add_watch.html).
+ pub struct AddWatchFlags: uint32_t {
+ 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! {
+ /// Configuration options for [`inotify_init1`](fn.inotify_init1.html).
+ pub struct InitFlags: c_int {
+ IN_CLOEXEC;
+ IN_NONBLOCK;
+ }
+}
+
+/// An inotify instance. This is also a file descriptor, you can feed it to
+/// other interfaces consuming file descriptors, epoll for example.
+#[derive(Debug, Clone, Copy)]
+pub struct Inotify {
+ fd: RawFd
+}
+
+/// This object is returned when you create a new watch on an inotify instance.
+/// It is then returned as part of an event once triggered. It allows you to
+/// know which watch triggered which event.
+#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Ord, PartialOrd)]
+pub struct WatchDescriptor {
+ wd: i32
+}
+
+/// A single inotify event.
+///
+/// For more documentation see, [inotify(7)](http://man7.org/linux/man-pages/man7/inotify.7.html).
+#[derive(Debug)]
+pub struct InotifyEvent {
+ /// Watch descriptor. This field corresponds to the watch descriptor you
+ /// were issued when calling add_watch. It allows you to know which watch
+ /// this event comes from.
+ pub wd: WatchDescriptor,
+ /// Event mask. This field is a bitfield describing the exact event that
+ /// occured.
+ pub mask: AddWatchFlags,
+ /// This cookie is a number that allows you to connect related events. For
+ /// now only IN_MOVED_FROM and IN_MOVED_TO can be connected.
+ pub cookie: u32,
+ /// Filename. This field exists only if the event was triggered for a file
+ /// inside the watched directory.
+ pub name: Option<OsString>
+}
+
+impl Inotify {
+ /// Initialize a new inotify instance.
+ ///
+ /// Returns a Result containing an inotify instance.
+ ///
+ /// For more information see, [inotify_init(2)](http://man7.org/linux/man-pages/man2/inotify_init.2.html).
+ pub fn init(flags: InitFlags) -> Result<Inotify> {
+ let res = Errno::result(unsafe {
+ libc::inotify_init1(flags.bits())
+ });
+
+ res.map(|fd| Inotify { fd })
+ }
+
+ /// Adds a new watch on the target file or directory.
+ ///
+ /// Returns a watch descriptor. This is not a File Descriptor!
+ ///
+ /// For more information see, [inotify_add_watch(2)](http://man7.org/linux/man-pages/man2/inotify_add_watch.2.html).
+ 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 })
+ }
+
+ /// Removes an existing watch using the watch descriptor returned by
+ /// inotify_add_watch.
+ ///
+ /// Returns an EINVAL error if the watch descriptor is invalid.
+ ///
+ /// For more information see, [inotify_rm_watch(2)](http://man7.org/linux/man-pages/man2/inotify_rm_watch.2.html).
+ #[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)
+ }
+
+ /// Reads a collection of events from the inotify file descriptor. This call
+ /// can either be blocking or non blocking depending on whether IN_NONBLOCK
+ /// was set at initialization.
+ ///
+ /// Returns as many events as available. If the call was non blocking and no
+ /// events could be read then the EAGAIN error is returned.
+ 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 }
+ }
+}