๐ Select Language | ้ๆฉ่ฏญ่จ
fsmon is a real-time Linux filesystem change monitor powered by fanotify. It watches files and directories, captures every event (create, modify, delete, move, attribute change, etc.), and attributes each change back to the process that caused it โ including the PID, command name, and user. Unlike polling-based tools (e.g. watch, find -newer), fsmon receives push notifications from the kernel with zero busy-wait. Unlike generic audit tools (e.g. auditd, inotifywait), fsmon is purpose-built for developer workflows: live troubleshooting, deployment forensics, security incident reconstruction, and system behavior analysis โ with sub-5MB memory footprint, nanosecond-fast in-process filters, and standard JSONL logs you can pipe to jq.
Features
- Real-time Monitoring: Captures 14 fanotify events (default: 8 core change events,
--all-eventsfor all 14) - Process Attribution: Tracks PID, command name, and user for every file change โ even short-lived processes like
touch,rm,mv - Recursive Monitoring: Watch entire directory trees with automatic tracking of newly created subdirectories
- Complete Deletion Capture: Captures every file deleted during
rm -rfvia persistent directory handle cache - High Performance: Rust + Tokio, <5MB memory footprint, zero-copy FID event parsing, binary-search log querying
- Flexible Capture Filtering: Filter at capture time by event type, size, path pattern, and process name โ all in-process, nanosecond-fast, no fork.
- Live Updates: Add/remove paths while daemon runs โ no restart needed.
Quick Start
Prerequisites
- OS: Linux 5.9+ (requires fanotify FID mode)
- Tested Filesystems: ext4, XFS, btrfs
- Build: Rust toolchain (
cargo)
# Verify kernel version
# Install Rust if needed
|
Installation
# Build from source
# Or install from crates.io
Fanotify requires root privileges for the daemon:
A Complete Walkthrough
Monitor a web project directory, see what gets logged, then use standard Unix tools to filter and clean.
# Terminal 1: start the daemon (sudo for fanotify)
&
# Terminal 1 (or another): add paths to monitor
# Monitor /var/www/myapp recursively, only MODIFY + CREATE events,
# exclude editor temp files, only capture nginx and vim processes
# List what's being monitored
# โ /var/www/myapp | types=MODIFY,CREATE | recursive | min_size=- | exclude-path=*.swp | exclude-cmd=- | only-cmd=nginx,vim | events=filtered
Now trigger some real file changes:
# Terminal 2: simulate real usage
Look at what fsmon captured:
# The raw log โ one JSONL line per event
# โ {"time":"2026-05-07T10:00:01+00:00","event_type":"MODIFY","path":"/var/www/myapp/index.html","pid":1234,"cmd":"nginx","user":"www-data","file_size":21,"monitored_path":"/var/www/myapp"}
# โ {"time":"2026-05-07T10:00:03+00:00","event_type":"DELETE","path":"/var/www/myapp/index.html","pid":5678,"cmd":"rm","user":"deploy","file_size":0,"monitored_path":"/var/www/myapp"}
# โ {"time":"2026-05-07T10:00:05+00:00","event_type":"CREATE","path":"/var/www/myapp/.config.json.swp","pid":9012,"cmd":"vim","user":"dev","file_size":4096,"monitored_path":"/var/www/myapp"}
Notice: vim's .swp was captured but won't be logged โ the --exclude "*.swp" filter drops it before writing. That means it never touches disk.
Query with pipe
Now use standard tools, not fsmon options:
# What did nginx do in the last hour?
|
# What files were deleted?
|
# Who made the biggest changes?
|
# Real-time tail with filter (watch for deployments)
|
No built-in --pid, --cmd, --user, --sort flags needed โ jq does it all.
Clean with safety
# Preview what would be deleted (config default: keep 30 days)
# Actually clean with custom retention
# Or just use Unix tools directly on the files
# Delete events older than 2026-04-01:
|
# Trim to last 500 lines per log file
for; do
&&
done
# Stop the daemon
File Locations
| Purpose | Path | Format |
|---|---|---|
| Infrastructure config | ~/.config/fsmon/config.toml |
TOML (generated via fsmon generate) |
| Path database | ~/.local/share/fsmon/managed.jsonl |
JSONL (one entry per line) |
| Event logs | ~/.local/state/fsmon/*_log.jsonl |
JSONL (one event per line) |
| Unix socket | /tmp/fsmon-<UID>.sock |
TOML over stream |
Both the store path and log directory are configurable in ~/.config/fsmon/config.toml
(see [managed].file and [logging].dir).
The daemon runs as root (via sudo) but resolves your original user's home directory
via SUDO_UID + getpwuid_r, so it writes to /home/<you>/... not /root/....
Note for vfat/exfat/NFS users: The daemon tries to chown log files back to your user. Filesystems without standard Unix ownership (vfat, exfat, NFS with no_root_squash off) don't support this. Logs remain owned by root. If
fsmon cleanfails as a normal user, runsudo fsmon cleanor use the Unix tools directly on the.jsonlfiles.
Auto-start on Boot (Optional)
fsmon does not install a systemd service. The daemon requires sudo (root) for fanotify. To start automatically on login, add to crontab with passwordless sudo configured:
&
Note: Use
sudo crontab -e(root's crontab) โ the daemon needs root privileges. Add thefsmoncommand to sudoers with NOPASSWD if using a user crontab instead.
Complete Commands
daemon
Start the fsmon daemon โ requires sudo for fanotify.
sudo fsmon daemon Start daemon in foreground
sudo fsmon daemon & Start daemon in background
Config: ~/.config/fsmon/config.toml
Managed paths: ~/.local/share/fsmon/managed.jsonl
Log dir: ~/.local/state/fsmon/
Socket: /tmp/fsmon-<UID>.sock
add
Add a path to the monitoring list. No sudo needed.
fsmon add <path> Monitor a path
fsmon add <path> -r Monitor recursively
fsmon add <path> --types MODIFY,CREATE Filter by event types
fsmon add <path> --exclude "*.swp" Exclude path patterns
fsmon add <path> --min-size 1MB Minimum file size change
fsmon add <path> --exclude-cmd rsync Exclude by process name
fsmon add <path> --only-cmd nginx,vim Only capture these processes
fsmon add <path> --all-events Capture all 14 fanotify events
All capture filters run inside the daemon process (nanosecond-fast, no fork). Events that don't match never touch disk.
remove
Remove a path from the monitoring list. No sudo needed.
fsmon remove <path> Remove a monitored path
managed
List all monitored paths with their filtering configuration.
fsmon managed Show all monitored paths
query
Query historical events from log files. Output is JSONL โ pipe to jq for filtering.
fsmon query Query all log files
fsmon query --path /tmp Query specific path's log
fsmon query --path /tmp --path /var Query multiple paths
fsmon query --since 1h Events from last hour
fsmon query --since "2026-05-01T00:00:00Z" From absolute time
fsmon query --until 30m Events until 30 minutes ago
fsmon query --since 1h --until now Time range
Examples with jq:
|
|
|
clean
Clean historical log files. Defaults from config.toml: keep_days=30, max_size=1GB.
)
Priority: CLI arg > config.toml > code default (30)
generate
Generate a default configuration file at ~/.config/fsmon/config.toml.
fsmon generate Create default config
fsmon generate -f Overwrite existing config
Configuration
Auto-generated on first daemon start or via fsmon generate.
# fsmon configuration file
#
# Infrastructure paths for fsmon. Monitored paths are managed separately
# via 'fsmon add' / 'fsmon remove' and persisted in [managed].file.
# All paths support ~ expansion. <UID> is replaced with the numeric UID at runtime.
[]
# Path to the auto-managed monitored paths database.
= "~/.local/share/fsmon/managed.jsonl"
[]
# Directory containing per-path log files (named by path hash).
= "~/.local/state/fsmon"
# Safety nets: keep at most 30 days, max 1GB per log file.
= 30
= "1GB"
[]
# Unix socket path for daemon-CLI live communication.
= "/tmp/fsmon-<UID>.sock"
Event Types
Default captures 8 core events. Use --all-events for all 14.
Default (8): CLOSE_WRITE, ATTRIB, CREATE, DELETE, DELETE_SELF, MOVED_FROM, MOVED_TO, MOVE_SELF
Additional (6, via --all-events): ACCESS, MODIFY, OPEN, OPEN_EXEC, CLOSE_NOWRITE, FS_ERROR
Architecture
Linux Kernel (fanotify)
โ FID events pushed to queue
โ tokio reads events asynchronously
โ fid_parser resolves paths (two-pass + dir cache)
โ Monitor filters (types, size, path pattern, cmd pattern)
โ JSONL โ per-path log files (*_log.jsonl)
User pipe:
cat/ tail *.jsonl โ jq โ your custom logic
Source Tree
src/
โโโ bin/fsmon.rs CLI: daemon, add, remove, managed, query, clean, generate
โโโ lib.rs FileEvent, EventType, clean engine, temp file safety
โโโ config.rs Infrastructure config, SUDO_UID home resolution
โโโ managed.rs Managed paths database (JSONL)
โโโ monitor.rs Fanotify loop, socket handler, all capture filters
โโโ fid_parser.rs Low-level FID event parsing, two-pass path recovery
โโโ dir_cache.rs Directory handle cache for rm -rf recovery
โโโ proc_cache.rs Netlink proc connector (short-lived process attribution)
โโโ query.rs Binary-search log query, JSONL output
โโโ socket.rs Unix socket protocol (TOML), error classification
โโโ utils.rs Size/time parsing, uid lookup, pathโlog name hash
โโโ help.rs Help text for all commands