Server IP : 85.214.239.14 / Your IP : 18.189.194.225 Web Server : Apache/2.4.62 (Debian) System : Linux h2886529.stratoserver.net 4.9.0 #1 SMP Tue Jan 9 19:45:01 MSK 2024 x86_64 User : www-data ( 33) PHP Version : 7.4.18 Disable Function : pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare, MySQL : OFF | cURL : OFF | WGET : ON | Perl : ON | Python : ON | Sudo : ON | Pkexec : OFF Directory : /proc/2/task/2/root/usr/share/perl5/Amavis/DB/ |
Upload File : |
# SPDX-License-Identifier: GPL-2.0-or-later package Amavis::DB::SNMP; use strict; use re 'taint'; use warnings; use warnings FATAL => qw(utf8 void); no warnings 'uninitialized'; # use warnings 'extra'; no warnings 'experimental::re_strict'; use re 'strict'; BEGIN { require Exporter; use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $VERSION); $VERSION = '2.412'; @ISA = qw(Exporter); } use BerkeleyDB; use MIME::Base64; use Time::HiRes (); use Amavis::Conf qw(:platform $myversion $nanny_details_level); use Amavis::Util qw(ll do_log do_log_safe snmp_initial_oids snmp_counters_get add_entropy fetch_entropy_bytes); # open existing databases (called by each child process) # sub new { my($class,$db_env) = @_; $! = 0; my $env = $db_env->get_db_env; defined $env or die "BDB get_db_env (dbS/dbN): $BerkeleyDB::Error, $!."; $! = 0; my $dbs = BerkeleyDB::Hash->new(-Filename=>'snmp.db', -Env=>$env); defined $dbs or die "BDB no dbS: $BerkeleyDB::Error, $!."; $! = 0; my $dbn = BerkeleyDB::Hash->new(-Filename=>'nanny.db',-Env=>$env); defined $dbn or die "BDB no dbN: $BerkeleyDB::Error, $!."; bless { 'db_snmp'=>$dbs, 'db_nanny'=>$dbn }, $class; } sub DESTROY { my $self = $_[0]; local($@,$!,$_); my $myactualpid = $$; if (defined($my_pid) && $myactualpid != $my_pid) { do_log_safe(5,"Amavis::DB::SNMP DESTROY skip, clone [%s] (born as [%s])", $myactualpid, $my_pid); } else { do_log_safe(5,"Amavis::DB::SNMP DESTROY called"); for my $db_name ('db_snmp', 'db_nanny') { my $db = $self->{$db_name}; if (defined $db) { eval { $db->db_close==0 or die "db_close: $BerkeleyDB::Error, $!."; 1; } or do { $@ = "errno=$!" if $@ eq '' }; if ($@ ne '' && $@ !~ /\bDatabase is already closed\b/) { warn "[$myactualpid] BDB S+N DESTROY INFO ($db_name): $@" } undef $db; } } } } #sub lock_stat($) { # my $label = $_[0]; # my $s = qx'/usr/local/bin/db_stat-4.2 -c -h /var/amavis/db | /usr/local/bin/perl -ne \'$a{$2}=$1 if /^(\d+)\s+Total number of locks (requested|released)/; END {printf("%d, %d\n",$a{requested}, $a{requested}-$a{released})}\''; # do_log(0, "lock_stat %s: %s", $label,$s); #} # insert startup time SNMP entry, called from the master process at startup # (a classical subroutine, not a method) # sub put_initial_snmp_data($) { my $db = $_[0]; my($eval_stat,$interrupt); $interrupt = ''; { my $cursor; my $h1 = sub { $interrupt = $_[0] }; local(@SIG{qw(INT HUP TERM TSTP QUIT ALRM USR1 USR2)}) = ($h1) x 8; eval { # ensure cursor will be unlocked even in case of errors or signals $cursor = $db->db_cursor(DB_WRITECURSOR); # obtain write lock defined $cursor or die "BDB S db_cursor: $BerkeleyDB::Error, $!."; my $list_ref = snmp_initial_oids(); for my $obj (@$list_ref) { my($key,$type,$val) = @$obj; $cursor->c_put($key, sprintf("%s %s",$type,$val), DB_KEYLAST) == 0 or die "BDB S c_put: $BerkeleyDB::Error, $!."; }; $cursor->c_close==0 or die "BDB S c_close: $BerkeleyDB::Error, $!."; undef $cursor; 1; } or do { $eval_stat = $@ ne '' ? $@ : "errno=$!" }; $cursor->c_close if defined $cursor; # unlock, ignoring status undef $cursor; }; # restore signal handlers if ($interrupt ne '') { kill($interrupt,$$) } # resignal, ignoring status elsif (defined $eval_stat) { chomp $eval_stat; die "put_initial_snmp_data: BDB S $eval_stat\n"; } } sub update_snmp_variables { my $self = $_[0]; do_log(5,"updating snmp variables in BDB"); my $snmp_var_names_ref = snmp_counters_get(); my($eval_stat,$interrupt); $interrupt = ''; if (defined $snmp_var_names_ref && @$snmp_var_names_ref) { my $db = $self->{'db_snmp'}; my $cursor; my $h1 = sub { $interrupt = $_[0] }; local(@SIG{qw(INT HUP TERM TSTP QUIT ALRM USR1 USR2)}) = ($h1) x 8; eval { # ensure cursor will be unlocked even in case of errors or signals $cursor = $db->db_cursor(DB_WRITECURSOR); # obtain write lock defined $cursor or die "db_cursor: $BerkeleyDB::Error, $!."; for my $key (@$snmp_var_names_ref) { my($snmp_var_name,$arg,$type) = ref $key ? @$key : ($key); $type = 'C32' if !defined($type) || $type eq ''; if ($type eq 'C32' || $type eq 'C64') { # a counter if (!defined($arg)) { $arg = 1 } # by default counter increments by 1 elsif ($arg < 0) { $arg = 0 } # counter is supposed to be unsigned } elsif ($type eq 'TIM') { # TimeTicks if ($arg < 0) { $arg = 0 } # non-decrementing } my($val,$flags); local($1); my $stat = $cursor->c_get($snmp_var_name,$val,DB_SET); if ($stat==0) { # exists, update it (or replace it) if ($type eq 'C32' && $val=~/^C32 (\d+)\z/) { $val = $1+$arg } elsif ($type eq 'C64' && $val=~/^C64 (\d+)\z/) { $val = $1+$arg } elsif ($type eq 'TIM' && $val=~/^TIM (\d+)\z/) { $val = $1+$arg } elsif ($type eq 'INT' && $val=~/^INT ([+-]?\d+)\z/) { $val = $arg } elsif ($type=~/^(STR|OID)\z/ && $val=~/^\Q$type\E (.*)\z/) { if ($snmp_var_name ne 'entropy') { $val = $arg } else { # blend-in entropy $val = $1; add_entropy($val, Time::HiRes::gettimeofday); $val = fetch_entropy_bytes(18); # 18 bytes $val = encode_base64($val,''); # 18*8/6 = 24 chars $val =~ tr{+/}{-_}; # base64 -> RFC 4648 base64url [A-Za-z0-9-_] } } else { do_log(-2,"WARN: variable syntax? %s: %s, clearing", $snmp_var_name,$val); $val = 0; } $flags = DB_CURRENT; } else { # create new entry $stat==DB_NOTFOUND or die "c_get: $BerkeleyDB::Error, $!."; $flags = DB_KEYLAST; $val = $arg; } my $fmt = $type eq 'C32' ? "%010d" : $type eq 'C64' ? "%020.0f" : $type eq 'INT' ? "%010d" : undef; # format for INT should really be %011d, but keep compatibility for now my $str = defined($fmt) ? sprintf($fmt,$val) : $val; $cursor->c_put($snmp_var_name, $type.' '.$str, $flags) == 0 or die "c_put: $BerkeleyDB::Error, $!."; } $cursor->c_close==0 or die "c_close: $BerkeleyDB::Error, $!."; undef $cursor; 1; } or do { $eval_stat = $@ ne '' ? $@ : "errno=$!" }; if (defined $db) { $cursor->c_close if defined $cursor; # unlock, ignoring status undef $cursor; # if (!defined($eval_stat)) { # my $stat; $db->db_sync(); # not really needed # $stat==0 or warn "BDB S db_sync,status $stat: $BerkeleyDB::Error, $!."; # } } }; # restore signal handlers delete $self->{'cnt'}; if ($interrupt ne '') { kill($interrupt,$$) } # resignal, ignoring status elsif (defined $eval_stat) { chomp $eval_stat; die $eval_stat if $eval_stat =~ /^timed out\b/; # resignal timeout die "update_snmp_variables: BDB S $eval_stat\n"; } } sub read_snmp_variables { my($self,@snmp_var_names) = @_; my($eval_stat,$interrupt); $interrupt = ''; my $db = $self->{'db_snmp'}; my $cursor; my(@values); { my $h1 = sub { $interrupt = $_[0] }; local(@SIG{qw(INT HUP TERM TSTP QUIT ALRM USR1 USR2)}) = ($h1) x 8; eval { # ensure cursor will be unlocked even in case of errors or signals $cursor = $db->db_cursor; # obtain read lock defined $cursor or die "db_cursor: $BerkeleyDB::Error, $!."; for my $cname (@snmp_var_names) { my $val; my $stat = $cursor->c_get($cname,$val,DB_SET); push(@values, $stat==0 ? $val : undef); $stat==0 || $stat==DB_NOTFOUND or die "c_get: $BerkeleyDB::Error, $!."; } $cursor->c_close==0 or die "c_close: $BerkeleyDB::Error, $!."; undef $cursor; 1; } or do { $eval_stat = $@ ne '' ? $@ : "errno=$!" }; if (defined $db) { $cursor->c_close if defined $cursor; # unlock, ignoring status undef $cursor; } }; # restore signal handlers if ($interrupt ne '') { kill($interrupt,$$) } # resignal, ignoring status elsif (defined $eval_stat) { chomp $eval_stat; die $eval_stat if $eval_stat =~ /^timed out\b/; # resignal timeout die "read_snmp_variables: BDB S $eval_stat\n"; } for my $val (@values) { if (!defined($val)) {} # keep undefined elsif ($val =~ /^(?:C32|C64) (\d+)\z/) { $val = 0+$1 } elsif ($val =~ /^(?:INT) ([+-]?\d+)\z/) { $val = 0+$1 } elsif ($val =~ /^(?:STR|OID) (.*)\z/) { $val = $1 } else { do_log(-2,"WARN: counter syntax? %s", $val); undef $val } } \@values; } sub register_proc { my($self, $details_level, $reset_timestamp, $state, $task_id) = @_; my $eval_stat; my $interrupt = ''; if (!defined($state) || $details_level <= $nanny_details_level) { $task_id = '' if !defined $task_id; my $db = $self->{'db_nanny'}; my $key = sprintf("%05d",$$); my $cursor; my $val; my $h1 = sub { $interrupt = $_[0] }; local(@SIG{qw(INT HUP TERM TSTP QUIT ALRM USR1 USR2)}) = ($h1) x 8; eval { # ensure cursor will be unlocked even in case of errors or signals $cursor = $db->db_cursor(DB_WRITECURSOR); # obtain write lock defined $cursor or die "db_cursor: $BerkeleyDB::Error, $!."; my $stat = $cursor->c_get($key,$val,DB_SET); $stat==0 || $stat==DB_NOTFOUND or die "c_get: $BerkeleyDB::Error, $!."; if ($stat==0 && !defined $state) { # remove existing entry $cursor->c_del==0 or die "c_del: $BerkeleyDB::Error, $!."; } elsif (defined $state) { # add new, or update existing entry my $timestamp; local($1); # keep its timestamp when updating existing record $timestamp = $1 if $stat==0 && $val=~/^(\d+(?:\.\d*)?) /s; $timestamp = sprintf("%014.3f", Time::HiRes::time) if !defined($timestamp) || $reset_timestamp; my $new_val = sprintf("%s %-14s", $timestamp, $state.$task_id); $cursor->c_put($key, $new_val, $stat==0 ? DB_CURRENT : DB_KEYLAST ) == 0 or die "c_put: $BerkeleyDB::Error, $!."; } $cursor->c_close==0 or die "c_close: $BerkeleyDB::Error, $!."; undef $cursor; 1; } or do { $eval_stat = $@ ne '' ? $@ : "errno=$!" }; if (defined $db) { $cursor->c_close if defined $cursor; # unlock, ignoring status undef $cursor; # if (!defined($eval_stat)) { # my $stat = $db->db_sync(); # not really needed # $stat==0 or warn "BDB N db_sync,status $stat: $BerkeleyDB::Error, $!."; # } } }; # restore signal handlers if ($interrupt ne '') { kill($interrupt,$$); # resignal, ignoring status } elsif (defined $eval_stat) { chomp $eval_stat; do_log_safe(5, "register_proc: BDB N %s", $eval_stat); die $eval_stat if $eval_stat =~ /^timed out\b/; # resignal timeout die "register_proc: BDB N $eval_stat\n"; } } 1;