[alt.sources] enhanced xtail; ulog script for HDB

dwd@cbnewsc.ATT.COM (david.w.dykstra) (05/10/89)

This is another version of xtail, enhanced to do everything I wanted
it to do to watch HDB uucp logfiles.

Enhancements:
    1. Ability to watch all files within a directory; if a new file appears,
	watch it too.
    2. Ability to silently ignore files that are missing.
    3. Performance enhancements.

This version uses the opendir-readdir-closedir functions which are standard
on System V Release 3.  Public Domain versions of these functions are
available (I could send you a copy if you'd like), otherwise it should be
fairly easy to modify xtail for your system.

- Dave Dykstra
  AT&T Bell Labs, Skokie, IL
  dwd@ttrdf.att.com
  att!ttrdf!dwd
  dykstra@cs.uiuc.edu

--------cut here--------------------------------------------------------
#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  Makefile ulog.1 ulog xtail.1 xtail.c
# Wrapped by dwd@ttrdf on Wed May 10 10:50:12 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'Makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Makefile'\"
else
echo shar: Extracting \"'Makefile'\" \(175 characters\)
sed "s/^X//" >'Makefile' <<'END_OF_FILE'
XINSBIN = $(HOME)/bin
X
XCFLAGS = -O
X
Xxtail: xtail.o
X	cc -o xtail xtail.o
X
Xinstall: xtail
X	cp xtail $(INSBIN)
X	strip $(INSBIN)/xtail
X	cp ulog $(INSBIN)
X	chmod +x $(INSBIN)/ulog
X
END_OF_FILE
if test 175 -ne `wc -c <'Makefile'`; then
    echo shar: \"'Makefile'\" unpacked with wrong size!
fi
# end of 'Makefile'
fi
if test -f 'ulog.1' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'ulog.1'\"
else
echo shar: Extracting \"'ulog.1'\" \(495 characters\)
sed "s/^X//" >'ulog.1' <<'END_OF_FILE'
X.TH ULOG 1L
X.SH NAME
Xulog - watch HDB uucp log files
X.SH SYNOPSIS
X.B ulog
X[machine_name] ...
X.SH DESCRIPTION
X.I ulog
Xmonitors Honey-Dan-Ber uucp log files and displays any new information
Xwritten into them.  If no machine_name is given, it will monitor
Xuucp interactions between all other machines; otherwise only those
Xmachines specified will be watched.  When switching logfiles, a banner
Xshowing the log file name is printed.
X.SH SEE ALSO
Xxtail(1)
X.SH AUTHOR
XDave Dykstra (dwd@ttrdf.att.com)
END_OF_FILE
if test 495 -ne `wc -c <'ulog.1'`; then
    echo shar: \"'ulog.1'\" unpacked with wrong size!
fi
# end of 'ulog.1'
fi
if test -f 'ulog' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'ulog'\"
else
echo shar: Extracting \"'ulog'\" \(306 characters\)
sed "s/^X//" >'ulog' <<'END_OF_FILE'
X# tail all interesting HDB log files using xtail
X# Dave Dykstra, 5/8/89
X
Xcase $# in
X    0)  cd /usr/spool/uucp
X	DIRS=".Log .Admin"
X	;;
X    *)  cd /usr/spool/uucp/.Log
X	DIRS=""
X	for ARG
X	do
X	    for DIR in uucico uucp uux uuxqt
X	    do
X		DIRS="$DIRS $DIR/$ARG"
X	    done
X	done
X	;;
Xesac
X
Xexec xtail -i $DIRS
END_OF_FILE
if test 306 -ne `wc -c <'ulog'`; then
    echo shar: \"'ulog'\" unpacked with wrong size!
fi
chmod +x 'ulog'
# end of 'ulog'
fi
if test -f 'xtail.1' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'xtail.1'\"
else
echo shar: Extracting \"'xtail.1'\" \(1018 characters\)
sed "s/^X//" >'xtail.1' <<'END_OF_FILE'
X.TH XTAIL 1L
X.SH NAME
Xxtail - Watch the growth of files.
X.SH SYNTAX
X.B xtail
X[-i] file_or_directory ...
X.SH DESCRIPTION
X.I Xtail
Xmonitors one or more files, and displays all data written to the file since
Xcommand invocation.  When switching files in the display, a banner showing
Xthe pathname of the file is printed.  This command is most useful for monitoring
Xmultiple logfiles simultaneously.  If a file turns out to be the directory
Xis monitored so if any new files appear they will also be monitored.  If
Xthe "-i" option is specified, missing files will be silently ignored.
X.P
XAfter a period of inactivity on the files being monitored,
X.I Xtail
Xreduces the frequency of checking for changes, so response may be slower
Xat that time.  Currently it checks every second until there is no
Xactivity for thirty seconds, and then it backs off to checking every
Xten seconds until there is activity again.
X.SH SEE ALSO
Xtail(1), ulog(1)
X.SH AUTHORS
XChip Rosenthal (chip@vector.Dallas.TX.US)
XDave Dykstra (dwd@ttrdf.att.com)
END_OF_FILE
if test 1018 -ne `wc -c <'xtail.1'`; then
    echo shar: \"'xtail.1'\" unpacked with wrong size!
fi
# end of 'xtail.1'
fi
if test -f 'xtail.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'xtail.c'\"
else
echo shar: Extracting \"'xtail.c'\" \(5563 characters\)
sed "s/^X//" >'xtail.c' <<'END_OF_FILE'
X/*
X * xtail - monitor the growth of files
X *
X * usage: xtail file_or_directory ...
X *
X * Mon May  1 14:44:30 1989 - Chip Rosenthal <chip@vector.Dallas.TX.US>
X *	Original composition.
X * Mon May  8 13:02:22 CDT 1989 - Dave Dykstra <dwd@ttrdf.attcom>
X *	Modified to monitor all files in directories.
X *	Added -i option.
X *	Performance enhancements.
X */
X
X#include <stdio.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <sys/fcntl.h>
X#include <dirent.h>
X
X#define MAXFILES	128	/* max number of files to watch		*/
X#define SLEEPTIME	1	/* time between checks, in seconds	*/
X#define BACKOFFCOUNT	30	/* number of SLEEPTIME intervals of	*/
X				/*  inactivity to count before backing	*/
X				/*  off to BACKTIME time between checks	*/
X#define BACKOFFTIME	10	/* time between checks when backed off	*/
X
Xint Nfiles;			/* number of files being watched	*/
Xint Lastfile;			/* Flist[] index of last file displayed	*/
Xint Ignore;			/* Silently ignore missing files */
X
Xstruct {
X    char *name;			/* path to file, or NULL for no file	*/
X    struct stat sbuf;		/* recent stat info on this file	*/
X} Flist[MAXFILES];
X
Xchar Buf[BUFSIZ];		/* general buffer			*/
X
Xmain(argc,argv)
Xint argc;
Xchar *argv[];
X{
X    int fd, ifile, argi;
X    int s, n;
X    long pos;
X    int backoffcount;
X    struct stat sbuf;
X    extern long lseek();
X    extern unsigned sleep();
X
X    /*
X     * Initialize.
X     */
X    Nfiles = 0;			/* no files specified		*/
X    Lastfile = -1;		/* no files printed		*/
X
X    argi = 1;
X    if ((argc >= 2) && (strcmp(argv[1],"-i") == 0)) {
X	Ignore = 1;
X	argi++;
X    }
X    else
X	Ignore = 0;
X
X    /*
X     * Get files from command line.  Save in list and get initial stat info.
X     */
X    for ( ; argi < argc ; ++argi ) {
X	if ( Nfiles >= MAXFILES ) {
X	    fprintf(stderr,"%s: too many files\n",argv[0]);
X	    exit(1);
X	}
X	if ( stat(Flist[Nfiles].name=argv[argi],&Flist[Nfiles].sbuf) != 0 ) {
X	    if ( Ignore ) {
X		Flist[Nfiles].sbuf.st_mtime = 0;
X		Flist[Nfiles].sbuf.st_size = 0;
X		++Nfiles;
X	    }
X	    else
X		perror(Flist[Nfiles].name);
X	}
X	else {
X	    ++Nfiles;
X	    if ( Flist[Nfiles-1].sbuf.st_mode & S_IFDIR )
X		getdirfiles( Flist[Nfiles-1].name, 0 );
X	}
X    }
X    if ( Nfiles == 0 ) {
X	fprintf(stderr,"Usage: xtail [-i] file_or_directory ...\n");
X	exit(1);
X    }
X
X    /*
X     * Loop forever.
X     */
X    backoffcount = BACKOFFCOUNT;
X    for (;;) {
X
X	/*
X	 * Go through each file in the list.
X	 */
X	for ( ifile = 0 ; ifile < Nfiles ; ++ifile ) {
X
X	    /*
X	     * Skip entries removed from list.
X	     */
X	    if ( Flist[ifile].name == NULL )
X		continue;
X
X	    /*
X	     * Stat the file.  Skip it if nothing has changed.
X	     */
X	    if ( stat(Flist[ifile].name,&sbuf) != 0 ) {
X		if ( Ignore ) {
X		    Flist[ifile].sbuf.st_mtime = 0;
X		    Flist[ifile].sbuf.st_size = 0;
X		}
X		else {
X		    perror(Flist[ifile].name);
X		    Flist[ifile].name = NULL;
X		}
X		continue;
X	    }
X	    if ( Flist[ifile].sbuf.st_mtime == sbuf.st_mtime )
X		continue;
X	    
X	    if ( Flist[ifile].sbuf.st_mode & S_IFDIR ) {
X		/*
X		 * A directory has changed, read it again.
X		 */
X		getdirfiles(Flist[ifile].name, 1);
X		Flist[ifile].sbuf.st_mtime = sbuf.st_mtime;
X		continue;
X	    }
X		
X	    if ( Flist[ifile].sbuf.st_size == sbuf.st_size )
X		continue;
X
X	    if ( sbuf.st_size != 0 ) {
X		/*
X		 * Open the file.
X		 */
X		if ( (fd=open(Flist[ifile].name,O_RDONLY)) < 0 ) {
X		    if (!Ignore) {
X			perror(Flist[ifile].name);
X			Flist[ifile].name = NULL;
X		    }
X		    continue;
X		}
X
X		/*
X		 * Display banner if we are switching files.
X		 */
X		if ( Lastfile != ifile )
X		    printf("***** %s *****\n",Flist[ifile].name);
X
X		/*
X		 * Dump the recently added info.
X		 */
X		if ( (pos=Flist[ifile].sbuf.st_size) > 0 )
X		    (void) lseek(fd,pos,0);
X
X		for (s = sbuf.st_size - pos; s > 0; s -= n) {
X		    if ( (n = read(fd, Buf, (s > BUFSIZ) ? BUFSIZ : s) ) <= 0 )
X			perror(Flist[ifile].name);
X		    else
X			fwrite(Buf, n, 1, stdout);
X		}
X
X		(void) close(fd);
X		Lastfile = ifile;
X	    }
X	    Flist[ifile].sbuf.st_mtime = sbuf.st_mtime;
X	    Flist[ifile].sbuf.st_size = sbuf.st_size;
X
X	    /*
X	     * Set the back-off counter back to maximum since something happend.
X	     */
X	    backoffcount = BACKOFFCOUNT;
X
X	}				/* next file in list */
X
X	if (backoffcount == 0)
X	    (void) sleep(BACKOFFTIME);
X	else {
X	    --backoffcount;
X	    (void) sleep(SLEEPTIME);
X	}
X    }					/* next iteration */
X
X    /*NOTREACHED*/
X
X}
X
Xgetdirfiles(dirname, watchnew)
Xchar *dirname;
Xint watchnew;
X{
X    DIR *dirp;
X    struct dirent *dp;
X    int i;
X    char *malloc();
X
X    if ((dirp = opendir(dirname)) == NULL) {
X	fprintf(stderr, "xtail: Cannot open %s directory\n", dirname);
X	return;
X    }
X
X    while((dp = readdir(dirp)) != NULL) {
X
X	if (dp->d_name[0] == '.')
X	    /* skip files beginning with a dot */
X	    continue;
X
X	for (i = 0; i < Nfiles; i++) {
X	    strcpy(Buf, dirname);
X	    strcat(Buf, "/");
X	    strcat(Buf, dp->d_name);
X
X	    if (strcmp(Buf, Flist[i].name) == 0)
X		break;
X	}
X	if (i < Nfiles)
X	    /* it's an old file */
X	    continue;
X
X	if (stat(Buf, &Flist[Nfiles].sbuf) != 0 )
X	    perror(Buf);
X	else {
X	    if ((Flist[Nfiles].name = malloc(strlen(Buf)+1)) == NULL) {
X		fprintf(stderr,"xtail: no space\n");
X		exit(1);
X	    }
X	    strcpy(Flist[Nfiles].name, Buf);
X
X	    ++Nfiles;
X	    if (Flist[Nfiles-1].sbuf.st_mode & S_IFDIR)
X		/* recursive call on sub-directory */
X		getdirfiles(Flist[Nfiles-1].name, watchnew);
X	    else if (watchnew) {
X		/* it's a new file, make sure first lines get printed out */
X		Flist[Nfiles-1].sbuf.st_mtime = 0;
X		Flist[Nfiles-1].sbuf.st_size = 0;
X	    }
X	}
X    }
X    closedir(dirp);
X}
END_OF_FILE
if test 5563 -ne `wc -c <'xtail.c'`; then
    echo shar: \"'xtail.c'\" unpacked with wrong size!
fi
# end of 'xtail.c'
fi
echo shar: End of shell archive.
exit 0