#!/opt/bin/perl

# Josh's Log Processor by josh@hitchhiker.org
# (C) 1998 Josh Wilmes, all rights reserved
# Note: Need to handle "last message repeated n times" messages!

$|=1;
$domain="sky.net";
my ($configfile)=@ARGV;
my $syslogpid="/etc/syslog.pid";

print ":: Loggo Logfile Processor (version 1.2) by Josh Wilmes\n";
print ":: " . localtime(time) . "\n\n";

open (C,"<$configfile") || die "can't open $configfile";
print ":: Reading $configfile\n";
while (!eof(C)) {
    $_=<C>;
    # Ignore comments
    s/#.*$//g;
    
    # Ignore blank lines
    if (/^\s*$/) { next; }
    
    ($logfile)=/\s*(\S+)/;
    # remove the token from the $_
    s/$logfile//;
		
    # OK, we have the block start token, we now scan for the beginning {.
    while(!eof(C)) {
	# Ignore comments
	s/#.*$//g;	
	# Skip blank lines
	if (/^\s*$/) { $_=<C>;next; }
	# Have we found the {?
	if (/{/) { s/{//g; last; } 
	$_=<C>;
     }

     while (!eof(C)) {
	# Ignore comments
	s/#.*$//g;	
	# Skip blank lines
	if (/^\s*$/) { $_=<C>; next; }
	$done=0;
	if (/}/) { $done=1; s/}//g; }

	if (/^\s*discard/) {
	    s/discard//;
	    ($regexp)=/^\s*(\S.*)$/;
	    $regexp =~ s/\s*$//g;	    
	    push (@{$code{$logfile}},"if (/$regexp/) { next; }\n");
	}

	if (/^\s*alert/) {
	    s/alert//;
	    ($regexp)=/^\s*(\S.*)$/;
	    $regexp =~ s/\s*$//g;	    
	    push (@{$code{$logfile}},"if (/$regexp/) { alert(\$_); next; }\n");
	}

	if (/^\s*groupcount\s+/) {
	    s/groupcount//;
	    ($countthing,$regexp)=/^\s*(\S+)\s+(\S.*)$/;
	    $regexp =~ s/\s*$//g;
	    push (@{$code{$logfile}},"if (/$regexp/) {\n ");
	    push (@{$code{$logfile}},"  (\$countre)=/$regexp/; if (\$countre =~ /$domain/) { \$countre=\"Inside $domain\"; }\n");
	    push (@{$code{$logfile}},"  \$groupcount{\"$logfile\"}{\"\$host/\$service $countthing \$countre\"}++; next;\n}\n");
	}
	
	if (/^\s*name\s+/) {
	    s/name//;
	    ($name)=/^\s*(\S.*)$/;
	    $name =~ s/\s*$//g;
	    $name{$logfile} = $name;
	}	

	if (/^\s*archive\s+/) {
	    s/archive//;
	    $numdays=$_+0;
	}

	if (/^\s*count\s+/) {
	    s/count//;
	    ($regexp)=/^\s*(\S.*)$/;
	    $regexp =~ s/\s*$//g;	    
	    ($countthing)=/($regexp)/;
	    push (@{$code{$logfile}},"if (/$regexp/) { \$count{\"$logfile\"}{\"\$host/\$service $countthing\"}++; next; }\n");
	}

	if ($done==1) { last; }
	$_=<C>;
    }
}
close(C);

foreach $file (sort keys %code) {
    print ":: $file: code..";
    $prog  = q[
                open (F,"<$file");
		foreach (<F>) {
		     $num{$file}++;
		     ($date,$host,$service,$message)=/(.*[0-9][0-9]:[0-9][0-9])\s+(\S+)\s+([^\[]+)\]?.*:(.*)$/;
		     next unless $message;
	      ];	      
    foreach (@{$code{$file}}) { $prog .= "\t$_"; }
    $prog .= q[
                     push @{$unknown{$file}},"$date $host/$service: $message";
	        }
		close(F); 
	      ];

#print "\n$prog\n";

    print "processing..";
    eval($prog);
    warn $@ if $@;

    print "archiving..";
    $pid = &stop_process($syslogpid);
    &shuffle_logs($file,$numdays);
    &archive_log($file);
    &cont_process($pid);
    print "\n";
}

foreach $file (sort keys %code) {
    $num{$file} = 0 unless defined($num{$file});
    $xtra = $name{$file} ? " - ($name{$file})" : "";
    
    print "=" x 70 . "\n:: $file ($num{$file} lines)$xtra ::\n";

    if ($count{$file}) {
	print "The following counts were found:\n";
	foreach (sort keys %{$count{$file}}) { print "$_: $count{$file}{$_}\n"; }
	print "\n";
    }

    if ($groupcount{$file}) {
	print "The following groupcounts were found: (>5)\n";
	foreach (sort keys %{$groupcount{$file}}) {
	  if ($groupcount{$file}{$_} > 5) {
   	      print "$_: $groupcount{$file}{$_}\n"; 
	  }
	 }
         print "\n";
    }

    if ($unknown{$file}) {
	print "The following unknown messages were found:\n";
	foreach (@{$unknown{$file}}) { print "$_\n"; }
    }
}




### The following code was lifted from chklogs.
# AUTHOR: D. Emilio Grimaldo T.         root@panama.iaehv.nl

# Gotta say, I dont think much of someone whose email address is "root".
# Also this code is really crude.  Can you say "first perl script"?
# I am just using it becuase I am lazy..
# --Josh

#
# Shuffles logs, when the maximum number of archived logs (per log name)
# is reached we remove the oldest so that we don't waste disk space with
# too many archived logs
#
sub shuffle_logs {
    local($fullpath,$f_max) = @_;
    local($logcnt,@LogList);

    $logcnt = 0;
    $dname = &dirname($fullpath);
    $bname = &basename($fullpath);
    chdir($dname);
    open(ARCHLIST,"ls $bname.*.gz 2> /dev/null |") || warn "Could not pipe shuffle";
    while (<ARCHLIST>) {
        chop($_);
	@LogList[$logcnt] = $_;
        $logcnt += 1;
    }
    close(ARCHLIST);
    if ($logcnt >= $f_max) {
        unlink(@LogList[0]);            # Remove oldest archived log
    }
}

sub basename {
    local($fpath) = @_;
    $fpath =~ s/[a-zA-Z0-9_.\/-]*\///;
    return $fpath;
}

sub dirname {
    local($fpath) = @_;
    $fpath =~ s/[a-zA-Z0-9._-]*$//;
    return $fpath;
}

#
# Archives the log by compressing it and adding a timestamp in
# the filename. No new log is created if it already exists.
#
sub archive_log {
    local($logname) = @_;
    local($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst);
    local($archiveName,$zipArchive);

    ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
    $ArchiveSuffix = sprintf(".%2d%02d%02d",$year,$mon+1,$mday);
    $archiveName = $logname . $ArchiveSuffix;
    $zipArchive = $archiveName . ".gz";

    if ( ! -e $zipArchive) {
        if (! system("/opt/bin/gzip < $logname > $zipArchive && : > $logname")) {
            chown $UID,$GID,$zipArchive;        # Set correct ownership
            chmod $mode, $zipArchive;           # Set correct permissions
            print STDOUT "archived";
        }
        else {
            print STDOUT "Could not create $zipArchive!\n";
        }
    }
    else {
        print STDOUT "$zipArchive already exists!";            # give a warning
    }
}

# STOP_PROCESS(path_of_pid_file)
sub stop_process {
    local($pid_file) = @_;
    local($itsPID);
    open(PID,"$pid_file");
    read(PID,$itsPID,10);
    kill 'STOP', $itsPID;
    close(PID);
    return $itsPID;
}

# CONT_PROCESS(pid)
sub cont_process {
    local($itsPID) = @_;
    kill 'CONT', $itsPID;
}



