inotifyd.c revision 6958429d84cff06fefdc898d837a71c239f1e214
16958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma/* inotifyd.c - inotify daemon.
26958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma *
36958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma * Copyright 2013 Ashwini Kumar <ak.ashwini1981@gmail.com>
46958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma * Copyright 2013 Kyungwan Han <asura321@gmail.com>
56958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma *
66958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma * No Standard.
76958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma
86958429d84cff06fefdc898d837a71c239f1e214Ashwini SharmaUSE_INOTIFYD(NEWTOY(inotifyd, "<2", TOYFLAG_USR|TOYFLAG_BIN))
96958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma
106958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharmaconfig INOTIFYD
116958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma  bool "inotifyd"
126958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma  default n
136958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma  help
146958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma    usage: inotifyd PROG FILE[:mask] ...
156958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma
166958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma    Run PROG on filesystem changes.
176958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma    When a filesystem event matching MASK occurs on FILEn,
186958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma    PROG ACTUAL_EVENTS FILEn [SUBFILE] is run.
196958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma    If PROG is -, events are sent to stdout.
206958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma
216958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma    Events:
226958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma      a   File is accessed
236958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma      c   File is modified
246958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma      e   Metadata changed
256958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma      w   Writable file is closed
266958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma      0   Unwritable file is closed
276958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma      r   File is opened
286958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma      D   File is deleted
296958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma      M   File is moved
306958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma      u   Backing fs is unmounted
316958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma      o   Event queue overflowed
326958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma      x   File can't be watched anymore
336958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma    If watching a directory:
346958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma      m   Subfile is moved into dir
356958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma      y   Subfile is moved out of dir
366958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma      n   Subfile is created
376958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma      d   Subfile is deleted
386958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma
396958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma    inotifyd waits for PROG to exit.
406958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma    When x event happens for all FILEs, inotifyd exits.
416958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma*/
426958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma#define FOR_inotifyd
436958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma#include "toys.h"
446958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma#include <sys/inotify.h>
456958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma
466958429d84cff06fefdc898d837a71c239f1e214Ashwini SharmaGLOBALS(
476958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma  int gotsignal;
486958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma)
496958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma
506958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharmastatic void sig_handler(int sig)
516958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma{
526958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma  TT.gotsignal = sig;
536958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma}
546958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma
556958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharmastatic int exec_wait(char **args)
566958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma{
576958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma  int status = 0;
586958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma  pid_t pid = fork();
596958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma
606958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma  if (!pid) xexec(args);
616958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma  else if (pid > 0) waitpid(pid, &status, 0);
626958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma  else perror_exit("fork");
636958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma  return WEXITSTATUS(status);
646958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma}
656958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma
666958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharmavoid inotifyd_main(void)
676958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma{
686958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma  struct pollfd fds;
696958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma  char *prog_args[5], **files, **restore;
706958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma  struct mask_nameval {
716958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma    char name;
726958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma    unsigned long val;
736958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma  } mask_nv[] = {
746958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma    { 'a', IN_ACCESS },
756958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma    { 'c', IN_MODIFY },
766958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma    { 'e', IN_ATTRIB },
776958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma    { 'w', IN_CLOSE_WRITE },
786958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma    { '0', IN_CLOSE_NOWRITE },
796958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma    { 'r', IN_OPEN },
806958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma    { 'm', IN_MOVED_FROM },
816958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma    { 'y', IN_MOVED_TO },
826958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma    { 'n', IN_CREATE },
836958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma    { 'd', IN_DELETE },
846958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma    { 'D', IN_DELETE_SELF },
856958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma    { 'M', IN_MOVE_SELF },
866958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma    { 'u', IN_UNMOUNT },
876958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma    { 'o', IN_Q_OVERFLOW },
886958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma    { 'x', IN_IGNORED }
896958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma  };
906958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma  int mask, masks_len = ARRAY_LEN(mask_nv);
916958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma
926958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma  prog_args[0] = toys.optargs[0];
936958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma  prog_args[4] = NULL;
946958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma  toys.optc--; // 1st one is program, rest are files to be watched
956958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma  //wd ZERO is not used, hence toys.optargs is assigned to files.
966958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma  restore = files = toys.optargs;
976958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma  if ((fds.fd = inotify_init()) == -1) perror_exit("initialization failed");
986958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma
996958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma  while (*++toys.optargs) {
1006958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma    char *path = *toys.optargs;
1016958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma    char *masks = strchr(path, ':');
1026958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma
1036958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma    mask = 0x0fff; //assuming all non-kernel events to be notified.
1046958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma    if (masks) {
1056958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma      *masks++ = '\0';
1066958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma      mask = 0;
1076958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma      while (*masks) {
1086958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma        int i = 0;
1096958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma
1106958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma        for (i = 0; i < masks_len; i++) {
1116958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma          if (*masks == mask_nv[i].name) {
1126958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma            mask |= mask_nv[i].val;
1136958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma            break;
1146958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma          }
1156958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma        }
1166958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma        if (i == masks_len) error_exit("wrong mask '%c'", *masks);
1176958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma        masks++;
1186958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma      }
1196958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma    }
1206958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma    if (inotify_add_watch(fds.fd, path, mask) < 0)
1216958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma      perror_exit("add watch '%s' failed", path);
1226958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma  }
1236958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma  toys.optargs = restore;
1246958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma  sigatexit(sig_handler);
1256958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma  fds.events = POLLIN;
1266958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma
1276958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma  while (1) {
1286958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma    int ret = 0, queue_len;
1296958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma    void *buf = NULL;
1306958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma    struct inotify_event *event;
1316958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma
1326958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharmaretry:
1336958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma    if (TT.gotsignal) break;
1346958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma    ret = poll(&fds, 1, -1);
1356958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma    if (ret < 0 && errno == EINTR) goto retry;
1366958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma    if (ret <= 0) break;
1376958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma    xioctl(fds.fd, FIONREAD, &queue_len);
1386958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma    event = buf = xmalloc(queue_len);
1396958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma    queue_len = readall(fds.fd, buf, queue_len);
1406958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma    while (queue_len > 0) {
1416958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma      uint32_t m = event->mask;
1426958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma
1436958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma      if (m) {
1446958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma        char evts[masks_len+1], *s = evts;
1456958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma        int i;
1466958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma
1476958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma        for (i = 0; i < masks_len; i++)
1486958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma          if (m & mask_nv[i].val) *s++ = mask_nv[i].name;
1496958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma        *s = '\0';
1506958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma
1516958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma        if (prog_args[0][0] == '-' && !prog_args[0][1]) { //stdout
1526958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma          printf(event->len ? "%s\t%s\t%s\n" : "%s\t%s\n", evts,
1536958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma              files[event->wd], event->name);
1546958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma          fflush(stdout);
1556958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma        } else {
1566958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma          prog_args[1] = evts;
1576958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma          prog_args[2] = files[event->wd];
1586958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma          prog_args[3] = event->len ? event->name : NULL;
1596958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma          exec_wait(prog_args); //exec and wait...
1606958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma        }
1616958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma        if (event->mask & IN_IGNORED) {
1626958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma          if (--toys.optc <= 0) {
1636958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma            free(buf);
1646958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma            goto done;
1656958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma          }
1666958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma          inotify_rm_watch(fds.fd, event->wd);
1676958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma        }
1686958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma      }
1696958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma      queue_len -= sizeof(struct inotify_event) + event->len;
1706958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma      event = (void*)((char*)event + sizeof(struct inotify_event) + event->len); //next event
1716958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma    }
1726958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma    free(buf);
1736958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma  }
1746958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharmadone:
1756958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma  toys.exitval = TT.gotsignal;
1766958429d84cff06fefdc898d837a71c239f1e214Ashwini Sharma}
177