#!/usr/bin/perl
# copyright: Joey Hess 1997 GPL.

die q{
Logtail watches one or more log files, and makes procmeter display graphs of
how many lines are added to each file.

Syntax:
	logtail filespec , [filespec , ... ] fifo

	fifo      Basename of the fifo file to use to communicate with
	          procmeter. The fifo.dat and fifo.def files will be created 
	          on the fly.
	filespec  Specifies a file to watch and options for this file.

Filespec Format:

	file=f units=u size=s pattern=p average=n rotate=[yes|no]

	file      Watch the file specified. A required part of each
	          filespec. This item is required.
	units     The units to use (default: "lines")
	size      How many lines in this file it takes to make one 
	          line on the graph (default: 1)
	name      Name of this item on the graph (default: basename of file)
	pattern   Pattern that the line must match to be counted. See perlre(1).
	          (default: none specified, which matches everything)
	          Note that if pattern contains a () grouping, then the
	          value of that grouping is used for the value of the line.
	          Useful for doing something like displayng stats on how many K
	          are being transfered from your web server.
	rotate    If "yes", try to figure out when the file is rotated, and
	          recover from that condition. (default: yes)

Example:
	logtail file=/var/log/httpd/access_log units=hits size=10 , \
		file=/var/log/messages rotate=yes size=2 \
		/tmp/logfifo

} unless @ARGV;

$fifo_basename=pop(@ARGV);
$fifo="$fifo_basename.dat";
system 'mkfifo',$fifo; # perl has no mkfifo command.

open (DEF,">$fifo_basename.def") ||
	die "open $fifo.def failed: $!\n";
my %filespec;
my $spec_counter=0;
foreach $word (@ARGV) {
	if ($word eq ',') {
		SaveFileSpec(%filespec);
		undef %filespec;
	}
	elsif ($word=~/(.*?)=(.*)/ ne undef) {
		$filespec{$1}=$2;
	}
	else {
		die "Unknown item found in filespec: $word\n";
	}
}
SaveFileSpec(%filespec);
undef %filespec;
close DEF;

# Saves a filespec into global data hashes.
sub SaveFileSpec { my %filespec=@_;
	my $filespec=$spec_counter++;
	if (!$filespec{file}) {
		die "Filespec does not contain file name.\n";
	}

	if (!$filespec{size}) {
		$filespec{size}=1;
	}
	if (!$filespec{name}) {
		($filespec{name})=$filespec{file}=~m:.*/(.*?)$:;
	}	
	if (!$filespec{units}) {
		$filespec{units}='lines';
	}
	if ($filespec{pattern}) {
		$log_pattern{$filespec}=$filespec{pattern};
	}
	open ($filespec,"<$filespec{file}")
		|| die "open $filespec{file} failed: $!\n";
	seek($filespec,0,2); # seek to eof.
	$log_counter{$filespec}=0;
	$log_filesize{$filespec}=-s $filspec{name};
	$log_filename{$filespec}=$filespec{file};
	print DEF "$filespec{name} $filespec{units} " .
		$filespec{size} . "\n";
	if ($filespec{rotate} eq 'yes') {
		$rotate_protect{$filespec}=1;
	}
}

for (;;) {
	foreach $filespec (keys %log_counter) {
		if ($rotate_protect && -r $log_filename{$filespec}) {
			if (-s $log_filename{$filespec} < $log_filesize{$filespec}) {
				# File size has decreased, assume the file has been
				# rotated on us.
				close $filespec;
				open ($filespec,"<$log_filename{$filespec}");
				seek($filespec,0,2); # seek to eof.
			}
			$log_filesize{$filespec}=-s $log_filename{$filespec};
		}
		while (!eof($filespec)) {
			$_=<$filespec>;
			if (! $log_pattern{$filespec} || 
			    m/$log_pattern{$filespec}/ ne undef) {
				if ($1) {
					$log_counter{$filespec} += $1;
				}
				else {
					$log_counter{$filespec} += 1;
				}
			}
		}
	}

	open (OUT,">$fifo") || die;
	foreach $filespec (keys %log_counter) {
		print OUT "$log_counter{$filespec}\n";
		$log_counter{$filespec}=0;
	}
	close OUT;

	select(undef,undef,undef,0.25); # why is this needed?
}
