From fc7aeb09d4b45b53e6c6a277157aee8674bf400c Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Fri, 7 Dec 2007 01:56:06 +0100 Subject: [PATCH] tailf: inotify based reimplementation This patch: - clean up tailf(1) code - remove stupid "for() { malloc() }" array allocation in the tailf() function - add inotify(7) support Signed-off-by: Karel Zak --- configure.ac | 1 + text-utils/tailf.1 | 4 +- text-utils/tailf.c | 240 +++++++++++++++++++++++++++++---------------- 3 files changed, 161 insertions(+), 84 deletions(-) diff --git a/configure.ac b/configure.ac index e38c5019..ca1c525f 100644 --- a/configure.ac +++ b/configure.ac @@ -61,6 +61,7 @@ AC_CHECK_FUNCS( personality \ updwtmp \ lchown \ + inotify_init \ rpmatch]) AC_FUNC_FSEEKO diff --git a/text-utils/tailf.1 b/text-utils/tailf.1 index 8bc53fca..294d9d6a 100644 --- a/text-utils/tailf.1 +++ b/text-utils/tailf.1 @@ -49,9 +49,11 @@ output the last .I N lines, instead of the last 10. .SH AUTHOR -This program was written by Rik Faith (faith@acm.org) and may be freely +This program was originally written by Rik Faith (faith@acm.org) and may be freely distributed under the terms of the X11/MIT License. There is ABSOLUTELY NO WARRANTY for this program. + +The latest inotify based implementation was written by Karel Zak (kzak@redhat.com). .SH "SEE ALSO" .BR tail "(1), " less "(1)" .SH AVAILABILITY diff --git a/text-utils/tailf.c b/text-utils/tailf.c index f4616ccb..e30e22f8 100644 --- a/text-utils/tailf.c +++ b/text-utils/tailf.c @@ -19,7 +19,7 @@ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. - * + * * less -F and tail -f cause a disk access every five seconds. This * program avoids this problem by waiting for the file size to change. * Hence, the file is not accessed, and the access time does not need to be @@ -31,111 +31,185 @@ #include #include #include +#include #include +#include +#include #include +#include #include +#include #include "nls.h" #define DEFAULT_LINES 10 -static size_t filesize(const char *filename) +static void +tailf(const char *filename, int lines) { - struct stat sb; + char *buf, *p; + int head = 0; + int tail = 0; + FILE *str; + int i; + + if (!(str = fopen(filename, "r"))) + err(EXIT_FAILURE, _("cannot open \"%s\" for read"), filename); + + buf = malloc(lines * BUFSIZ); + p = buf; + while (fgets(p, BUFSIZ, str)) { + if (++tail >= lines) { + tail = 0; + head = 1; + } + p = buf + (tail * BUFSIZ); + } + + if (head) { + for (i = tail; i < lines; i++) + fputs(buf + (i * BUFSIZ), stdout); + for (i = 0; i < tail; i++) + fputs(buf + (i * BUFSIZ), stdout); + } else { + for (i = head; i < tail; i++) + fputs(buf + (i * BUFSIZ), stdout); + } - if (!stat(filename, &sb)) return sb.st_size; - return 0; + fflush(stdout); + free(buf); + fclose(str); } -static void tailf(const char *filename, int lines) +static void +roll_file(const char *filename, off_t *size) { - char **buffer; - int head = 0; - int tail = 0; - FILE *str; - int i; - - if (!(str = fopen(filename, "r"))) - err(1, _("cannot open \"%s\" for read"), filename); - - buffer = malloc(lines * sizeof(*buffer)); - for (i = 0; i < lines; i++) buffer[i] = malloc(BUFSIZ + 1); - - while (fgets(buffer[tail], BUFSIZ, str)) { - if (++tail >= lines) { - tail = 0; - head = 1; + char buf[BUFSIZ]; + int fd; + struct stat st; + + if (!(fd = open(filename, O_RDONLY))) + err(EXIT_FAILURE, _("cannot open \"%s\" for read"), filename); + + if (fstat(fd, &st) == -1) + err(EXIT_FAILURE, _("cannot stat \"%s\""), filename); + + if (st.st_size == *size) { + close(fd); + return; } - } - if (head) { - for (i = tail; i < lines; i++) fputs(buffer[i], stdout); - for (i = 0; i < tail; i++) fputs(buffer[i], stdout); - } else { - for (i = head; i < tail; i++) fputs(buffer[i], stdout); - } - fflush(stdout); + if (lseek(fd, *size, SEEK_SET) != (off_t)-1) { + ssize_t rc, wc; - for (i = 0; i < lines; i++) free(buffer[i]); - free(buffer); + while ((rc = read(fd, buf, sizeof(buf))) > 0) { + wc = write(STDOUT_FILENO, buf, rc); + if (rc != wc) + warnx(_("incomplete write to \"%s\" (written %ld, expected %ld)\n"), + filename, wc, rc); + } + fflush(stdout); + } + close(fd); + *size = st.st_size; +} - fclose(str); +static void +watch_file(const char *filename, off_t *size) +{ + do { + roll_file(filename, size); + usleep(250000); + } while(1); } -int main(int argc, char **argv) + +#ifdef HAVE_INOTIFY_INIT + +#define EVENTS (IN_MODIFY|IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT) +#define NEVENTS 4 + +static int +watch_file_inotify(const char *filename, off_t *size) { - char buffer[BUFSIZ]; - size_t osize, nsize; - FILE *str; - const char *filename; - int count, wcount; - int lines = DEFAULT_LINES; - - setlocale(LC_ALL, ""); - bindtextdomain(PACKAGE, LOCALEDIR); - textdomain(PACKAGE); - - argc--; - argv++; - - for (; argc > 0 && argv[0][0] == '-'; argc--, argv++) { - if (!strcmp(*argv, "-n") || !strcmp(*argv, "--lines")) { - argc--; - argv++; - if (argc > 0 && (lines = atoi(argv[0])) <= 0) - errx(EXIT_FAILURE, _("invalid number of lines")); + char buf[ NEVENTS * sizeof(struct inotify_event) ]; + int fd, ffd, e; + ssize_t len; + + fd = inotify_init(); + if (fd == -1) + return 0; + + ffd = inotify_add_watch(fd, filename, EVENTS); + while (ffd >= 0) { + len = read(fd, buf, sizeof(buf)); + if (len < 0 && (errno == EINTR || errno == EAGAIN)) + continue; + if (len < 0) + err(EXIT_FAILURE, "%s: cannot read inotify events", filename); + + for (e = 0; e < len; ) { + struct inotify_event *ev = (struct inotify_event *) &buf[e]; + + if (ev->mask & IN_MODIFY) + roll_file(filename, size); + else { + close(ffd); + ffd = -1; + break; + } + e += sizeof(struct inotify_event) + ev->len; + } } - else if (isdigit(argv[0][1])) { - if ((lines = atoi(*argv + 1)) <= 0) - errx(EXIT_FAILURE, _("invalid number of lines")); + close(fd); + return 1; +} + +#endif /* HAVE_INOTIFY_INIT */ + +int main(int argc, char **argv) +{ + const char *filename; + int lines = DEFAULT_LINES; + struct stat st; + off_t size = 0; + + setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); + + argc--; + argv++; + + for (; argc > 0 && argv[0][0] == '-'; argc--, argv++) { + if (!strcmp(*argv, "-n") || !strcmp(*argv, "--lines")) { + argc--; argv++; + if (argc > 0 && (lines = atoi(argv[0])) <= 0) + errx(EXIT_FAILURE, _("invalid number of lines")); + } + else if (isdigit(argv[0][1])) { + if ((lines = atoi(*argv + 1)) <= 0) + errx(EXIT_FAILURE, _("invalid number of lines")); + } + else + errx(EXIT_FAILURE, _("invalid option")); } - else - errx(EXIT_FAILURE, _("invalid option")); - } - if (argc != 1) - errx(EXIT_FAILURE, _("usage: tailf [-n N | -N] logfile")); + if (argc != 1) + errx(EXIT_FAILURE, _("usage: tailf [-n N | -N] logfile")); - filename = argv[0]; - tailf(filename, lines); + filename = argv[0]; - for (osize = filesize(filename);;) { - nsize = filesize(filename); - if (nsize != osize) { - if (!(str = fopen(filename, "r"))) - err(EXIT_FAILURE, _("cannot open \"%s\" for read"), filename); + if (stat(filename, &st) != 0) + err(EXIT_FAILURE, _("cannot stat \"%s\""), filename); - if (!fseek(str, osize, SEEK_SET)) - while ((count = fread(buffer, 1, sizeof(buffer), str)) > 0) { - wcount = fwrite(buffer, 1, count, stdout); - if (wcount != count) - warnx(_("incomplete write to \"%s\" (written %d, expected %d)\n"), - filename, wcount, count); - } - fflush(stdout); - fclose(str); - osize = nsize; - } - usleep(250000); /* 250mS */ - } - return EXIT_SUCCESS; + size = st.st_size;; + tailf(filename, lines); + +#ifdef HAVE_INOTIFY_INIT + if (!watch_file_inotify(filename, &size)) +#endif + watch_file(filename, &size); + + return EXIT_SUCCESS; } + -- 2.39.5