diff options
Diffstat (limited to 'miscutils/inotifyd.c')
| -rw-r--r-- | miscutils/inotifyd.c | 211 |
1 files changed, 211 insertions, 0 deletions
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 | ||
