#!/usr/bin/perl -T #------------------------------------------------------------------------------ # This is amavisd-agent, a demo program to display # SNMP-like counters updated by amavisd-new. # # Author: Mark Martinec # Copyright (C) 2004 Mark Martinec, All Rights Reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # * Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # * Neither the name of the author, nor the name of the "Jozef Stefan" # Institute, nor the names of contributors may be used to endorse or # promote products derived from this software without specific prior # written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER # OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; # OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, # WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR # OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # #(the license above is the new BSD license, and pertains to this program only) # # Patches and problem reports are welcome. # The latest version of this program is available at: # http://www.ijs.si/software/amavisd/ #------------------------------------------------------------------------------ use strict; use re 'taint'; use Time::HiRes (); use BerkeleyDB; use vars qw($VERSION); $VERSION = 1.02; use vars qw(%keys %virus %types %history $avg_int $uptime); $avg_int = 5*60; # 5 minute interval sub p1($$@) { my($k,$avg,@tot_k) = @_; printf("%-23s %6d %6.0f/h", $k, $keys{$k}, $avg*3600); for my $tot_k (@tot_k) { if ($keys{$tot_k} <= 0) { printf(" --- %%") } else { printf(" %6.1f %%", 100*$keys{$k}/$keys{$tot_k}) } print " ($tot_k)"; } print "\n"; } sub p2($$$) { my($k,$avg,$tot_k) = @_; printf("%-23s %6d %6.0f/h %6.1f %% (%s)\n", $k, $virus{$k}, $avg*3600, 100*$virus{$k}/$keys{$tot_k}, $tot_k); } sub enqueue($$$$) { my($name,$now,$val,$hold_time) = @_; if (ref $history{$name} ne 'ARRAY') { $history{$name} = [] } my($oldest_useful); for my $j (0..$#{$history{$name}}) { if ($history{$name}->[$j][0] + $hold_time >= $now) { $oldest_useful = $j; last } } if (defined $oldest_useful) { @{$history{$name}} = @{$history{$name}}[$oldest_useful..$#{$history{$name}}]; } push(@{$history{$name}}, [$now,$val]); my($average,$dv,$dt); my($n) = scalar(@{$history{$name}}); my($oldest) = $history{$name}->[0]; my($latest) = $history{$name}->[$n-1]; $dt = $latest->[0] - $oldest->[0]; $dv = $latest->[1] - $oldest->[1]; if ($n < 2 || $dt < $hold_time/2) { $dt = $uptime; $dv = $val; # average since the start time } if ($dt > 0) { $average = $dv/$dt } ($average, $dv, $dt, $n); } sub fmt_ticks($) { my($t) = @_; my($hh)= $t % 100; $t = int($t/100); my($s) = $t % 60; $t = int($t/60); my($m) = $t % 60; $t = int($t/60); my($h) = $t % 24; $t = int($t/24); my($d) = $t; sprintf("%d days, %d:%02d:%02d.%02d", $d,$h,$m,$s,$hh); }; # main program starts here $SIG{INT} = sub { die "\n" }; # do the END code block my($env) = BerkeleyDB::Env->new( '-Home'=>'/var/amavis/db', '-Flags'=> DB_INIT_CDB | DB_INIT_MPOOL); defined $env or die "BDB no env: $BerkeleyDB::Error $!"; my($db) = BerkeleyDB::Hash->new( '-Filename'=>'snmp.db', '-Flags'=>DB_RDONLY, '-Env'=>$env ); defined $db or die "BDB no dbS 1: $BerkeleyDB::Error $!"; my($cursor); $| = 1; my($stat,$key,$val); for (;;) { %keys = (); %virus = (); %types = (); my($now); my($eval_stat,$interrupt); $interrupt = ''; print "\n"; { my($h1) = sub { $interrupt = $_[0] }; local(@SIG{qw(INT HUP TERM TSTP QUIT ALRM USR1 USR2)}) = ($h1) x 8; eval { $cursor = $db->db_cursor; # obtain read lock defined $cursor or die "db_cursor error: $BerkeleyDB::Error"; $now = Time::HiRes::time; while ( ($stat=$cursor->c_get($key,$val,DB_NEXT)) == 0 ) { if ($key =~ /^virus\.byname\.(.*)\z/s) { $virus{$1} = $val } else { $keys{$key} = $val } } $stat==DB_NOTFOUND or die "c_get: $BerkeleyDB::Error $!"; $cursor->c_close==0 or die "c_close error: $BerkeleyDB::Error"; $cursor = undef; }; $eval_stat = $@; if (defined $db) { $cursor->c_close if defined $cursor; # unlock, ignoring status $cursor = undef; } } if ($interrupt ne '') { kill($interrupt,$$) } # resignal elsif ($eval_stat ne '') { chomp($eval_stat); die "BDB $eval_stat\n" } for my $k (sort keys %keys) { if ($keys{$k} =~ /^C32 (.*)\z/) { $keys{$k} = $1; } elsif ($k eq 'sysUpTime' && $keys{$k} =~ /^INT (.*)\z/) { $uptime = $now - $1; my($ticks) = int($uptime*100); printf("%-15s %s %s (%s)\n", $k,'Timeticks', $ticks, fmt_ticks($ticks)); delete($keys{$k}); } else { printf("%-15s %s\n", $k,$keys{$k}); delete($keys{$k}); } } for (sort keys %keys) { my($avg,$dv,$dt,$n) = enqueue($_, $now, $keys{$_}, $avg_int); if (/^OpsDecTyp/) {} # later elsif (/^CacheHitsVirusMsgs$/) { p1($_,$avg,'ContentVirusMsgs') } elsif (/^CacheHitsBannedMsgs$/) { p1($_,$avg,'ContentBannedMsgs') } elsif (/^CacheHitsSpamMsgs$/) { p1($_,$avg,'ContentSpamMsgs') } elsif (/^Cache/) { p1($_,$avg,'CacheAttempts') } elsif (/^Content/) { p1($_,$avg,'InMsgs') } elsif (/^Quar/) { p1($_,$avg,'QuarMsgs') } elsif (/^OpsSql/) { p1($_,$avg,'InMsgsRecips') } elsif (/^(InMsgs|Ops)/) { p1($_,$avg,'InMsgs') } elsif (/^Out/) { p1($_,$avg,'OutMsgs') } else { p1($_,$avg,undef) } } for (sort { $keys{$b} <=> $keys{$a} } grep {/^OpsDecTyp/} keys %keys) { my($avg,$dv,$dt,$n) = enqueue($_, $now, $keys{$_}, $avg_int); p1($_,$avg,'InMsgs'); } for (keys %virus) { $virus{$_} = $1 if $virus{$_} =~ /^C32 (.*)\z/ } for (sort { $virus{$b} <=> $virus{$a} } keys %virus) { my($avg,$dv,$dt,$n) = enqueue($_, $now, $virus{$_}, $avg_int); p2($_,$avg,'ContentVirusMsgs'); } sleep 10; } END { if (defined $db) { $cursor->c_close if defined $cursor; # ignoring status $db->db_close==0 or die "BDB db_close error: $BerkeleyDB::Error $!"; } print STDERR "exited\n"; }