| Server IP : 85.214.239.14 / Your IP : 216.73.216.99 Web Server : Apache/2.4.65 (Debian) System : Linux h2886529.stratoserver.net 4.9.0 #1 SMP Mon Sep 30 15:36:27 MSK 2024 x86_64 User : www-data ( 33) PHP Version : 8.2.29 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : ON | Sudo : ON | Pkexec : OFF Directory : /proc/3/task/3/cwd/proc/3/task/3/cwd/proc/2/task/2/cwd/usr/sbin/ |
Upload File : |
#!/usr/bin/perl -T
# SPDX-License-Identifier: BSD-2-Clause
#------------------------------------------------------------------------------
# This program implements a SNMP AgentX (RFC 2741) subagent for amavis.
#
# Author: Mark Martinec <Mark.Martinec@ijs.si>
#
# Copyright (c) 2009-2014, 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:
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# 2. 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.
#
# 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 views and conclusions contained in the software and documentation are
# those of the authors and should not be interpreted as representing official
# policies, either expressed or implied, of the Jozef Stefan Institute.
# (the above license is the 2-clause BSD license, also known as
# a "Simplified 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/
#------------------------------------------------------------------------------
package AmavisAgent;
use strict;
use re 'taint';
use warnings;
use warnings FATAL => qw(utf8 void);
no warnings 'uninitialized';
use Errno qw(ESRCH ENOENT EACCES EEXIST);
use POSIX ();
use Time::HiRes ();
use IO::File qw(O_RDONLY O_WRONLY O_RDWR O_CREAT O_EXCL);
use Unix::Syslog qw(:macros :subs);
use BerkeleyDB;
use vars qw($VERSION); $VERSION = 1.007;
use vars qw($myversion $myproduct_name $myversion_id $myversion_date);
$myproduct_name = 'amavis-agentx';
$myversion_id = '1.7'; $myversion_date = '20140127';
$myversion = "$myproduct_name-$myversion_id ($myversion_date)";
my($agent_name) = $myproduct_name;
use vars qw($syslog_ident $syslog_facility);
$syslog_ident = $myproduct_name;
$syslog_facility = LOG_MAIL;
my($db_home) = # DB databases directory
defined $ENV{'AMAVISD_DB_HOME'} ? $ENV{'AMAVISD_DB_HOME'} : '/var/lib/amavis/db';
my($mta_queue_dir);
my($top) = '1.3.6.1.4.1.15312.2.1';
my(@databases) = (
{ root_oid_str => "$top.1", name => 'am.snmp', file => 'snmp.db' },
{ root_oid_str => "$top.2", name => 'am.nanny', file => 'nanny.db' },
{ root_oid_str => "$top.3.1.1", name => 'pf.maildrop', file => 'maildrop',
ttl => 18 },
{ root_oid_str => "$top.3.1.2", name => 'pf.incoming', file => 'incoming',
ttl => 18 },
{ root_oid_str => "$top.3.1.3", name => 'pf.active', file => 'active',
ttl => 18 },
{ root_oid_str => "$top.3.1.4", name => 'pf.deferred', file => 'deferred',
ttl => 18 },
);
# 1.3.6.1.4.1.15312 enterprises . Jozef Stefan Institute
# 1.3.6.1.4.1.15312.2 amavis
# 1.3.6.1.4.1.15312.2.1 amavis SNMP
# 1.3.6.1.4.1.15312.2.1.1 amavis Statistics
# 1.3.6.1.4.1.15312.2.1.2 amavis Process status
# 1.3.6.1.4.1.15312.2.1.3 amavis (a view into MTA queue sizes)
# 1.3.6.1.4.1.15312.2.2 amavis LDAP Elements
my($log_level) = 0;
my($daemonize) = 1;
my($pid_filename) = "/var/run/amavis/amavisd-snmp-subagent.pid"; # e.g. "/var/run/amavisd-snmp-subagent.pid";
my($pid_file_created) = 0;
my($syslog_open) = 0;
my($num_proc_gone) = 0;
# geometic progression, rounded,
# common ratio = exp((ln(60)-ln(1))/6) = 1.97860
my(@age_slots) = (
0.1, 0.2, 0.5,
1, 2, 4, 8, 15, 30, # seconds
1*60, 2*60, 4*60, 8*60, 15*60, 30*60, # minutes
1*3600, 2*3600, 4*3600, 8*3600, 15*3600, 30*3600); # hours
package AmavisVariable;
sub new { my($class) = @_; bless [(undef) x 7], $class }
sub oid { my($self)=shift; !@_ ? $self->[0] : ($self->[0]=shift) }
sub oidstr { my($self)=shift; !@_ ? $self->[1] : ($self->[1]=shift) }
sub name { my($self)=shift; !@_ ? $self->[2] : ($self->[2]=shift) }
sub type { my($self)=shift; !@_ ? $self->[3] : ($self->[3]=shift) }
sub suffix { my($self)=shift; !@_ ? $self->[4] : ($self->[4]=shift) }
sub value { my($self)=shift; !@_ ? $self->[5] : ($self->[5]=shift) }
sub next { my($self)=shift; !@_ ? $self->[6] : ($self->[6]=shift) }
package AmavisAgent;
use NetSNMP::OID;
use NetSNMP::ASN qw(:all);
use NetSNMP::agent qw(:all);
use NetSNMP::default_store qw(:all);
my(%oidstr_to_obj);
my(@oid_sorted_list);
my($keep_running) = 1;
my(%variables);
my(%asn_name_to_type) = (
'C32' => ASN_COUNTER,
'C64' => ASN_COUNTER64,
'G32' => ASN_GAUGE,
'INT' => ASN_INTEGER,
'I64' => ASN_INTEGER64,
'U32' => ASN_UNSIGNED,
'U64' => ASN_UNSIGNED64,
'STR' => ASN_OCTET_STR,
'OID' => ASN_OBJECT_ID,
'TIM' => ASN_TIMETICKS,
);
my(%asn_type_to_full_name) = (
ASN_COUNTER, 'Counter32',
ASN_COUNTER64, 'Counter64',
ASN_GAUGE, 'Gauge32',
ASN_INTEGER, 'Integer32',
# 'IpAddress',
ASN_INTEGER64, 'Integer64',
ASN_UNSIGNED, 'Unsigned32',
ASN_UNSIGNED64, 'Unsigned64',
ASN_OCTET_STR, 'DisplayString',
ASN_OBJECT_ID, 'OBJECT IDENTIFIER',
ASN_TIMETICKS, 'TimeTicks',
);
sub do_log($$;@) {
my($level,$errmsg,@args) = @_;
if ($level <= $log_level) {
# treat $errmsg as sprintf format string if additional arguments provided
if (@args) { $errmsg = sprintf($errmsg,@args) }
if (!$syslog_open) {
print STDERR $errmsg."\n"; # ignoring I/O status
} else {
my($prio) = $level <= -2 ? LOG_ERR
: $level <= -1 ? LOG_WARNING
: $level <= 0 ? LOG_NOTICE
: $level <= 1 ? LOG_INFO
: LOG_DEBUG;
syslog(LOG_INFO, "%s", $errmsg);
}
}
}
# Returns the smallest defined number from the list, or undef
sub min(@) {
my($r) = @_ == 1 && ref($_[0]) ? $_[0] : \@_; # accept list, or a list ref
my($m); for (@$r) { $m = $_ if defined $_ && (!defined $m || $_ < $m) }
$m;
}
# Returns the largest defined number from the list, or undef
sub max(@) {
my($r) = @_ == 1 && ref($_[0]) ? $_[0] : \@_; # accept list, or a list ref
my($m); for (@$r) { $m = $_ if defined $_ && (!defined $m || $_ > $m) }
$m;
}
# Return untainted copy of a string (argument can be a string or a string ref)
sub untaint($) {
no re 'taint';
my($str);
if (defined($_[0])) {
local($1); # avoid Perl taint bug: tainted global $1 propagates taintedness
$str = $1 if (ref($_[0]) ? ${$_[0]} : $_[0]) =~ /^(.*)\z/s;
}
$str;
}
sub declare_variable($$;$$$) {
my($oid_str,$name, $typename,$instance_lo,$instance_hi) = @_;
$typename = 'C32' if !defined $typename;
$instance_lo = 0 if !defined $instance_lo;
$instance_hi = $instance_lo if !defined $instance_hi;
$instance_hi = $instance_lo if $instance_hi < $instance_lo;
my($type) = $asn_name_to_type{$typename};
for my $ind ($instance_lo .. $instance_hi) {
my($full_oid_str) = sprintf("%s.%d", $oid_str,$ind);
# do_log(5, "declaring variable %s, %s", $full_oid_str, $name);
my($var) = AmavisVariable->new;
$var->oidstr($full_oid_str);
# $var->oid(NetSNMP::OID->new($full_oid_str)); # later
$var->type($type);
# $var->suffix($suffix);
$var->name("$name.$ind");
if (!exists $variables{"$name.$ind"}) {
$variables{"$name.$ind"} = $var;
} else {
# allow an amavisd variable name to map to multiple SNMP variables
if (ref $variables{"$name.$ind"} ne 'ARRAY') {
$variables{"$name.$ind"} = [ $variables{"$name.$ind"} ];
}
push(@{$variables{"$name.$ind"}}, $var);
}
}
}
sub set_variable_value($$) {
my($name, $value) = @_;
my($instance); local($1,$2);
if ($name =~ /^(.*)\.(\d+)/) { $name = $1; $instance = $2 }
$instance = 0 if !defined $instance;
my($v) = $variables{"$name.$instance"};
if (!defined($v)) {
do_log(5, "No such variable %s.%s", $name,$instance);
} else {
my(@var) = ref $v eq 'ARRAY' ? @$v : $v;
for my $var (@var) {
my($type) = $var->type;
if ($name =~ /^TimeElapsed/) { $value = $value/10 } # ms -> 0.01s ticks
elsif ($type == ASN_COUNTER || $type == ASN_GAUGE ||
$type == ASN_INTEGER || $type == ASN_UNSIGNED ||
$type == ASN_TIMETICKS) { $value = 0+$value }
elsif ($type == ASN_COUNTER64 || $type == ASN_INTEGER64 ||
$type == ASN_UNSIGNED64) { $value = sprintf("%1.0f",$value) }
elsif ($type == ASN_OCTET_STR) { $value = "$value" }
$var->value($value);
}
}
}
sub reset_all_variable_values($) {
my($root_oid_str) = @_;
while (my($key,$v) = each(%variables)) {
my(@var) = ref $v eq 'ARRAY' ? @$v : $v;
for my $var (@var) {
if (!defined($root_oid_str) || $var->oidstr =~ /^\Q$root_oid_str\E\./) {
$var->value(undef);
}
}
}
}
sub dump_variables() {
for my $oid (@oid_sorted_list) {
my(@oidlist) = $oid->to_array;
my($oidstr) = join('.', @oidlist);
my($descr) = "";
my($suffix_sp) = join(' ', @oidlist[9 .. ($#oidlist-1)]);
my($var) = $oidstr_to_obj{$oidstr};
my($mib_type_name) = $asn_type_to_full_name{$var->type};
my($name) = $var->name;
$name =~ s/\.0\z//;
printf STDERR (<<'END', $name, $mib_type_name, $descr, $suffix_sp);
%s OBJECT-TYPE
SYNTAX %s
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"%s"
::= { amavis %s }
END
}
}
sub init_data() {
while (my($name,$v) = each(%variables)) {
my(@var) = ref $v eq 'ARRAY' ? @$v : $v;
for my $var (@var) {
my($oidstr) = $var->oidstr;
$var->oid(NetSNMP::OID->new($oidstr));
$oidstr_to_obj{$oidstr} = $var;
}
}
@oid_sorted_list = sort { snmp_oid_compare($a,$b) }
map { $_->oid }
map { ref $_ eq 'ARRAY' ? @$_ : $_ } values(%variables);
# build a linked list of variable objects in OID sorted order
# to speed up sequential MIB traversal by getnext
my($prev_var);
for my $oid (@oid_sorted_list) {
my($oidstr) = join('.', $oid->to_array);
my($var) = $oidstr_to_obj{$oidstr};
$prev_var->next($var) if defined $prev_var;
$prev_var = $var;
}
}
sub collect_all_db_data($$) {
my($database,$values) = @_;
my($dbfile) = $database->{file};
my(@dbstat) = stat("$db_home/$dbfile");
my($errn) = @dbstat ? 0 : 0+$!;
$errn==0 || $errn==ENOENT or die "stat $db_home/$dbfile: $!";
if (defined $database->{db} && $database->{old_db_inode} != $dbstat[1]) {
$database->{db}->db_close==0
or die "BDB db_close error: $BerkeleyDB::Error $!";
undef $database->{db};
do_log(1, "Reopening snmp database %s/%s", $db_home,$dbfile);
}
if (!defined $database->{db} && $errn==0) {
reset_all_variable_values($database->{root_oid_str});
$database->{old_db_inode} = $dbstat[1];
$database->{env} = BerkeleyDB::Env->new(
-Home => $db_home, -Flags => DB_INIT_CDB | DB_INIT_MPOOL,
-ErrFile => \*STDOUT, -Verbose => 1);
defined $database->{env} or die "BDB no env: $BerkeleyDB::Error $!";
$database->{db} = BerkeleyDB::Hash->new(-Filename => $dbfile,
-Env => $database->{env});
defined $database->{db} or die "BDB no dbS 1: $BerkeleyDB::Error $!";
}
my($eval_stat,$interrupt); $interrupt = '';
if (!defined $database->{db}) {
do_log(1, "No snmp database %s/%s", $db_home,$dbfile);
} else {
my($stat,$key,$val);
my($h1) = sub { $interrupt = $_[0] };
local(@SIG{qw(INT HUP TERM TSTP QUIT ALRM USR1 USR2)}) = ($h1) x 8;
eval {
# be as quick as possible while a database is locked by a cursor
my($cursor) = $database->{db}->db_cursor; # obtain read lock
$database->{cursor} = $cursor;
defined $cursor or die "db_cursor error: $BerkeleyDB::Error";
while ( ($stat=$cursor->c_get($key,$val,DB_NEXT)) == 0 ) {
$values->{$key} = $val;
}
$stat==DB_NOTFOUND or die "c_get: $BerkeleyDB::Error $!";
$cursor->c_close==0 or die "c_close error: $BerkeleyDB::Error";
undef $database->{cursor};
1;
} or do {
$eval_stat = $@ ne '' ? $@ : "errno=$!"; chomp $eval_stat;
};
if (defined $database->{db}) {
# unlock, ignoring status
$database->{cursor}->c_close if defined $database->{cursor};
undef $database->{cursor};
}
}
if ($interrupt ne '') { kill($interrupt,$$) } # resignal
elsif ($eval_stat ne '') { chomp($eval_stat); die "BDB $eval_stat\n" }
}
sub count_files_in_postfix_dir($$); # prototype
sub count_files_in_postfix_dir($$) {
my($dir,$deadline) = @_;
local(*DIR); my($f); my($cnt) = 0; my($aborted) = 0;
if (!opendir(DIR,$dir)) {
do_log(-1, "Can't open directory %s: %s", $dir,$!);
} else {
while (defined($f = readdir(DIR))) {
next if $f eq '.' || $f eq '..';
# Postfix uses one-character subdirs
if (length($f) == 1 && -d "$dir/$f") {
my($n,$abt) = count_files_in_postfix_dir("$dir/$f", $deadline);
$cnt += $n;
if ($abt) { $aborted = 1; last }
} else {
$cnt++;
}
if (defined $deadline && Time::HiRes::time > $deadline) {
$aborted = 1; last;
}
}
closedir(DIR) or die "Error closing directory $dir: $!";
}
($cnt,$aborted);
}
sub update_data($) {
my($database) = @_;
do_log(3, "updating variables from %s", $database->{name});
my($start_time) = Time::HiRes::time;
if ($database->{name} =~ /^pf/) {
# not really a database file, just a 'view' into an MTA spool directory
my($dir) = $database->{file};
my($cnt,$aborted) =
count_files_in_postfix_dir("$mta_queue_dir/$dir", $start_time + 5);
my($var_name) = "MtaQueueEntries\u$dir";
set_variable_value($var_name, $cnt);
do_log(3, "mta queue: %s %d", $var_name,$cnt);
do_log(-1,"exceeded time limit on dir %s, aborted after %.1f s, ".
"count so far: %d",
$var_name, Time::HiRes::time - $start_time, $cnt) if $aborted;
} elsif ($database->{file} eq 'snmp.db') {
my(%values); collect_all_db_data($database,\%values);
while (my($key,$val) = each(%values)) {
next if $key =~ /\.byOS\./s;
next if $key =~ /^virus\.byname\./s;
next if $key =~ /^(?:OpsDecType|OpsDecBy|OpsSql)/s;
next if $key =~ /^entropy/s;
next if $key =~ /^ContentBadHdr/ && $key !~ /^ContentBadHdrMsgs/;
local($1);
if ($val =~ /^(?:C32|C64) (.*)\z/) {
set_variable_value($key, 0+$1);
} elsif ($val =~ /^STR (.*)\z/) {
set_variable_value($key, "$1");
} elsif ($key eq 'sysUpTime' && $val =~ /^INT (.*)\z/) {
my($uptime) = $start_time - $1; my($ticks) = int($uptime*100);
set_variable_value($key, $ticks);
} elsif ($val =~ /^(?:G32|INT|I64|U32|U64|TIM) (.*)\z/) {
set_variable_value($key, 0+$1);
} elsif ($val =~ /^OID (.*)\z/) {
set_variable_value($key, $1);
} else {
# set_variable_value($key, $val);
}
}
} elsif ($database->{file} eq 'nanny.db') {
my(%values); collect_all_db_data($database,\%values);
my(%proc_timestamp, %proc_state, %proc_task_id);
while (my($key,$val) = each(%values)) {
local($1,$2);
if ($val !~ /^(\d+(?:\.\d*)?) (.*?) *\z/s) {
do_log(0, "Bad %s db entry: %s, %s", $database->{file},$key,$val);
} else {
$proc_timestamp{$key} = $1; my($task_id) = $2;
$proc_state{$key} = $1 if $task_id =~ s/^([^0-9])//;
$proc_task_id{$key} = $task_id;
}
}
my(@to_be_removed); my($num_proc_idle) = 0; my($num_proc_busy) = 0;
my(@num_proc_busy_by_age); my(%num_proc_busy_by_activity);
for my $pid (keys(%proc_timestamp)) {
my($idling) = $proc_task_id{$pid} eq '' &&
$proc_state{$pid} =~ /^[. ]?\z/s;
my($age) = $start_time - $proc_timestamp{$pid};
my($n) = kill(0,$pid); # test if the process is still there
if ($n == 0 && $! != ESRCH) {
do_log(-1, "Can't check the process %s: %s", $pid,$!);
} elsif ($n == 0) { # ESRCH means there is no such process
push(@to_be_removed, $pid); # process went away!
} elsif ($idling) {
$num_proc_idle++;
} else { # busy for $age seconds
$num_proc_busy++;
$num_proc_busy_by_age[0]++;
my($j) = 1;
for my $t (@age_slots) {
if ($age >= $t) { $num_proc_busy_by_age[$j]++ }
$j++;
}
my($s) = $proc_state{$pid};
if ($s eq 'm' || $s eq 'd' || $s eq 'F') { $s = 'm' }
elsif ($s eq 'D' || $s eq 'V' || $s eq 'S') { }
else { $s = ' ' }
$num_proc_busy_by_activity{$s}++;
}
}
$num_proc_gone += scalar(@to_be_removed);
# all are gauges, except ProcGone
set_variable_value('ProcAll', $num_proc_idle+$num_proc_busy);
set_variable_value('ProcIdle', $num_proc_idle);
set_variable_value('ProcBusy', $num_proc_busy);
set_variable_value('ProcGone', $num_proc_gone); # counter!
for my $j (0..@age_slots) { # age_slots start at 1, zero is in addition
set_variable_value("ProcBusy$j", $num_proc_busy_by_age[$j] || 0);
}
set_variable_value("ProcBusyTransfer",$num_proc_busy_by_activity{'m'}||0);
set_variable_value("ProcBusyDecode", $num_proc_busy_by_activity{'D'}||0);
set_variable_value("ProcBusyVirus", $num_proc_busy_by_activity{'V'}||0);
set_variable_value("ProcBusySpam", $num_proc_busy_by_activity{'S'}||0);
set_variable_value("ProcBusyOther", $num_proc_busy_by_activity{' '}||0);
if (@to_be_removed) { # some processes no longer exist, update db
my($eval_stat,$interrupt); $interrupt = '';
my($h1) = sub { $interrupt = $_[0] };
local(@SIG{qw(INT HUP TERM TSTP QUIT ALRM USR1 USR2)}) = ($h1) x 8;
eval {
# obtain a write lock
my($cursor) = $database->{db}->db_cursor(DB_WRITECURSOR);
$database->{cursor} = $cursor;
defined $cursor or die "BDB db_cursor error: $BerkeleyDB::Error";
for my $key (@to_be_removed) {
my($val); my($stat) = $cursor->c_get($key,$val,DB_SET);
$stat==0 || $stat==DB_NOTFOUND
or die "BDB c_get: $BerkeleyDB::Error, $!.";
if ($stat==0) { # remove existing entry
$cursor->c_del==0 or die "c_del: $BerkeleyDB::Error, $!.";
}
}
$cursor->c_close==0 or die "c_close error: $BerkeleyDB::Error";
undef $database->{cursor};
1;
} or do {
$eval_stat = $@ ne '' ? $@ : "errno=$!"; chomp $eval_stat;
};
if (defined $database->{db}) {
# unlock, ignoring status
$database->{cursor}->c_close if defined $database->{cursor};
undef $database->{cursor};
}
}
}
my($now) = Time::HiRes::time;
my($elapsed) = $now - $start_time;
$elapsed = 0 if $elapsed < 0; # clock jump?
my($ll) = $elapsed >= 30 ? -1 : $elapsed >= 5 ? 0 : $elapsed >= 1 ? 2 : 3;
do_log($ll, "updating %s took %.3f s", $database->{name}, $elapsed);
my($ttl_lower_bound) = 8*$elapsed; # don't be a hog!
my($since_query) = $database->{last_query_timestamp};
$since_query = $now - $since_query if defined $since_query;
if (defined $since_query && $elapsed > 4) {
# there is a chance that a SNMP client timed out on this query;
# stretch the next update period to allow one quick next response
# from cached data, assuming queries are at about regular intervals
$ttl_lower_bound = max($ttl_lower_bound, 1.5 * $since_query);
}
$ttl_lower_bound = min($ttl_lower_bound, 20*60); # cap at 20 minutes
my($ttl) = $database->{ttl};
$ttl = 4 if !defined $ttl || $ttl <= 0;
if ($ttl < $ttl_lower_bound) {
$ttl = $ttl_lower_bound;
do_log(3, "postponing refresh on %s for another %.1f s%s",
$database->{name}, $ttl,
!defined $since_query ? ''
: sprintf(", %.1f s since query",$since_query) );
}
$database->{last_refreshed} = $now;
$database->{update_due_at} = $now + $ttl;
}
sub find_next_gt($$) {
my($x, $a_ref) = @_;
my($l, $u) = (0, $#$a_ref);
my $j;
while ($l <= $u) {
$j = $l + int(($u - $l)/2); # good practices: avoids integer overflow
if ($a_ref->[$j] > $x) { $u = $j-1 } else { $l = $j+1 }
}
$l > $#$a_ref ? -1 : $l;
}
my($fast_poll) = 0;
my($last_query_timestamp) = 0;
sub snmp_handler($$$$) {
my($handler, $registration_info, $request_info, $requests) = @_;
my($now) = Time::HiRes::time;
my($dt) = $now - $last_query_timestamp;
if ($dt < 1.5) { $fast_poll = 1 } elsif ($dt > 4) { $fast_poll = 0 }
$last_query_timestamp = $now;
my($mode) = $request_info->getMode;
for (my $req=$requests; $req; $req=$req->next) {
my($oid_in_request) = $req->getOID; # OID from a request
my($actual_oid); my($err); my($eom) = 0;
if ($mode == MODE_GET) {
$actual_oid = $oid_in_request;
do_log(5, "Get %s", $oid_in_request);
} elsif ($mode == MODE_GETBULK) {
# never happens, not registered for getbulk
do_log(2, "GetBulk %s", $oid_in_request);
} elsif ($mode == MODE_GETNEXT) {
if (!@oid_sorted_list) {
$eom = 1; # end of MIB
} elsif ($oid_in_request < $oid_sorted_list[0]) {
$actual_oid = $oid_sorted_list[0];
$req->setOID($actual_oid);
do_log(4, "First: %s -> %s", $oid_in_request,$actual_oid);
} elsif ($oid_in_request > $oid_sorted_list[-1]) {
$eom = 1; # end of MIB
do_log(4, "Last: %s", $oid_in_request);
} else {
# check first for a sequential traversal, likely faster
my($var) = $oidstr_to_obj{join('.', $oid_in_request->to_array)};
if ($var) {
my($next_var) = $var->next;
if (!$next_var) {
$eom = 1; # end of MIB
} else {
$actual_oid = $next_var->oid;
$req->setOID($actual_oid);
}
}
if (!$err && !defined $actual_oid) { # fall back to a binary search
do_log(5, "Using a binary search for %s", $oid_in_request);
my($ind) = find_next_gt($oid_in_request, \@oid_sorted_list);
if ($ind < 0) {
$eom = 1; # end of MIB
} else {
$actual_oid = $oid_sorted_list[$ind];
$req->setOID($actual_oid);
}
}
}
do_log(5, "GetNext %s -> %s", $oid_in_request,
!defined $actual_oid ? 'undef' : $actual_oid);
} else {
do_log(0, "Unknown request %s", $oid_in_request);
$req->setError($request_info, SNMP_ERR_NOTWRITABLE); $err = 1;
}
if ($err) {
# already dealt with
} elsif ($eom || !defined $actual_oid) { # end of MIB
# just silently not provide a value
do_log(5, "No more MIB beyond %s", $oid_in_request);
} else {
my($oid_str) = join('.', $actual_oid->to_array);
my($var) = $oidstr_to_obj{$oid_str};
if (!$var) {
$req->setError($request_info, SNMP_ERR_NOSUCHNAME);
} else {
# find out under which OID root the query falls
for my $database (@databases) {
next if !$database->{registered};
my($root_oid_str) = $database->{root_oid_str};
if ($oid_str =~ /^\Q$root_oid_str\E\./) {
$database->{last_query_timestamp} = $now;
if (!defined($database->{update_due_at}) ||
Time::HiRes::time >=
$database->{update_due_at} + ($fast_poll ? 4 : 0) ) {
# fast polling stretches time-to-update a bit, increasing
# chances of collecting consistent data from the same moment
update_data($database); # stale MIB, needs updating
}
}
}
my($type, $value, $name) = ($var->type, $var->value, $var->name);
if (!defined $type) {
$req->setError($request_info, SNMP_ERR_BADVALUE);
} else {
if (!defined $value) {
if ($type == ASN_OCTET_STR) { $value = "" }
elsif ($type == ASN_OBJECT_ID) { $value = "0" }
elsif ($type == ASN_COUNTER64 || $type == ASN_INTEGER64 ||
$type == ASN_UNSIGNED64) { $value = "0" }
else { $value = 0 }
}
my($status) = $req->setValue($type,$value);
if (!$status) {
do_log(0, "setValue error: %s, %s, %s", $type,$name,$value);
$req->setError($request_info, SNMP_ERR_BADVALUE);
}
}
}
}
}
1;
}
sub daemonize() {
my($pid);
closelog(); $syslog_open = 0;
STDOUT->autoflush(1);
STDERR->autoflush(1);
# the first fork allows the shell to return and allows doing a setsid
eval { $pid = fork(); 1 }
or do {
my($eval_stat) = $@ ne '' ? $@ : "errno=$!"; chomp $eval_stat;
die "Error forking #1: $eval_stat";
};
defined $pid or die "Can't fork #1: $!";
if ($pid) { # parent process terminates here
POSIX::_exit(0); # avoid END and destructor processing
}
# disassociate from a controlling terminal
my($pgid) = POSIX::setsid();
defined $pgid && $pgid >= 0 or die "Can't start a new session: $!";
# We are now a session leader. As a session leader, opening a file
# descriptor that is a terminal will make it our controlling terminal.
# The second fork makes us NOT a session leader. Only session leaders
# can acquire a controlling terminal, so we may now open up any file
# we wish without worrying that it will become a controlling terminal.
# second fork prevents from accidentally reacquiring a controlling terminal
eval { $pid = fork(); 1 }
or do {
my($eval_stat) = $@ ne '' ? $@ : "errno=$!"; chomp $eval_stat;
die "Error forking #2: $eval_stat";
};
defined $pid or die "Can't fork #2: $!";
if ($pid) { # parent process terminates here
POSIX::_exit(0); # avoid END and destructor processing
}
# a daemonized child process, live long and prosper...
do_log(2, "Daemonized as process [%s]", $$);
chdir('/') or die "Can't chdir to '/': $!";
openlog($syslog_ident, LOG_PID | LOG_NDELAY, $syslog_facility);
$syslog_open = 1;
close(STDIN) or die "Can't close STDIN: $!";
close(STDOUT) or die "Can't close STDOUT: $!";
open(STDIN, '</dev/null') or die "Can't open /dev/null: $!";
open(STDOUT, '>/dev/null') or die "Can't open /dev/null: $!";
close(STDERR) or die "Can't close STDERR: $!";
open(STDERR, '>&STDOUT') or die "Can't dup STDOUT: $!";
}
sub usage() {
return <<"EOD";
Usage:
$0 [options]
Options:
-V show version, then exit
-h show help, then exit
-f stay in foreground
-d log_level debugging level, 0..5, default 0
-P pid_file a file name to receive a PID of a damonized process
-D db_home_dir amavis database directory ($db_home),
default AMAVISD_DB_HOME or /var/lib/amavis/db
EOD
}
# main program starts here
delete @ENV{'PATH', 'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
$SIG{INT} = sub { die "interrupted\n" }; # do the END code block
$SIG{TERM} = sub { die "terminated\n" }; # do the END code block
$SIG{PIPE} = 'IGNORE'; # don't signal on a write to a widowed pipe
while (@ARGV >= 2 && $ARGV[0] =~ /^-[dDP]\z/ ||
@ARGV >= 1 && $ARGV[0] =~ /^-[hVf-]\z/) {
my($opt,$val);
$opt = shift @ARGV;
$val = shift @ARGV if $opt !~ /^-[hVf-]\z/; # these take no arguments
if ($opt eq '--') {
last;
} elsif ($opt eq '-h') { # -h (help)
die "$myversion\n\n" . usage();
} elsif ($opt eq '-V') { # -V (version)
die "$myversion\n";
} elsif ($opt eq '-f') { # -f (foreground)
$daemonize = 0;
} elsif ($opt eq '-d') { # -d log_level
$log_level = 0+$val;
} elsif ($opt eq '-D') { # -D db_home_dir, empty string turns off db use
$db_home = untaint($val) if $val ne '';
} elsif ($opt eq '-P') { # -P pid_file
$pid_filename = untaint($val) if $val ne '';
} else {
die "Error in parsing command line options: $opt\n\n" . usage();
}
}
!@ARGV or die "Unprocessed command line options: $ARGV[0]\n\n" . usage();
if (!defined $mta_queue_dir) { # test for access to Postfix queue directory
local($ENV{PATH}) = '/usr/sbin:/usr/local/sbin:/opt/postfix/sbin';
$! = 0;
$mta_queue_dir = qx(postconf -h queue_directory 2>/dev/null);
if (!defined $mta_queue_dir) {
if ($! != 0) {
do_log(1, "no postfix (unable to run postconf command): $!");
} else {
do_log(1, "failed to execute \"postconf queue_directory\": $?");
}
} else {
chomp $mta_queue_dir;
if ($mta_queue_dir =~ /^\s*\z/) {
do_log(1, "unknown Postfix queue directory");
undef $mta_queue_dir;
} else {
do_log(2, "got a Postfix queue directory: %s", $mta_queue_dir);
my($dir) = "$mta_queue_dir/active";
local(*DIR);
if (!opendir(DIR,$dir)) { # testing access
do_log(1, "can't open directory %s: %s", $dir,$!);
undef $mta_queue_dir;
} else {
closedir(DIR) or die "Error closing directory $dir: $!";
}
}
}
}
{ # amavisd statistics MIB
my($r) = $databases[0]->{root_oid_str};
declare_variable("$r.1.1", 'sysDescr', 'STR');
declare_variable("$r.1.2", 'sysObjectID', 'OID');
declare_variable("$r.1.3", 'sysUpTime', 'TIM');
declare_variable("$r.1.4", 'sysContact', 'STR');
declare_variable("$r.1.5", 'sysName', 'STR');
declare_variable("$r.1.6", 'sysLocation', 'STR');
declare_variable("$r.1.7", 'sysServices', 'INT');
declare_variable("$r.2.1", 'InMsgs'); # orig=x locl=x
declare_variable("$r.2.2", 'InMsgsInbound'); # orig=0 locl=1
declare_variable("$r.2.3", 'InMsgsOutbound'); # orig=1 locl=0
declare_variable("$r.2.4", 'InMsgsInternal'); # orig=1 locl=1
declare_variable("$r.2.5", 'InMsgsOriginating'); # orig=1 locl=x
declare_variable("$r.2.6", 'InMsgsOpenRelay'); # orig=0 locl=0
# these have duplicates at $r.{19..26}.1, except InMsgsStatusRelayed
declare_variable("$r.2.7", 'InMsgsStatusAccepted'); # 2xx, AM.PDP
declare_variable("$r.2.8", 'InMsgsStatusRelayed'); # 2xx, forward
declare_variable("$r.2.9", 'InMsgsStatusDiscarded'); # 2xx, no DSN
declare_variable("$r.2.10", 'InMsgsStatusNoBounce'); # 2xx, no DSN
declare_variable("$r.2.11", 'InMsgsStatusBounced'); # 2xx, DSN sent
declare_variable("$r.2.12", 'InMsgsStatusRejected'); # 5xx
declare_variable("$r.2.13", 'InMsgsStatusTempFailed'); # 4xx
declare_variable("$r.3.1", 'InMsgsSize', 'C64');
declare_variable("$r.3.2", 'InMsgsSizeInbound', 'C64');
declare_variable("$r.3.3", 'InMsgsSizeOutbound', 'C64');
declare_variable("$r.3.4", 'InMsgsSizeInternal', 'C64');
declare_variable("$r.3.5", 'InMsgsSizeOriginating', 'C64');
declare_variable("$r.3.6", 'InMsgsSizeOpenRelay', 'C64');
declare_variable("$r.4.1", 'InMsgsRecips'); # orig=x locl=x
declare_variable("$r.4.2", 'InMsgsRecipsInbound'); # orig=0 locl=1
declare_variable("$r.4.3", 'InMsgsRecipsOutbound'); # orig=1 locl=0
declare_variable("$r.4.4", 'InMsgsRecipsInternal'); # orig=1 locl=1
declare_variable("$r.4.5", 'InMsgsRecipsOriginating'); # orig=1 locl=x
declare_variable("$r.4.6", 'InMsgsRecipsOpenRelay'); # orig=0 locl=0
declare_variable("$r.4.7", 'InMsgsRecipsLocal'); # orig=x locl=1
declare_variable("$r.5.1", 'InMsgsBounce');
declare_variable("$r.5.2", 'InMsgsBounceNullRPath');
declare_variable("$r.5.3", 'InMsgsBounceKilled');
declare_variable("$r.5.4", 'InMsgsBounceUnverifiable');
declare_variable("$r.5.5", 'InMsgsBounceRescuedByDomain');
declare_variable("$r.5.6", 'InMsgsBounceRescuedByOriginating');
declare_variable("$r.5.7", 'InMsgsBounceRescuedByPenPals');
declare_variable("$r.6.1", 'OutMsgs');
declare_variable("$r.6.2", 'OutMsgsRelay');
declare_variable("$r.6.3", 'OutMsgsSubmit');
declare_variable("$r.6.4", 'OutMsgsSubmitQuar');
declare_variable("$r.6.5", 'OutMsgsSubmitDsn');
declare_variable("$r.6.6", 'OutMsgsSubmitNotif');
declare_variable("$r.6.7", 'OutMsgsSubmitAV');
declare_variable("$r.6.8", 'OutMsgsSubmitArf');
declare_variable("$r.6.9", 'OutMsgsProtoLocal');
declare_variable("$r.6.10", 'OutMsgsProtoLocalRelay');
declare_variable("$r.6.11", 'OutMsgsProtoLocalSubmit');
declare_variable("$r.6.12", 'OutMsgsProtoSMTP');
declare_variable("$r.6.13", 'OutMsgsProtoSMTPRelay');
declare_variable("$r.6.14", 'OutMsgsProtoSMTPSubmit');
declare_variable("$r.6.15", 'OutMsgsProtoLMTP');
declare_variable("$r.6.16", 'OutMsgsProtoLMTPRelay');
declare_variable("$r.6.17", 'OutMsgsProtoLMTPSubmit');
declare_variable("$r.6.18", 'OutMsgsProtoBSMTP');
declare_variable("$r.6.19", 'OutMsgsProtoBSMTPRelay');
declare_variable("$r.6.20", 'OutMsgsProtoBSMTPSubmit');
declare_variable("$r.6.21", 'OutMsgsProtoPipe');
declare_variable("$r.6.22", 'OutMsgsProtoPipeRelay');
declare_variable("$r.6.23", 'OutMsgsProtoPipeSubmit');
declare_variable("$r.6.24", 'OutMsgsProtoSQL');
declare_variable("$r.6.25", 'OutMsgsProtoSQLRelay');
declare_variable("$r.6.26", 'OutMsgsProtoSQLSubmit');
declare_variable("$r.6.27", 'OutMsgsDelivers'); # 2xx
declare_variable("$r.6.28", 'OutMsgsAttemptFails'); # 4xx
declare_variable("$r.6.29", 'OutMsgsRejects'); # 5xx
declare_variable("$r.7.1", 'OutMsgsSize', 'C64');
declare_variable("$r.7.2", 'OutMsgsSizeRelay', 'C64');
declare_variable("$r.7.3", 'OutMsgsSizeSubmit', 'C64');
declare_variable("$r.7.4", 'OutMsgsSizeSubmitQuar', 'C64');
declare_variable("$r.7.5", 'OutMsgsSizeSubmitDsn', 'C64');
declare_variable("$r.7.6", 'OutMsgsSizeSubmitNotif', 'C64');
declare_variable("$r.7.7", 'OutMsgsSizeSubmitAV', 'C64');
declare_variable("$r.7.8", 'OutMsgsSizeSubmitArf', 'C64');
declare_variable("$r.7.9", 'OutMsgsSizeProtoLocal', 'C64');
declare_variable("$r.7.10", 'OutMsgsSizeProtoLocalRelay', 'C64');
declare_variable("$r.7.11", 'OutMsgsSizeProtoLocalSubmit','C64');
declare_variable("$r.7.12", 'OutMsgsSizeProtoSMTP', 'C64');
declare_variable("$r.7.13", 'OutMsgsSizeProtoSMTPRelay', 'C64');
declare_variable("$r.7.14", 'OutMsgsSizeProtoSMTPSubmit', 'C64');
declare_variable("$r.7.15", 'OutMsgsSizeProtoLMTP', 'C64');
declare_variable("$r.7.16", 'OutMsgsSizeProtoLMTPRelay', 'C64');
declare_variable("$r.7.17", 'OutMsgsSizeProtoLMTPSubmit', 'C64');
declare_variable("$r.7.18", 'OutMsgsSizeProtoBSMTP', 'C64');
declare_variable("$r.7.19", 'OutMsgsSizeProtoBSMTPRelay', 'C64');
declare_variable("$r.7.20", 'OutMsgsSizeProtoBSMTPSubmit','C64');
declare_variable("$r.7.21", 'OutMsgsSizeProtoPipe', 'C64');
declare_variable("$r.7.22", 'OutMsgsSizeProtoPipeRelay', 'C64');
declare_variable("$r.7.23", 'OutMsgsSizeProtoPipeSubmit', 'C64');
declare_variable("$r.7.24", 'OutMsgsSizeProtoSQL', 'C64');
declare_variable("$r.7.25", 'OutMsgsSizeProtoSQLRelay', 'C64');
declare_variable("$r.7.26", 'OutMsgsSizeProtoSQLSubmit', 'C64');
declare_variable("$r.8.1", 'QuarMsgs');
declare_variable("$r.8.2", 'QuarMsgsArch');
declare_variable("$r.8.3", 'QuarMsgsClean');
declare_variable("$r.8.4", 'QuarMsgsMtaFailed');
declare_variable("$r.8.5", 'QuarMsgsOversized');
declare_variable("$r.8.6", 'QuarMsgsBadHdr');
declare_variable("$r.8.7", 'QuarMsgsSpammy');
declare_variable("$r.8.8", 'QuarMsgsSpam');
declare_variable("$r.8.9", 'QuarMsgsUnchecked');
declare_variable("$r.8.10", 'QuarMsgsBanned');
declare_variable("$r.8.11", 'QuarMsgsVirus');
declare_variable("$r.8.12", 'QuarAttemptTempFails');
declare_variable("$r.8.13", 'QuarAttemptFails');
declare_variable("$r.9.1", 'QuarMsgsSize', 'C64');
declare_variable("$r.9.2", 'QuarMsgsSizeArch', 'C64');
declare_variable("$r.9.3", 'QuarMsgsSizeClean', 'C64');
declare_variable("$r.9.4", 'QuarMsgsSizeMtaFailed', 'C64');
declare_variable("$r.9.5", 'QuarMsgsSizeOversized', 'C64');
declare_variable("$r.9.6", 'QuarMsgsSizeBadHdr', 'C64');
declare_variable("$r.9.7", 'QuarMsgsSizeSpammy', 'C64');
declare_variable("$r.9.8", 'QuarMsgsSizeSpam', 'C64');
declare_variable("$r.9.9", 'QuarMsgsSizeUnchecked', 'C64');
declare_variable("$r.9.10", 'QuarMsgsSizeBanned', 'C64');
declare_variable("$r.9.11", 'QuarMsgsSizeVirus', 'C64');
declare_variable("$r.10.1.1", 'ContentCleanMsgs');
declare_variable("$r.10.1.2", 'ContentCleanMsgsInbound');
declare_variable("$r.10.1.3", 'ContentCleanMsgsOutbound');
declare_variable("$r.10.1.4", 'ContentCleanMsgsInternal');
declare_variable("$r.10.1.5", 'ContentCleanMsgsOriginating');
declare_variable("$r.10.1.6", 'ContentCleanMsgsOpenRelay');
declare_variable("$r.10.2.1", 'ContentMtaFailedMsgs');
declare_variable("$r.10.2.2", 'ContentMtaFailedMsgsInbound');
declare_variable("$r.10.2.3", 'ContentMtaFailedMsgsOutbound');
declare_variable("$r.10.2.4", 'ContentMtaFailedMsgsInternal');
declare_variable("$r.10.2.5", 'ContentMtaFailedMsgsOriginating');
declare_variable("$r.10.2.6", 'ContentMtaFailedMsgsOpenRelay');
declare_variable("$r.10.3.1", 'ContentOversizedMsgs');
declare_variable("$r.10.3.2", 'ContentOversizedMsgsInbound');
declare_variable("$r.10.3.3", 'ContentOversizedMsgsOutbound');
declare_variable("$r.10.3.4", 'ContentOversizedMsgsInternal');
declare_variable("$r.10.3.5", 'ContentOversizedMsgsOriginating');
declare_variable("$r.10.3.6", 'ContentOversizedMsgsOpenRelay');
declare_variable("$r.10.4.1", 'ContentBadHdrMsgs');
declare_variable("$r.10.4.2", 'ContentBadHdrMsgsInbound');
declare_variable("$r.10.4.3", 'ContentBadHdrMsgsOutbound');
declare_variable("$r.10.4.4", 'ContentBadHdrMsgsInternal');
declare_variable("$r.10.4.5", 'ContentBadHdrMsgsOriginating');
declare_variable("$r.10.4.6", 'ContentBadHdrMsgsOpenRelay');
declare_variable("$r.10.5.1", 'ContentSpammyMsgs');
declare_variable("$r.10.5.2", 'ContentSpammyMsgsInbound');
declare_variable("$r.10.5.3", 'ContentSpammyMsgsOutbound');
declare_variable("$r.10.5.4", 'ContentSpammyMsgsInternal');
declare_variable("$r.10.5.5", 'ContentSpammyMsgsOriginating');
declare_variable("$r.10.5.6", 'ContentSpammyMsgsOpenRelay');
declare_variable("$r.10.6.1", 'ContentSpamMsgs');
declare_variable("$r.10.6.2", 'ContentSpamMsgsInbound');
declare_variable("$r.10.6.3", 'ContentSpamMsgsOutbound');
declare_variable("$r.10.6.4", 'ContentSpamMsgsInternal');
declare_variable("$r.10.6.5", 'ContentSpamMsgsOriginating');
declare_variable("$r.10.6.6", 'ContentSpamMsgsOpenRelay');
declare_variable("$r.10.7.1", 'ContentUncheckedMsgs');
declare_variable("$r.10.7.2", 'ContentUncheckedMsgsInbound');
declare_variable("$r.10.7.3", 'ContentUncheckedMsgsOutbound');
declare_variable("$r.10.7.4", 'ContentUncheckedMsgsInternal');
declare_variable("$r.10.7.5", 'ContentUncheckedMsgsOriginating');
declare_variable("$r.10.7.6", 'ContentUncheckedMsgsOpenRelay');
declare_variable("$r.10.8.1", 'ContentBannedMsgs');
declare_variable("$r.10.8.2", 'ContentBannedMsgsInbound');
declare_variable("$r.10.8.3", 'ContentBannedMsgsOutbound');
declare_variable("$r.10.8.4", 'ContentBannedMsgsInternal');
declare_variable("$r.10.8.5", 'ContentBannedMsgsOriginating');
declare_variable("$r.10.8.6", 'ContentBannedMsgsOpenRelay');
declare_variable("$r.10.9.1", 'ContentVirusMsgs');
declare_variable("$r.10.9.2", 'ContentVirusMsgsInbound');
declare_variable("$r.10.9.3", 'ContentVirusMsgsOutbound');
declare_variable("$r.10.9.4", 'ContentVirusMsgsInternal');
declare_variable("$r.10.9.5", 'ContentVirusMsgsOriginating');
declare_variable("$r.10.9.6", 'ContentVirusMsgsOpenRelay');
declare_variable("$r.11.1", 'CacheAttempts');
declare_variable("$r.11.2", 'CacheMisses');
declare_variable("$r.11.3", 'CacheHits');
declare_variable("$r.11.4", 'CacheHitsVirusCheck');
declare_variable("$r.11.5", 'CacheHitsVirusMsgs');
declare_variable("$r.11.6", 'OutConnNew');
declare_variable("$r.11.7", 'OutConnQuit');
declare_variable("$r.11.8", 'OutConnTransact');
declare_variable("$r.11.9", 'OutConnReuseFail');
declare_variable("$r.11.10", 'OutConnReuseRecent');
declare_variable("$r.11.11", 'OutConnReuseRefreshed');
declare_variable("$r.12.1", 'OpsDec');
declare_variable("$r.12.2", 'OpsSpamCheck');
declare_variable("$r.12.3", 'OpsVirusCheck');
declare_variable("$r.13.1", 'PenPalsAttempts');
declare_variable("$r.13.2", 'PenPalsAttemptsRid');
declare_variable("$r.13.3", 'PenPalsAttemptsMid');
declare_variable("$r.13.4", 'PenPalsMisses');
declare_variable("$r.13.5", 'PenPalsHits');
declare_variable("$r.13.6", 'PenPalsHitsRid');
declare_variable("$r.13.7", 'PenPalsHitsMid');
declare_variable("$r.13.8", 'PenPalsHitsMidRid');
declare_variable("$r.13.9", 'PenPalsSavedFromTag2');
declare_variable("$r.13.10", 'PenPalsSavedFromTag3');
declare_variable("$r.13.11", 'PenPalsSavedFromKill');
declare_variable("$r.14.1", 'SqlAddrSenderAttempts');
declare_variable("$r.14.2", 'SqlAddrSenderMisses');
declare_variable("$r.14.3", 'SqlAddrSenderHits');
declare_variable("$r.14.4", 'SqlAddrRecipAttempts');
declare_variable("$r.14.5", 'SqlAddrRecipMisses');
declare_variable("$r.14.6", 'SqlAddrRecipHits');
declare_variable("$r.15.1", 'LogEntries', 'C64');
declare_variable("$r.15.2", 'LogEntriesEmerg', 'C64');
declare_variable("$r.15.3", 'LogEntriesAlert', 'C64');
declare_variable("$r.15.4", 'LogEntriesCrit', 'C64'); # lvl le -3
declare_variable("$r.15.5", 'LogEntriesErr', 'C64'); # lvl le -2
declare_variable("$r.15.6", 'LogEntriesWarning', 'C64'); # lvl le -1
declare_variable("$r.15.7", 'LogEntriesNotice', 'C64'); # lvl le 0
declare_variable("$r.15.8", 'LogEntriesInfo', 'C64'); # lvl le 1
declare_variable("$r.15.9", 'LogEntriesDebug', 'C64'); # lvl le 2
declare_variable("$r.15.10", 'LogEntriesLevel0', 'C64'); # le 0
declare_variable("$r.15.11", 'LogEntriesLevel1', 'C64'); # eq 1
declare_variable("$r.15.12", 'LogEntriesLevel2', 'C64'); # eq 2
declare_variable("$r.15.13", 'LogEntriesLevel3', 'C64'); # eq 3
declare_variable("$r.15.14", 'LogEntriesLevel4', 'C64'); # eq 4
declare_variable("$r.15.15", 'LogEntriesLevel5', 'C64'); # ge 5
declare_variable("$r.15.16", 'LogLines', 'C64');
declare_variable("$r.15.17", 'LogRetries', 'C64');
declare_variable("$r.16.1", 'TimeElapsedTotal', 'INT');
declare_variable("$r.16.2", 'TimeElapsedReceiving', 'INT');
declare_variable("$r.16.3", 'TimeElapsedSending', 'INT');
declare_variable("$r.16.4", 'TimeElapsedDecoding', 'INT');
declare_variable("$r.16.5", 'TimeElapsedPenPals', 'INT');
declare_variable("$r.16.6", 'TimeElapsedVirusCheck','INT');
declare_variable("$r.16.7", 'TimeElapsedSpamCheck', 'INT');
declare_variable("$r.17.1", 'UserCounter1', 'C64');
declare_variable("$r.17.2", 'UserCounter2', 'C64');
declare_variable("$r.17.3", 'UserCounter3', 'C64');
declare_variable("$r.17.4", 'UserCounter4', 'C64');
declare_variable("$r.17.5", 'UserCounter5', 'C64');
declare_variable("$r.17.6", 'UserCounter6', 'C64');
declare_variable("$r.17.7", 'UserCounter7', 'C64');
declare_variable("$r.17.8", 'UserCounter8', 'C64');
declare_variable("$r.17.9", 'UserCounter9', 'C64');
declare_variable("$r.17.10", 'UserCounter10', 'C64');
declare_variable("$r.18.1", 'UserGauge1', 'G32');
declare_variable("$r.18.2", 'UserGauge2', 'G32');
declare_variable("$r.18.3", 'UserGauge3', 'G32');
declare_variable("$r.18.4", 'UserGauge4', 'G32');
declare_variable("$r.18.5", 'UserGauge5', 'G32');
declare_variable("$r.18.6", 'UserGauge6', 'G32');
declare_variable("$r.18.7", 'UserGauge7', 'G32');
declare_variable("$r.18.8", 'UserGauge8', 'G32');
declare_variable("$r.18.9", 'UserGauge9', 'G32');
declare_variable("$r.18.10", 'UserGauge10', 'G32');
declare_variable("$r.19.1", 'InMsgsStatusAccepted'); # 2xx, AM.PDP
declare_variable("$r.19.2", 'InMsgsStatusAcceptedInbound');
declare_variable("$r.19.3", 'InMsgsStatusAcceptedOutbound');
declare_variable("$r.19.4", 'InMsgsStatusAcceptedInternal');
declare_variable("$r.19.5", 'InMsgsStatusAcceptedOriginating');
declare_variable("$r.19.6", 'InMsgsStatusAcceptedOpenRelay');
declare_variable("$r.20.1", 'InMsgsStatusRelayedUntagged'); # 2xx, fwd
declare_variable("$r.20.2", 'InMsgsStatusRelayedUntaggedInbound');
declare_variable("$r.20.3", 'InMsgsStatusRelayedUntaggedOutbound');
declare_variable("$r.20.4", 'InMsgsStatusRelayedUntaggedInternal');
declare_variable("$r.20.5", 'InMsgsStatusRelayedUntaggedOriginating');
declare_variable("$r.20.6", 'InMsgsStatusRelayedUntaggedOpenRelay');
declare_variable("$r.21.1", 'InMsgsStatusRelayedTagged'); # 2xx, forward
declare_variable("$r.21.2", 'InMsgsStatusRelayedTaggedInbound');
declare_variable("$r.21.3", 'InMsgsStatusRelayedTaggedOutbound');
declare_variable("$r.21.4", 'InMsgsStatusRelayedTaggedInternal');
declare_variable("$r.21.5", 'InMsgsStatusRelayedTaggedOriginating');
declare_variable("$r.21.6", 'InMsgsStatusRelayedTaggedOpenRelay');
declare_variable("$r.22.1", 'InMsgsStatusDiscarded'); # 2xx, no DSN
declare_variable("$r.22.2", 'InMsgsStatusDiscardedInbound');
declare_variable("$r.22.3", 'InMsgsStatusDiscardedOutbound');
declare_variable("$r.22.4", 'InMsgsStatusDiscardedInternal');
declare_variable("$r.22.5", 'InMsgsStatusDiscardedOriginating');
declare_variable("$r.22.6", 'InMsgsStatusDiscardedOpenRelay');
declare_variable("$r.23.1", 'InMsgsStatusNoBounce'); # 2xx, no DSN
declare_variable("$r.23.2", 'InMsgsStatusNoBounceInbound');
declare_variable("$r.23.3", 'InMsgsStatusNoBounceOutbound');
declare_variable("$r.23.4", 'InMsgsStatusNoBounceInternal');
declare_variable("$r.23.5", 'InMsgsStatusNoBounceOriginating');
declare_variable("$r.23.6", 'InMsgsStatusNoBounceOpenRelay');
declare_variable("$r.24.1", 'InMsgsStatusBounced'); # 2xx, DSN sent
declare_variable("$r.24.2", 'InMsgsStatusBouncedInbound');
declare_variable("$r.24.3", 'InMsgsStatusBouncedOutbound');
declare_variable("$r.24.4", 'InMsgsStatusBouncedInternal');
declare_variable("$r.24.5", 'InMsgsStatusBouncedOriginating');
declare_variable("$r.24.6", 'InMsgsStatusBouncedOpenRelay');
declare_variable("$r.25.1", 'InMsgsStatusRejected'); # 5xx
declare_variable("$r.25.2", 'InMsgsStatusRejectedInbound');
declare_variable("$r.25.3", 'InMsgsStatusRejectedOutbound');
declare_variable("$r.25.4", 'InMsgsStatusRejectedInternal');
declare_variable("$r.25.5", 'InMsgsStatusRejectedOriginating');
declare_variable("$r.25.6", 'InMsgsStatusRejectedOpenRelay');
declare_variable("$r.26.1", 'InMsgsStatusTempFailed'); # 4xx
declare_variable("$r.26.2", 'InMsgsStatusTempFailedInbound');
declare_variable("$r.26.3", 'InMsgsStatusTempFailedOutbound');
declare_variable("$r.26.4", 'InMsgsStatusTempFailedInternal');
declare_variable("$r.26.5", 'InMsgsStatusTempFailedOriginating');
declare_variable("$r.26.6", 'InMsgsStatusTempFailedOpenRelay');
}
{ # amavisd child processes MIB
my($r) = $databases[1]->{root_oid_str};
declare_variable("$r.1.1", 'ProcGone'); # counter!
declare_variable("$r.1.2", 'ProcAll', 'G32');
declare_variable("$r.1.3", 'ProcIdle', 'G32');
declare_variable("$r.1.4", 'ProcBusy', 'G32');
declare_variable("$r.1.5", 'ProcBusyTransfer', 'G32');
declare_variable("$r.1.6", 'ProcBusyDecode', 'G32');
declare_variable("$r.1.7", 'ProcBusyVirus', 'G32');
declare_variable("$r.1.8", 'ProcBusySpam', 'G32');
declare_variable("$r.1.9", 'ProcBusyOther', 'G32');
declare_variable(sprintf("%s.2.%d", $r,$_+1),
'ProcBusy'.$_, 'G32') for (0..@age_slots);
}
if (defined $mta_queue_dir) {
# Postfix queue size MIB
declare_variable($databases[2]->{root_oid_str},
'MtaQueueEntriesMaildrop', 'G32');
declare_variable($databases[3]->{root_oid_str},
'MtaQueueEntriesIncoming', 'G32');
declare_variable($databases[4]->{root_oid_str},
'MtaQueueEntriesActive', 'G32');
declare_variable($databases[5]->{root_oid_str},
'MtaQueueEntriesDeferred', 'G32');
}
if (!$daemonize) {
do_log(0,"%s starting in foreground, perl %s", $myversion,$]);
} else { # daemonize
$SIG{'__WARN__'} = # log warnings
sub { my($m) = @_; chomp($m); do_log(-1,"_WARN: %s",$m) };
$SIG{'__DIE__' } = # log uncaught errors
sub { if (!$^S) { my($m) = @_; chomp($m); do_log(-2,"_DIE: %s",$m) } };
openlog($syslog_ident, LOG_PID | LOG_NDELAY, $syslog_facility);
$syslog_open = 1;
do_log(2,"to be daemonized");
daemonize();
do_log(0,"%s starting. daemonized as PID [%s], perl %s", $myversion,$$,$]);
if (defined $pid_filename && $pid_filename ne '') {
my($pidf) = IO::File->new;
my($stat) = $pidf->open($pid_filename, O_CREAT|O_EXCL|O_RDWR, 0640);
if (!$stat && $! == EEXIST) {
do_log(0,"PID file %s exists, overwriting", $pid_filename);
$stat = $pidf->open($pid_filename, O_CREAT|O_RDWR, 0640);
}
$stat or die "Can't create file $pid_filename: $!";
$pid_file_created = 1;
$pidf->print("$$\n") or die "Can't write to $pid_filename: $!";
$pidf->close or die "Can't close $pid_filename: $!";
}
}
#netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID,
# NETSNMP_DS_LIB_DONT_READ_CONFIGS, 1);
my($agent) = NetSNMP::agent->new('Name' => $agent_name, 'AgentX' => 1)
or die "Can't create a SNMP agent $agent_name";
init_data(); # must come *after* NetSNMP::agent->new
# dump_variables();
for my $database (@databases) {
my($root_oid_str) = $database->{root_oid_str};
my($db_name) = $database->{name};
if ($db_name =~ /^pf/ && !defined $mta_queue_dir) {
do_log(2, "not registering root OID %s for %s", $root_oid_str,$db_name);
} else {
do_log(2, "registering root OID %s for %s", $root_oid_str,$db_name);
$root_oid_str = '.' . $root_oid_str;
$agent->register($agent_name, $root_oid_str, \&snmp_handler)
or die "Can't register a SNMP agent $agent_name under $root_oid_str";
$database->{registered} = 1;
}
}
while ($keep_running) {
$agent->agent_check_and_process(1);
}
exit;
END {
if (defined $agent) {
eval { $agent->shutdown }; # ignoring status
}
for my $database (@databases) {
eval {
if (defined $database->{db}) {
if (defined $database->{cursor}) {
$database->{cursor}->c_close; # close database, ignoring status
undef $database->{cursor};
}
$database->{db}->db_close == 0
or warn(sprintf("BDB db_close error on a %s file: %s %s",
$database->{db}, $BerkeleyDB::Error, $!));
}
}; # ignoring status
}
if ($pid_file_created) {
unlink($pid_filename)
or eval { do_log(0, "Can't remove file %s: %s", $pid_filename,$!) };
}
eval { do_log(2, "%s shutting down", $myproduct_name) };
if ($syslog_open) {
eval { closelog() }; $syslog_open = 0;
}
}