summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile8
-rw-r--r--changelog11
-rw-r--r--debian/changelog6
-rw-r--r--inotail.c149
-rw-r--r--inotail.h7
5 files changed, 133 insertions, 48 deletions
diff --git a/Makefile b/Makefile
index cfeb89a..bcb8cd4 100644
--- a/Makefile
+++ b/Makefile
@@ -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
@@ -12,7 +12,7 @@ BINDIR = $(prefix)/bin
MANDIR = $(prefix)/share/man/man1
CC := gcc
-CFLAGS := $(CFLAGS) -W -Wall -pipe -D_USE_SOURCE -DVERSION="\"$(VERSION)\"" \
+CFLAGS := $(CFLAGS) -pipe -D_USE_SOURCE -DVERSION="\"$(VERSION)\"" -W -Wall \
-Wstrict-prototypes -Wsign-compare -Wshadow -Wchar-subscripts \
-Wmissing-declarations -Wpointer-arith -Wcast-align -Wmissing-prototypes
@@ -31,6 +31,10 @@ inotail: inotail.o
install: inotail
install -m 775 -D inotail $(BINDIR)/inotail
install -m 644 -D inotail.1 $(MANDIR)/inotail.1
+ gzip -9 $(MANDIR)/inotail.1
+
+uninstall:
+ rm $(BINDIR)/inotail $(MANDIR)/inotail.1*
cscope:
cscope -b
diff --git a/changelog b/changelog
index 8af070a..851ac3e 100644
--- a/changelog
+++ b/changelog
@@ -1,3 +1,14 @@
+inotail 0.4
+
+ * Use dynamic buffers of optimal size (st_blksize in struct stat) for
+ filesystem I/O (patch by Folkert van Heusden)
+ * Added handling of EINTR/EAGAIN while watching files for changes (patch by
+ Anthony Martinez)
+ * Better error checking and handling (patch by Folkert van Heusden)
+ * Various cleanups
+
+ -- Tobias Klauser <tklauser@distanz.ch> 2007-06-20 15:00
+
inotail 0.3
* Follow files even if they were moved
diff --git a/debian/changelog b/debian/changelog
index ca3a68f..0ef3352 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+inotail (0.4-1) unstable; urgency=low
+
+ * New upstream release
+
+ -- Tobias Klauser <tklauser@access.unizh.ch> Wed, 20 Jun 2007 15:10:19 +0200
+
inotail (0.3-1) unstable; urgency=low
* New upstream release
diff --git a/inotail.c b/inotail.c
index 6f8be7c..8561054 100644
--- a/inotail.c
+++ b/inotail.c
@@ -30,7 +30,6 @@
#include <fcntl.h>
#include <errno.h>
#include <getopt.h>
-#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
@@ -40,7 +39,7 @@
#include "inotail.h"
#define PROGRAM_NAME "inotail"
-#define BUFFER_SIZE 4096
+#define DEFAULT_BUFFER_SIZE 4096
/* inotify event buffer length for one file */
#define INOTIFY_BUFLEN (4 * sizeof(struct inotify_event))
@@ -64,6 +63,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"
@@ -82,10 +93,10 @@ static void usage(const int status)
static inline void setup_file(struct file_struct *f)
{
- f->fd = -1;
+ f->fd = f->i_watch = -1;
f->st_size = 0;
+ f->st_blksize = DEFAULT_BUFFER_SIZE;
f->ignore = 0;
- f->i_watch = -1;
}
static void ignore_file(struct file_struct *f)
@@ -117,43 +128,51 @@ static void write_header(char *filename)
static off_t lines_to_offset_from_end(struct file_struct *f, unsigned long n_lines)
{
- char buf[BUFFER_SIZE];
off_t offset = f->st_size;
+ char *buf = emalloc(f->st_blksize);
n_lines++; /* We also count the last \n */
while (offset > 0 && n_lines > 0) {
int i;
- ssize_t rc, block_size = BUFFER_SIZE; /* Size of the current block we're reading */
+ ssize_t rc, block_size = f->st_blksize; /* Size of the current block we're reading */
- if (offset < BUFFER_SIZE)
+ if (offset < block_size)
block_size = offset;
/* Start of current block */
offset -= block_size;
- lseek(f->fd, offset, SEEK_SET);
+ 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));
+ free(buf);
+ return -1;
+ }
- rc = read(f->fd, &buf, block_size);
+ rc = read(f->fd, buf, block_size);
if (unlikely(rc < 0)) {
fprintf(stderr, "Error: Could not read from file '%s' (%s)\n", f->name, strerror(errno));
+ free(buf);
return -1;
}
for (i = block_size - 1; i > 0; i--) {
if (buf[i] == '\n') {
- if (--n_lines == 0)
+ if (--n_lines == 0) {
+ free(buf);
return offset += i + 1; /* We don't want the first \n */
+ }
}
}
}
+ free(buf);
return offset;
}
static off_t lines_to_offset_from_begin(struct file_struct *f, unsigned long n_lines)
{
- char buf[BUFFER_SIZE];
+ char *buf;
off_t offset = 0;
/* tail everything for 'inotail -n +0' */
@@ -161,30 +180,39 @@ static off_t lines_to_offset_from_begin(struct file_struct *f, unsigned long n_l
return 0;
n_lines--;
+ buf = emalloc(f->st_blksize);
while (offset <= f->st_size && n_lines > 0) {
int i;
- ssize_t rc, block_size = BUFFER_SIZE;
+ ssize_t rc, block_size = f->st_blksize;
- lseek(f->fd, offset, SEEK_SET);
+ 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));
+ free(buf);
+ return -1;
+ }
- rc = read(f->fd, &buf, block_size);
+ rc = read(f->fd, buf, block_size);
if (unlikely(rc < 0)) {
fprintf(stderr, "Error: Could not read from file '%s' (%s)\n", f->name, strerror(errno));
+ free(buf);
return -1;
} else if (rc < block_size)
block_size = rc;
for (i = 0; i < block_size; i++) {
if (buf[i] == '\n') {
- if (--n_lines == 0)
+ if (--n_lines == 0) {
+ free(buf);
return offset + i + 1;
+ }
}
}
offset += block_size;
}
+ free(buf);
return offset;
}
@@ -204,10 +232,8 @@ static off_t bytes_to_offset(struct file_struct *f, unsigned long n_bytes)
if (from_begin) {
if (n_bytes > 0)
offset = (off_t) n_bytes - 1;
- } else {
- if ((off_t) n_bytes < f->st_size)
- offset = f->st_size - (off_t) n_bytes;
- }
+ } else if ((off_t) n_bytes < f->st_size)
+ offset = f->st_size - (off_t) n_bytes;
return offset;
}
@@ -215,15 +241,22 @@ static off_t bytes_to_offset(struct file_struct *f, unsigned long n_bytes)
static ssize_t tail_pipe(struct file_struct *f)
{
ssize_t rc;
- char buf[BUFFER_SIZE];
+ char *buf = emalloc(f->st_blksize);
if (verbose)
write_header(f->name);
/* We will just tail everything here */
- while ((rc = read(f->fd, &buf, BUFFER_SIZE)) > 0)
- write(STDOUT_FILENO, buf, (size_t) rc);
+ while ((rc = read(f->fd, buf, f->st_blksize)) > 0) {
+ if (write(STDOUT_FILENO, buf, (size_t) rc) <= 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;
+ }
+ }
+ free(buf);
return rc;
}
@@ -231,7 +264,7 @@ static int tail_file(struct file_struct *f, unsigned long n_units, char mode, ch
{
ssize_t bytes_read = 0;
off_t offset = 0;
- char buf[BUFFER_SIZE];
+ char *buf;
struct stat finfo;
if (strcmp(f->name, "-") == 0)
@@ -262,6 +295,7 @@ static int tail_file(struct file_struct *f, unsigned long n_units, char mode, ch
return tail_pipe(f);
f->st_size = finfo.st_size;
+ f->st_blksize = finfo.st_blksize; /* TODO: Can this value be 0? */
if (mode == M_LINES)
offset = lines_to_offset(f, n_units);
@@ -277,35 +311,50 @@ static int tail_file(struct file_struct *f, unsigned long n_units, char mode, ch
if (verbose)
write_header(f->name);
- lseek(f->fd, offset, SEEK_SET);
+ 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;
+ }
+
+ buf = emalloc(f->st_blksize);
- while ((bytes_read = read(f->fd, &buf, BUFFER_SIZE)) > 0)
+ while ((bytes_read = read(f->fd, buf, f->st_blksize)) > 0)
write(STDOUT_FILENO, buf, (size_t) bytes_read);
if (!forever) {
if (close(f->fd) < 0) {
fprintf(stderr, "Error: Could not close file '%s' (%s)\n", f->name, strerror(errno));
+ free(buf);
return -1;
}
} /* Let the fd open otherwise, we'll need it */
+ free(buf);
return 0;
}
static int handle_inotify_event(struct inotify_event *inev, struct file_struct *f)
{
+ char *fbuf;
int ret = 0;
if (inev->mask & IN_MODIFY) {
ssize_t rc;
- char fbuf[BUFFER_SIZE];
struct stat finfo;
+ fbuf = emalloc(f->st_blksize);
+
if (verbose)
write_header(f->name);
- lseek(f->fd, f->st_size, SEEK_SET); /* Old file size */
- while ((rc = read(f->fd, &fbuf, BUFFER_SIZE)) != 0)
+ /* Seek to old file size */
+ if (lseek(f->fd, f->st_size, SEEK_SET) == (off_t) -1) {
+ fprintf(stderr, "Error: Could not seek in file '%s' (%s)\n", f->name, strerror(errno));
+ ret = -1;
+ goto ignore;
+ }
+
+ while ((rc = read(f->fd, fbuf, f->st_blksize)) != 0)
write(STDOUT_FILENO, fbuf, (size_t) rc);
if (fstat(f->fd, &finfo) < 0) {
@@ -316,6 +365,7 @@ static int handle_inotify_event(struct inotify_event *inev, struct file_struct *
f->st_size = finfo.st_size;
+ free(fbuf);
return ret;
} else if (inev->mask & IN_DELETE_SELF) {
fprintf(stderr, "File '%s' deleted.\n", f->name);
@@ -328,7 +378,7 @@ static int handle_inotify_event(struct inotify_event *inev, struct file_struct *
ignore:
ignore_file(f);
-
+ free(fbuf);
return ret;
}
@@ -339,10 +389,10 @@ static int watch_files(struct file_struct *files, int n_files)
ifd = inotify_init();
if (errno == ENOSYS) {
- fprintf(stderr, "Error: Inotify is not supported by the kernel you're currently running.\n");
+ fprintf(stderr, "Error: inotify is not supported by the kernel you're currently running.\n");
exit(EXIT_FAILURE);
} else if (unlikely(ifd < 0)) {
- fprintf(stderr, "Error: Could not initialize Inotify (%s)\n", strerror(errno));
+ fprintf(stderr, "Error: Could not initialize inotify (%s)\n", strerror(errno));
exit(EXIT_FAILURE);
}
@@ -363,10 +413,15 @@ static int watch_files(struct file_struct *files, int n_files)
ssize_t len;
int ev_idx = 0;
- len = read(ifd, &buf, (n_files * INOTIFY_BUFLEN));
+ len = read(ifd, buf, (n_files * INOTIFY_BUFLEN));
if (unlikely(len < 0)) {
- fprintf(stderr, "Error: Could not read inotify events (%s)\n", strerror(errno));
- exit(EXIT_FAILURE);
+ /* Some signal, likely ^Z/fg's STOP and CONT interrupted the inotify read, retry */
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ else {
+ fprintf(stderr, "Error: Could not read inotify events (%s)\n", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
}
while (ev_idx < len) {
@@ -377,7 +432,9 @@ static int watch_files(struct file_struct *files, int n_files)
/* Which file has produced the event? */
for (i = 0; i < n_files; i++) {
- if (!files[i].ignore && files[i].fd >= 0 && files[i].i_watch == inev->wd) {
+ if (!files[i].ignore
+ && files[i].fd >= 0
+ && files[i].i_watch == inev->wd) {
f = &files[i];
break;
}
@@ -388,12 +445,12 @@ static int watch_files(struct file_struct *files, int n_files)
if (handle_inotify_event(inev, f) < 0)
break;
- ev_idx += INOTIFY_BUFLEN + inev->len;
+
+ ev_idx += sizeof(struct inotify_event) + inev->len;
}
}
close(ifd);
-
return -1;
}
@@ -418,7 +475,7 @@ int main(int argc, char **argv)
} else if (*optarg == '-')
optarg++;
- if (!isdigit(*optarg)) {
+ if (!is_digit(*optarg)) {
fprintf(stderr, "Invalid number of lines: %s\n", optarg);
exit(EXIT_FAILURE);
}
@@ -454,17 +511,19 @@ int main(int argc, char **argv)
specified and standard input is a pipe. */
if (forever) {
struct stat finfo;
- if (fstat(STDIN_FILENO, &finfo) == 0
- && IS_PIPELIKE(finfo.st_mode))
+ int rc = fstat(STDIN_FILENO, &finfo);
+
+ if (unlikely(rc == -1)) {
+ fprintf(stderr, "Error: Could not stat stdin (%s)\n", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ if (rc == 0 && IS_PIPELIKE(finfo.st_mode))
forever = 0;
}
}
- files = malloc(n_files * sizeof(struct file_struct));
- if (unlikely(!files)) {
- fprintf(stderr, "Error: Not enough memory (%s)\n", strerror(errno));
- exit(EXIT_FAILURE);
- }
+ files = emalloc(n_files * sizeof(struct file_struct));
for (i = 0; i < n_files; i++) {
files[i].name = filenames[i];
diff --git a/inotail.h b/inotail.h
index 4c57867..f7c1406 100644
--- a/inotail.h
+++ b/inotail.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2005-2006, Tobias Klauser <tklauser@distanz.ch>
+ * Copyright (C) 2005-2007, Tobias Klauser <tklauser@distanz.ch>
*
* Licensed under the terms of the GNU General Public License; version 2 or later.
*/
@@ -7,6 +7,8 @@
#ifndef _INOTAIL_H
#define _INOTAIL_H
+#include <sys/types.h>
+
/* Number of items to tail. */
#define DEFAULT_N_LINES 10
@@ -18,6 +20,7 @@ struct file_struct {
char *name; /* Name of file (or '-' for stdin) */
int fd; /* File descriptor (or -1 if file is not open */
off_t st_size; /* File size */
+ blksize_t st_blksize; /* Blocksize for filesystem I/O */
unsigned ignore; /* Whether to ignore the file in further processing */
int i_watch; /* Inotify watch associated with file_struct */
};
@@ -29,6 +32,8 @@ struct file_struct {
#define IS_TAILABLE(mode) \
(S_ISREG(mode) || IS_PIPELIKE(mode) || S_ISCHR(mode))
+#define is_digit(c) ((c) >= '0' && (c) <= '9')
+
#ifdef DEBUG
# define dprintf(fmt, args...) fprintf(stderr, fmt, ##args)
#else