Monitoring file system activity with file-watchers
1 Quick Start
watch-directories
suggest-approach
2 Synchronization
file-activity-channel
file-watcher-status-channel
file-watcher-channel-try-get
file-watcher-channel-get
3 Detecting changes without concern for root cause
apathetic-watch
4 Poll-based file monitoring
robust-watch
robust-poll-milliseconds
5 Verbose file-level monitoring
intensive-watch
7.3

Monitoring file system activity with file-watchers

Sage Gerard

 (require file-watchers) package: file-watchers

Use file-watchers to audit and react to file activity in a system.

1 Quick Start

Use the following Racket definitions with your chosen directories.

(require file-watchers)
 
(define watcher (watch-directories '("/path/to/dir")))

By default, lists describing file activity from the watched directory will appear via displayln.

procedure

(watch-directories [directories    
  on-activity    
  on-status    
  thread-maker])  void?
  directories : (listof directory-exists?)
   = (list (current-directory))
  on-activity : (-> list? any) = displayln
  on-status : (-> list? any) = displayln
  thread-maker : (-> path? thread?)
   = (suggest-approach #:apathetic #f)
Starts threads using thread-maker to watch each given directory. Meant for use with file-watcher procedures, namely apathetic-watch, intensive-watch, or robust-watch. This will block the current thread until all threads created with thread-maker terminate.

procedure

(suggest-approach #:apathetic apathetic)  procedure?

  apathetic : boolean?
Returns a file watcher procedure depending on the output of (system-type 'fs-change). If apathetic is true, apathetic-watch will be returned instead of intensive-watch in the event that file-level monitoring is supported.

If file change events are not supported on the operating system or if file-level monitoring is unavailable, then robust-watch is returned.

2 Synchronization

All file monitoring occurs in at least one thread. Activity and status information are each conveyed on a dedicated asynchronous channel. For more, see Buffered Asynchronous Channels.

Each channel message is a list that starts with a symbol for the associated file monitoring method, followed by a symbol indicating the kind of activity or status reported. For example, an apathetic-watch will convey that it is watching a directory and a change was detected somewhere inside it.

'(apathetic watching /path/to/dir)

A 'watching status comes from file-watcher-status-channel, while detected file activity comes from file-activity-channel.

value

file-activity-channel : (parameter/c async-channel?)

A parameter for a channel that reports file system activity depending on the monitoring approach.

value

file-watcher-status-channel : (parameter/c async-channel?)

A parameter for a channel that reports a specific watchers status. The meaning of a status depends on how a watcher carries out its task.

procedure

(file-watcher-channel-try-get)  (or/c boolean? list?)

Returns the next available message from file-watcher-status-channel, or file-activity-channel, in that order. Returns #f if no message is available.

procedure

(file-watcher-channel-get)  (or/c boolean? list?)

Waits for and returns the next available message from file-watcher-status-channel, or file-activity-channel.

3 Detecting changes without concern for root cause

file-watcher

(apathetic-watch path)  thread?

  path : directory-exists?
An apathetic thread recursively scans the given directory and waits for the first to trigger a filesystem-change-evt.

An apathetic thread reports a (list 'apathetic 'watching path) status on file-watcher-status-channel each time it starts waiting for a change. There are no other status messages and the thread will terminate when it can no longer access the directory located at the given path.

file-activity-channel will only report (list 'apathetic 'change path) when any change is detected.

The below example starts an apathetic watch thread, waits for the thread to report that it is watching "dir", then deletes "dir". The apathetic watcher thread will report that the change occurred on file-activity-channel before terminating, since "dir" was the root path for the watching thread.

(define apathetic-watcher (apathetic-watch "dir"))
 
(sync/enable-break (file-watcher-status-channel))
(delete-directory "dir")
(displayln (sync/enable-break (file-activity-channel)))
 
(thread-wait apathetic-watcher)
(displayln (thread-dead? apathetic-watcher))

4 Poll-based file monitoring

file-watcher

(robust-watch path)  thread?

  path : directory-exists?
A robust watch operates on a polling mechanism that compares recursive listings of the given directory path to report changes. This approach is cross-platform, but cannot detect any activity between filesystem polls.

Furthermore, robust-watch only detects changes in file permissions and access time.

robust-watch only reports 'add, 'change, and 'remove events on file-activity-channel. It does not report status information on file-watcher-status-channel.

A parameter for the number of milliseconds a robust watch poll should wait before comparing directory listings. This defaults to 250.

5 Verbose file-level monitoring

file-watcher

(intensive-watch path)  thread?

  path : directory-exists?
An intensive watch dedicates a thread to each file in the directory to monitor with a separate filesystem-change-evt. Due to the resource-hungry nature of the model, an intensive watch may warrant a dedicated custodian.

If a link file is accessed in a way that impacts the link’s target, both the link file and the target file will be marked as changed.

Status information appears on file-watcher-status-channel under the following rules:

Activity information appears on file-activity-channel under the following rules: