Annotation of /trunk/mkinitrd-magellan/busybox/miscutils/inotifyd.c
Parent Directory | Revision Log
Revision 1123 -
(hide annotations)
(download)
Wed Aug 18 21:56:57 2010 UTC (13 years, 9 months ago) by niro
File MIME type: text/plain
File size: 4891 byte(s)
Wed Aug 18 21:56:57 2010 UTC (13 years, 9 months ago) by niro
File MIME type: text/plain
File size: 4891 byte(s)
-updated to busybox-1.17.1
1 | niro | 816 | /* 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 | |||
33 | static const char mask_names[] ALIGN1 = | ||
34 | "a" // 0x00000001 File was accessed | ||
35 | "c" // 0x00000002 File was modified | ||
36 | "e" // 0x00000004 Metadata changed | ||
37 | niro | 984 | "w" // 0x00000008 Writable file was closed |
38 | "0" // 0x00000010 Unwritable file closed | ||
39 | niro | 816 | "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 | niro | 984 | "\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 | niro | 816 | ; |
52 | niro | 984 | enum { |
53 | MASK_BITS = sizeof(mask_names) - 1 | ||
54 | }; | ||
55 | niro | 816 | |
56 | int inotifyd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
57 | niro | 984 | int inotifyd_main(int argc, char **argv) |
58 | niro | 816 | { |
59 | int n; | ||
60 | niro | 984 | unsigned mask; |
61 | niro | 816 | struct pollfd pfd; |
62 | niro | 984 | char **watches; // names of files being watched |
63 | const char *args[5]; | ||
64 | niro | 816 | |
65 | // sanity check: agent and at least one watch must be given | ||
66 | niro | 984 | if (!argv[1] || !argv[2]) |
67 | niro | 816 | bb_show_usage(); |
68 | |||
69 | niro | 984 | 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 | niro | 816 | // open inotify |
79 | pfd.fd = inotify_init(); | ||
80 | if (pfd.fd < 0) | ||
81 | bb_perror_msg_and_die("no kernel support"); | ||
82 | |||
83 | niro | 984 | // setup watches |
84 | niro | 816 | while (*++argv) { |
85 | char *path = *argv; | ||
86 | char *masks = strchr(path, ':'); | ||
87 | niro | 984 | |
88 | mask = 0x0fff; // assuming we want all non-kernel events | ||
89 | niro | 816 | // 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 | niro | 984 | const char *found; |
96 | found = memchr(mask_names, *masks, MASK_BITS); | ||
97 | if (found) | ||
98 | mask |= (1 << (found - mask_names)); | ||
99 | niro | 816 | } |
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 | niro | 984 | int len; |
115 | niro | 816 | void *buf; |
116 | struct inotify_event *ie; | ||
117 | again: | ||
118 | if (bb_got_signal) | ||
119 | break; | ||
120 | n = poll(&pfd, 1, -1); | ||
121 | niro | 984 | // Signal interrupted us? |
122 | niro | 816 | 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 | niro | 984 | // (NB: len must be int, not ssize_t or long!) |
136 | niro | 816 | 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 | niro | 984 | // 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 | niro | 1123 | spawn_and_wait((char **)args); |
159 | niro | 984 | // 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 | niro | 816 | } |
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 | niro | 984 | } // while (1) |
174 | done: | ||
175 | return bb_got_signal; | ||
176 | niro | 816 | } |