source: MondoRescue/branches/3.3/mindi-busybox/miscutils/inotifyd.c@ 3865

Last change on this file since 3865 was 3621, checked in by Bruno Cornec, 10 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

  • Property svn:eol-style set to native
File size: 6.2 KB
Line 
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 source tree.
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//usage:#define inotifyd_trivial_usage
31//usage: "PROG FILE1[:MASK]..."
32//usage:#define inotifyd_full_usage "\n\n"
33//usage: "Run PROG on filesystem changes."
34//usage: "\nWhen a filesystem event matching MASK occurs on FILEn,"
35//usage: "\nPROG ACTUAL_EVENTS FILEn [SUBFILE] is run."
36//usage: "\nIf PROG is -, events are sent to stdout."
37//usage: "\nEvents:"
38//usage: "\n a File is accessed"
39//usage: "\n c File is modified"
40//usage: "\n e Metadata changed"
41//usage: "\n w Writable file is closed"
42//usage: "\n 0 Unwritable file is closed"
43//usage: "\n r File is opened"
44//usage: "\n D File is deleted"
45//usage: "\n M File is moved"
46//usage: "\n u Backing fs is unmounted"
47//usage: "\n o Event queue overflowed"
48//usage: "\n x File can't be watched anymore"
49//usage: "\nIf watching a directory:"
50//usage: "\n y Subfile is moved into dir"
51//usage: "\n m Subfile is moved out of dir"
52//usage: "\n n Subfile is created"
53//usage: "\n d Subfile is deleted"
54//usage: "\n"
55//usage: "\ninotifyd waits for PROG to exit."
56//usage: "\nWhen x event happens for all FILEs, inotifyd exits."
57
58#include "libbb.h"
59#include "common_bufsiz.h"
60#include <sys/inotify.h>
61
62static const char mask_names[] ALIGN1 =
63 "a" // 0x00000001 File was accessed
64 "c" // 0x00000002 File was modified
65 "e" // 0x00000004 Metadata changed
66 "w" // 0x00000008 Writable file was closed
67 "0" // 0x00000010 Unwritable file closed
68 "r" // 0x00000020 File was opened
69 "m" // 0x00000040 File was moved from X
70 "y" // 0x00000080 File was moved to Y
71 "n" // 0x00000100 Subfile was created
72 "d" // 0x00000200 Subfile was deleted
73 "D" // 0x00000400 Self was deleted
74 "M" // 0x00000800 Self was moved
75 "\0" // 0x00001000 (unused)
76 // Kernel events, always reported:
77 "u" // 0x00002000 Backing fs was unmounted
78 "o" // 0x00004000 Event queued overflowed
79 "x" // 0x00008000 File is no longer watched (usually deleted)
80;
81enum {
82 MASK_BITS = sizeof(mask_names) - 1
83};
84
85int inotifyd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
86int inotifyd_main(int argc, char **argv)
87{
88 int n;
89 unsigned mask;
90 struct pollfd pfd;
91 char **watches; // names of files being watched
92 const char *args[5];
93
94 // sanity check: agent and at least one watch must be given
95 if (!argv[1] || !argv[2])
96 bb_show_usage();
97
98 argv++;
99 // inotify_add_watch will number watched files
100 // starting from 1, thus watches[0] is unimportant,
101 // and 1st file name is watches[1].
102 watches = argv;
103 args[0] = *argv;
104 args[4] = NULL;
105 argc -= 2; // number of files we watch
106
107 // open inotify
108 pfd.fd = inotify_init();
109 if (pfd.fd < 0)
110 bb_perror_msg_and_die("no kernel support");
111
112 // setup watches
113 while (*++argv) {
114 char *path = *argv;
115 char *masks = strchr(path, ':');
116
117 mask = 0x0fff; // assuming we want all non-kernel events
118 // if mask is specified ->
119 if (masks) {
120 *masks = '\0'; // split path and mask
121 // convert mask names to mask bitset
122 mask = 0;
123 while (*++masks) {
124 const char *found;
125 found = memchr(mask_names, *masks, MASK_BITS);
126 if (found)
127 mask |= (1 << (found - mask_names));
128 }
129 }
130 // add watch
131 n = inotify_add_watch(pfd.fd, path, mask);
132 if (n < 0)
133 bb_perror_msg_and_die("add watch (%s) failed", path);
134 //bb_error_msg("added %d [%s]:%4X", n, path, mask);
135 }
136
137 // setup signals
138 bb_signals(BB_FATAL_SIGS, record_signo);
139
140 // do watch
141 pfd.events = POLLIN;
142 while (1) {
143 int len;
144 void *buf;
145 struct inotify_event *ie;
146 again:
147 if (bb_got_signal)
148 break;
149 n = poll(&pfd, 1, -1);
150 // Signal interrupted us?
151 if (n < 0 && errno == EINTR)
152 goto again;
153 // Under Linux, above if() is not necessary.
154 // Non-fatal signals, e.g. SIGCHLD, when set to SIG_DFL,
155 // are not interrupting poll().
156 // Thus we can just break if n <= 0 (see below),
157 // because EINTR will happen only on SIGTERM et al.
158 // But this might be not true under other Unixes,
159 // and is generally way too subtle to depend on.
160 if (n <= 0) // strange error?
161 break;
162
163 // read out all pending events
164 // (NB: len must be int, not ssize_t or long!)
165#define eventbuf bb_common_bufsiz1
166 setup_common_bufsiz();
167 xioctl(pfd.fd, FIONREAD, &len);
168 ie = buf = (len <= COMMON_BUFSIZE) ? eventbuf : xmalloc(len);
169 len = full_read(pfd.fd, buf, len);
170 // process events. N.B. events may vary in length
171 while (len > 0) {
172 int i;
173 // cache relevant events mask
174 unsigned m = ie->mask & ((1 << MASK_BITS) - 1);
175 if (m) {
176 char events[MASK_BITS + 1];
177 char *s = events;
178 for (i = 0; i < MASK_BITS; ++i, m >>= 1) {
179 if ((m & 1) && (mask_names[i] != '\0'))
180 *s++ = mask_names[i];
181 }
182 *s = '\0';
183 if (LONE_CHAR(args[0], '-')) {
184 /* "inotifyd - FILE": built-in echo */
185 printf(ie->len ? "%s\t%s\t%s\n" : "%s\t%s\n", events,
186 watches[ie->wd],
187 ie->name);
188 fflush(stdout);
189 } else {
190// bb_error_msg("exec %s %08X\t%s\t%s\t%s", args[0],
191// ie->mask, events, watches[ie->wd], ie->len ? ie->name : "");
192 args[1] = events;
193 args[2] = watches[ie->wd];
194 args[3] = ie->len ? ie->name : NULL;
195 spawn_and_wait((char **)args);
196 }
197 // we are done if all files got final x event
198 if (ie->mask & 0x8000) {
199 if (--argc <= 0)
200 goto done;
201 inotify_rm_watch(pfd.fd, ie->wd);
202 }
203 }
204 // next event
205 i = sizeof(struct inotify_event) + ie->len;
206 len -= i;
207 ie = (void*)((char*)ie + i);
208 }
209 if (eventbuf != buf)
210 free(buf);
211 } // while (1)
212 done:
213 return bb_got_signal;
214}
Note: See TracBrowser for help on using the repository browser.