/* diraction.c diraction is done by Anthony de Boer adopted from Documentation/dnotify.txt in the Linux kernel distribution, which was writen by Stephen Rothwell Copyright: GPL, since this is a derived work of the kernel source tree, which is GPL'ed. General theme: SCRIPTDIR has executables triggered for each file change in WATCHDIR. We're only interested in files that have scripts. SCRIPTDIR is argv[1] and WATCHDIR is cwd. # Copyright (C) 2004 Anthony de Boer # # This program is free software; you can redistribute it and/or modify # it under the terms of version 2 of the GNU General Public License as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define _GNU_SOURCE /* needed to get the defines */ #include /* in glibc 2.2 this has the needed values defined */ #include #include #include #include #include #include #include #include /* kludge so we can compile on older Linux */ #ifdef F_NOTIFY #if F_NOTIFY != 1026 #warning F_NOTIFY value has changed #endif #if DN_MODIFY != 0x00000002 #warning DN_MODIFY value has changed #endif #if DN_CREATE != 0x00000004 #warning DN_CREATE value has changed #endif #if DN_RENAME != 0x00000010 #warning DN_RENAME value has changed #endif #if DN_MULTISHOT != 0x80000000 #warning DN_MULTISHOT value has changed #endif #else #define F_NOTIFY 1026 #define DN_ACCESS 0x00000001 /* File accessed */ #define DN_MODIFY 0x00000002 /* File modified */ #define DN_CREATE 0x00000004 /* File created */ #define DN_DELETE 0x00000008 /* File removed */ #define DN_RENAME 0x00000010 /* File renamed */ #define DN_ATTRIB 0x00000020 /* File changed attibutes */ #define DN_MULTISHOT 0x80000000 /* Don't remove notifier */ #endif #define barf(x) { botch = x; goto punt; } #define CFREQ 60 #define EFREQ 10 static volatile int sigged; /* void (*sa_handler)(int) handler; */ struct filent { char *name; struct filent *next; ino_t inode; pid_t runscript; time_t lastmod; time_t lastrun; int need; }; void handler(int it) { sigged = it ? it : 1; } int main(int argc, char *argv[]) { struct sigaction act; int fd; struct filent *head = NULL; struct filent *pf; char *botch; char *curobj; DIR *dirhandle; struct dirent *thisdir; int maxname = 0; act.sa_handler = handler; sigemptyset(&act.sa_mask); act.sa_flags = 0; if (sigaction(SIGRTMIN, &act, NULL) == -1) barf("Cannot install SIGRTMIN handler\n"); if (sigaction(SIGALRM, &act, NULL) == -1) barf("Cannot install SIGALRM handler\n"); act.sa_flags = SA_NOCLDSTOP; if (sigaction(SIGCHLD, &act, NULL) == -1) barf("Cannot install SIGCHLD handler\n"); if (argc != 2) barf("Usage: diraction /SCRIPTDIR\n"); curobj = argv[1]; if ((dirhandle = opendir(curobj)) == NULL) goto syserr; while (thisdir = readdir(dirhandle)) { int mylen; curobj = thisdir->d_name; if (*curobj == '.') continue; pf = (struct filent *) malloc(sizeof(struct filent)); curobj = strdup(curobj); if (pf == NULL || curobj == NULL) barf("malloc\n"); pf->name = curobj; pf->next = head; pf->inode = 0; pf->runscript = 0; pf->lastrun = pf->lastmod = 0; pf->need = 0; head = pf; mylen = strlen(curobj); if (mylen > maxname) maxname = mylen; } closedir(dirhandle); curobj = "."; if ((fd = open(".", O_RDONLY)) == -1) goto syserr; if (fcntl(fd, F_SETSIG, SIGRTMIN) == -1) goto syserr; fprintf(stderr, "dnotify %s\n", fcntl(fd, F_NOTIFY, DN_MODIFY|DN_CREATE|DN_RENAME|DN_MULTISHOT) == -1 ? "unavailable" : "okay"); /* from this point curobj is a buffer for scriptdir paths */ curobj = (char *) malloc(strlen(argv[1] + maxname + 3)); if (curobj == NULL) barf("malloc\n"); strcpy(curobj, argv[1]); strcat(curobj, "/"); sigged = 0; while (1) { pid_t pid; int status; time_t now = time(NULL); int atime = CFREQ; /* fprintf(stderr, "kick on signal %d\n", sigged); */ sigged = 0; while (pid = waitpid(-1, &status, WNOHANG)) { if (pid <= 0) break; pf = head; while(pf) { if (pf->runscript == pid) { pf->runscript = 0; fprintf(stderr, "script done for %s, %s\n", pf->name, WIFSIGNALED(status) ? "killed" : (WIFEXITED(status) && WEXITSTATUS(status) == 0) ? "okay" : "failed"); break; } pf = pf->next; } } pf = head; while(pf) { struct stat stbuf; if (stat(pf->name, &stbuf) == -1) { pf->need = 0; } else { if (stbuf.st_ino != pf->inode) { pf->inode = stbuf.st_ino; pf->need = 1; } if (stbuf.st_mtime != pf->lastmod) { pf->lastmod = stbuf.st_mtime; pf->need = 1; } } if (pf->need) { if (pf->runscript == 0 && pf->lastrun < now-EFREQ) { fprintf(stderr, "event on %s, running script\n", pf->name); pf->need = 0; pf->lastrun = now; switch(pid = fork()) { case 0: strcat(curobj, pf->name); alarm(0); execl(curobj, curobj, pf->name, NULL); goto syserr; case -1: barf("fork()\n"); default: pf->runscript = pid; } } else { fprintf(stderr, "deferred event on %s\n", pf->name); atime = EFREQ; } } pf = pf->next; } alarm(atime); if (sigged == 0) pause(); } syserr: perror(curobj); exit(1); punt: write(2, botch, strlen(botch)); exit(1); }