Haiku Node Monitoring: Receiving Live File-System Changes Through the Storage Kit
A practical model of Haiku node monitoring, including watched objects, BMessages, event races, recursive trees, and recovery after missed changes.
Haiku’s Storage Kit can notify an application when files, directories, or volumes change. That sounds like a simple callback facility, but the useful mental model is a stream of hints about namespace and node activity—not a transaction log and not a replacement for reading the file system. Correct clients combine watch_node() with ordinary Storage Kit objects, validate every message, and know when to rescan.
Nodes, entries, and paths are different identities
A path names an entry in a directory. A node identifies the underlying file-system object. Renaming an entry changes its pathname without necessarily changing the node; a hard link can give one node multiple entries. Haiku therefore represents watched objects with stable device and node identifiers and reports namespace operations with the parent directory and entry name needed to reconstruct what moved.
Before starting a watch, resolve the target with BEntry, obtain its node_ref, and decide which event classes matter. Watching every possible event makes race handling harder and wastes ports and messages. The public flags include name changes, stat changes, attribute changes, directory changes, and volume changes; B_WATCH_ALL is convenient for a diagnostic tool but usually too broad for a production component.
node_ref ref;
BEntry entry("/boot/home/config/settings/MyApp");
if (entry.GetNodeRef(&ref) == B_OK) {
status_t status = watch_node(&ref,
B_WATCH_NAME | B_WATCH_STAT | B_WATCH_ATTR,
BMessenger(this));
if (status != B_OK)
fprintf(stderr, "watch_node: %s\n", strerror(status));
}
The receiver must be a live BHandler attached to a running BLooper; notifications arrive as B_NODE_MONITOR messages. Check every field with FindInt32(), FindInt64(), or FindString() instead of assuming one payload shape. The opcode determines which additional fields are meaningful.
A notification is a reason to re-read state
Suppose an editor writes a temporary file and atomically renames it over a settings file. A client may see create, move, and remove messages rather than a single “contents changed” event. Another process can make a second change before the handler reads the first message. Treat the message as a trigger to reopen the current entry, verify its type and identity, and derive state from disk.
There is also an unavoidable setup race: state can change between the initial directory scan and installation of the watch. A robust directory tracker scans, installs the watch, and reconciles again, or installs first and accepts that queued events may refer to objects not present in its first snapshot. Keep updates idempotent so either order converges.
Recursive watching is an application policy. Watching a directory reports changes to its immediate entries; it does not magically attach watches to an unlimited descendant tree. A recursive indexer must add a watch for each discovered subdirectory, add one when a new directory appears, remove bookkeeping when one disappears, and impose resource limits for hostile or unexpectedly large trees.
Lifecycle and failure handling
Call watch_node() with B_STOP_WATCHING when the consumer no longer needs the stream, or use stop_watching() for all watches associated with a messenger. Teardown order matters: stop delivery before destroying the handler or looper. If the target volume is unmounted, the receiver exits, or resources are exhausted, rebuild state rather than pretending the stream was complete.
For debugging, log the opcode, device, node, directory, and name fields, then compare them with stat and the current directory listing. This separates a misunderstood rename from a missing event. Node monitoring becomes reliable when it is treated as an efficient invalidation mechanism wrapped around authoritative reads, explicit lifecycle management, and a rescan path.
Related:
- BMessage Flattening and IPC: How Haiku Moves Typed Data Between Processes
- How to Build a Haiku Replicant That Can Live on the Desktop or Deskbar
Sources: