"Fossies" - the Fresh Open Source Software Archive

Member "inotify-tools-3.20.11.0/src/inotifywatch.c" (13 Nov 2020, 21527 Bytes) of package /linux/privat/inotify-tools-3.20.11.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "inotifywatch.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 3.20.2.2_vs_3.20.11.0.

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