aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2020-04-15 10:17:48 +0100
committerRon Yorston <rmy@pobox.com>2020-04-15 10:17:48 +0100
commitdb46c292750493d2e98a21ec24a8a407fd8b3207 (patch)
tree6b6d872da31276cbdc329347a1aac9dece0eb086
parentc2f7974901118ff4ec004da1e71a7386b900a505 (diff)
downloadbusybox-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_defconfig2
-rw-r--r--configs/mingw64_defconfig2
-rw-r--r--miscutils/inotifyd.c211
-rw-r--r--win32/sys/inotify.h0
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
802CONFIG_ICONV=y 802CONFIG_ICONV=y
803# CONFIG_INOTIFYD is not set 803CONFIG_INOTIFYD=y
804CONFIG_LESS=y 804CONFIG_LESS=y
805CONFIG_FEATURE_LESS_MAXLINES=9999999 805CONFIG_FEATURE_LESS_MAXLINES=9999999
806CONFIG_FEATURE_LESS_BRACKETS=y 806CONFIG_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
802CONFIG_ICONV=y 802CONFIG_ICONV=y
803# CONFIG_INOTIFYD is not set 803CONFIG_INOTIFYD=y
804CONFIG_LESS=y 804CONFIG_LESS=y
805CONFIG_FEATURE_LESS_MAXLINES=9999999 805CONFIG_FEATURE_LESS_MAXLINES=9999999
806CONFIG_FEATURE_LESS_BRACKETS=y 806CONFIG_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
72static const char mask_names[] ALIGN1 = 79static 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 */
238static 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
247enum {
248 MASK_BITS = sizeof(mask_names) - 1
249};
250
251static 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
260struct 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
270static 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
305static 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
318int inotifyd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
319int 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