1 #include "../config.h"
2 #include "../libinotifytools/src/inotifytools_p.h"
3 #include "common.h"
4
5 #include <sys/select.h>
6 #include <sys/stat.h>
7 #include <sys/time.h>
8 #include <sys/types.h>
9
10 #include <assert.h>
11 #include <errno.h>
12 #include <fcntl.h>
13 #include <getopt.h>
14 #if defined(__FreeBSD__) || defined(__DragonFly__)
15 #include <pthread.h>
16 #endif // __FreeBSD__
17 #include <limits.h>
18 #include <regex.h>
19 #include <signal.h>
20 #include <stdbool.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
25
26 #include <inotifytools/inotify.h>
27 #include <inotifytools/inotifytools.h>
28
29 extern char *optarg;
30 extern int optind, opterr, optopt;
31
32 // METHODS
33 static bool parse_opts(int* argc,
34 char*** argv,
35 int* events,
36 unsigned int* timeout,
37 int* verbose,
38 int* zero,
39 int* sort,
40 int* recursive,
41 int* no_dereference,
42 char** fromfile,
43 char** exc_regex,
44 char** exc_iregex,
45 char** inc_regex,
46 char** inc_iregex,
47 int* fanotify,
48 bool* filesystem);
49
50 void print_help();
51
52 static bool done;
53
handle_impatient_user(int signal)54 void handle_impatient_user(int signal __attribute__((unused))) {
55 static int times_called = 0;
56 if (times_called) {
57 fprintf(stderr, "No statistics collected, asked to abort before all "
58 "watches could be established.\n");
59 exit(1);
60 }
61 fprintf(stderr,
62 "No statistics have been collected because I haven't "
63 "finished establishing\n"
64 "inotify watches yet. If you are sure you want me to exit, "
65 "interrupt me again.\n");
66 ++times_called;
67 }
68
handle_signal(int signal)69 void handle_signal(int signal __attribute__((unused))) {
70 done = true;
71 }
72
73 int print_info();
74
print_info_now(int signal)75 void print_info_now(int signal __attribute__((unused))) {
76 print_info();
77 printf("\n");
78 }
79
80 int events;
81 int sort;
82 int zero;
83
main(int argc,char ** argv)84 int main(int argc, char **argv) {
85 events = 0;
86 unsigned int timeout = BLOCKING_TIMEOUT;
87 int verbose = 0;
88 zero = 0;
89 int recursive = 0;
90 int fanotify = DEFAULT_FANOTIFY_MODE;
91 bool filesystem = false;
92 int no_dereference = 0;
93 char *fromfile = 0;
94 sort = -1;
95 done = false;
96 char *exc_regex = NULL;
97 char *exc_iregex = NULL;
98 char *inc_regex = NULL;
99 char *inc_iregex = NULL;
100 int rc;
101
102 #if defined(__FreeBSD__) || defined(__DragonFly__)
103 sigset_t set, oset;
104 #endif // __FreeBSD__
105
106 signal(SIGINT, handle_impatient_user);
107
108 #if defined(__FreeBSD__) || defined(__DragonFly__)
109 // Block some signals in libinotify's worker thread, so that
110 // handle_signal runs in the context of the main thread and
111 // the 'done' flag is actually honored.
112 sigemptyset(&set);
113 sigaddset(&set, SIGINT);
114 sigaddset(&set, SIGHUP);
115 sigaddset(&set, SIGTERM);
116 sigaddset(&set, SIGALRM);
117 pthread_sigmask(SIG_BLOCK, &set, &oset);
118 #endif // __FreeBSD__
119
120 // Parse commandline options, aborting if something goes wrong
121 if (!parse_opts(&argc, &argv, &events, &timeout, &verbose, &zero, &sort,
122 &recursive, &no_dereference, &fromfile, &exc_regex,
123 &exc_iregex, &inc_regex, &inc_iregex, &fanotify,
124 &filesystem)) {
125 return EXIT_FAILURE;
126 }
127
128 if ((exc_regex &&
129 !inotifytools_ignore_events_by_regex(exc_regex, REG_EXTENDED)) ||
130 (exc_iregex &&
131 !inotifytools_ignore_events_by_regex(exc_iregex,
132 REG_EXTENDED | REG_ICASE))) {
133 fprintf(stderr, "Error in `exclude' regular expression.\n");
134 return EXIT_FAILURE;
135 }
136
137 if ((inc_regex &&
138 !inotifytools_ignore_events_by_inverted_regex(inc_regex,
139 REG_EXTENDED)) ||
140 (inc_iregex &&
141 !inotifytools_ignore_events_by_inverted_regex(
142 inc_iregex, REG_EXTENDED | REG_ICASE))) {
143 fprintf(stderr, "Error in `include' regular expression.\n");
144 return EXIT_FAILURE;
145 }
146
147 rc = inotifytools_init(fanotify, filesystem, verbose);
148 if (!rc) {
149 warn_inotify_init_error(fanotify);
150 return EXIT_FAILURE;
151 }
152
153 #if defined(__FreeBSD__) || defined(__DragonFly__)
154 pthread_sigmask(SIG_SETMASK, &oset, NULL);
155 #endif // __FreeBSD__
156
157 // Attempt to watch file
158 // If events is still 0, make it all events.
159 if (!events)
160 events = IN_ALL_EVENTS;
161 if (no_dereference)
162 events = events | IN_DONT_FOLLOW;
163
164 if (fanotify)
165 events |= IN_ISDIR;
166
167 FileList list;
168 construct_path_list(argc, argv, fromfile, &list);
169
170 if (0 == list.watch_files[0]) {
171 fprintf(stderr, "No files specified to watch!\n");
172 goto failure;
173 }
174
175 unsigned int num_watches = 0;
176 unsigned int status;
177 fprintf(stderr, "Establishing watches...\n");
178 for (int i = 0; list.watch_files[i]; ++i) {
179 char const* this_file = list.watch_files[i];
180 if (filesystem) {
181 fprintf(stderr, "Setting up filesystem watch on %s\n",
182 this_file);
183 if (!inotifytools_watch_files(list.watch_files, events)) {
184 fprintf(stderr,
185 "Couldn't add filesystem watch %s: %s\n",
186 this_file, strerror(inotifytools_error()));
187 goto failure;
188 }
189 break;
190 }
191
192 if (recursive && verbose) {
193 fprintf(stderr, "Setting up watch(es) on %s\n", this_file);
194 }
195
196 if (recursive) {
197 status = inotifytools_watch_recursively_with_exclude(
198 this_file, events, list.exclude_files);
199 } else {
200 status = inotifytools_watch_file(this_file, events);
201 }
202 if (!status) {
203 if (inotifytools_error() == ENOSPC) {
204 const char* backend =
205 fanotify ? "fanotify" : "inotify";
206 const char* resource =
207 fanotify ? "marks" : "watches";
208 fprintf(stderr,
209 "Failed to watch %s; upper limit on %s %s "
210 "reached!\n",
211 this_file, backend, resource);
212 fprintf(stderr,
213 "Please increase the amount of %s %s "
214 "allowed per user via `/proc/sys/fs/%s/"
215 "max_user_%s'.\n",
216 backend, resource, backend, resource);
217 } else {
218 fprintf(stderr, "Failed to watch %s: %s\n",
219 this_file, strerror(inotifytools_error()));
220 }
221
222 goto failure;
223 }
224 if (recursive && verbose) {
225 fprintf(stderr, "OK, %s is now being watched.\n",
226 this_file);
227 }
228 }
229 num_watches = inotifytools_get_num_watches();
230
231 if (verbose) {
232 fprintf(stderr, "Total of %u watches.\n", num_watches);
233 }
234 fprintf(stderr,
235 "Finished establishing watches, now collecting statistics.\n");
236
237 if (timeout && verbose) {
238 fprintf(stderr, "Will listen for events for %u seconds.\n",
239 timeout);
240 }
241
242 signal(SIGINT, handle_signal);
243 signal(SIGHUP, handle_signal);
244 signal(SIGTERM, handle_signal);
245 if (timeout) {
246 signal(SIGALRM, handle_signal);
247 alarm(timeout);
248 } else {
249 alarm(UINT_MAX);
250 }
251
252 signal(SIGUSR1, print_info_now);
253
254 inotifytools_initialize_stats();
255 // Now wait till we get event
256 struct inotify_event *event;
257 char *moved_from = 0;
258
259 do {
260 event = inotifytools_next_event(BLOCKING_TIMEOUT);
261 if (!event) {
262 if (!inotifytools_error()) {
263 goto timeout;
264 } else if (inotifytools_error() != EINTR) {
265 fprintf(stderr, "%s\n", strerror(inotifytools_error()));
266
267 goto failure;
268 } else {
269 continue;
270 }
271 }
272
273 // TODO: replace filename of renamed filesystem watch entries
274 if (filesystem)
275 continue;
276
277 // if we last had MOVED_FROM and don't currently have MOVED_TO,
278 // moved_from file must have been moved outside of tree - so unwatch it.
279 if (moved_from && !(event->mask & IN_MOVED_TO)) {
280 if (!inotifytools_remove_watch_by_filename(moved_from)) {
281 fprintf(stderr, "Error removing watch on %s: %s\n", moved_from,
282 strerror(inotifytools_error()));
283 }
284 free(moved_from);
285 moved_from = 0;
286 }
287
288 if (recursive) {
289 if ((event->mask & IN_CREATE) ||
290 (!moved_from && (event->mask & IN_MOVED_TO))) {
291 // New file - if it is a directory, watch it
292 char* new_file = inotifytools_dirpath_from_event(event);
293 if (new_file && *new_file && isdir(new_file) &&
294 !inotifytools_watch_recursively(new_file, events)) {
295 fprintf(stderr, "Couldn't watch new directory %s: %s\n",
296 new_file, strerror(inotifytools_error()));
297 }
298 free(new_file);
299 } // IN_CREATE
300 else if (event->mask & IN_MOVED_FROM) {
301 moved_from = inotifytools_dirpath_from_event(event);
302 // if not watched...
303 if (inotifytools_wd_from_filename(moved_from) == -1) {
304 free(moved_from);
305 moved_from = 0;
306 }
307 } // IN_MOVED_FROM
308 else if (event->mask & IN_MOVED_TO) {
309 if (moved_from) {
310 char* new_name = inotifytools_dirpath_from_event(event);
311 inotifytools_replace_filename(moved_from, new_name);
312 free(new_name);
313 free(moved_from);
314 moved_from = 0;
315 } // moved_from
316 }
317 }
318
319 } while (!done);
320
321 free_list(argc, argv, &list);
322
323 return print_info();
324
325 failure:
326 free_list(argc, argv, &list);
327
328 return EXIT_FAILURE;
329
330 timeout:
331 free_list(argc, argv, &list);
332
333 return EXIT_TIMEOUT;
334 }
335
print_info()336 int print_info() {
337 if (!inotifytools_get_stat_total(0)) {
338 fprintf(stderr, "No events occurred.\n");
339 return EXIT_SUCCESS;
340 }
341
342 // OK, go through the watches and print stats.
343 printf("total ");
344 if ((IN_ACCESS & events) &&
345 (zero || inotifytools_get_stat_total(IN_ACCESS)))
346 printf("access ");
347 if ((IN_MODIFY & events) &&
348 (zero || inotifytools_get_stat_total(IN_MODIFY)))
349 printf("modify ");
350 if ((IN_ATTRIB & events) &&
351 (zero || inotifytools_get_stat_total(IN_ATTRIB)))
352 printf("attrib ");
353 if ((IN_CLOSE_WRITE & events) &&
354 (zero || inotifytools_get_stat_total(IN_CLOSE_WRITE)))
355 printf("close_write ");
356 if ((IN_CLOSE_NOWRITE & events) &&
357 (zero || inotifytools_get_stat_total(IN_CLOSE_NOWRITE)))
358 printf("close_nowrite ");
359 if ((IN_OPEN & events) && (zero || inotifytools_get_stat_total(IN_OPEN)))
360 printf("open ");
361 if ((IN_MOVED_FROM & events) &&
362 (zero || inotifytools_get_stat_total(IN_MOVED_FROM)))
363 printf("moved_from ");
364 if ((IN_MOVED_TO & events) &&
365 (zero || inotifytools_get_stat_total(IN_MOVED_TO)))
366 printf("moved_to ");
367 if ((IN_MOVE_SELF & events) &&
368 (zero || inotifytools_get_stat_total(IN_MOVE_SELF)))
369 printf("move_self ");
370 if ((IN_CREATE & events) &&
371 (zero || inotifytools_get_stat_total(IN_CREATE)))
372 printf("create ");
373 if ((IN_DELETE & events) &&
374 (zero || inotifytools_get_stat_total(IN_DELETE)))
375 printf("delete ");
376 if ((IN_DELETE_SELF & events) &&
377 (zero || inotifytools_get_stat_total(IN_DELETE_SELF)))
378 printf("delete_self ");
379 if ((IN_UNMOUNT & events) &&
380 (zero || inotifytools_get_stat_total(IN_UNMOUNT)))
381 printf("unmount ");
382
383 printf("filename\n");
384
385 struct rbtree *tree = inotifytools_wd_sorted_by_event(sort);
386 RBLIST *rblist = rbopenlist(tree);
387 watch *w = (watch *)rbreadlist(rblist);
388
389 while (w) {
390 if (!zero && !w->hit_total) {
391 w = (watch *)rbreadlist(rblist);
392 continue;
393 }
394 printf("%-5u ", w->hit_total);
395 if ((IN_ACCESS & events) &&
396 (zero || inotifytools_get_stat_total(IN_ACCESS)))
397 printf("%-6u ", w->hit_access);
398 if ((IN_MODIFY & events) &&
399 (zero || inotifytools_get_stat_total(IN_MODIFY)))
400 printf("%-6u ", w->hit_modify);
401 if ((IN_ATTRIB & events) &&
402 (zero || inotifytools_get_stat_total(IN_ATTRIB)))
403 printf("%-6u ", w->hit_attrib);
404 if ((IN_CLOSE_WRITE & events) &&
405 (zero || inotifytools_get_stat_total(IN_CLOSE_WRITE)))
406 printf("%-11u ", w->hit_close_write);
407 if ((IN_CLOSE_NOWRITE & events) &&
408 (zero || inotifytools_get_stat_total(IN_CLOSE_NOWRITE)))
409 printf("%-13u ", w->hit_close_nowrite);
410 if ((IN_OPEN & events) &&
411 (zero || inotifytools_get_stat_total(IN_OPEN)))
412 printf("%-4u ", w->hit_open);
413 if ((IN_MOVED_FROM & events) &&
414 (zero || inotifytools_get_stat_total(IN_MOVED_FROM)))
415 printf("%-10u ", w->hit_moved_from);
416 if ((IN_MOVED_TO & events) &&
417 (zero || inotifytools_get_stat_total(IN_MOVED_TO)))
418 printf("%-8u ", w->hit_moved_to);
419 if ((IN_MOVE_SELF & events) &&
420 (zero || inotifytools_get_stat_total(IN_MOVE_SELF)))
421 printf("%-9u ", w->hit_move_self);
422 if ((IN_CREATE & events) &&
423 (zero || inotifytools_get_stat_total(IN_CREATE)))
424 printf("%-6u ", w->hit_create);
425 if ((IN_DELETE & events) &&
426 (zero || inotifytools_get_stat_total(IN_DELETE)))
427 printf("%-6u ", w->hit_delete);
428 if ((IN_DELETE_SELF & events) &&
429 (zero || inotifytools_get_stat_total(IN_DELETE_SELF)))
430 printf("%-11u ", w->hit_delete_self);
431 if ((IN_UNMOUNT & events) &&
432 (zero || inotifytools_get_stat_total(IN_UNMOUNT)))
433 printf("%-7u ", w->hit_unmount);
434
435 printf("%s\n", inotifytools_filename_from_watch(w));
436 w = (watch *)rbreadlist(rblist);
437 }
438 rbcloselist(rblist);
439 rbdestroy(tree);
440
441 return EXIT_SUCCESS;
442 }
443
parse_opts(int * argc,char *** argv,int * e,unsigned int * timeout,int * verbose,int * z,int * s,int * recursive,int * no_dereference,char ** fromfile,char ** exc_regex,char ** exc_iregex,char ** inc_regex,char ** inc_iregex,int * fanotify,bool * filesystem)444 static bool parse_opts(int* argc,
445 char*** argv,
446 int* e,
447 unsigned int* timeout,
448 int* verbose,
449 int* z,
450 int* s,
451 int* recursive,
452 int* no_dereference,
453 char** fromfile,
454 char** exc_regex,
455 char** exc_iregex,
456 char** inc_regex,
457 char** inc_iregex,
458 int* fanotify,
459 bool* filesystem) {
460 assert(argc);
461 assert(argv);
462 assert(e);
463 assert(timeout);
464 assert(verbose);
465 assert(z);
466 assert(s);
467 assert(recursive);
468 assert(fanotify);
469 assert(filesystem);
470 assert(no_dereference);
471 assert(fromfile);
472 assert(exc_regex);
473 assert(exc_iregex);
474 assert(inc_regex);
475 assert(inc_iregex);
476
477 // Settings for options
478 int new_event;
479 bool sort_set = false;
480
481 // Short options
482 static const char opt_string[] = "hrPa:d:zve:t:IFS";
483
484 // Construct array
485 static const struct option long_opts[] = {
486 {"help", no_argument, NULL, 'h'},
487 {"event", required_argument, NULL, 'e'},
488 {"timeout", required_argument, NULL, 't'},
489 {"verbose", no_argument, NULL, 'v'},
490 {"zero", no_argument, NULL, 'z'},
491 {"ascending", required_argument, NULL, 'a'},
492 {"descending", required_argument, NULL, 'd'},
493 {"recursive", no_argument, NULL, 'r'},
494 {"inotify", no_argument, NULL, 'I'},
495 {"fanotify", no_argument, NULL, 'F'},
496 {"filesystem", no_argument, NULL, 'S'},
497 {"no-dereference", no_argument, NULL, 'P'},
498 {"fromfile", required_argument, NULL, 'o'},
499 {"exclude", required_argument, NULL, 'c'},
500 {"excludei", required_argument, NULL, 'b'},
501 {"include", required_argument, NULL, 'j'},
502 {"includei", required_argument, NULL, 'k'},
503 {NULL, 0, 0, 0},
504 };
505
506 // Get first option
507 char curr_opt = getopt_long(*argc, *argv, opt_string, long_opts, NULL);
508
509 // While more options exist...
510 while ((curr_opt != '?') && (curr_opt != (char)-1)) {
511 switch (curr_opt) {
512 // --help or -h
513 case 'h':
514 print_help();
515 // Shouldn't process any further...
516 return false;
517 break;
518
519 // --verbose or -v
520 case 'v':
521 ++(*verbose);
522 break;
523
524 // --recursive or -r
525 case 'r':
526 ++(*recursive);
527 break;
528
529 #ifdef ENABLE_FANOTIFY
530 // --inotify or -I
531 case 'I':
532 (*fanotify) = 0;
533 break;
534
535 // --fanotify or -F
536 case 'F':
537 (*fanotify) = 1;
538 break;
539
540 // --filesystem or -S
541 case 'S':
542 (*filesystem) = true;
543 (*fanotify) = 1;
544 break;
545 #endif
546
547 case 'P':
548 ++(*no_dereference);
549 break;
550
551 // --zero or -z
552 case 'z':
553 ++(*z);
554 break;
555
556 // --exclude
557 case 'c':
558 (*exc_regex) = optarg;
559 break;
560
561 // --excludei
562 case 'b':
563 (*exc_iregex) = optarg;
564 break;
565
566 // --include
567 case 'j':
568 (*inc_regex) = optarg;
569 break;
570
571 // --includei
572 case 'k':
573 (*inc_iregex) = optarg;
574 break;
575
576 // --fromfile
577 case 'o':
578 if (*fromfile) {
579 fprintf(stderr,
580 "Multiple --fromfile options "
581 "given.\n");
582 return false;
583 }
584 (*fromfile) = optarg;
585 break;
586
587 // --timeout or -t
588 case 't':
589 if (!is_timeout_option_valid(timeout, optarg)) {
590 return false;
591 }
592 break;
593
594 // --event or -e
595 case 'e':
596 // Get event mask from event string
597 new_event = inotifytools_str_to_event(optarg);
598
599 // If optarg was invalid, abort
600 if (new_event == -1) {
601 fprintf(
602 stderr,
603 "'%s' is not a valid event! Run "
604 "with the "
605 "'--help' option to see a list of "
606 "events.\n",
607 optarg);
608 return false;
609 }
610
611 // Add the new event to the event mask
612 (*e) = ((*e) | new_event);
613
614 break;
615
616 // --ascending or -a
617 case 'a':
618 assert(optarg);
619 if (sort_set) {
620 fprintf(stderr,
621 "Please specify -a or -d once "
622 "only!\n");
623 return false;
624 }
625
626 if (0 == strcasecmp(optarg, "total")) {
627 (*s) = 0;
628 } else if (0 == strcasecmp(optarg, "move")) {
629 fprintf(
630 stderr,
631 "Cannot sort by `move' event; "
632 "please use "
633 "`moved_from' or `moved_to'.\n");
634 return false;
635 } else if (0 == strcasecmp(optarg, "close")) {
636 fprintf(stderr,
637 "Cannot sort by `close' event; "
638 "please use "
639 "`close_write' or "
640 "`close_nowrite'.\n");
641 return false;
642 } else {
643 int event =
644 inotifytools_str_to_event(optarg);
645
646 // If optarg was invalid, abort
647 if (event == -1) {
648 fprintf(stderr,
649 "'%s' is not a valid "
650 "key for "
651 "sorting!\n",
652 optarg);
653 return false;
654 }
655
656 (*s) = event;
657 }
658 sort_set = true;
659 break;
660
661 // --descending or -d
662 case 'd':
663 assert(optarg);
664 if (sort_set) {
665 fprintf(stderr,
666 "Please specify -a or -d once "
667 "only!\n");
668 return false;
669 }
670
671 if (0 == strcasecmp(optarg, "total")) {
672 (*s) = -1;
673 } else {
674 int event =
675 inotifytools_str_to_event(optarg);
676
677 // If optarg was invalid, abort
678 if (event == -1) {
679 fprintf(stderr,
680 "'%s' is not a valid "
681 "key for "
682 "sorting!\n",
683 optarg);
684 return false;
685 }
686
687 (*s) = -event;
688 }
689 break;
690 }
691
692 curr_opt =
693 getopt_long(*argc, *argv, opt_string, long_opts, NULL);
694 }
695
696 (*argc) -= optind;
697 *argv = &(*argv)[optind];
698
699 if ((*s) != 0 && (*s) != -1 &&
700 !(abs(*s) & ((*e) ? (*e) : IN_ALL_EVENTS))) {
701 fprintf(stderr,
702 "Can't sort by an event which isn't being watched "
703 "for!\n");
704 return false;
705 }
706
707 if (*exc_regex && *exc_iregex) {
708 fprintf(stderr,
709 "--exclude and --excludei cannot both be specified.\n");
710 return false;
711 }
712 if (*inc_regex && *inc_iregex) {
713 fprintf(stderr,
714 "--include and --includei cannot both be specified.\n");
715 return false;
716 }
717 if ((*inc_regex && *exc_regex) || (*inc_regex && *exc_iregex) ||
718 (*inc_iregex && *exc_regex) || (*inc_iregex && *exc_iregex)) {
719 fprintf(
720 stderr,
721 "include and exclude regexp cannot both be specified.\n");
722 return false;
723 }
724
725 // If ? returned, invalid option
726 return (curr_opt != '?');
727 }
728
729 #define TOOL_NAME TOOLS_PREFIX "watch"
730
print_help()731 void print_help() {
732 printf("%s %s\n", TOOL_NAME, PACKAGE_VERSION);
733 printf("Gather filesystem usage statistics using %s.\n", TOOL_NAME);
734 printf("Usage: %s [ options ] file1 [ file2 ] [ ... ]\n", TOOL_NAME);
735 printf("Options:\n");
736 printf("\t-h|--help \tShow this help text.\n");
737 printf("\t-v|--verbose \tBe verbose.\n");
738 printf(
739 "\t@<file> \tExclude the specified file from being "
740 "watched.\n");
741 printf(
742 "\t--fromfile <file>\n"
743 "\t\tRead files to watch from <file> or `-' for stdin.\n");
744 printf(
745 "\t--exclude <pattern>\n"
746 "\t\tExclude all events on files matching the extended regular\n"
747 "\t\texpression <pattern>.\n");
748 printf(
749 "\t--excludei <pattern>\n"
750 "\t\tLike --exclude but case insensitive.\n");
751 printf(
752 "\t--include <pattern>\n"
753 "\t\tExclude all events on files except the ones\n"
754 "\t\tmatching the extended regular expression\n"
755 "\t\t<pattern>.\n");
756 printf(
757 "\t--includei <pattern>\n"
758 "\t\tLike --include but case insensitive.\n");
759 printf(
760 "\t-z|--zero\n"
761 "\t\tIn the final table of results, output rows and columns even\n"
762 "\t\tif they consist only of zeros (the default is to not output\n"
763 "\t\tthese rows and columns).\n");
764 printf("\t-r|--recursive\tWatch directories recursively.\n");
765 #ifdef ENABLE_FANOTIFY
766 printf("\t-I|--inotify\tWatch with inotify.\n");
767 printf("\t-F|--fanotify\tWatch with fanotify.\n");
768 printf("\t-S|--filesystem\tWatch entire filesystem with fanotify.\n");
769 #endif
770 printf(
771 "\t-P|--no-dereference\n"
772 "\t\tDo not follow symlinks.\n");
773 printf(
774 "\t-t|--timeout <seconds>\n"
775 "\t\tListen only for specified amount of time in seconds; if\n"
776 "\t\tomitted or zero, %s will execute until receiving an\n"
777 "\t\tinterrupt signal.\n",
778 TOOL_NAME);
779 printf(
780 "\t-e|--event <event1> [ -e|--event <event2> ... ]\n"
781 "\t\tListen for specific event(s). If omitted, all events are \n"
782 "\t\tlistened for.\n");
783 printf(
784 "\t-a|--ascending <event>\n"
785 "\t\tSort ascending by a particular event, or `total'.\n");
786 printf(
787 "\t-d|--descending <event>\n"
788 "\t\tSort descending by a particular event, or `total'.\n\n");
789 printf("Exit status:\n");
790 printf("\t%d - Exited normally.\n", EXIT_SUCCESS);
791 printf("\t%d - Some error occurred.\n\n", EXIT_FAILURE);
792 printf("Events:\n");
793 print_event_descriptions();
794 }
795