summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--inotail.c88
1 files changed, 81 insertions, 7 deletions
diff --git a/inotail.c b/inotail.c
index 4195e13..8c161e9 100644
--- a/inotail.c
+++ b/inotail.c
@@ -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)