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);
}
}