diff options
author | Ron Yorston <rmy@pobox.com> | 2020-04-15 10:17:48 +0100 |
---|---|---|
committer | Ron Yorston <rmy@pobox.com> | 2020-04-15 10:17:48 +0100 |
commit | db46c292750493d2e98a21ec24a8a407fd8b3207 (patch) | |
tree | 6b6d872da31276cbdc329347a1aac9dece0eb086 | |
parent | c2f7974901118ff4ec004da1e71a7386b900a505 (diff) | |
download | busybox-w32-db46c292750493d2e98a21ec24a8a407fd8b3207.tar.gz busybox-w32-db46c292750493d2e98a21ec24a8a407fd8b3207.tar.bz2 busybox-w32-db46c292750493d2e98a21ec24a8a407fd8b3207.zip |
inotifyd: WIN32 port
Use ReadDirectoryChangesW to implement inotifyd for WIN32.
There are limitations:
- It's only possible to watch directories, not files.
- The notification doesn't distinguish between different changes to
file state. All changes other than creation, deletion and renaming
are reported as 'c'.
-rw-r--r-- | configs/mingw32_defconfig | 2 | ||||
-rw-r--r-- | configs/mingw64_defconfig | 2 | ||||
-rw-r--r-- | miscutils/inotifyd.c | 211 | ||||
-rw-r--r-- | win32/sys/inotify.h | 0 |
4 files changed, 213 insertions, 2 deletions
diff --git a/configs/mingw32_defconfig b/configs/mingw32_defconfig index d93eefcf5..592a46f34 100644 --- a/configs/mingw32_defconfig +++ b/configs/mingw32_defconfig | |||
@@ -800,7 +800,7 @@ CONFIG_FEATURE_CROND_DIR="" | |||
800 | # CONFIG_I2CDETECT is not set | 800 | # CONFIG_I2CDETECT is not set |
801 | # CONFIG_I2CTRANSFER is not set | 801 | # CONFIG_I2CTRANSFER is not set |
802 | CONFIG_ICONV=y | 802 | CONFIG_ICONV=y |
803 | # CONFIG_INOTIFYD is not set | 803 | CONFIG_INOTIFYD=y |
804 | CONFIG_LESS=y | 804 | CONFIG_LESS=y |
805 | CONFIG_FEATURE_LESS_MAXLINES=9999999 | 805 | CONFIG_FEATURE_LESS_MAXLINES=9999999 |
806 | CONFIG_FEATURE_LESS_BRACKETS=y | 806 | CONFIG_FEATURE_LESS_BRACKETS=y |
diff --git a/configs/mingw64_defconfig b/configs/mingw64_defconfig index 3bd6beb70..c238c0623 100644 --- a/configs/mingw64_defconfig +++ b/configs/mingw64_defconfig | |||
@@ -800,7 +800,7 @@ CONFIG_FEATURE_CROND_DIR="" | |||
800 | # CONFIG_I2CDETECT is not set | 800 | # CONFIG_I2CDETECT is not set |
801 | # CONFIG_I2CTRANSFER is not set | 801 | # CONFIG_I2CTRANSFER is not set |
802 | CONFIG_ICONV=y | 802 | CONFIG_ICONV=y |
803 | # CONFIG_INOTIFYD is not set | 803 | CONFIG_INOTIFYD=y |
804 | CONFIG_LESS=y | 804 | CONFIG_LESS=y |
805 | CONFIG_FEATURE_LESS_MAXLINES=9999999 | 805 | CONFIG_FEATURE_LESS_MAXLINES=9999999 |
806 | CONFIG_FEATURE_LESS_BRACKETS=y | 806 | CONFIG_FEATURE_LESS_BRACKETS=y |
diff --git a/miscutils/inotifyd.c b/miscutils/inotifyd.c index 8bff86ae5..fdd04c292 100644 --- a/miscutils/inotifyd.c +++ b/miscutils/inotifyd.c | |||
@@ -45,8 +45,11 @@ | |||
45 | //usage: "\nPROG ACTUAL_EVENTS FILEn [SUBFILE] is run." | 45 | //usage: "\nPROG ACTUAL_EVENTS FILEn [SUBFILE] is run." |
46 | //usage: "\nIf PROG is -, events are sent to stdout." | 46 | //usage: "\nIf PROG is -, events are sent to stdout." |
47 | //usage: "\nEvents:" | 47 | //usage: "\nEvents:" |
48 | //usage: IF_NOT_PLATFORM_MINGW32( | ||
48 | //usage: "\n a File is accessed" | 49 | //usage: "\n a File is accessed" |
50 | //usage: ) | ||
49 | //usage: "\n c File is modified" | 51 | //usage: "\n c File is modified" |
52 | //usage: IF_NOT_PLATFORM_MINGW32( | ||
50 | //usage: "\n e Metadata changed" | 53 | //usage: "\n e Metadata changed" |
51 | //usage: "\n w Writable file is closed" | 54 | //usage: "\n w Writable file is closed" |
52 | //usage: "\n 0 Unwritable file is closed" | 55 | //usage: "\n 0 Unwritable file is closed" |
@@ -55,8 +58,11 @@ | |||
55 | //usage: "\n M File is moved" | 58 | //usage: "\n M File is moved" |
56 | //usage: "\n u Backing fs is unmounted" | 59 | //usage: "\n u Backing fs is unmounted" |
57 | //usage: "\n o Event queue overflowed" | 60 | //usage: "\n o Event queue overflowed" |
61 | //usage: ) | ||
58 | //usage: "\n x File can't be watched anymore" | 62 | //usage: "\n x File can't be watched anymore" |
63 | //usage: IF_NOT_PLATFORM_MINGW32( | ||
59 | //usage: "\nIf watching a directory:" | 64 | //usage: "\nIf watching a directory:" |
65 | //usage: ) | ||
60 | //usage: "\n y Subfile is moved into dir" | 66 | //usage: "\n y Subfile is moved into dir" |
61 | //usage: "\n m Subfile is moved out of dir" | 67 | //usage: "\n m Subfile is moved out of dir" |
62 | //usage: "\n n Subfile is created" | 68 | //usage: "\n n Subfile is created" |
@@ -69,6 +75,7 @@ | |||
69 | #include "common_bufsiz.h" | 75 | #include "common_bufsiz.h" |
70 | #include <sys/inotify.h> | 76 | #include <sys/inotify.h> |
71 | 77 | ||
78 | #if !ENABLE_PLATFORM_MINGW32 | ||
72 | static const char mask_names[] ALIGN1 = | 79 | static const char mask_names[] ALIGN1 = |
73 | "a" // 0x00000001 File was accessed | 80 | "a" // 0x00000001 File was accessed |
74 | "c" // 0x00000002 File was modified | 81 | "c" // 0x00000002 File was modified |
@@ -222,3 +229,207 @@ int inotifyd_main(int argc, char **argv) | |||
222 | done: | 229 | done: |
223 | return bb_got_signal; | 230 | return bb_got_signal; |
224 | } | 231 | } |
232 | #else /* ENABLE_PLATFORM_MINGW32 */ | ||
233 | /* | ||
234 | * Order is important: the indices match the values taken by the | ||
235 | * Action member of the FILE_NOTIFY_INFORMATION structure, including | ||
236 | * the undocumented zero value when the directory itself is deleted. | ||
237 | */ | ||
238 | static const char mask_names[] ALIGN1 = | ||
239 | "x" // File is no longer watched (usually deleted) | ||
240 | "n" // Subfile was created | ||
241 | "d" // Subfile was deleted | ||
242 | "c" // File was modified | ||
243 | "m" // File was moved from X | ||
244 | "y" // File was moved to Y | ||
245 | ; | ||
246 | |||
247 | enum { | ||
248 | MASK_BITS = sizeof(mask_names) - 1 | ||
249 | }; | ||
250 | |||
251 | static const unsigned mask_values[] = { | ||
252 | 0x000, // File is no longer watched (usually deleted) | ||
253 | 0x003, // Subfile was created | ||
254 | 0x003, // Subfile was deleted | ||
255 | 0x1fc, // File was modified (everything except create/delete/move) | ||
256 | 0x003, // File was moved from X | ||
257 | 0x003, // File was moved to Y | ||
258 | }; | ||
259 | |||
260 | struct watch { | ||
261 | HANDLE hdir; | ||
262 | HANDLE hevent; | ||
263 | DWORD mask; // notification filter | ||
264 | DWORD bits; // events to report | ||
265 | OVERLAPPED overlap; | ||
266 | const char *dirname; | ||
267 | char buf[2048]; | ||
268 | }; | ||
269 | |||
270 | static void run_agent(const char *agent, FILE_NOTIFY_INFORMATION *info, | ||
271 | struct watch *w) | ||
272 | { | ||
273 | int len; | ||
274 | char filename[MAX_PATH]; | ||
275 | char event[2]; | ||
276 | const char *args[5]; | ||
277 | |||
278 | memset(filename, 0, sizeof(filename)); | ||
279 | len = WideCharToMultiByte(CP_ACP, 0, info->FileName, | ||
280 | info->FileNameLength/2, filename, sizeof(filename), | ||
281 | NULL, NULL); | ||
282 | |||
283 | if (info->Action >= 0 && info->Action < 6 && | ||
284 | ((1 << info->Action) & w->bits)) { | ||
285 | event[0] = mask_names[info->Action]; | ||
286 | event[1] = '\0'; | ||
287 | |||
288 | if (LONE_CHAR(agent, '-')) { | ||
289 | /* "inotifyd - FILE": built-in echo */ | ||
290 | printf(len ? "%s\t%s\t%s\n" : "%s\t%s\n", | ||
291 | event, w->dirname, filename); | ||
292 | fflush(stdout); | ||
293 | } | ||
294 | else { | ||
295 | args[0] = agent; | ||
296 | args[1] = event; | ||
297 | args[2] = w->dirname; | ||
298 | args[3] = len ? filename : NULL; | ||
299 | args[4] = NULL; | ||
300 | spawn_and_wait((char **)args); | ||
301 | } | ||
302 | } | ||
303 | } | ||
304 | |||
305 | static BOOL start_watch(struct watch *w) | ||
306 | { | ||
307 | DWORD nret; | ||
308 | |||
309 | memset(w->buf, 0, sizeof(w->buf)); | ||
310 | memset(&w->overlap, 0, sizeof(OVERLAPPED)); | ||
311 | w->overlap.hEvent = w->hevent; | ||
312 | ResetEvent(w->hevent); | ||
313 | |||
314 | return ReadDirectoryChangesW(w->hdir, w->buf, sizeof(w->buf), | ||
315 | FALSE, w->mask, &nret, &w->overlap, NULL); | ||
316 | } | ||
317 | |||
318 | int inotifyd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
319 | int inotifyd_main(int argc, char **argv) | ||
320 | { | ||
321 | int n; | ||
322 | unsigned mask, bits; | ||
323 | const char *agent; | ||
324 | HANDLE *hevent; | ||
325 | struct watch *watch; | ||
326 | |||
327 | // sanity check: agent and at least one watch must be given | ||
328 | if (!argv[1] || !argv[2]) | ||
329 | bb_show_usage(); | ||
330 | |||
331 | argv++; | ||
332 | agent = *argv; | ||
333 | argc -= 2; // number of files we watch | ||
334 | |||
335 | watch = (struct watch *)xzalloc(argc * sizeof(struct watch)); | ||
336 | hevent = (HANDLE *)xzalloc(argc * sizeof(HANDLE)); | ||
337 | |||
338 | // setup watches | ||
339 | for (n = 0; *++argv; ++n) { | ||
340 | char *masks; | ||
341 | |||
342 | masks = strrchr(*argv, ':'); | ||
343 | // don't confuse a drive prefix with a mask | ||
344 | if (masks && masks != (*argv)+1) | ||
345 | *masks = '\0'; | ||
346 | |||
347 | mask = 0x01ff; // assuming we want all notifications | ||
348 | bits = 0x3f; // assuming we want to report everything | ||
349 | // if mask is specified -> | ||
350 | if (masks && *masks == '\0') { | ||
351 | // convert names to notification filter and report bitmask | ||
352 | mask = bits = 0; | ||
353 | while (*++masks) { | ||
354 | const char *found; | ||
355 | found = memchr(mask_names, *masks, MASK_BITS); | ||
356 | if (found) { | ||
357 | mask |= mask_values[(found - mask_names)]; | ||
358 | bits |= (1 << (found - mask_names)); | ||
359 | } | ||
360 | } | ||
361 | } | ||
362 | |||
363 | if (mask == 0) | ||
364 | bb_error_msg_and_die("%s: invalid mask\n", *argv); | ||
365 | |||
366 | if (!is_directory(*argv, FALSE)) | ||
367 | bb_error_msg_and_die("%s: not a directory", *argv); | ||
368 | |||
369 | watch[n].hdir = CreateFile(*argv, GENERIC_READ|FILE_LIST_DIRECTORY, | ||
370 | FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, | ||
371 | NULL, OPEN_EXISTING, | ||
372 | FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_OVERLAPPED, NULL); | ||
373 | if (watch[n].hdir == INVALID_HANDLE_VALUE) | ||
374 | break; | ||
375 | |||
376 | watch[n].dirname = *argv; | ||
377 | watch[n].mask = mask; | ||
378 | watch[n].bits = bits; | ||
379 | watch[n].hevent = hevent[n] = CreateEvent(NULL, TRUE, FALSE, NULL); | ||
380 | |||
381 | if (!start_watch(watch+n)) | ||
382 | break; | ||
383 | } | ||
384 | |||
385 | if (*argv != NULL) { | ||
386 | errno = err_win_to_posix(); | ||
387 | bb_perror_msg_and_die("add watch (%s) failed", *argv); | ||
388 | } | ||
389 | |||
390 | while (1) { | ||
391 | DWORD status; | ||
392 | |||
393 | status = WaitForMultipleObjects(n, hevent, FALSE, INFINITE); | ||
394 | if (WAIT_OBJECT_0 <= status && status < WAIT_OBJECT_0 + n) { | ||
395 | FILE_NOTIFY_INFORMATION *info; | ||
396 | int index = status - WAIT_OBJECT_0; | ||
397 | int offset = 0; | ||
398 | struct watch *w = watch + index; | ||
399 | int got_zero = 0; | ||
400 | |||
401 | do { | ||
402 | info = (FILE_NOTIFY_INFORMATION *)(w->buf + offset); | ||
403 | got_zero += (info->Action == 0); | ||
404 | run_agent(agent, info, w); | ||
405 | offset += info->NextEntryOffset; | ||
406 | } while (info->NextEntryOffset); | ||
407 | |||
408 | if (!start_watch(w)) { | ||
409 | // directory was deleted? | ||
410 | int i, count; | ||
411 | |||
412 | if (!got_zero) { | ||
413 | // we haven't seen an 'x' event, fake one | ||
414 | memset(info, 0, sizeof(FILE_NOTIFY_INFORMATION)); | ||
415 | run_agent(agent, info, w); | ||
416 | } | ||
417 | |||
418 | // mark watch as dead, terminate if all are dead | ||
419 | w->mask = 0; | ||
420 | for (count = i = 0; i<n; ++i) | ||
421 | if (watch[i].mask) | ||
422 | ++count; | ||
423 | if (count == 0) | ||
424 | break; | ||
425 | } | ||
426 | } | ||
427 | else { | ||
428 | errno = err_win_to_posix(); | ||
429 | bb_perror_msg_and_die("watch failed"); | ||
430 | } | ||
431 | } | ||
432 | |||
433 | return EXIT_SUCCESS; | ||
434 | } | ||
435 | #endif | ||
diff --git a/win32/sys/inotify.h b/win32/sys/inotify.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/win32/sys/inotify.h | |||