diff options
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | changelog | 9 | ||||
-rw-r--r-- | inotail.c | 144 | ||||
-rw-r--r-- | inotail.h | 12 |
4 files changed, 153 insertions, 14 deletions
@@ -4,7 +4,7 @@ # # Licensed under the terms of the GNU General Public License; version 2 or later. -VERSION = 0.3 +VERSION = 0.4 # Paths prefix = /usr/local @@ -1,3 +1,12 @@ +inotail 0.4 + + * Implemented tailing of stdin + * Fixed handling of EINTR signal (^Z/fg) (patch by Anthony Martinez) + * Improved error handling (patch by Folkert van Heusden) + * Added uninstall target to Makefile (patch by Folkert van Heusden) + + -- Tobias Klauser <tklauser@distanz.ch> 2007-06-04 14:44 + inotail 0.3 * Follow files even if they were moved @@ -39,7 +39,6 @@ #include "inotail.h" #define PROGRAM_NAME "inotail" -#define BUFFER_SIZE 4096 /* inotify event buffer length for one file */ #define INOTIFY_BUFLEN (4 * sizeof(struct inotify_event)) @@ -63,6 +62,18 @@ static const struct option long_opts[] = { { NULL, 0, NULL, 0 } }; +static void *emalloc(size_t size) +{ + void *ret = malloc(size); + + if (unlikely(!ret)) { + fprintf(stderr, "Error: Failed to allocate %d bytes of memory (%s)\n", size, strerror(errno)); + exit(EXIT_FAILURE); + } + + return ret; +} + static void usage(const int status) { fprintf(stdout, "Usage: %s [OPTION]... [FILE]...\n\n" @@ -214,15 +225,119 @@ static off_t bytes_to_offset(struct file_struct *f, unsigned long n_bytes) return offset; } -static ssize_t tail_pipe(struct file_struct *f) +/* For now more or less a copy of pipe_lines() from coreutils tail */ +static ssize_t tail_pipe_lines(struct file_struct *f, unsigned long n_lines) { + struct line_buf *first, *last, *tmp; ssize_t rc; - char buf[BUFFER_SIZE]; + unsigned long total_lines = 0; - if (verbose) - write_header(f->name); + if (n_lines == 0) + return 0; + + first = last = emalloc(sizeof(struct line_buf)); + first->n_bytes = first->n_lines = 0; + first->next = NULL; + tmp = emalloc(sizeof(struct line_buf)); + + while (1) { + const char *p; + + if ((rc = read(f->fd, tmp->buf, BUFFER_SIZE)) <= 0) { + if (rc < 0 && (errno == EINTR || errno == EAGAIN)) + continue; + else + break; + } + tmp->n_bytes = rc; + tmp->n_lines = 0; + tmp->next = NULL; + p = tmp->buf; + + /* Count the lines in the current buffer */ + while ((p = memchr(p, '\n', tmp->buf + rc - p))) { + ++p; + ++tmp->n_lines; + } + total_lines += tmp->n_lines; + + /* Try to append to the previous buffer if there's enough free + * space + */ + if (tmp->n_bytes + last->n_bytes < BUFFER_SIZE) { + memcpy(&last->buf[last->n_bytes], tmp->buf, tmp->n_bytes); + last->n_bytes += tmp->n_bytes; + last->n_lines += tmp->n_lines; + } else { + last = last->next = tmp; + if (total_lines - first->n_lines > n_lines) { + tmp = first; + total_lines -= first->n_lines; + first = first->next; + } else + tmp = emalloc(sizeof(struct line_buf)); + } + } + + free(tmp); + + if (rc < 0) { + fprintf(stderr, "Error: Could not read from %s\n", pretty_name(f->name)); + goto err_out; + } + + if (last->n_bytes == 0) + goto err_out; + + /* Count incomplete lines */ + if (last->buf[last->n_bytes - 1] != '\n') { + ++last->n_lines; + ++total_lines; + } + + /* Skip unneeded buffers */ + for (tmp = first; total_lines - tmp->n_lines > n_lines; tmp = tmp->next) + total_lines -= tmp->n_lines; + + { + char const *p = tmp->buf; + if (total_lines > n_lines) { + size_t j; + for (j = total_lines - n_lines; j; --j) { + p = memchr(p, '\n', tmp->buf + tmp->n_bytes - p); + ++p; + } + } + + write(STDOUT_FILENO, p, tmp->buf + tmp->n_bytes - p); + } + + for (tmp = tmp->next; tmp; tmp = tmp->next) + if (write(STDOUT_FILENO, tmp->buf, tmp->n_bytes) <= 0) { + /* e.g. when writing to a pipe which gets closed */ + fprintf(stderr, "Error: Could not write to stdout (%s)\n", strerror(errno)); + rc = -1; + break; + } + + rc = 0; + +err_out: + while (first) { + tmp = first->next; + free(first); + first = tmp; + } + + return rc; +} + +/* TODO: Implement me :) */ +static ssize_t tail_pipe_bytes(struct file_struct *f, unsigned long n_bytes) +{ + ssize_t rc; + char buf[BUFFER_SIZE]; - /* FIXME: We will just tail everything here for now */ while ((rc = read(f->fd, buf, BUFFER_SIZE)) > 0) { if (write(STDOUT_FILENO, buf, (size_t) rc) <= 0) { /* e.g. when writing to a pipe which gets closed */ @@ -265,8 +380,15 @@ static int tail_file(struct file_struct *f, unsigned long n_units, char mode, ch } /* Cannot seek on these */ - if (IS_PIPELIKE(finfo.st_mode) || f->fd == STDIN_FILENO) - return tail_pipe(f); + if (IS_PIPELIKE(finfo.st_mode) || f->fd == STDIN_FILENO) { + if (verbose) + write_header(f->name); + + if (mode == M_LINES) + return tail_pipe_lines(f, n_units); + else + return tail_pipe_bytes(f, n_units); + } f->st_size = finfo.st_size; @@ -281,14 +403,14 @@ static int tail_file(struct file_struct *f, unsigned long n_units, char mode, ch return -1; } - if (verbose) - write_header(f->name); - if (lseek(f->fd, offset, SEEK_SET) == (off_t) -1) { fprintf(stderr, "Error: Could not seek in file '%s' (%s)\n", f->name, strerror(errno)); return -1; } + if (verbose) + write_header(f->name); + while ((bytes_read = read(f->fd, buf, BUFFER_SIZE)) > 0) write(STDOUT_FILENO, buf, (size_t) bytes_read); @@ -7,8 +7,8 @@ #ifndef _INOTAIL_H #define _INOTAIL_H -/* Number of items to tail. */ -#define DEFAULT_N_LINES 10 +#define BUFFER_SIZE 4096 +#define DEFAULT_N_LINES 10 /* Number of items to tail. */ /* tail modes */ enum { M_LINES, M_BYTES }; @@ -22,6 +22,14 @@ struct file_struct { int i_watch; /* Inotify watch associated with file_struct */ }; +/* struct for linked list of buffers/lines in tail_pipe_lines */ +struct line_buf { + char buf[BUFFER_SIZE]; + size_t n_lines; + size_t n_bytes; + struct line_buf *next; +}; + #define IS_PIPELIKE(mode) \ (S_ISFIFO(mode) || S_ISSOCK(mode)) |