FreeBSD daemon that watches files and runs commands when they change
git clone
Log | Files | Refs | README | LICENSE (5166B)

      1 # Overview
      3 `filewatcherd` is a daemon inspired by cron, that run commands based on
      4 file changes instead of time.
      6 In principle it is similar to `incron`, but it's simpler, more limited,
      7 and does not depend on anything outside of FreeBSD base.
      9 # Watchtab
     11 Usage of `filewatcherd` is quite straightforward: the daemon has a few
     12 basic command-line options, and takes a _watchtab_ file as main input.
     14 The watchtab is heavily inspired from `crontab`. Blank lines are ignored,
     15 leading and trailing blanks in line are ignored, line starting with a
     16 hash sign (`#`) are ignored as comments.
     18 Environment lines are defined as having an equal sign (`=`) before any
     19 backslash (`\\`) or tabulation character. They represent environment
     20 variables available for commands, and only affect the entries below them.
     22 Entry lines consist of 3 to 6 tabulation-separated fields. A complete line
     23 contains the following fields in respective order:
     25 1. Path of the file to watch
     26 2. Event set to consider
     27 3. Delay between the first triggering event and command run
     28 4. User, and optionally group, to set for the command
     29 5. `chroot` to set for the command
     30 6. The command itself
     32 When only 5 fields are present, `chroot` is skipped. When there are only
     33 4 fields, user is also skipped. When there are only 3 field, delay is
     34 considered 0.
     36 In path, `chroot` and command fields, backslashes (`\\`) act as an escape
     37 character, allowing to embed tabulations, backslashes and/or equal signs
     38 in those fields without misinterpretation.
     40 The event set can be a single star sign (`*`) to mean all available events,
     41 or a list of any number of event names separated by a single non-letter
     42 byte. The available events are `delete`, `write`, `extend`, `attrib`,
     43 `link`, `rename` and `revoke`, with semantics matching those of
     44 similar-named `fflags` for vnode filter.
     46 The delay is given in seconds and can be fractional, up to the nanosecond
     47 (though most system probably do not have such a resolution in
     48 `nanosleep(3)`).
     50 The user can be a login string or a numeric id, and is optionally followed
     51 by a group string or numeric id after a colon (`:`). When specified, those
     52 must exist and have a matching `passwd` or `group` entry.
     54 The command is run in a clean environment, containing only variables
     55 explicitly declared in the watchtab file, and `SHELL`, `PATH`, `HOME`,
     56 `TRIGGER`, `USER`, `LOGNAME`.
     58   * `SHELL` and `PATH` default respectively to `/bin/sh` and
     59 `/usr/bin:/bin`, but they can be overwritten in the watchtab.
     60   * `HOME` default to the home directory of the user running the command,
     61 but can be overwritten in the watchtab.
     62   * `USER` and `LOGNAME` are both forced to the login name of the user
     63 running the command, and values provided in the watchtab are ignored.
     64   * `TRIGGER` is forced to the path of the file triggering the event
     65 (seen from outside the `chroot`), ignoring any value provided in the
     66 watchtab.
     68 The watchtab is automatically watched by `filewatcherd` itself, and is
     69 automatically reloaded when it changes.
     71 # Internals
     73 ## Source organization
     75 `filewatcherd` is split between 4 `.c` modules:
     77   * `log.c` implements logging functions, which means all user-facing
     78 output
     79   * `watchtab.c` implements watchtab parsing and upkeep of structures
     80 related to watchtab entries
     81   * `run.c` implements actual execution of a watchtab entry
     82   * `filewatcherd.c` implements the event loop directly in `main()`
     83 function
     85 ## Event loop overview
     87 ### Watchtab entries
     89 Watchtab entries oscillate between two states:
     91   * waiting for an `EVFILT_VNODE` event described in the watchtab,
     92 which triggers execution of the associated command
     93   * waiting for an `EVFILT_PROC` event that signals the end of the command
     94 to switch back to `EVFILT_VNODE` wait.
     96 Events are not reused, at each step of cycle a new one is added to the
     97 kernel queue with `EV_ONESHOT` flag.
     99 This architecture guarantees that there cannot be more than one file
    100 descriptor per watchtab entry or more than one process started per watchtab
    101 entry. System resources consumed by `filewatcherd` are therefore bounded
    102 by the watchtab length.
    104 Whenever an error happens, e.g. when spawning the command or opening the
    105 watched path, the cycle is broken and the watchtab entry becomes inactive
    106 until the watchtab is reloaded.
    108 There is currently no way to re-enable a single inactive watchtab entry.
    110 ### Watchtab watcher
    112 The watchtab file itself is also watched by `filewatcherd`, in a process
    113 similar to a watchtab entry except that `EVFILT_VNODE` events trigger an
    114 `EVFILT_TIMER` addition before reloading the file.
    116 Errors are handled so that this cycle can only be broken by a failure to
    117 insert an event in the queue:
    119   * When the watchtab file cannot be opened, the timer event is left in the
    120 kernel queue to trigger another attempt after the delay. To prevent log
    121 spamming, only the first failure is logged, even though subsequent failures
    122 might have other causes.
    123   * When the watchtab file is opened, the timer event is removed and an
    124 `EVFILT_VNODE` filter is added to track watchtab changes. Should a parse
    125 error occurs, the old watchtab is used instead, and a subsequent change in
    126 the watchtab file will trigger a reload.