Update remaining menuconfig items with approximate applet sizes
[oweals/busybox.git] / miscutils / inotifyd.c
index 656510453a3269ac84936c7112f9f9d6e60d49c1..2e0163e35640f2d696a57d5dbb7b08f51c8f2919 100644 (file)
@@ -5,14 +5,14 @@
  *
  * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com>
  *
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
 /*
  * Use as follows:
  * # inotifyd /user/space/agent dir/or/file/being/watched[:mask] ...
  *
- * When a filesystem event matching the specified mask is occured on specified file (or directory)
+ * When a filesystem event matching the specified mask is occurred on specified file (or directory)
  * a userspace agent is spawned and given the following parameters:
  * $1. actual event(s)
  * $2. file (or directory) name
  *
  * See below for mask names explanation.
  */
+//config:config INOTIFYD
+//config:      bool "inotifyd (3.5 kb)"
+//config:      default n  # doesn't build on Knoppix 5
+//config:      help
+//config:        Simple inotify daemon. Reports filesystem changes. Requires
+//config:        kernel >= 2.6.13
+
+//applet:IF_INOTIFYD(APPLET(inotifyd, BB_DIR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_INOTIFYD) += inotifyd.o
+
+//usage:#define inotifyd_trivial_usage
+//usage:       "PROG FILE1[:MASK]..."
+//usage:#define inotifyd_full_usage "\n\n"
+//usage:       "Run PROG on filesystem changes."
+//usage:     "\nWhen a filesystem event matching MASK occurs on FILEn,"
+//usage:     "\nPROG ACTUAL_EVENTS FILEn [SUBFILE] is run."
+//usage:     "\nIf PROG is -, events are sent to stdout."
+//usage:     "\nEvents:"
+//usage:     "\n       a       File is accessed"
+//usage:     "\n       c       File is modified"
+//usage:     "\n       e       Metadata changed"
+//usage:     "\n       w       Writable file is closed"
+//usage:     "\n       0       Unwritable file is closed"
+//usage:     "\n       r       File is opened"
+//usage:     "\n       D       File is deleted"
+//usage:     "\n       M       File is moved"
+//usage:     "\n       u       Backing fs is unmounted"
+//usage:     "\n       o       Event queue overflowed"
+//usage:     "\n       x       File can't be watched anymore"
+//usage:     "\nIf watching a directory:"
+//usage:     "\n       y       Subfile is moved into dir"
+//usage:     "\n       m       Subfile is moved out of dir"
+//usage:     "\n       n       Subfile is created"
+//usage:     "\n       d       Subfile is deleted"
+//usage:     "\n"
+//usage:     "\ninotifyd waits for PROG to exit."
+//usage:     "\nWhen x event happens for all FILEs, inotifyd exits."
 
 #include "libbb.h"
+#include "common_bufsiz.h"
 #include <sys/inotify.h>
 
 static const char mask_names[] ALIGN1 =
        "a"     // 0x00000001   File was accessed
        "c"     // 0x00000002   File was modified
        "e"     // 0x00000004   Metadata changed
-       "w"     // 0x00000008   Writtable file was closed
-       "0"     // 0x00000010   Unwrittable file closed
+       "w"     // 0x00000008   Writable file was closed
+       "0"     // 0x00000010   Unwritable file closed
        "r"     // 0x00000020   File was opened
        "m"     // 0x00000040   File was moved from X
        "y"     // 0x00000080   File was moved to Y
@@ -44,6 +83,7 @@ static const char mask_names[] ALIGN1 =
        "D"     // 0x00000400   Self was deleted
        "M"     // 0x00000800   Self was moved
        "\0"    // 0x00001000   (unused)
+       // Kernel events, always reported:
        "u"     // 0x00002000   Backing fs was unmounted
        "o"     // 0x00004000   Event queued overflowed
        "x"     // 0x00008000   File is no longer watched (usually deleted)
@@ -52,31 +92,39 @@ enum {
        MASK_BITS = sizeof(mask_names) - 1
 };
 
-extern int inotify_init(void);
-extern int inotify_add_watch(int fd, const char *path, uint32_t mask);
-
 int inotifyd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
