busybox/miscutils/inotifyd.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 * simple inotify daemon
   4 * reports filesystem changes via userspace agent
   5 *
   6 * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com>
   7 *
   8 * Licensed under GPLv2, see file LICENSE in this tarball for details.
   9 */
  10
  11/*
  12 * Use as follows:
  13 * # inotifyd /user/space/agent dir/or/file/being/watched[:mask] ...
  14 *
  15 * When a filesystem event matching the specified mask is occured on specified file (or directory)
  16 * a userspace agent is spawned and given the following parameters:
  17 * $1. actual event(s)
  18 * $2. file (or directory) name
  19 * $3. name of subfile (if any), in case of watching a directory
  20 *
  21 * E.g. inotifyd ./dev-watcher /dev:n
  22 *
  23 * ./dev-watcher can be, say:
  24 * #!/bin/sh
  25 * echo "We have new device in here! Hello, $3!"
  26 *
  27 * See below for mask names explanation.
  28 */
  29
  30#include "libbb.h"
  31#include <sys/inotify.h>
  32
  33static const char mask_names[] ALIGN1 =
  34        "a"     // 0x00000001   File was accessed
  35        "c"     // 0x00000002   File was modified
  36        "e"     // 0x00000004   Metadata changed
  37        "w"     // 0x00000008   Writtable file was closed
  38        "0"     // 0x00000010   Unwrittable file closed
  39        "r"     // 0x00000020   File was opened
  40        "m"     // 0x00000040   File was moved from X
  41        "y"     // 0x00000080   File was moved to Y
  42        "n"     // 0x00000100   Subfile was created
  43        "d"     // 0x00000200   Subfile was deleted
  44        "D"     // 0x00000400   Self was deleted
  45        "M"     // 0x00000800   Self was moved
  46        "\0"    // 0x00001000   (unused)
  47        // Kernel events, always reported:
  48        "u"     // 0x00002000   Backing fs was unmounted
  49        "o"     // 0x00004000   Event queued overflowed
  50        "x"     // 0x00008000   File is no longer watched (usually deleted)
  51;
  52enum {
  53        MASK_BITS = sizeof(mask_names) - 1
  54};
  55
  56int inotifyd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
  57int inotifyd_main(int argc, char **argv)
  58{
  59        int n;
  60        unsigned mask;
  61        struct pollfd pfd;
  62        char **watches; // names of files being watched
  63        const char *args[5];
  64
  65        // sanity check: agent and at least one watch must be given
  66        if (!argv[1] || !argv[2])
  67                bb_show_usage();
  68
  69        argv++;
  70        // inotify_add_watch will number watched files
  71        // starting from 1, thus watches[0] is unimportant,
  72        // and 1st file name is watches[1].
  73        watches = argv;
  74        args[0] = *argv;
  75        args[4] = NULL;
  76        argc -= 2; // number of files we watch
  77
  78        // open inotify
  79        pfd.fd = inotify_init();
  80        if (pfd.fd < 0)
  81                bb_perror_msg_and_die("no kernel support");
  82
  83        // setup watches
  84        while (*++argv) {
  85                char *path = *argv;
  86                char *masks = strchr(path, ':');
  87
  88                mask = 0x0fff; // assuming we want all non-kernel events
  89                // if mask is specified ->
  90                if (masks) {
  91                        *masks = '\0'; // split path and mask
  92                        // convert mask names to mask bitset
  93                        mask = 0;
  94                        while (*++masks) {
  95                                const char *found;
  96                                found = memchr(mask_names, *masks, MASK_BITS);
  97                                if (found)
  98                                        mask |= (1 << (found - mask_names));
  99                        }
 100                }
 101                // add watch
 102                n = inotify_add_watch(pfd.fd, path, mask);
 103                if (n < 0)
 104                        bb_perror_msg_and_die("add watch (%s) failed", path);
 105                //bb_error_msg("added %d [%s]:%4X", n, path, mask);
 106        }
 107
 108        // setup signals
 109        bb_signals(BB_FATAL_SIGS, record_signo);
 110
 111        // do watch
 112        pfd.events = POLLIN;
 113        while (1) {
 114                int len;
 115                void *buf;
 116                struct inotify_event *ie;
 117 again:
 118                if (bb_got_signal)
 119                        break;
 120                n = poll(&pfd, 1, -1);
 121                // Signal interrupted us?
 122                if (n < 0 && errno == EINTR)
 123                        goto again;
 124                // Under Linux, above if() is not necessary.
 125                // Non-fatal signals, e.g. SIGCHLD, when set to SIG_DFL,
 126                // are not interrupting poll().
 127                // Thus we can just break if n <= 0 (see below),
 128                // because EINTR will happen only on SIGTERM et al.
 129                // But this might be not true under other Unixes,
 130                // and is generally way too subtle to depend on.
 131                if (n <= 0) // strange error?
 132                        break;
 133
 134                // read out all pending events
 135                // (NB: len must be int, not ssize_t or long!)
 136                xioctl(pfd.fd, FIONREAD, &len);
 137#define eventbuf bb_common_bufsiz1
 138                ie = buf = (len <= sizeof(eventbuf)) ? eventbuf : xmalloc(len);
 139                len = full_read(pfd.fd, buf, len);
 140                // process events. N.B. events may vary in length
 141                while (len > 0) {
 142                        int i;
 143                        // cache relevant events mask
 144                        unsigned m = ie->mask & ((1 << MASK_BITS) - 1);
 145                        if (m) {
 146                                char events[MASK_BITS + 1];
 147                                char *s = events;
 148                                for (i = 0; i < MASK_BITS; ++i, m >>= 1) {
 149                                        if ((m & 1) && (mask_names[i] != '\0'))
 150                                                *s++ = mask_names[i];
 151                                }
 152                                *s = '\0';
 153//                              bb_error_msg("exec %s %08X\t%s\t%s\t%s", args[0],
 154//                                      ie->mask, events, watches[ie->wd], ie->len ? ie->name : "");
 155                                args[1] = events;
 156                                args[2] = watches[ie->wd];
 157                                args[3] = ie->len ? ie->name : NULL;
 158                                wait4pid(xspawn((char **)args));
 159                                // we are done if all files got final x event
 160                                if (ie->mask & 0x8000) {
 161                                        if (--argc <= 0)
 162                                                goto done;
 163                                        inotify_rm_watch(pfd.fd, ie->wd);
 164                                }
 165                        }
 166                        // next event
 167                        i = sizeof(struct inotify_event) + ie->len;
 168                        len -= i;
 169                        ie = (void*)((char*)ie + i);
 170                }
 171                if (eventbuf != buf)
 172                        free(buf);
 173        } // while (1)
 174 done:
 175        return bb_got_signal;
 176}
 177