int main()

in scripts/inotify/inotify.c [96:190]


int main(int argc, char* argv[]) {
  if (argc < 4) {
    printf("Usage: %s path file callback [callback-arg]...\n", argv[0]);
    printf("This utility watches inotify events at 'path', "
           "optionally filtered by file name 'file',\n"
           "and calls 'callback' with 'callback-arg' "
           "upon inotify events firing. Additionally,\n"
           "'callback' is also called at start up or "
           "every %d milliseconds if no inotify event fires.\n"
           "This program exits as success when 'callback' "
           "exits as success, or keeps running otherwise.\n",
           kPollTimeout);
    exit(EXIT_FAILURE);
  }

  char** callback = argv + 3;
  struct pollfd fds;

  fds.fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
  if (fds.fd == -1) {
    perror("inotify_init1");
    exit(EXIT_FAILURE);
  }

  fds.events = POLLIN;

  // flags taken from Golang fsnotify.
  uint32_t flags = IN_MOVED_TO | IN_MOVED_FROM | IN_CREATE | IN_ATTRIB
                   | IN_MODIFY | IN_MOVE_SELF | IN_DELETE | IN_DELETE_SELF;
  int wd = inotify_add_watch(fds.fd, argv[1], flags);
  if (wd == -1) {
    perror("inotify_add_watch");
    exit(EXIT_FAILURE);
  }

  struct timespec tp;
  printf("inotify: calling %s as initial run\n", callback[0]);
  run_callback(callback, &tp, true);

  for (;;) {
    // inotify(7) states several limitations and polling the filesystem (or
    // calling the callback in our case) in addition to inotify is required.
    // We utilize the 'timeout' parameter to poll(2) to achieve it by running
    // the callback at each time out event.
    int ret = poll(&fds, 1, kPollTimeout);
    if (ret == -1) {
      if (errno == EINTR) {
        run_callback(callback, &tp, false);
        continue;
      }
      perror("poll");
      exit(EXIT_FAILURE);
    }

    if (ret == 0) {
      run_callback(callback, &tp, false);
      continue;
    }

    int matches = 0;
    for (;;) {
      ssize_t len = read(fds.fd, buf, sizeof(buf));
      if (len == -1) {
        if (errno == EINTR) {
          continue;
        }
        if (errno == EAGAIN) {
          break;
        }
        perror("read");
        exit(EXIT_FAILURE);
      }

      const struct inotify_event *event;
      for (char *ptr = buf; ptr < buf + len;
           ptr += sizeof(struct inotify_event) + event->len) {
        event = (const struct inotify_event *)ptr;

        if (argv[2][0] == '\0' ||
            (event->len > 0 && strcmp(event->name, argv[2]) == 0)) {
          matches++;
        }
      }
    }

    if (matches == 0) {
      run_callback(callback, &tp, false);
      continue;
    }

    printf("inotify: calling %s for %d matching event(s)\n",
           callback[0], matches);
    run_callback(callback, &tp, true);
  }
}