| /* |
| * Copyright 2012-2014 Google Inc. All rights reserved. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /* |
| * A simple program that uses the inotify api to watch a directory given as |
| * argument. If any of the files in that directory suffers any change, |
| * this program will output the name of the file, without including the |
| * whole path. |
| */ |
| |
| #include <errno.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <sys/inotify.h> |
| #include <sys/stat.h> |
| #include <unistd.h> |
| |
| static void die(const char *tag) { |
| perror(tag); |
| exit(1); |
| } |
| |
| static void close_and_die(int fd, const char *tag) { |
| close(fd); |
| die(tag); |
| } |
| |
| |
| int main(int argc, const char **argv) { |
| int inotify_fd, len, i, nr_dirs = argc-1; |
| int *dir_wds; |
| char buf[4096], *ptr; |
| struct inotify_event *event; |
| |
| if (argc < 2) { |
| fprintf(stderr, |
| "usage: %s <dirname>...\n" |
| " Outputs the name of the files in the given " |
| " directories that have been modified.\n", |
| argv[0]); |
| exit(2); |
| } |
| |
| for (i = 0; i < nr_dirs; ++i) { |
| const char *dir_name = argv[1+i]; |
| |
| // Sanity checks. |
| struct stat sb; |
| if (stat(dir_name, &sb) == 0) { |
| if (!S_ISDIR(sb.st_mode)) { |
| fprintf(stderr, "%s is not a directory\n", dir_name); |
| exit(1); |
| } |
| } else { |
| if (mkdir(dir_name, S_IRWXU | S_IRWXG | S_IRWXO)) |
| die("mkdir"); |
| } |
| } |
| |
| inotify_fd = inotify_init(); |
| if (inotify_fd < 0) |
| die("inotify_init"); |
| |
| dir_wds = calloc(nr_dirs, sizeof(*dir_wds)); |
| if (!dir_wds) |
| die("calloc"); |
| |
| for (i = 0; i < nr_dirs; i++) { |
| dir_wds[i] = inotify_add_watch(inotify_fd, argv[1+i], |
| IN_MOVE | IN_CREATE | IN_DELETE | IN_MODIFY); |
| if (dir_wds[i] < 0) |
| die("inotify_add_watch"); |
| } |
| |
| while (1) { |
| len = read(inotify_fd, buf, sizeof(buf)); |
| if (len == 0) { |
| fprintf(stderr, "inotify read EOF"); |
| break; |
| } |
| if (len < 0) { |
| if (errno == EINTR || errno == EAGAIN) |
| continue; |
| close_and_die(inotify_fd, "inotify read"); |
| } |
| |
| for (ptr = buf; ptr < buf + len; ptr += event->len + sizeof(*event)) { |
| |
| event = (struct inotify_event *)ptr; |
| // Check to see if the the event struct is not incomplete. |
| if (ptr + sizeof(*event) > buf + len) { |
| fprintf(stderr, "inotify: incomplete inotify event\n"); |
| break; |
| } |
| if (event->mask & (IN_IGNORED | IN_UNMOUNT)) { |
| die("bailing out, non-existing directory"); |
| } else if (event->mask & IN_Q_OVERFLOW) { |
| fprintf(stderr, "inotify: event queue overflowed\n"); |
| break; |
| } else if (event->mask & IN_ISDIR) { |
| fprintf(stderr, "inotify: directory triggered event, will ignore\n"); |
| continue; |
| } |
| |
| if (event->len && ptr + sizeof(*event) + event->len <= buf + len) { |
| // Pathname is null terminated. |
| fprintf(stdout, "%s\n", event->name); |
| fflush(stdout); |
| } |
| } |
| } |
| |
| free(dir_wds); |
| close(inotify_fd); |
| return 0; |
| } |