xref: /aosp_15_r20/external/toybox/toys/other/inotifyd.c (revision cf5a6c84e2b8763fc1a7db14496fd4742913b199)
1 /* inotifyd.c - inotify daemon.
2  *
3  * Copyright 2013 Ashwini Kumar <[email protected]>
4  * Copyright 2013 Kyungwan Han <[email protected]>
5  *
6  * No Standard.
7 
8 USE_INOTIFYD(NEWTOY(inotifyd, "<2", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LINEBUF))
9 
10 config INOTIFYD
11   bool "inotifyd"
12   default y
13   help
14     usage: inotifyd PROG FILE[:MASK] ...
15 
16     When a filesystem event matching MASK occurs to a FILE, run PROG as:
17 
18       PROG EVENTS FILE [DIRFILE]
19 
20     If PROG is "-" events are sent to stdout.
21 
22     This file is:
23       a  accessed    c  modified    e  metadata change  w  closed (writable)
24       r  opened      D  deleted     M  moved            0  closed (unwritable)
25       u  unmounted   o  overflow    x  unwatchable
26 
27     A file in this directory is:
28       m  moved in    y  moved out   n  created          d  deleted
29 
30     When x event happens for all FILEs, inotifyd exits (after waiting for PROG).
31 */
32 
33 #define FOR_inotifyd
34 #include "toys.h"
35 #include <sys/inotify.h>
36 
inotifyd_main(void)37 void inotifyd_main(void)
38 {
39   struct pollfd fds;
40   char *prog_args[5], **ss = toys.optargs;
41   char *masklist ="acew0rmyndDM uox";
42 
43   fds.events = POLLIN;
44 
45   *prog_args = *toys.optargs;
46   prog_args[4] = 0;
47   if ((fds.fd = inotify_init()) == -1) perror_exit(0);
48 
49   // Track number of watched files. First one was program to run.
50   toys.optc--;
51 
52   while (*++ss) {
53     char *path = *ss, *masks = strchr(*ss, ':');
54     int i, mask = 0;
55 
56     if (!masks) mask = 0xfff; // default to all
57     else{
58       for (*masks++ = 0; *masks; masks++) {
59         i = stridx(masklist, *masks);;
60         if (i == -1) error_exit("bad mask '%c'", *masks);
61         mask |= 1<<i;
62       }
63     }
64 
65     // This returns increasing numbers starting from 1, which coincidentally
66     // is the toys.optargs position of the file. (0 is program to run.)
67     if (inotify_add_watch(fds.fd, path, mask) < 0) perror_exit_raw(path);
68   }
69 
70   for (;;) {
71     int ret = 0, len;
72     void *buf = 0;
73     struct inotify_event *event;
74 
75     // Read next event(s)
76     ret = poll(&fds, 1, -1);
77     if (ret < 0 && errno == EINTR) continue;
78     if (ret <= 0) break;
79     xioctl(fds.fd, FIONREAD, &len);
80     event = buf = xmalloc(len);
81     len = readall(fds.fd, buf, len);
82 
83     // Loop through set of events.
84     for (;;) {
85       int left = len - (((char *)event)-(char *)buf),
86           size = sizeof(struct inotify_event);
87 
88       // Don't dereference event if ->len is off end of bufer
89       if (left >= size) size += event->len;
90       if (left < size) break;
91 
92       if (event->mask) {
93         char *s = toybuf, *m;
94 
95         for (m = masklist; *m; m++)
96           if (event->mask & (1<<(m-masklist))) *s++ = *m;
97         *s = 0;
98 
99         if (**prog_args == '-' && !prog_args[0][1]) {
100           xprintf("%s\t%s\t%s\n" + 3*!event->len, toybuf,
101               toys.optargs[event->wd], event->name);
102         } else {
103           prog_args[1] = toybuf;
104           prog_args[2] = toys.optargs[event->wd];
105           prog_args[3] = event->len ? event->name : 0;
106           xrun(prog_args);
107         }
108 
109         if (event->mask & IN_IGNORED) {
110           if (--toys.optc <= 0) {
111             free(buf);
112 
113             goto done;
114           }
115           inotify_rm_watch(fds.fd, event->wd);
116         }
117       }
118       event = (void*)(size + (char*)event);
119     }
120     free(buf);
121   }
122 
123 done:
124   toys.exitval = !!toys.signal;
125 }
126