-int inotifyd_main(int argc UNUSED_PARAM, char **argv)
+int inotifyd_main(int argc, char **argv)
 {
        int n;
-       unsigned mask = IN_ALL_EVENTS; // assume we want all events
+       unsigned mask;
        struct pollfd pfd;
-       char **watched = ++argv; // watched name list
-       const char *args[] = { *argv, NULL, NULL, NULL, NULL };
+       char **watches; // names of files being watched
+       const char *args[5];
 
        // sanity check: agent and at least one watch must be given
-       if (!argv[0] || !argv[1])
+       if (!argv[1] || !argv[2])
                bb_show_usage();
 
+       argv++;
+       // inotify_add_watch will number watched files
+       // starting from 1, thus watches[0] is unimportant,
+       // and 1st file name is watches[1].
+       watches = argv;
+       args[0] = *argv;
+       args[4] = NULL;
+       argc -= 2; // number of files we watch
+
        // open inotify
        pfd.fd = inotify_init();
        if (pfd.fd < 0)
                bb_perror_msg_and_die("no kernel support");
 
-       // setup watched
+       // setup watches
        while (*++argv) {
                char *path = *argv;
                char *masks = strchr(path, ':');
+
+               mask = 0x0fff; // assuming we want all non-kernel events
                // if mask is specified ->
                if (masks) {
                        *masks = '\0'; // split path and mask
@@ -102,10 +150,9 @@ int inotifyd_main(int argc UNUSED_PARAM, char **argv)
        // do watch
        pfd.events = POLLIN;
        while (1) {
-               ssize_t len;
+               int len;
                void *buf;
                struct inotify_event *ie;
-
  again:
                if (bb_got_signal)
                        break;
@@ -124,9 +171,11 @@ int inotifyd_main(int argc UNUSED_PARAM, char **argv)
                        break;
 
                // read out all pending events
-               xioctl(pfd.fd, FIONREAD, &len);
+               // (NB: len must be int, not ssize_t or long!)
 #define eventbuf bb_common_bufsiz1
-               ie = buf = (len <= sizeof(eventbuf)) ? eventbuf : xmalloc(len);
+               setup_common_bufsiz();
+               xioctl(pfd.fd, FIONREAD, &len);
+               ie = buf = (len <= COMMON_BUFSIZE) ? eventbuf : xmalloc(len);
                len = full_read(pfd.fd, buf, len);
                // process events. N.B. events may vary in length
                while (len > 0) {
@@ -141,12 +190,26 @@ int inotifyd_main(int argc UNUSED_PARAM, char **argv)
                                                *s++ = mask_names[i];
                                }
                                *s = '\0';
-//                             bb_error_msg("exec %s %08X\t%s\t%s\t%s", args[0],
-//                                     ie->mask, events, watched[ie->wd], ie->len ? ie->name : "");
-                               args[1] = events;
-                               args[2] = watched[ie->wd];
-                               args[3] = ie->len ? ie->name : NULL;
-                               wait4pid(xspawn((char **)args));
+                               if (LONE_CHAR(args[0], '-')) {
+                                       /* "inotifyd - FILE": built-in echo */
+                                       printf(ie->len ? "%s\t%s\t%s\n" : "%s\t%s\n", events,
+                                                       watches[ie->wd],
+                                                       ie->name);
+                                       fflush(stdout);
+                               } else {
+//                                     bb_error_msg("exec %s %08X\t%s\t%s\t%s", args[0],
+//                                             ie->mask, events, watches[ie->wd], ie->len ? ie->name : "");
+                                       args[1] = events;
+                                       args[2] = watches[ie->wd];
+                                       args[3] = ie->len ? ie->name : NULL;
+                                       spawn_and_wait((char **)args);
+                               }
+                               // we are done if all files got final x event
+                               if (ie->mask & 0x8000) {
+                                       if (--argc <= 0)
+                                               goto done;
+                                       inotify_rm_watch(pfd.fd, ie->wd);
+                               }
                        }
                        // next event
                        i = sizeof(struct inotify_event) + ie->len;
@@ -155,7 +218,7 @@ int inotifyd_main(int argc UNUSED_PARAM, char **argv)
                }
                if (eventbuf != buf)
                        free(buf);
-       }
-
-       return EXIT_SUCCESS;
+       } // while (1)
+ done:
+       return bb_got_signal;
 }