diff options
-rw-r--r-- | inotail.c | 88 |
1 files changed, 81 insertions, 7 deletions
@@ -30,19 +30,31 @@ #include <fcntl.h> #include <errno.h> #include <getopt.h> +#include <libgen.h> #include <sys/types.h> #include <sys/stat.h> #include "inotify.h" #include "inotify-syscalls.h" + #include "inotail.h" #define PROGRAM_NAME "inotail" +/* inotify event buffer length for one file */ +#define INOTIFY_BUFLEN (4 * sizeof(struct inotify_event)) /* Print header with filename before tailing the file? */ static char verbose = 0; + /* Tailing relative to begin or end of file */ static char from_begin = 0; + +/* Follow the file by name or by fd */ +static enum follow_mode follow = FOLLOW_DESCRIPTOR; + +/* Retry accessing the file if it got lost (e.g. deleted, moved) */ +static char retry = 0; + /* Number of ignored files */ static int n_ignored = 0; @@ -73,7 +85,9 @@ static void usage(const int status) { fprintf(stdout, "Usage: %s [OPTION]... [FILE]...\n\n" " -c N, --bytes=N output the last N bytes\n" - " -f, --follow output as the file grows\n" + " -f, --follow[={descriptor|name}]\n" + " output as the file grows (default: descriptor)\n" + " -F same as --follow=name\n" " -n N, --lines=N output the last N lines (default: %d)\n" " -v, --verbose print headers with file names\n" " -h, --help show this help and exit\n" @@ -87,7 +101,7 @@ static void usage(const int status) static inline void setup_file(struct file_struct *f) { - f->fd = f->i_watch = -1; + f->fd = f->i_watch = f->i_d_watch = -1; f->size = 0; f->blksize = BUFSIZ; f->ignore = 0; @@ -95,6 +109,7 @@ static inline void setup_file(struct file_struct *f) static void ignore_file(struct file_struct *f) { + dprintf("File '%s' ignored\n", f->name); if (f->fd != -1) { close(f->fd); f->fd = -1; @@ -567,10 +582,23 @@ static int tail_file(struct file_struct *f, unsigned long n_units, mode_t mode, return 0; } +static int follow_file_by_name(struct file_struct *f) +{ + inotify_rm_watch(f->fd, INOTAIL_EVENT_MASK); + close(f->fd); + + /* TODO: Set up a watch for the directory the file was in and + * wait for the file to be created again. + */ + return 0; +} + static int handle_inotify_event(struct inotify_event *inev, struct file_struct *f) { int ret = 0; + ddump(inev); + if (inev->mask & IN_MODIFY) { char *fbuf; ssize_t bytes_read; @@ -607,11 +635,22 @@ static int handle_inotify_event(struct inotify_event *inev, struct file_struct * return ret; } else if (inev->mask & IN_DELETE_SELF) { fprintf(stderr, "File '%s' deleted.\n", f->name); + + if (retry && follow == FOLLOW_NAME) { + follow_file_by_name(f); + } } else if (inev->mask & IN_MOVE_SELF) { fprintf(stderr, "File '%s' moved.\n", f->name); + + if (follow == FOLLOW_NAME) { + follow_file_by_name(f); + } + return 0; } else if (inev->mask & IN_UNMOUNT) { fprintf(stderr, "Device containing file '%s' unmounted.\n", f->name); + } else if (f->dir && (inev->mask & (IN_CREATE | IN_MOVED_TO))) { + /* File was moved/deleted and became available again */ } else if (inev->mask & IN_IGNORED) { return 0; } @@ -637,14 +676,24 @@ static int watch_files(struct file_struct *files, int n_files) for (i = 0; i < n_files; i++) { if (!files[i].ignore) { - files[i].i_watch = inotify_add_watch(ifd, files[i].name, - IN_MODIFY|IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT); + files[i].i_watch = inotify_add_watch(ifd, files[i].name, INOTAIL_EVENT_MASK); if (files[i].i_watch < 0) { fprintf(stderr, "Error: Could not create inotify watch on file '%s' (%s)\n", files[i].name, strerror(errno)); ignore_file(&files[i]); } + + if (files[i].dir) { + files[i].i_d_watch = inotify_add_watch(ifd, files[i].dir, + IN_CREATE | IN_MOVED_TO | IN_DELETE | IN_ONLYDIR); + + if (files[i].i_d_watch < 0) { + fprintf(stderr, "Error: Could not create inotify watch on directory '%s' (%s)\n", + files[i].dir, strerror(errno)); + files[i].dir = NULL; + } + } } } @@ -698,12 +747,12 @@ int main(int argc, char **argv) int i, c, ret = 0; int n_files; unsigned long n_units = DEFAULT_N_LINES; - mode_t mode = M_LINES; + enum tail_mode mode = M_LINES; char forever = 0; char **filenames; struct file_struct *files = NULL; - while ((c = getopt_long(argc, argv, "c:n:fvVh", long_opts, NULL)) != -1) { + while ((c = getopt_long(argc, argv, "c:n:fFvVh", long_opts, NULL)) != -1) { switch (c) { case 'c': mode = M_BYTES; @@ -721,8 +770,23 @@ int main(int argc, char **argv) } n_units = strtoul(optarg, NULL, 0); break; - case 'f': + case 'f': forever = 1; + if (!optarg || strncmp(optarg, "descriptor", 11) == 0) + follow = FOLLOW_DESCRIPTOR; + else if (strncmp(optarg, "name", 5) == 0) + follow = FOLLOW_NAME; + else { + fprintf(stderr, + "Error: Invalid follow mode: %s\nSee %s --help for valid options\n", + optarg, PROGRAM_NAME); + exit(EXIT_FAILURE); + } + break; + case 'F': + forever = 1; + follow = FOLLOW_NAME; + retry = 1; break; case 'v': verbose = 1; @@ -767,6 +831,16 @@ int main(int argc, char **argv) for (i = 0; i < n_files; i++) { files[i].name = filenames[i]; + + dprintf("basename: %s\n", basename(files[i].name)); + dprintf("dirname: %s\n", dirname(files[i].name)); + + if (follow == FOLLOW_NAME) { + /* TODO: Set dir member of file_struct */ + } else { + files[i].dir = NULL; + } + setup_file(&files[i]); ret = tail_file(&files[i], n_units, mode, forever); if (ret < 0) |