view toys/pending/inotifyd.c @ 1306:2d4675617dff draft

inotifyd - watch for filesystem events.
author Ashwini Sharma <ak.ashwini1981@gmail.com>
date Fri, 23 May 2014 08:58:08 -0500
parents
children 85f297591693
line wrap: on
line source

/* inotifyd.c - inotify daemon. 
 *
 * Copyright 2013 Ashwini Kumar <ak.ashwini1981@gmail.com>
 * Copyright 2013 Kyungwan Han <asura321@gmail.com>
 *
 * No Standard.

USE_INOTIFYD(NEWTOY(inotifyd, "<2", TOYFLAG_USR|TOYFLAG_BIN))

config INOTIFYD
  bool "inotifyd"
  default n
  help
    usage: inotifyd PROG FILE[:mask] ...

    Run PROG on filesystem changes.
    When a filesystem event matching MASK occurs on FILEn,
    PROG ACTUAL_EVENTS FILEn [SUBFILE] is run.
    If PROG is -, events are sent to stdout.

    Events:
      a   File is accessed
      c   File is modified
      e   Metadata changed
      w   Writable file is closed
      0   Unwritable file is closed
      r   File is opened
      D   File is deleted
      M   File is moved
      u   Backing fs is unmounted
      o   Event queue overflowed
      x   File can't be watched anymore
    If watching a directory:
      m   Subfile is moved into dir
      y   Subfile is moved out of dir
      n   Subfile is created
      d   Subfile is deleted

    inotifyd waits for PROG to exit.
    When x event happens for all FILEs, inotifyd exits.
*/
#define FOR_inotifyd
#include "toys.h"
#include <sys/inotify.h>

GLOBALS(
  int gotsignal;
)

static void sig_handler(int sig)
{
  TT.gotsignal = sig;
}

static int exec_wait(char **args)
{
  int status = 0;
  pid_t pid = fork();

  if (!pid) xexec(args);
  else if (pid > 0) waitpid(pid, &status, 0);
  else perror_exit("fork");
  return WEXITSTATUS(status);
}

void inotifyd_main(void)
{
  struct pollfd fds;
  char *prog_args[5], **files, **restore;
  struct mask_nameval {
    char name;
    unsigned long val;
  } mask_nv[] = {
    { 'a', IN_ACCESS },
    { 'c', IN_MODIFY },
    { 'e', IN_ATTRIB },
    { 'w', IN_CLOSE_WRITE },
    { '0', IN_CLOSE_NOWRITE },
    { 'r', IN_OPEN },
    { 'm', IN_MOVED_FROM },
    { 'y', IN_MOVED_TO },
    { 'n', IN_CREATE },
    { 'd', IN_DELETE },
    { 'D', IN_DELETE_SELF },
    { 'M', IN_MOVE_SELF },
    { 'u', IN_UNMOUNT },
    { 'o', IN_Q_OVERFLOW },
    { 'x', IN_IGNORED }
  };
  int mask, masks_len = ARRAY_LEN(mask_nv);

  prog_args[0] = toys.optargs[0];
  prog_args[4] = NULL;
  toys.optc--; // 1st one is program, rest are files to be watched
  //wd ZERO is not used, hence toys.optargs is assigned to files.
  restore = files = toys.optargs;
  if ((fds.fd = inotify_init()) == -1) perror_exit("initialization failed");

  while (*++toys.optargs) {
    char *path = *toys.optargs;
    char *masks = strchr(path, ':');

    mask = 0x0fff; //assuming all non-kernel events to be notified.
    if (masks) {
      *masks++ = '\0';
      mask = 0; 
      while (*masks) {
        int i = 0;

        for (i = 0; i < masks_len; i++) {
          if (*masks == mask_nv[i].name) {
            mask |= mask_nv[i].val;
            break;
          }
        }
        if (i == masks_len) error_exit("wrong mask '%c'", *masks);
        masks++;
      }
    }
    if (inotify_add_watch(fds.fd, path, mask) < 0) 
      perror_exit("add watch '%s' failed", path);
  }
  toys.optargs = restore;
  sigatexit(sig_handler);
  fds.events = POLLIN;

  while (1) {
    int ret = 0, queue_len;
    void *buf = NULL;
    struct inotify_event *event;

retry:
    if (TT.gotsignal) break;
    ret = poll(&fds, 1, -1);
    if (ret < 0 && errno == EINTR) goto retry;
    if (ret <= 0) break;
    xioctl(fds.fd, FIONREAD, &queue_len);
    event = buf = xmalloc(queue_len);
    queue_len = readall(fds.fd, buf, queue_len);
    while (queue_len > 0) {
      uint32_t m = event->mask;

      if (m) {
        char evts[masks_len+1], *s = evts;
        int i;

        for (i = 0; i < masks_len; i++) 
          if (m & mask_nv[i].val) *s++ = mask_nv[i].name;
        *s = '\0';

        if (prog_args[0][0] == '-' && !prog_args[0][1]) { //stdout
          printf(event->len ? "%s\t%s\t%s\n" : "%s\t%s\n", evts,
              files[event->wd], event->name);
          fflush(stdout);
        } else {
          prog_args[1] = evts;
          prog_args[2] = files[event->wd];
          prog_args[3] = event->len ? event->name : NULL;
          exec_wait(prog_args); //exec and wait...
        }
        if (event->mask & IN_IGNORED) {
          if (--toys.optc <= 0) {
            free(buf);
            goto done;
          }
          inotify_rm_watch(fds.fd, event->wd);
        }
      }
      queue_len -= sizeof(struct inotify_event) + event->len;
      event = (void*)((char*)event + sizeof(struct inotify_event) + event->len); //next event
    }
    free(buf);
  }
done:
  toys.exitval = TT.gotsignal;
}