"Fossies" - the Fresh Open Source Software Archive

Member "dnotify-0.18.0/src/main.c" (18 Oct 2004, 16543 Bytes) of package /linux/privat/old/dnotify-0.18.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 "main.c" see the Fossies "Dox" file reference documentation.

    1 /* main.c - Main routines for dnotify
    2  *
    3  * Copyright (C) 2002  Oskar Liljeblad
    4  *
    5  * This program is free software; you can redistribute it and/or modify
    6  * it under the terms of the GNU General Public License as published by
    7  * the Free Software Foundation; either version 2 of the License, or
    8  * (at your option) any later version.
    9  *
   10  * This program is distributed in the hope that it will be useful,
   11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   13  * GNU Library General Public License for more details.
   14  *
   15  * You should have received a copy of the GNU General Public License
   16  * along with this program; if not, write to the Free Software
   17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
   18  */
   19 
   20 #include <config.h>
   21 #include <sys/types.h>
   22 #include <sys/wait.h>
   23 #include <stdarg.h>
   24 #include <errno.h>
   25 #include <getopt.h>
   26 #include <dirent.h>
   27 #include <fcntl.h>
   28 #include <signal.h>
   29 #include <stdio.h>
   30 #include <unistd.h>
   31 #include <string.h>
   32 #include <stdlib.h>
   33 #include <stdio.h>
   34 #include "dnotify.h"
   35 #include "gettext.h"
   36 #define _(s) gettext(s)
   37 #define N_(s) gettext_noop(s)
   38 
   39 #define HELP_MESSAGE \
   40 
   41 typedef struct {
   42     int fd;
   43     char *name;
   44 } MonitorFD;
   45 
   46 typedef struct {
   47     void *next;
   48     int fd;
   49 } QueueFD;
   50 
   51 static QueueFD *first_queue_fd = NULL;
   52 static QueueFD *last_queue_fd = NULL;
   53 static char *prepend_dir = NULL;
   54 
   55 static const char *const short_opts = "-ABCDMRabef:op:q:rst:";
   56 static struct option long_opts[] = {
   57     { "access",             no_argument,        NULL, 'A' },
   58     { "modify",             no_argument,        NULL, 'M' },
   59     { "create",             no_argument,        NULL, 'C' },
   60     { "delete",             no_argument,        NULL, 'D' },
   61     { "rename",             no_argument,        NULL, 'R' },
   62     { "attrib",             no_argument,        NULL, 'B' },
   63     { "all",                no_argument,        NULL, 'a' },
   64     { "execute",            no_argument,        NULL, 'e' },
   65     { "file",               required_argument,  NULL, 'f' },
   66     { "processes",          required_argument,  NULL, 'p' },
   67     { "queue",              required_argument,  NULL, 'q' },
   68     { "times",              required_argument,  NULL, 't' },
   69     { "once",               no_argument,        NULL, 'o' },
   70     { "recursive",          no_argument,        NULL, 'r' },
   71     { "background",         no_argument,        NULL, 'b' },
   72     { "silent",             no_argument,        NULL, 's' },
   73     { "quiet",              no_argument,        NULL, 's' },
   74     { "version",            no_argument,        NULL, 'V' },
   75     { "help",               no_argument,        NULL, 'H' },
   76     { 0,                    0,                  0,    0,  },
   77 };
   78 
   79 static void
   80 sigrt_handler(int sig, siginfo_t *si, void *data)
   81 {
   82     /* No operation. */
   83 }
   84 
   85 static void
   86 sigchld_handler(int sig)
   87 {
   88     /* No operation. */
   89 }
   90 
   91 /* errno must be set to zero prior to calling readdir, otherwise
   92  * there is no way to distinguish between EOF and an error.
   93  * GNU libc does not modify errno on EOF or success.
   94  */
   95 static struct dirent *
   96 safe_readdir(DIR *dir)
   97 {
   98     errno = 0;
   99     return readdir(dir);
  100 }
  101 
  102 static char *
  103 catfiles(const char *path1, const char *path2)
  104 {
  105     char *out;
  106     char *sep;
  107 
  108     if (path1[0] == '\0' && path2[0] == '\0')
  109         return strdup("/");
  110     if (path2[0] == '\0')
  111         return strdup(path1);
  112     if (path1[0] == '\0')
  113         return strdup(path2);
  114 
  115     sep = (path1[strlen(path1)-1] == '/' ? "" : "/");
  116     if (asprintf(&out, "%s%s%s", path1, sep, path2) < 0)
  117         return NULL;
  118 
  119     return out;
  120 }
  121 
  122 static void
  123 add_directory(const char *dirname, MonitorFD **monv, int *monc, int *monvsize, int recursive)
  124 {
  125     int fd;
  126 
  127     fd = open(dirname, O_RDONLY);
  128     if (fd < 0)
  129     die(_("cannot open directory `%s' - %s"), dirname, errstr);
  130 
  131     if ((*monc)+1 >= *monvsize) {
  132     *monvsize = (*monvsize == 0 ? 8 : (*monvsize) * 2);
  133     *monv = realloc(*monv, (*monvsize) * sizeof(MonitorFD));
  134     }
  135     (*monv)[*monc].fd = fd;
  136 
  137     if (prepend_dir != NULL && dirname[0] != '/') {
  138         (*monv)[*monc].name = catfiles(prepend_dir, dirname);
  139     } else {
  140         (*monv)[*monc].name = strdup(dirname);
  141     }
  142     if ((*monv)[*monc].name == NULL)
  143     die("%s", errstr);
  144     
  145     (*monc)++;
  146 
  147     if (recursive) {
  148     DIR *dir;
  149     struct dirent *ep;
  150 
  151     dir = opendir(dirname);
  152     if (dir == NULL)
  153         die(_("cannot open directory `%s' - %s"), dirname, errstr);
  154 
  155     while ((ep = safe_readdir(dir)) != NULL) {
  156         if ((strcmp(ep->d_name, ".") == 0 || strcmp(ep->d_name, "..") == 0))
  157         continue;
  158 
  159         if (ep->d_type == DT_UNKNOWN) {
  160         char *subdirname;
  161         struct stat statbuf;
  162 
  163         /* I admit this is slow but modern libc's don't return DT_UNKNOWN */
  164         subdirname = concat_dirname(dirname, ep->d_name);
  165 
  166         if (lstat(subdirname, &statbuf) < 0)
  167             die(_("cannot stat file `%s' - %s"), subdirname, errstr);
  168         if (S_ISDIR(statbuf.st_mode))
  169             add_directory(subdirname, monv, monc, monvsize, recursive);
  170         free(subdirname);
  171         }
  172         else if (ep->d_type == DT_DIR) {
  173         char *subdirname;
  174 
  175         subdirname = concat_dirname(dirname, ep->d_name);
  176         add_directory(subdirname, monv, monc, monvsize, recursive);
  177         free(subdirname);
  178         }
  179         /* subdirname will be freed when the MonitorFD is freed! */
  180     }
  181 
  182     if (errno != 0) /* assume readdir sets errno to 0 if all is ok */
  183         die(_("cannot read directory `%s' - %s"), dirname, errstr);
  184     if (closedir(dir) < 0)
  185         die(_("cannot read directory `%s' - %s"), dirname, errstr);
  186     }
  187 }
  188 
  189 /* This function returns non-zero if a child had terminated. */
  190 static int
  191 wait_process(void)
  192 {
  193     int status;
  194     int rc;
  195 
  196     rc = waitpid(-1, &status, WNOHANG);
  197     if (rc < 0)
  198     die(_("waitpid on child failed - %s"), errstr);
  199     if (rc > 0) {
  200     if (!WIFEXITED(status))
  201         warn(_("child terminated abnormally"));
  202     if (WEXITSTATUS(status) != 0)
  203         warn(_("child terminated with non-zero status"));
  204     return 1;
  205     }
  206 
  207     return 0;
  208 }
  209 
  210 static void
  211 run_child(int fd, char **argv, int argcount, MonitorFD *monv, int monc)
  212 {
  213     pid_t child;
  214 
  215     child = fork();
  216     if (child < 0)
  217     die(_("cannot fork child - %s"), errstr);
  218     if (child == 0) {
  219     char *child_argv[argcount == 0 ? 3 : argcount+1];
  220     const char *trigfile = NULL;
  221     int c;
  222 
  223     /* Try a smart guess to find monitor filename, otherwise do O(n) search */
  224     c = fd-monv[0].fd;
  225     if (c >= 0 && c < monc && monv[c].fd == fd) {
  226         trigfile = monv[c].name;
  227     } else {
  228         for (c = 0; c < monc; c++) {
  229         if (monv[c].fd == fd) {
  230             trigfile = monv[c].name;
  231             break;
  232         }
  233         }
  234         if (trigfile == NULL)
  235         exit(EXIT_FAILURE);
  236     }
  237 
  238     if (argcount == 0) {
  239         puts(trigfile);
  240         exit(EXIT_SUCCESS);
  241     }
  242 
  243     for (c = 0; c < monc; c++)
  244         close(monv[c].fd); /* ignore errors */
  245 
  246     child_argv[0] = argv[0];
  247     for (c = 1; c < argcount; c++)
  248         child_argv[c] = expand_braces(argv[c], trigfile);
  249     child_argv[argcount] = NULL;
  250 
  251     if (execvp(child_argv[0], child_argv) < 0)
  252         die(_("cannot execute `%s' - %s"), child_argv[0], errstr);
  253     exit(EXIT_FAILURE);
  254     }
  255 }
  256 
  257 static int
  258 unqueue_fd(void)
  259 {
  260     QueueFD *qfd = first_queue_fd;
  261     int ret = qfd->fd;
  262 
  263     first_queue_fd = qfd->next;
  264     if (qfd->next == NULL)
  265     last_queue_fd = NULL;
  266 
  267     free(qfd);
  268 
  269     return ret;
  270 }
  271 
  272 static void
  273 queue_fd(int fd)
  274 {
  275     QueueFD *qfd = malloc(sizeof(QueueFD));
  276     if (qfd == NULL)
  277     die("%s", errstr);
  278     qfd->fd = fd;
  279 
  280     if (last_queue_fd != NULL)
  281     last_queue_fd->next = qfd;
  282     qfd->next = NULL;
  283     last_queue_fd = qfd;
  284     if (first_queue_fd == NULL)
  285     first_queue_fd = qfd;
  286 }
  287 
  288 int main(int argc, char **argv)
  289 {
  290     MonitorFD *monv = NULL;
  291     int monc = 0;
  292     int monvsize = 0;
  293     char *dirv[argc-1];
  294     char *listv[argc-1];
  295     int listc = 0;
  296     int dirc = 0;
  297     struct sigaction act;
  298     long notifyarg = 0;
  299     int c;
  300     int background = 0;     /* corresponds to --background */
  301     int recursive = 0;          /* corresponds to --recursive */
  302     int32_t times = -1;         /* corresponds to --times */
  303     int32_t max_processes = 1;  /* corresponds to --processes */
  304     int32_t max_queue = -1;     /* corresponds to --queue */
  305     int32_t cur_processes = 0;
  306     int32_t cur_queue = 0;
  307     sigset_t signalset;
  308     char *line = NULL;
  309     size_t line_size = 0;
  310     int done = 0;
  311 
  312 #ifdef ENABLE_NLS
  313     setlocale(LC_ALL, "");
  314     bindtextdomain(PACKAGE, LOCALEDIR);
  315     textdomain(PACKAGE);
  316 #endif
  317 
  318     while (1) {
  319     c = getopt_long(argc, argv, short_opts, long_opts, NULL);
  320     if (c == -1 || c == 'e')
  321         break;
  322 
  323     switch (c) {
  324     case 1: /* non-option argument */
  325         /*dirv[dirc] = strdup(optarg);
  326         if (dirv[dirc] == NULL)
  327         die("%s", errstr);
  328         dirc++;*/
  329         dirv[dirc++] = optarg;
  330         break;
  331     case 'f':
  332         listv[listc++] = optarg;
  333         break;
  334     case 'A':
  335         notifyarg |= DN_ACCESS;
  336         break;
  337     case 'M':
  338         notifyarg |= DN_MODIFY;
  339         break;
  340     case 'C':
  341         notifyarg |= DN_CREATE;
  342         break;
  343     case 'D':
  344         notifyarg |= DN_DELETE;
  345         break;
  346     case 'R':
  347         notifyarg |= DN_RENAME;
  348         break;
  349     case 'B':
  350         notifyarg |= DN_ATTRIB;
  351         break;
  352     case 'a':
  353         notifyarg |= DN_ACCESS|DN_MODIFY|DN_CREATE|DN_DELETE|DN_RENAME|DN_ATTRIB;
  354         break;
  355     case 's':
  356         quiet = 1;
  357         break;
  358         case 'b':
  359             background = 1;
  360             break;
  361     case 'r':
  362         recursive = 1;
  363         break;
  364     case 'p':
  365         if (!parse_int32(optarg, &max_processes))
  366         die(_("invalid processes value: %s"), optarg);
  367         if (max_processes <= 0)
  368         max_processes = -1;
  369         break;
  370     case 'q':
  371         if (!parse_int32(optarg, &max_queue))
  372         die(_("invalid queue value: %s"), optarg);
  373         break;
  374     case 't':
  375         if (!parse_int32(optarg, &times))
  376         die(_("invalid times value: %s"), optarg);
  377         if (times <= 0)
  378         times = -1;
  379         break;
  380     case 'o':
  381         times = 1;
  382         break;
  383     case 'V': /* --version */
  384             printf("%s %s\n", PACKAGE, VERSION);
  385             printf(_("Written by %s.\n"), AUTHOR);
  386             puts("\nCopyright (C) 2002-2004 Oskar Liljeblad.");
  387             puts(_("\
  388 This is free software; see the source for copying conditions.  There is NO\n\
  389 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."));
  390         exit(EXIT_SUCCESS);
  391     case 'H': /* --help */
  392         printf(_("Usage: %s [OPTION]... DIRECTORY... [-e COMMAND...]\n"), argv[0]);
  393             puts(_("Execute a command every time the contents of a directory change."));
  394             puts(_("\nEvents:"));
  395             puts(_("  -A, --access             trigger when a file in the directory was accessed"));
  396             puts(_("  -M, --modify             trigger when a file in the directory was modified"));
  397             puts(_("  -C, --create             trigger when a file was created in the directory"));
  398             puts(_("  -D, --delete             trigger whan a file was unlinked from the directory"));
  399             puts(_("  -R, --rename             trigger when a file in the directory was renamed"));
  400             puts(_("\
  401   -B, --attrib             trigger when the directory had its attributes\n\
  402                            changed (after chmod, chown)"));
  403             puts(_("  -a, --all                all of the above"));
  404             puts(_("\nGeneral:"));
  405             puts(_("\
  406   -e, --execute=COMMAND..  command to execute when an event is triggered\n\
  407                            (all remaining args are treated as command args)"));
  408             puts(_("  -f, --file=FILE          read directories to monitor from FILE, one per line"));
  409             puts(_("  -p, --processes=COUNT    max number of commands to run at a time"));
  410             puts(_("  -q, --queue=DEPTH        max depth of queue holding commands to be run"));
  411             puts(_("  -t, --times=COUNT        exit after running the command COUNT times"));
  412             puts(_("  -o, --once               same as `--times 1'"));
  413             puts(_("  -r, --recursive          monitor subdirectories too (recursively)"));
  414             puts(_("  -b, --background         run in background (detach)"));
  415             puts(_("  -s, --silent             don't print warnings about non-zero child exits"));
  416             puts(_("      --quiet              same as `--silent'"));
  417             puts(_("      --help               display this help and exit"));
  418             puts(_("      --version            output version information and exit"));
  419             puts(_("\nUnless events have been specified, the events are set to create and delete.\n"));
  420             puts(_("\
  421 Specifying -1 for queue means unlimited depth. The default value for --queue\n\
  422 is -1, and 1 for --processes.\n"));
  423             puts(_("\
  424 The default command (unless specified with -e) is to print the name of the\n\
  425 of the directory updated. The string `{}' in the command specification is\n\
  426 replaced by the name of the directory that was updated.\n"));
  427             printf(_("Report bugs to <%s>.\n"), PACKAGE_BUGREPORT);
  428         exit(EXIT_SUCCESS);
  429     case '?':
  430         exit(EXIT_FAILURE);
  431     }
  432     }
  433 
  434     if (dirc == 0 && listc == 0)
  435     die(_("missing directory argument\nTry `%s --help' for more information."), argv[0]);
  436 
  437     if (notifyarg == 0)
  438     notifyarg = DN_CREATE|DN_DELETE;
  439 
  440     if (background) {
  441         pid_t child;
  442 
  443         prepend_dir = get_current_dir_name();
  444     if (prepend_dir == NULL)
  445         die(_("cannot get current directory name: %s"), errstr);
  446 
  447         child = fork();
  448         if (child < 0)
  449             die(_("fork failed: %s"), errstr);
  450         if (child > 0)
  451             exit(EXIT_SUCCESS);
  452 
  453         if (setsid() < 0)
  454             die(_("setsid failed: %s"), errstr);
  455 
  456         close(STDIN_FILENO); /* ignore errors */
  457         close(STDOUT_FILENO); /* ignore errors */
  458         close(STDERR_FILENO); /* ignore errors */
  459     }
  460 
  461     sigemptyset(&signalset);
  462     sigaddset(&signalset, SIGCHLD);
  463     sigaddset(&signalset, SIGRTMIN);
  464 
  465     /* Work around lpthread bug (libc/4927). */
  466     if (sigprocmask(SIG_UNBLOCK, &signalset, NULL) < 0)
  467     die(_("sigprocmask failed - %s"), errstr);
  468 
  469     /* Register SIGRTMIN and SIGCHLD signal handlers. */
  470     act.sa_sigaction = sigrt_handler;
  471     sigemptyset(&act.sa_mask);
  472     act.sa_flags = SA_SIGINFO | SA_RESTART | SA_NODEFER;
  473     if (sigaction(SIGRTMIN, &act, NULL) < 0)
  474     die(_("sigaction failed - %s"), errstr);
  475 
  476     act.sa_sigaction = (void (*)(int, siginfo_t *, void *)) sigchld_handler;
  477     act.sa_flags = SA_RESTART | SA_NODEFER | SA_NOCLDSTOP;
  478     if (sigaction(SIGCHLD, &act, NULL) < 0)
  479     die(_("sigaction failed - %s"), errstr);
  480 
  481     /* Block SIGRTMIN and SIGCHLD. */
  482     if (sigprocmask(SIG_BLOCK, &signalset, NULL) < 0)
  483     die(_("sigprocmask failed - %s"), errstr);
  484 
  485     for (c = 0; c < listc; c++) {
  486     FILE *file;
  487     ssize_t len;
  488 
  489     file = fopen(listv[c], "r");
  490     if (file == NULL)
  491         die(_("%s: cannot open for reading - %s"), listv[c], errstr);
  492     while ((len = getline(&line, &line_size, file)) >= 0) {
  493         if (len != 0) {
  494         if (line[len-1] == '\n')
  495             line[len-1] = '\0';
  496         add_directory(line, &monv, &monc, &monvsize, recursive);
  497         }
  498     }
  499     if (ferror(file))
  500         die(_("%s: cannot read - %s"), listv[c], errstr);
  501     fclose(file);
  502     }
  503     for (c = 0; c < dirc; c++)
  504     add_directory(dirv[c], &monv, &monc, &monvsize, recursive);
  505 
  506     for (c = 0; c < monc; c++) {
  507     if (fcntl(monv[c].fd, F_SETSIG, SIGRTMIN) < 0)
  508         die(_("fcntl F_SETSIG on `%s' failed - %s"), monv[c].name, errstr);
  509     if (fcntl(monv[c].fd, F_NOTIFY, notifyarg|DN_MULTISHOT) < 0)
  510         die(_("fcntl F_NOTIFY on `%s' failed - %s"), monv[c].name, errstr);
  511     }
  512 
  513     if (background && chdir("/") < 0)
  514         die(_("cannot change current directory to `/': %s"), errstr);
  515 
  516     while (!done) {
  517     siginfo_t signalinfo;
  518     int signal;
  519 
  520     signal = TEMP_FAILURE_RETRY(sigwaitinfo(&signalset, &signalinfo));
  521     if (signal < 0)
  522         die(_("sigwaitinfo failed - %s"), errstr);
  523 
  524     if (signal == SIGCHLD) {
  525         while (cur_processes > 0 && wait_process()) {
  526         cur_processes--;
  527         if (times == 0 && cur_processes == 0)
  528             done = 1; /* Terminate program */
  529         if (cur_queue > 0) {
  530             cur_queue--;
  531             run_child(unqueue_fd(), argv+optind, argc-optind, monv, monc);
  532             cur_processes++;
  533             if (times > 0)
  534             times--;
  535         }
  536         }
  537     }
  538     else if (signal == SIGRTMIN && (times < 0 || times > 0)) {
  539         int fd = signalinfo.si_fd;
  540 
  541         if (max_processes < 0 || cur_processes < max_processes) {
  542         run_child(fd, argv+optind, argc-optind, monv, monc);
  543         cur_processes++;
  544         if (times > 0)
  545             times--;
  546         } else if (max_queue < 0 || cur_queue < max_queue) {
  547         queue_fd(fd);
  548         cur_queue++;
  549         }
  550     }
  551     }
  552 
  553     for (c = 0; c < monc; c++) {
  554     close(monv[c].fd); /* ignore errors */
  555     free(monv[c].name);
  556     }
  557     free(monv);
  558     free(prepend_dir);
  559 
  560     exit(EXIT_SUCCESS);
  561 }