#!/usr/bin/env perl ## ## Assumes that the log file is rotated daily. ## ## $Id: dams-stats.pl,v 1.3 2004/02/25 01:19:36 dgc Exp $ ## $damsdb = "/var/mail/VM00/dams/.stats"; # is a directory $damslog = "log:/var/log/local3.log"; ## Any time the name of a pattern is changed in dams.sigs, ## put a translation here. %translations = ( 'W32.Novarg.A@MM / W32.Mydoom@MM [b64]' => 'W32.Mydoom@MM (b64)', 'W32.Novarg.A@MM / W32.Mydoom@MM [zip]' => 'W32.Mydoom@MM (zip)', 'W32.Novarg.A@MM / W32.Mydoom@MM' => 'W32.Mydoom@MM (unclassified transport)', ); use Fcntl; use NDBM_File; use Getopt::Std; # basename ($A0 = $0) =~ s,.*/,,; sub declare { printf @_ if ($verbose); } sub _unlink { my ($file) = @_; if (-r $file) { unlink($file) || print STDERR "$A0: cannot unlink \"$file\": $!\n"; return 1; } return 0; } sub flush { my ($type) = @_; my $errs = 0; for $suffix qw(db dir pag) { $errs += _unlink("$damsdb/$type.$suffix"); } $errs += _unlink("$damsdb/$type"); } sub collate { my ($file, $z) = @_; my $current = {}; $file =~ s/^log://; if ($z || $file =~ /\.(Z|gz)$/) { declare ("Collating from compressed file: $file\n"); open (LOG, "gzip -dc <$file |") || die "cannot decompress DAMS log \"$file\""; } else { declare ("Collating from file: $file\n"); open (LOG, $file) || die "cannot open DAMS log \"$file\""; } while () { if (/Detected <([^>]+)> in/) { $key = $translations{$1} || $1; $current->{$key}++; } } close(LOG); return $current; } sub update { my ($dbfile, $cur) = @_; my (%db, $key); my $dir = $dbfile; if ($dir =~ s!/[^/]+$!!) { mkdir ("$damsdb/$dir", 0755); } tie (%db, 'NDBM_File', "$damsdb/$dbfile", O_RDWR|O_CREAT, 0644) || do { print STDERR "$A0: cannot load \"$db\": $!\n\n"; return; }; for $key (keys %{$cur}) { $db{$key} += $cur->{$key}; } untie(%db); } sub report { my ($when, $db) = @_; my $total = 0; if ($db =~ /^log:/) { my $log = $db; $log =~ s/^log://; my $current = collate($log, $compressed); print "$when [$db]:\n"; map { print "\t$_: ", $current->{$_}, "\n"; $total += $current->{$_}; } sort keys %{$current}; } else { tie (%db, 'NDBM_File', "$damsdb/$db", O_RDONLY, 0) || do { print "$A0: cannot load \"$db\": $!\n\n"; return 1; }; print "$when [$db]:\n"; map { print "\t$_: ", $db{$_}, "\n"; $total += $db{$_}; } sort keys %db; untie(%db); } print "\tALL BRANDS: $total\n"; print "\n"; return 0; } sub usage { print STDERR "usage: $A0 [-v] [-t offset] [-z] [-f file] [-c]\n"; print STDERR " $A0 [-v] [-t offset] {yd|y|m|w|d} spec\n"; print STDERR " $A0 [-v] [-t offset] [-z] [-f file] -u\n"; } sub mkspec { my ($fmt, $year, @foo) = @_; return sprintf("%04d/$fmt", $year, $year, @foo); } sub main { my $t = time; my $file; getopts('hvczuf:t:', \%o); if ($o{h}) { usage(); exit(1); } $verbose = 1 if ($o{v}); if ($o{t} =~ /^=([0-9]+)$/) { ## -t is a time value $t = $1; } elsif ($o{t} && $o{t} =~ /^-/) { # -t is an adjustment $o{t} *= 60 if ($o{t} =~ s/^-(\d+)m$/$1/); $o{t} *= 60*60 if ($o{t} =~ s/^-(\d+)h$/$1/); $o{t} *= 60*60*24 if ($o{t} =~ s/^-(\d+)d$/$1/); $o{t} *= 60*60*24*7 if ($o{t} =~ s/^-(\d+)w$/$1/); $o{t} *= 60*60*24*30 if ($o{t} =~ s/^-(\d+)M$/$1/); $o{t} *= 60*60*24*365 if ($o{t} =~ s/^-(\d+)y$/$1/); $o{t} = -$o{t} if ($o{t} > 0); declare ("Time offset: %d seconds\n", $o{t}); $t += $o{t}; } my @lt = localtime($t); my $year = $lt[5]; my $month = $lt[4] + 1; my $day = $lt[3]; my $yday = $lt[7] + 1; my $week = int(($yday + 6) / 7); $year += 1900 if ($year < 1900); $db_all = "all"; $db_year = mkspec("y-%04d", $year); $db_month = mkspec("m-%04d-%02d", $year, $month); $db_week = mkspec("w-%04d-%02d", $year, $week); $db_day = mkspec("d-%04d-%02d-%02d", $year, $month, $day); $db_yday = mkspec("x-%04d-%03d", $year, $yday); declare ("Date: %04d-%02d-%02d (%03d)\n", $year, $month, $day, $yday); $file = $o{f} || $damslog; $compressed = 0; $compressed = 1 if $o{z}; if ($o{u}) { my $current = collate($file, $compressed); update($db_all, $current); update($db_year, $current); update($db_month, $current); update($db_week, $current); update($db_day, $current); symlink($db_day, "$damsdb/$db_yday"); exit(0); } $errs = 0; ## No arguments? Report current stats. if ($#ARGV < 0) { # If -t, use adjusted time. Without, use current logfile. if ($o{t}) { $errs += report("Date", $db_day); } else { $errs += report("From log", $file); } # If -c, add cumulative stats. if ($o{c}) { $errs += report("Week to date", $db_week); $errs += report("Month to date", $db_month); $errs += report("Year to date", $db_year); $errs += report("Forever", $db_all); } exit($errs); } ## else... ## Interpret as date $mode = shift @ARGV; ## Require more args if ($#ARGV < 0) { usage(); exit(2); } ## Year-day ## Funky: uses a symlink mapping if ($mode =~ /^yd/ && $#ARGV <= 1) { if ($#ARGV == 1) { # year day $spec = mkspec("x-%04d-%03d", $ARGV[0], $ARGV[1]); } else { # day $spec = mkspec("x-%04d-%03d", $year, $ARGV[0]); } $spec = readlink("$damsdb/$spec"); $errs += report("Selected day", $spec); } ## Year elsif ($mode =~ /^y/ && $#ARGV == 0) { $spec = mkspec("y-%04d", $ARGV[0]); $errs += report("Selected year", $spec); } ## Month elsif ($mode =~ /^m/ && $#ARGV <= 1) { if ($#ARGV == 1) { # year month $spec = mkspec("m-%04d-%02d", $ARGV[0], $ARGV[1]); } else { # just month $spec = mkspec("m-%04d-%02d", $year, $ARGV[0]); } $errs += report("Selected month", $spec); } ## Week elsif ($mode =~ /^w/ && $#ARGV <= 1) { if ($#ARGV == 1) { # year week $spec = mkspec("w-%04d-%02d", $ARGV[0], $ARGV[1]); } else { # just week $spec = mkspec("w-%04d-%02d", $year, $ARGV[0]); } $errs += report("Selected week", $spec); } ## Day elsif ($mode =~ /^d/ && $#ARGV <= 2) { if ($#ARGV == 2) { # year month day $spec = mkspec("d-%04d-%02d-%02d", $ARGV[2], $ARGV[1], $ARGV[0]); } elsif ($#ARGV == 1) { # month day $spec = mkspec("d-%04d-%02d-%02d", $year, $ARGV[0], $ARGV[1]); } else { # day $spec = mkspec("d-%04d-%02d-%02d*", $year, $month, $ARGV[0]); } $errs += report("Selected day", $spec); } ## bah else { usage(); exit(2); } exit(100 + $errs); } main;