James.Aspnes@CS.CMU.EDU (05/05/89)
This is a lookalike replacement for Chip Rosenthal's xtail program for watching the growth of multiple files. It tends to be much faster than the original, as it avoids large amounts of system call overhead by keeping an open file descriptor for each file being watched and by buffering I/O a bit better. There is, of course, a cost to the increased performance: 1. The number of files that can be watched is now limited by the per-process file descriptor limit. 2. Since xtail never stats the filename, it can't tell if a file has been renamed (e.g., using mv). It does detect when files are unlinked. Except for a few error messages, the output of the program is identical to the output of its predecessor. Jim Aspnes <asp@cs.cmu.edu> ---------------------------------------------------------------- #!/bin/sh sed -e 's:^X::' > xtail.c << EOF X/* xtail -- fast BSD version of Chip Rosenthal's xtail program */ X X/* Copyright 1989 by Jim Aspnes */ X/* distributed under the terms of the GNU General Public License */ X X#include <stdio.h> X X#include <sys/types.h> X#include <sys/stat.h> X#include <sys/file.h> X#include <sys/param.h> X X#define NEW_FILE_FORMAT "\n***** %s *****\n" /* banner for changing files */ X#define SLEEPTIME 1 /* seconds between checks */ X Xstruct { X char *name; /* name of file */ X int fd; /* file descriptor for file */ X off_t size; /* size of file */ X} files[NOFILE]; X Xint topfile = 0; /* number of files */ Xint curfile = -1; /* file currently being displayed */ X Xmain(argc, argv) Xint argc; Xchar **argv; X{ X int i; X struct stat sbuf; X X /* check argc */ X if(argc < 2) { X fprintf(stderr, "Usage: %s filenames\n", argv[0]); X exit(1); X } X X /* load up file list */ X for(i = 1; i < argc && topfile < NOFILE; i++) { X if((files[topfile].fd = open(argv[i], O_RDONLY, 0)) < 0 X || (files[topfile].size X = lseek(files[topfile].fd, 0, L_XTND)) < 0) { X /* tell him the file is bogus */ X perror(argv[i]); X } else { X files[topfile].name = argv[i]; X topfile++; X } X } X X while(topfile != 0) { X /* check each file */ X for(i = 0; i < topfile; i++) { X if(fstat(files[i].fd, &sbuf) < 0) { X /* fstat failed */ X perror(files[i].name); X remove_file(i--); X } else if(sbuf.st_nlink == 0) { X /* file is no longer linked */ X fprintf(stderr, X "%s: file has been unlinked\n", files[i].name); X remove_file(i--); X } else if(files[i].size < sbuf.st_size X && show_file(i, sbuf.st_size) < 0) { X /* show_file failed */ X perror(files[i].name); X remove_file(i--); X } X } X X sleep(SLEEPTIME); X } X X fputs("No files.\n", stderr); X exit(2); X} X Xremove_file(i) Xint i; X{ X /* take it out of the list */ X close(files[i].fd); X files[i] = files[--topfile]; X if(curfile == i) curfile = -1; X} X X#define Min(x, y) ((x) < (y) ? (x) : (y)) X Xshow_file(i, newsize) Xint i; Xoff_t newsize; X{ X int bytes; X int bytesread; X static char buf[MAXBSIZE]; X X /* print new file banner if needed */ X if(i != curfile) { X printf(NEW_FILE_FORMAT, files[i].name); X curfile = i; X } X X /* suck off the bits */ X while(files[i].size < newsize) { X bytes = Min(MAXBSIZE, newsize - files[i].size); X if(read(files[i].fd, buf, bytes) != bytes) { X return(-1); X } else { X /* print out buf and adjust file size */ X fwrite(buf, sizeof(char), bytes, stdout); X files[i].size += bytes; X } X } X X /* everything ok */ X fflush(stdout); X return(0); X} X EOF