summaryrefslogtreecommitdiffabout
Unidiff
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--.gitignore5
-rw-r--r--Makefile44
-rw-r--r--dircond.c1062
-rw-r--r--dlist.c35
4 files changed, 1146 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ef072ec
--- a/dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
1dircond
2*~
3*.o
4*.tar.*
5.emacs*
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..5651d93
--- a/dev/null
+++ b/Makefile
@@ -0,0 +1,44 @@
1CFLAGS=-ggdb -Wall
2PREFIX=/usr/local
3BINDIR=$(PREFIX)/bin
4MANDIR=$(PREFIX)/share/man
5PACKAGE=upev
6VERSION=1.0
7DISTFILES=Makefile dircond.c dlist.c
8
9dircond: dircond.c dlist.c
10 cc -odircond $(CFLAGS) dircond.c
11
12install-bin: dircond
13 mkdir -p $(DESTDIR)$(BINDIR)
14 cp dircond $(DESTDIR)$(BINDIR)
15
16install-man:;
17#install-man: dircond.1
18 #mkdir -p $(DESTDIR)$(MANDIR)/man1
19 #cp dircond.1 $(DESTDIR)$(MANDIR)/man1
20
21install: install-bin install-man
22
23distdir = $(PACKAGE)-$(VERSION)
24
25distdir:
26 rm -rf $(distdir)
27 mkdir $(distdir)
28 cp $(DISTFILES) $(distdir)
29
30dist: distdir
31 tar cfz $(distdir).tar.gz $(distdir)
32 rm -rf $(distdir)
33
34distcheck: distdir
35 mkdir $(distdir)/_inst; \
36 cd $(distdir) || exit 2;\
37 make || exit 2; \
38 make DESTDIR=`pwd`/_inst install || exit 2
39 (cd $(distdir)/_inst; find . -type f)|sort|cut -c2- | \
40 cmp - instlist
41 make dist
42
43clean:
44 rm -f dircond
diff --git a/dircond.c b/dircond.c
new file mode 100644
index 0000000..682e1c3
--- a/dev/null
+++ b/dircond.c
@@ -0,0 +1,1062 @@
1#include <stdio.h>
2#include <stdlib.h>
3#include <stdarg.h>
4#include <fcntl.h>
5#include <syslog.h>
6#include <unistd.h>
7#include <getopt.h>
8#include <string.h>
9#include <errno.h>
10#include <pwd.h>
11#include <signal.h>
12#include <time.h>
13#include <sys/inotify.h>
14#include <sys/wait.h>
15#include <sys/stat.h>
16
17/* Configuration settings */
18const char *program_name; /* This program name */
19int foreground; /* Remain in the foreground */
20int facility = -1; /* Use this syslog facility for logging.
21 -1 means log to stderr */
22int debug_level; /* Debug verbosity level */
23unsigned handler_timeout = 5; /* Timeout for handler program (seconds) */
24int autowatch; /* Automatically add directories created
25 under watchpoints to the watcher. If set
26 to -1, nesting level is not limited. If
27 set to a positive value, this value limits
28 the nesting depth. */
29
30/* Event codes */
31enum {
32 evt_create, /* file has been created */
33 evt_delete, /* file has been deleted */
34 evt_close, /* file has been modified and closed */
35 evt_max /* number of handled events */
36};
37
38/* Handler flags. */
39#define HF_NOWAIT 0x01 /* Don't wait for termination */
40#define HF_STDOUT 0x02 /* Capture stdout */
41#define HF_STDERR 0x04 /* Capture stderr */
42
43/* Handler structure */
44struct handler {
45 int flags; /* Handler flags */
46 const char *prog; /* Handler program (no arguments allowed) */
47};
48
49/* Array of handlers for each event */
50struct handler handler[evt_max];
51
52/* Process list */
53
54/* Redirector codes */
55#define REDIR_OUT 0
56#define REDIR_ERR 1
57
58/* A running process is described by this structure */
59struct process {
60 struct process *next, *prev;
61 int master; /* Master process or redirector */
62 pid_t pid; /* PID */
63 time_t start; /* Time when the process started */
64 pid_t redir[2]; /* If master==1, holds PIDs of redirector
65 processes (0 if no redirector) */
66};
67
68/* List of running processes */
69struct process *proc_list;
70/* List of available process slots */
71struct process *proc_avail;
72/* Declare functions for handling process lists */
73#define LIST process
74#define LIST_PUSH proc_push
75#define LIST_POP proc_pop
76#define LIST_UNLINK proc_unlink
77#include "dlist.c"
78
79/* Diagnostic functions */
80const char *
81severity(int prio)
82{
83 switch (prio) {
84 case LOG_EMERG:
85 return "EMERG";
86 case LOG_ALERT:
87 return "ALERT";
88 case LOG_CRIT:
89 return "CRIT";
90 case LOG_ERR:
91 return "ERROR";
92 case LOG_WARNING:
93 return "WARNING";
94 case LOG_NOTICE:
95 return "NOTICE";
96 case LOG_INFO:
97 return "INFO";
98 case LOG_DEBUG:
99 return "DEBUG";
100 }
101 return NULL;
102}
103
104void
105vdiag(int prio, const char *fmt, va_list ap)
106{
107 const char *s;
108
109 if (facility <= 0) {
110 fprintf(stderr, "%s: ", program_name);
111 s = severity(prio);
112 if (s)
113 fprintf(stderr, "[%s] ", s);
114 vfprintf(stderr, fmt, ap);
115 fputc('\n', stderr);
116 } else {
117 vsyslog(prio, fmt, ap);
118 }
119}
120
121void
122diag(int prio, const char *fmt, ...)
123{
124 va_list ap;
125
126 va_start(ap, fmt);
127 vdiag(prio, fmt, ap);
128 va_end(ap);
129}
130
131static void
132debugprt(const char *fmt, ...)
133{
134 va_list ap;
135
136 va_start(ap, fmt);
137 vdiag(LOG_DEBUG, fmt, ap);
138 va_end(ap);
139}
140
141#define debug(l, c) do { if (debug_level>=(l)) debugprt c; } while(0)
142
143/* Command line processing and auxiliary functions */
144
145static void
146set_program_name(const char *arg)
147{
148 char *p = strrchr(arg, '/');
149 if (p)
150 program_name = p + 1;
151 else
152 program_name = arg;
153}
154
155static int
156read_facility(const char *arg)
157{
158 static struct transtab { int f; char *s; } ftab[] = {
159 { LOG_AUTH, "auth" },
160 { LOG_AUTHPRIV, "authpriv" },
161 { LOG_CRON, "cron" },
162 { LOG_DAEMON, "daemon" },
163 { LOG_FTP, "ftp" },
164 { LOG_LOCAL0, "local0" },
165 { LOG_LOCAL1, "local1" },
166 { LOG_LOCAL2, "local2" },
167 { LOG_LOCAL3, "local3" },
168 { LOG_LOCAL4, "local4" },
169 { LOG_LOCAL5, "local5" },
170 { LOG_LOCAL6, "local6" },
171 { LOG_LOCAL7, "local7" },
172 { LOG_LPR, "lpr" },
173 { LOG_MAIL, "mail" },
174 { LOG_NEWS, "news" },
175 { LOG_USER, "user" },
176 { LOG_UUCP, "uucp" },
177 { 0, NULL }
178 };
179 struct transtab *p;
180 char *s;
181 unsigned long n;
182
183 for (p = ftab; p->s; p++) {
184 if (strcmp(p->s, arg) == 0)
185 return p->f;
186 }
187 n = strtoul(arg, &s, 10);
188 if (*s) {
189 diag(LOG_CRIT, "unknown facility: %s", arg);
190 exit(1);
191 }
192 if (n > 256) {
193 diag(LOG_CRIT, "facility out of range: %s", arg);
194 exit(1);
195 }
196 return n;
197}
198
199void
200set_handler(const char *arg)
201{
202 int len;
203 int n = -1;
204
205 /* Event code */
206 len = strcspn(arg,",");
207 if (strncmp(arg, "create", len) == 0)
208 n = evt_create;
209 else if (strncmp(arg, "delete", len) == 0)
210 n = evt_delete;
211 else if (strncmp(arg, "close", len) == 0)
212 n = evt_close;
213 else {
214 diag(LOG_CRIT, "unrecognized event: %*.*s", len, len, arg);
215 exit(1);
216 }
217
218 /* flag */
219 handler[n].flags = 0;
220
221 for (arg += len; *arg == ','; arg += len) {
222 ++arg;
223 len = strcspn(arg, ",");
224 if (arg[len] == 0)
225 break;
226 if (strncmp(arg, "wait", len) == 0)
227 handler[n].flags &= ~HF_NOWAIT;
228 else if (strncmp(arg, "nowait", len) == 0)
229 handler[n].flags |= HF_NOWAIT;
230 else if (strncmp(arg, "stdout", len) == 0)
231 handler[n].flags |= HF_STDOUT;
232 else if (strncmp(arg, "stderr", len) == 0)
233 handler[n].flags |= HF_STDERR;
234 else {
235 diag(LOG_CRIT, "unknown flag %*.*s", len, len, arg);
236 exit(1);
237 }
238 }
239
240 if (*arg == 0) {
241 diag(LOG_CRIT, "bad handler specification: %s", arg);
242 exit(1);
243 }
244 handler[n].prog = arg;
245}
246
247/* Memory allocation with error checking */
248void *
249emalloc(size_t size)
250{
251 void *p = malloc(size);
252 if (!p) {
253 diag(LOG_CRIT, "not enough memory");
254 exit(2);
255 }
256 return p;
257}
258
259void *
260ecalloc(size_t nmemb, size_t size)
261{
262 void *p = calloc(nmemb, size);
263 if (!p) {
264 diag(LOG_CRIT, "not enough memory");
265 exit(2);
266 }
267 return p;
268}
269
270/* Create a full file name from directory and file name */
271char *
272mkfilename(const char *dir, const char *file)
273{
274 char *tmp;
275 size_t dirlen = strlen(dir);
276 size_t fillen = strlen(file);
277 size_t len;
278
279 while (dirlen > 0 && dir[dirlen-1] == '/')
280 dirlen--;
281
282 len = dirlen + (dir[0] ? 1 : 0) + fillen;
283 tmp = malloc(len + 1);
284 if (tmp) {
285 memcpy(tmp, dir, dirlen);
286 if (dir[0])
287 tmp[dirlen++] = '/';
288 memcpy(tmp + dirlen, file, fillen);
289 tmp[len] = 0;
290 }
291 return tmp;
292}
293
294void
295signal_setup(void (*sf) (int))
296{
297 signal(SIGTERM, sf);
298 signal(SIGQUIT, sf);
299 signal(SIGINT, sf);
300 signal(SIGHUP, sf);
301 signal(SIGALRM, sf);
302 signal(SIGUSR1, sf);
303 signal(SIGUSR2, sf);
304}
305
306static void
307close_fds(fd_set *fdset)
308{
309 int i;
310
311 for (i = sysconf(_SC_OPEN_MAX); i >= 0; i--) {
312 if (fdset && FD_ISSET(i, fdset))
313 continue;
314 close(i);
315 }
316}
317
318void
319storepid(const char *pidfile)
320{
321 FILE *fp = fopen(pidfile, "w");
322 if (!fp) {
323 diag(LOG_ERR, "cannot open pidfile %s for writing: %s",
324 pidfile, strerror(errno));
325 } else {
326 fprintf(fp, "%lu\n", (unsigned long) getpid());
327 fclose(fp);
328 }
329}
330
331void
332setuser(const char *user)
333{
334 struct passwd *pw;
335
336 pw = getpwnam(user);
337 if (!pw) {
338 diag(LOG_CRIT, "getpwnam(%s): %s", user, strerror(errno));
339 exit(2);
340 }
341 if (pw->pw_uid == 0)
342 return;
343 if (setgid(pw->pw_gid)) {
344 diag(LOG_CRIT, "setgid(%lu): %s", (unsigned long) pw->pw_gid,
345 strerror(errno));
346 exit(2);
347 }
348 if (setuid(pw->pw_uid)) {
349 diag(LOG_CRIT, "setuid(%lu): %s", (unsigned long) pw->pw_uid,
350 strerror(errno));
351 exit(2);
352 }
353}
354
355/* Process list handling (high-level) */
356
357struct process *
358register_process(pid_t pid, time_t t)
359{
360 struct process *p;
361
362 if (proc_avail)
363 p = proc_pop(&proc_avail);
364 else
365 p = emalloc(sizeof(*p));
366 memset(p, 0, sizeof(*p));
367 p->master = 0;
368 p->pid = pid;
369 p->start = t;
370 proc_push(&proc_list, p);
371 return p;
372}
373
374void
375deregister_process(pid_t pid, time_t t)
376{
377 struct process *p;
378
379 for (p = proc_list; p; p = p->next)
380 if (p->pid == pid) {
381 if (p->prev)
382 p->prev->next = p->next;
383 else
384 proc_list = p;
385 if (p->next)
386 p->next->prev = p->prev;
387 free(p);
388 break;
389 }
390}
391
392struct process *
393process_lookup(pid_t pid)
394{
395 struct process *p;
396
397 for (p = proc_list; p; p = p->next)
398 if (p->pid == pid)
399 return p;
400 return NULL;
401}
402
403static void
404print_status(pid_t pid, int status, int expect_term)
405{
406 if (WIFEXITED(status)) {
407 if (WEXITSTATUS(status) == 0)
408 debug(1, ("process %lu exited successfully",
409 (unsigned long) pid));
410 else
411 diag(LOG_ERR, "process %lu failed with status %d",
412 (unsigned long) pid, WEXITSTATUS(status));
413 } else if (WIFSIGNALED(status)) {
414 int prio;
415
416 if (expect_term && WTERMSIG(status) == SIGTERM)
417 prio = LOG_DEBUG;
418 else
419 prio = LOG_ERR;
420
421 diag(prio, "process %lu terminated on signal %d",
422 (unsigned long) pid, WTERMSIG(status));
423 } else if (WIFSTOPPED(status))
424 diag(LOG_ERR, "process %lu stopped on signal %d",
425 (unsigned long) pid, WSTOPSIG(status));
426#ifdef WCOREDUMP
427 else if (WCOREDUMP(status))
428 diag(LOG_ERR,
429 "process %lu dumped core", (unsigned long) pid);
430#endif
431 else
432 diag(LOG_ERR,
433 "process %lu terminated with unrecognized status",
434 (unsigned long) pid);
435}
436
437void
438process_cleanup(int expect_term)
439{
440 pid_t pid;
441 int status;
442
443 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
444 struct process *p = process_lookup(pid);
445 print_status(pid, status, expect_term);
446 if (p) {
447 if (p->master) {
448 if (p->redir[REDIR_OUT])
449 kill(p->redir[REDIR_OUT], SIGKILL);
450 if (p->redir[REDIR_ERR])
451 kill(p->redir[REDIR_ERR], SIGKILL);
452 }
453 p->pid = 0;
454 proc_unlink(&proc_list, p);
455 proc_push(&proc_avail, p);
456 }
457 }
458}
459
460void
461process_timeouts()
462{
463 struct process *p;
464 time_t now = time(NULL);
465 time_t alarm_time = 0, x;
466
467 debug(2, ("begin scanning process list"));
468 for (p = proc_list; p; p = p->next) {
469 x = now - p->start;
470 if (x >= handler_timeout) {
471 diag(LOG_ERR, "process %lu timed out",
472 (unsigned long) p->pid);
473 kill(p->pid, SIGKILL);
474 } else if (alarm_time == 0 || handler_timeout - x< alarm_time)
475 alarm_time = handler_timeout - x;
476 }
477
478 if (alarm_time) {
479 debug(2, ("scheduling alarm in %lu seconds",
480 (unsigned long) alarm_time));
481 alarm(alarm_time);
482 }
483 debug(2, ("end scanning process list"));
484}
485
486/* Operations with handlers and redirections */
487
488static void
489redir_exit(int sig)
490{
491 _exit(0);
492}
493
494int
495open_redirector(const char *tag, int prio, pid_t *return_pid)
496{
497 int p[2];
498 FILE *fp;
499 char buf[512];
500 pid_t pid;
501 fd_set fdset;
502
503 if (pipe(p)) {
504 diag(LOG_ERR,
505 "cannot start redirector for %s, pipe failed: %s",
506 tag, strerror(errno));
507 return -1;
508 }
509 switch (pid = fork()) {
510 case 0:
511 /* Redirector process */
512 FD_ZERO(&fdset);
513 FD_SET(p[0], &fdset);
514 if (facility <= 0)
515 FD_SET(2, &fdset);
516 close_fds(&fdset);
517
518 alarm(0);
519 signal_setup(redir_exit);
520
521 fp = fdopen(p[0], "r");
522 if (fp == NULL)
523 _exit(1);
524 if (facility >= 0)
525 openlog(tag, LOG_PID, facility);
526
527 while (fgets(buf, sizeof(buf), fp) > 0) {
528 int len = strlen(buf);
529 if (len && buf[len-1] == '\n')
530 buf[len-1] = 0;
531 diag(prio, "%s", buf);
532 }
533 _exit(0);
534
535 case -1:
536 diag(LOG_CRIT,
537 "cannot run redirector `%s': fork failed: %s",
538 tag, strerror(errno));
539 return -1;
540
541 default:
542 debug(1, ("redirector for %s started, pid=%lu",
543 tag, (unsigned long) pid));
544 close (p[0]);
545 *return_pid = pid;
546 return p[1];
547 }
548}
549
550static int
551run_handler(int event, const char *dir, const char *file)
552{
553 pid_t pid;
554 char buf[1024];
555 int redir_fd[2] = { -1, -1 };
556 pid_t redir_pid[2];
557 struct process *p;
558 struct handler *hp = &handler[event];
559
560 if (!hp->prog)
561 return 0;
562
563 debug(1, ("starting %s, dir=%s, file=%s", hp->prog, dir, file));
564 if (hp->flags & HF_STDERR)
565 redir_fd[REDIR_ERR] = open_redirector(hp->prog, LOG_INFO,
566 &redir_pid[REDIR_ERR]);
567 if (hp->flags & HF_STDOUT)
568 redir_fd[REDIR_OUT] = open_redirector(hp->prog, LOG_ERR,
569 &redir_pid[REDIR_OUT]);
570
571 pid = fork();
572 if (pid == -1) {
573 close(redir_fd[REDIR_OUT]);
574 close(redir_fd[REDIR_ERR]);
575 diag(LOG_ERR, "fork: %s", strerror(errno));
576 return -1;
577 }
578
579 if (pid == 0) {
580 /* child */
581 char *argv[2];
582 fd_set fdset;
583
584 if (chdir(dir)) {
585 diag(LOG_CRIT, "cannot change to %s: %s",
586 dir, strerror(errno));
587 exit(1);
588 }
589
590 FD_ZERO(&fdset);
591
592 if (redir_fd[REDIR_OUT] != -1) {
593 if (redir_fd[REDIR_OUT] != 1 &&
594 dup2(redir_fd[REDIR_OUT], 1) == -1) {
595 diag(LOG_ERR, "dup2: %s", strerror(errno));
596 _exit(127);
597 }
598 FD_SET(1, &fdset);
599 }
600 if (redir_fd[REDIR_ERR] != -1) {
601 if (redir_fd[REDIR_ERR] != 2 &&
602 dup2(redir_fd[REDIR_ERR], 2) == -1) {
603 diag(LOG_ERR, "dup2: %s", strerror(errno));
604 _exit(127);
605 }
606 FD_SET(2, &fdset);
607 }
608 close_fds(&fdset);
609 alarm(0);
610 signal_setup(SIG_DFL);
611 signal(SIGCHLD, SIG_DFL);
612 argv[0] = (char*) hp->prog;
613 argv[1] = NULL;
614 snprintf(buf, sizeof buf, "%d", event);
615 setenv("DIRCOND_EVENT", buf, 1);
616 setenv("DIRCOND_FILE", file, 1);
617 execv(argv[0], argv);
618 _exit(127);
619 }
620
621 /* master */
622 debug(1, ("%s running; dir=%s, file=%s, pid=%lu",
623 hp->prog, dir, file, (unsigned long)pid));
624
625 p = register_process(pid, time(NULL));
626
627 memcpy(p->redir, redir_pid, sizeof(p->redir));
628 p->master = 1;
629
630 close(redir_fd[REDIR_OUT]);
631 close(redir_fd[REDIR_ERR]);
632
633 if (hp->flags & HF_NOWAIT) {
634 return 0;
635 }
636
637 debug(1, ("waiting for %s (%lu) to terminate",
638 hp->prog, (unsigned long)pid));
639 while (time(NULL) - p->start < 2*handler_timeout) {
640 sleep(1);
641 process_cleanup(1);
642 if (p->pid == 0)
643 break;
644 }
645 return 0;
646}
647
648
649/* Directory watcher functions */
650
651/* A directory watcher is described by the following structure */
652struct dirwatcher {
653 struct dirwatcher *next, *prev;
654 struct dirwatcher *parent; /* Points to the parent watcher.
655 NULL for top-level watchers */
656 char *name; /* Pathname being watched */
657 int wd; /* Watch descriptor */
658};
659
660/* A doubly-linked list of active watchers */
661struct dirwatcher *dirwatcher_list;
662/* Declare low-level functions for handling the watchers list */
663#define LIST dirwatcher
664#define LIST_PUSH dirwatcher_push
665#define LIST_POP dirwatcher_pop
666#define LIST_UNLINK dirwatcher_unlink
667#include "dlist.c"
668
669/* Free the allocated watcher (must have been unlinked from the list first) */
670void
671dirwatcher_free(struct dirwatcher *dwp)
672{
673 free(dwp->name);
674 free(dwp);
675}
676
677/* Create a new watcher and attach it to the list. */
678struct dirwatcher *
679dirwatcher_create(int ifd, const char *name)
680{
681 struct dirwatcher *dwp;
682 int wd;
683
684 debug(1, ("creating watcher %s", name));
685 dwp = malloc(sizeof(*dwp));
686 if (!dwp) {
687 diag(LOG_ERR, "not enough memory");
688 return NULL;
689 }
690 dwp->name = strdup(name);
691 if (!dwp->name) {
692 diag(LOG_ERR, "not enough memory");
693 free(dwp);
694 return NULL;
695 }
696 dwp->parent = NULL;
697
698 wd = inotify_add_watch(ifd, name,
699 IN_DELETE|IN_CREATE|IN_CLOSE_WRITE);
700 if (wd == -1) {
701 diag(LOG_ERR, "cannot set watch on %s: %s",
702 name, strerror(errno));
703 dirwatcher_free(dwp);
704 return NULL;
705 }
706
707 dwp->wd = wd;
708 dirwatcher_push(&dirwatcher_list, dwp);
709
710 return dwp;
711}
712
713/* Destroy a watcher, unlink it and reclaim the allocated memory. */
714void
715dirwatcher_destroy(int ifd, struct dirwatcher *dwp)
716{
717 debug(1, ("removing watcher %s", dwp->name));
718 dirwatcher_unlink(&dirwatcher_list, dwp);
719 inotify_rm_watch(ifd, dwp->wd);
720 dirwatcher_free(dwp);
721}
722
723/* Find a watcher with the given descriptor */
724struct dirwatcher *
725dirwatcher_find_wd(int wd)
726{
727 struct dirwatcher *dwp;
728
729 for (dwp = dirwatcher_list; dwp; dwp = dwp->next)
730 if (dwp->wd == wd)
731 break;
732 return dwp;
733}
734
735/* Find a watcher with the given pathname */
736struct dirwatcher *
737dirwatcher_find_name(const char *name)
738{
739 struct dirwatcher *dwp;
740
741 for (dwp = dirwatcher_list; dwp; dwp = dwp->next)
742 if (strcmp(dwp->name, name) == 0)
743 break;
744 return dwp;
745}
746
747/* Compare full pathname with a directory and file name. Return
748 semantics is the same as in strcmp(2). */
749int
750name2cmp(const char *pathname, const char *dir, const char *name)
751{
752 int c;
753
754 for (; *pathname && *dir; pathname++, dir++)
755 if (c = *pathname - *dir)
756 return c;
757 while (*pathname && *pathname == '/')
758 ++pathname;
759 while (*dir && *dir == '/')
760 ++dir;
761 if (*dir)
762 return - *dir;
763
764 for (; *pathname && *name; pathname++, name++)
765 if (c = *pathname - *name)
766 return c;
767 if (*pathname)
768 return *pathname;
769 if (*name)
770 return - *name;
771 return 0;
772}
773
774/* Remove a watcher identified by its directory and file name */
775void
776remove_watcher(int ifd, const char *dir, const char *name)
777{
778 struct dirwatcher *dwp;
779 for (dwp = dirwatcher_list; dwp; dwp = dwp->next)
780 if (name2cmp(dwp->name, dir, name) == 0) {
781 dirwatcher_destroy(ifd, dwp);
782 return;
783 }
784}
785
786/* Return nesting level of a watcher */
787int
788dirlevel(struct dirwatcher *dw)
789{
790 int lev = 0;
791 while (dw = dw->parent)
792 ++lev;
793 return lev;
794}
795
796/* Check if a new watcher must be created and create it if so.
797
798 A watcher must be created if (1) autowatch has negative value,
799 or (2) it has a positive value and the nesting level of the parent
800 watcher does not exceed it.
801
802 Return 0 on success, -1 on error.
803*/
804int
805check_new_watcher(int ifd, const char *dir, const char *name)
806{
807 int rc;
808 char *fname;
809 struct stat st;
810 struct dirwatcher *parent;
811
812 if (autowatch == 0)
813 return 0;
814 parent = dirwatcher_find_name(dir);
815 if (autowatch > 0 && dirlevel(parent) >= autowatch)
816 return 0;
817
818 fname = mkfilename(dir, name);
819 if (!fname) {
820 diag(LOG_ERR, "cannot create watcher %s/%s: not enough memory",
821 dir, name);
822 return -1;
823 }
824
825 if (stat(fname, &st)) {
826 diag(LOG_ERR, "cannot create watcher %s/%s, stat failed: %s",
827 dir, name, strerror(errno));
828 rc = -1;
829 } else if (S_ISDIR(st.st_mode)) {
830 struct dirwatcher *dwp = dirwatcher_create(ifd, fname);
831 if (dwp) {
832 rc = 0;
833 dwp->parent = parent;
834 } else
835 rc = -1;
836 } else
837 rc = 0;
838 free(fname);
839 return rc;
840}
841
842/* Output a help summary. Return a code suitable for exit(2). */
843int
844help()
845{
846 printf("Usage: %s [OPTIONS] DIR [DIR...]\n", program_name);
847 printf("OPTIONS are:\n\n");
848
849 printf(" -a automatically watch created directories\n");
850 printf(" -d increase debug verbosity\n");
851 printf(" -f run in the foreground\n");
852 printf(" -F FACILITY log under this syslog facility\n");
853 printf(" -l N automatically watch new directories located\n");
854 printf(" up to Nth nesting level\n");
855 printf(" -P FILE write PID to FILE\n");
856 printf(" -p EVENT,[FLAG[,FLAG...],]COMMAND\n");
857 printf(" start COMMAND upon EVENT\n");
858 printf(" -T TIMEOUT set timeout for external commands\n");
859 printf(" -t TAG log with this syslog tag\n");
860 printf(" -u USER run as this USER\n");
861
862 printf(" -h output this help summary\n\n");
863 printf("Report bugs to <gray+dircond@gnu.org.ua>.\n");
864
865 return 0;
866}
867
868int signo = 0;
869
870void
871sigmain(int sig)
872{
873 signo = sig;
874 signal(sig, sigmain);
875}
876
877char buffer[4096];
878
879int
880main(int argc, char **argv)
881{
882 int ifd, c, i;
883 char *tag = NULL;
884 int fac = LOG_DAEMON;
885 struct dirwatcher *dp;
886 char *pidfile = NULL;
887 char *user = "nobody";
888
889 set_program_name(argv[0]);
890
891 while ((c = getopt(argc, argv, "adF:fhl:P:p:T:t:u:")) != EOF) {
892 switch (c) {
893 case 'a':
894 autowatch = -1;
895 break;
896 case 'd':
897 debug_level++;
898 break;
899 case 'f':
900 foreground++;
901 break;
902 case 'h':
903 exit(help());
904 break;
905 case 'l':
906 autowatch = atoi(optarg);
907 break;
908 case 'T':
909 handler_timeout = atoi(optarg);
910 break;
911 case 't':
912 tag = optarg;
913 break;
914 case 'F':
915 fac = read_facility(optarg);
916 break;
917 case 'P':
918 pidfile = optarg;
919 break;
920 case 'p':
921 set_handler(optarg);
922 break;
923 case 'u':
924 user = optarg;
925 if (!getpwnam(user)) {
926 diag(LOG_CRIT, "no such user: %s", user);
927 exit(1);
928 }
929 break;
930 default:
931 exit(1);
932 }
933 }
934
935 argc -= optind;
936 argv += optind;
937
938 if (argc == 0) {
939 diag(LOG_CRIT, "not enough arguments");
940 exit(1);
941 }
942
943 ifd = inotify_init();
944 if (ifd == -1) {
945 diag(LOG_CRIT, "inotify_init: %s", strerror(errno));
946 exit(1);
947 }
948
949 for (i = 0; i < argc; i++) {
950 if (!dirwatcher_create(ifd, argv[i])) {
951 diag(LOG_CRIT, "cannot create watcher; exiting");
952 exit(1);
953 }
954 }
955
956 /* Become a daemon */
957 if (!foreground) {
958 if (daemon(0, 0)) {
959 diag(LOG_CRIT, "daemon: %s", strerror(errno));
960 exit(1);
961 }
962 }
963
964 facility = fac;
965 if (facility >= 0)
966 openlog(tag, LOG_PID, facility);
967
968 diag(LOG_INFO, "started");
969
970 /* Write pidfile */
971 if (pidfile)
972 storepid(pidfile);
973
974 /* Relinquish superuser privileges */
975 if (getuid() == 0)
976 setuser(user);
977
978 signal_setup(sigmain);
979 signal(SIGCHLD, sigmain);
980
981 /* Main loop */
982 while (1) {
983 struct inotify_event *ep;
984 size_t size;
985 ssize_t rdbytes;
986
987 process_timeouts();
988 process_cleanup(0);
989
990 rdbytes = read(ifd, buffer, sizeof(buffer));
991 if (rdbytes == -1) {
992 if (errno == EINTR) {
993 if (signo == SIGCHLD || signo == SIGALRM)
994 continue;
995 diag(LOG_NOTICE, "got signal %d", signo);
996 break;
997 }
998
999 diag(LOG_NOTICE, "read failed: %s", strerror(errno));
1000 break;
1001 }
1002
1003 ep = (struct inotify_event *) buffer;
1004 while (rdbytes) {
1005 if (ep->wd >= 0) {
1006 int ev = -1;
1007 dp = dirwatcher_find_wd(ep->wd);
1008 if (ep->mask & IN_IGNORED)
1009 /* nothing */;
1010 else if (ep->mask & IN_Q_OVERFLOW)
1011 diag(LOG_NOTICE,
1012 "event queue overflow");
1013 else if (ep->mask & IN_UNMOUNT)
1014 /* FIXME: not sure if there's
1015 anything to do. Perhaps we should
1016 deregister the watched dirs that
1017 were located under the mountpoint
1018 */;
1019 else if (!dp) {
1020 if (ep->name)
1021 diag(LOG_NOTICE,
1022 "unrecognized event %x"
1023 "for %s", ep->mask,
1024 ep->name);
1025 else
1026 diag(LOG_NOTICE,
1027 "unrecognized event %x",
1028 ep->mask);
1029 } else if (ep->mask & IN_CREATE) {
1030 ev = evt_create;
1031 debug(1, ("%s/%s created",
1032 dp->name, ep->name));
1033 check_new_watcher(ifd,
1034 dp->name, ep->name);
1035 } else if (ep->mask & IN_DELETE) {
1036 ev = evt_delete;
1037 debug(1, ("%s/%s deleted",
1038 dp->name, ep->name));
1039 remove_watcher(ifd, dp->name,
1040 ep->name);
1041 } else if (ep->mask & IN_CLOSE_WRITE) {
1042 ev = evt_close;
1043 debug(1, ("%s/%s written",
1044 dp->name, ep->name));
1045 } else
1046 diag(LOG_NOTICE,
1047 "%s/%s: unexpected event %x",
1048 dp->name, ep->name, ep->mask);
1049
1050 if (ev >= 0 && ev < evt_max) {
1051 run_handler(ev, dp->name, ep->name);
1052 }
1053 }
1054 size = sizeof(*ep) + ep->len;
1055 ep = (struct inotify_event *) ((char*) ep + size);
1056 rdbytes -= size;
1057 }
1058 }
1059 diag(LOG_INFO, "stopped");
1060
1061 return 0;
1062}
diff --git a/dlist.c b/dlist.c
new file mode 100644
index 0000000..766f940
--- a/dev/null
+++ b/dlist.c
@@ -0,0 +1,35 @@
1struct LIST *
2LIST_UNLINK(struct LIST **root, struct LIST *p)
3{
4 if (p->prev)
5 p->prev->next = p->next;
6 else
7 *root = p->next;
8 if (p->next)
9 p->next->prev = p->prev;
10 p->next = p->prev = NULL;
11 return p;
12}
13
14struct LIST *
15LIST_POP(struct LIST **pp)
16{
17 if (*pp)
18 return LIST_UNLINK(pp, *pp);
19 return NULL;
20}
21
22void
23LIST_PUSH(struct LIST **pp, struct LIST *p)
24{
25 p->prev = NULL;
26 p->next = *pp;
27 if (*pp)
28 (*pp)->prev = p;
29 *pp = p;
30}
31
32#undef LIST
33#undef LIST_PUSH
34#undef LIST_POP
35#undef LIST_UNLINK

Return to:

Send suggestions and report system problems to the System administrator.