wendy

run arbitrary commands on inotify events
git clone git://iotek.org/wendy
Log | Files | Refs | README | LICENSE

commit d9b41bb26d563a50964b320cbe9f2634e7c0c784
parent 44ee3d650cfbd4e583dfac2f978ceb82d031f6c2
Author: z3bra <willy@mailoo.org>
Date:   Wed Feb 12 13:37:01 2014

Merge branch 'master' of z3bra.org:wendy

Diffstat:
 Makefile |  4 +---
 README   | 38 ++++++++++++++++++++++++++++++---
 wendy.c  | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------
 3 files changed, 100 insertions(+), 18 deletions(-)

diff --git a/Makefile b/Makefile @@ -14,9 +14,7 @@ wendy : wendy.o all : wendy clean : - ${RM} wendy - ${RM} -f *.o - ${RM} -f *~ + ${RM} -f wendy *.o *~ path: @echo PREFIX: ${PREFIX} diff --git a/README b/README @@ -12,13 +12,45 @@ specific command when an event occurs. The program is made the moire simple possible, to leave the room to unlimited uses. Be creative ! +Every event raised by inotify is handled. Just sum them up to watch multiple +event at the same time. Here is the full table: +(see inotify(1) for a better explanation of those events) + + IN_ACCESS ........ 1 + IN_MODIFY ........ 2 + IN_ATTRIB ........ 4 + IN_CLOSE_WRITE ... 8 + IN_CLOSE_NOWRITE . 16 + IN_OPEN .......... 32 + IN_MOVED_FROM .... 64 + IN_MOVED_TO ...... 128 + IN_CREATE ........ 256 + IN_DELETE ........ 512 + IN_DELETE_SELF ... 1024 + IN_MOVE_SELF ..... 2048 + +To watch for both creation AND deletion in a directory, do some math: + + 256 + 512 = 768 + +then, pass that value to wendy so that she can watch after both of them (did I +just say 'she'?). + +For more convenience, the IN_CREATE, IN_DELETE and IN_MODIFY events are bound to +(respectively) -C, -D and -M. + Here are some examples: # Tell me whenever I have a new mail - wendy -C ~/mails/INBOX/new -t 60 -e espeak "You got a new mail" + wendy -C -d ~/mails/INBOX/new -t 60 -e espeak "You got a new mail" # On-the-fly recompilation - wendy -M -f ~/src/dev/program/source.c -t 1 -e make + wendy -M -q -d ~/src/dev/program/ -f source.c -t 1 -e make + # or eventually + wendy -l | grep -i close_write + IN_CLOSE_WRITE ... 8 + wendy -m 8 -q -d ~/src/dev/program/ -f source.c -t 1 -e make + # Get up to date with community based projects - wendy -DMC -f /mnt/nfs/project/ -t 30 -e notify-send 'project updated' + wendy -D -M -C -d /mnt/nfs/project/ -t 30 -e notify-send 'project updated' diff --git a/wendy.c b/wendy.c @@ -34,18 +34,53 @@ extern char **environ; void usage() { - fputs("usage: wendy [-C] [-D] [-M] [-f file] [-t timeout] " - "-e command [arguments]\n" - "\t-C : raise creation events\n" + fputs("usage: wendy [-C] [-D] [-M] [-m mask] [-l] [-f file] [-t timeout] [-q] " + "[-e command [args] ..]\n" + "\t-C : raise creation events (default)\n" "\t-D : raise deletion events\n" "\t-M : raise modification events\n" + "\t-m mask : set mask manually (see -l))\n" + "\t-l : list events mask values\n" "\t-f file : file to watch (everything is a file)\n" "\t-t timeout : time between event check (in seconds)\n" + "\t-q : don't talk to me, program\n" "\t-e command : command to launch (must be the last argument!)\n", stdout); exit(1); } +void +list_events() +{ + fprintf(stdout, + "IN_ACCESS ........ %u\n" + "IN_MODIFY ........ %u\n" + "IN_ATTRIB ........ %u\n" + "IN_CLOSE_WRITE ... %u\n" + "IN_CLOSE_NOWRITE . %u\n" + "IN_OPEN .......... %u\n" + "IN_MOVED_FROM .... %u\n" + "IN_MOVED_TO ...... %u\n" + "IN_CREATE ........ %u\n" + "IN_DELETE ........ %u\n" + "IN_DELETE_SELF ... %u\n" + "IN_MOVE_SELF ..... %u\n", + IN_ACCESS, + IN_MODIFY, + IN_ATTRIB, + IN_CLOSE_WRITE, + IN_CLOSE_NOWRITE, + IN_OPEN, + IN_MOVED_FROM, + IN_MOVED_TO, + IN_CREATE, + IN_DELETE, + IN_DELETE_SELF, + IN_MOVE_SELF + ); + exit(0); +} + int execvpe(const char *program, char **argv, char **envp) { @@ -60,7 +95,8 @@ execvpe(const char *program, char **argv, char **envp) int main (int argc, char **argv) { - int fd, wd, len, mask = 0, i = 0, timeout = 0, ignore = 0; + int fd, wd, len, i = 0, timeout = 0, ignore = 0, quiet = 0; + uint32_t mask = 0; char buf[BUF_LEN]; char *file = NULL, **cmd = NULL; struct inotify_event *ev; @@ -68,22 +104,24 @@ main (int argc, char **argv) if ((argc == 2 && argv[1][0] == '-' && argv[1][1] == 'h')) usage(); /* parse option given. see usage() above */ - for(i = 1; (i + 1 < argc) && (argv[i][0] == '-') && !ignore; i++) { + for(i = 1; (i < argc) && (argv[i][0] == '-') && !ignore; i++) { switch (argv[i][1]) { case 'C': mask |= IN_CREATE; break; case 'D': mask |= IN_DELETE; break; case 'M': mask |= IN_MODIFY; break; + case 'm': mask |= atoi(argv[++i]); break; + case 'l': list_events(); break; + case 'q': quiet = 1; break; case 'f': file = argv[++i]; break; case 't': timeout = atoi(argv[++i]); break; case 'e': cmd = &argv[++i]; ignore=1; break; + default: usage(); } } /* test given arguments */ if (!file) { file = DEFAULT_FILE; } if (!timeout) { timeout = DEFAULT_CHECK; } - if (!cmd) { usage(); } - if (!mask) { mask |= IN_CREATE; } /* get file descriptor */ fd = inotify_init(); @@ -96,8 +134,12 @@ main (int argc, char **argv) if (wd < 0) perror("inotify_add_watch"); + if (!quiet) { + printf( "watching file %s with event mask %u\n", file, mask); + } + /* start looping */ - while (1) { + for (;;) { /* get every event raised, and queue them */ len = read(fd, buf, BUF_LEN); @@ -114,13 +156,23 @@ main (int argc, char **argv) /* get events one by one */ ev = (struct inotify_event *) &buf[i]; - if (ev->len > 0) { + if (!quiet && ev->len > 0) { printf("event on file %s: %u\n", ev->name, ev->mask); } - /* OMG a new event ! Quick, raise an alert ! */ - if (!fork()) { - execvpe(cmd[0], cmd, environ); + /* + * do not do anything if no command given. + * Also only execute the command if the file concerned by the event + * is the one we're watching, or if we're not looking for a specific + * file. + * + * If you don't undersand this sentence, don't worry. Me neither. + * Just trust the if(). + */ + if (cmd && !(file && strncmp(file, ev->name, 255))) { + + /* OMG a new event ! Quick, raise an alert ! */ + if (!fork()) { execvpe(cmd[0], cmd, environ); } } /* jump to the next one */