inotify

Idiomatic Rust wrapper around the Linux inotify API for watching files and directories for changes

Library
Cargo
v0.11.5
295stars
ISC

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
71/100Good
Development Activity84
Maintenance44
Community76
Maturity60
Momentum20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
77/100Good
Architecture80
Code Quality78
Innovation72
Learning Curve78

inotify is a safe, idiomatic Rust wrapper around the Linux kernel’s inotify subsystem, letting applications watch files and directories for creation, modification, deletion, and other filesystem events without dealing directly with raw file descriptors or unsafe FFI calls.

The crate closely mirrors the underlying inotify API while adding Rust-native ergonomics: typed watch masks and event masks via bitflags, RAII cleanup of file descriptors and watch descriptors, and both a synchronous blocking read API and an optional async EventStream built on Tokio. It is Linux-specific by design, targeting developers who need direct, low-level access to inotify rather than a cross-platform notification abstraction.

What You Get

  • An Inotify::init() entry point that safely wraps inotify_init1 with IN_CLOEXEC and IN_NONBLOCK set automatically
  • A Watches API for adding/removing watches on paths with typed WatchMask flags (CREATE, MODIFY, DELETE, MOVE, ACCESS, and more)
  • Synchronous read_events_blocking and read_events (non-blocking) methods for consuming raw kernel events into typed Event structs
  • An optional stream feature (enabled by default) providing EventStream, a Tokio-backed async stream of inotify events via into_event_stream
  • Typed EventMask flags (e.g. CREATE, DELETE, ISDIR, MOVE_SELF) so event matching is done with bitflag checks instead of raw integers
  • RAII-managed file descriptors (FdGuard) and watch descriptors, so watches and the inotify instance are cleaned up automatically on drop

Common Use Cases

  • Building a file-watcher or live-reload tool that reacts when source files in a directory change
  • Implementing a lightweight config-reload mechanism that re-reads a config file when it’s modified
  • Writing a sync/backup daemon that mirrors filesystem changes (create/delete/move) into another location
  • Feeding filesystem change events into an async application via EventStream alongside other Tokio-based I/O

Under The Hood

Architecture — The crate is organized around a single Inotify handle (src/inotify.rs) that owns an Arc<FdGuard> wrapping the raw inotify file descriptor returned by inotify_init1. Watches are managed through a separate Watches struct (src/watches.rs) that holds a Weak reference back to the same FdGuard, so watch descriptors can be added or removed via inotify_add_watch/inotify_rm_watch without duplicating fd ownership. Reading events flows through util::read_into_buffer into src/events.rs, which parses the kernel’s packed inotify_event byte layout (fixed header plus a variable-length, NUL-padded name field) into safe Event/EventOwned structs via unsafe pointer arithmetic isolated to that one module. The optional stream.rs module layers a Tokio AsyncFd-based EventStream on top of the same blocking read primitives, so the sync and async paths share the same event-decoding logic rather than duplicating it.

Tech Stack — The crate targets Rust edition 2018 with a minimum supported Rust version of 1.70. Core runtime dependencies are inotify-sys (raw FFI bindings to the kernel syscalls), libc (for fcntl/O_NONBLOCK handling), and bitflags (for the typed WatchMask/EventMask constants). The stream feature, enabled by default, pulls in futures-util and tokio (with the net feature) to provide async event polling. Dev-dependencies (tempfile, maplit, rand, plus tokio with macros/rt-multi-thread/time) support the integration test suite. There is no build script or code generation step — the crate builds as a plain cargo build against these dependencies.

Code Qualitytests/main.rs contains a substantial integration test suite (595 lines, 16 #[test] functions) exercising watch add/remove, event masks, non-blocking reads, and the async stream path against real temporary files via tempfile. Source files are consistently documented with rustdoc comments that include compiling, runnable examples (visible throughout lib.rs, inotify.rs, and watches.rs), which also serve as doctests run by cargo test. Error handling is idiomatic: public methods return std::io::Error rather than panicking, and resource cleanup (file descriptors, watch descriptors) is handled via Drop implementations rather than requiring explicit close calls from callers. Unsafe code is present but narrowly scoped to the FFI boundary and buffer-parsing logic in events.rs and fd_guard.rs.

API Design — The public API is small and consistent: Inotify::init() to create a handle, .watches().add(path, mask) / .remove(descriptor) to manage watches, and read_events_blocking/read_events/into_event_stream to consume events, all returning io::Result. Naming maps closely to the underlying inotify concepts (WatchMask, EventMask, WatchDescriptor) which keeps the wrapper’s mental model close to the man page while still being idiomatic Rust (bitflags instead of raw ints, Result instead of errno). Getting started requires minimal boilerplate — a handle, a watch, and a read loop, as shown directly in the crate-level doctest — and the README plus two runnable examples (watch.rs, stream.rs) cover both the sync and async usage paths.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search