Файловый менеджер - Редактировать - /usr/sbin/amavisd-status
Назад
#!/usr/bin/perl -T #------------------------------------------------------------------------------ # This is amavisd-status, a program to show status of child processes # in amavisd-new. # # Author: Mark Martinec <Mark.Martinec@ijs.si> # # Copyright (c) 2012-2016, 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/ #------------------------------------------------------------------------------ use strict; use re 'taint'; use warnings; use warnings FATAL => qw(utf8 void); no warnings 'uninitialized'; use Errno qw(ESRCH ENOENT); use POSIX qw(strftime); use Time::HiRes (); use vars qw($VERSION); $VERSION = 2.011000; use vars qw($myversion $myproduct_name $myversion_id $myversion_date); use vars qw($outer_sock_specs); $myproduct_name = 'amavisd-status'; $myversion_id = '2.11.0'; $myversion_date = '20150720'; $myversion = "$myproduct_name-$myversion_id ($myversion_date)"; ### USER CONFIGURABLE: # should match a socket of the same name in amavis-services $outer_sock_specs = "tcp://127.0.0.1:23232"; ### END OF USER CONFIGURABLE use vars qw($zmq_mod_name $zmq_mod_version $zmq_lib_version); BEGIN { my($zmq_major, $zmq_minor, $zmq_patch); if (eval { require ZMQ::LibZMQ3 && require ZMQ::Constants }) { $zmq_mod_name = 'ZMQ::LibZMQ3'; # new interface module to zmq v3 or libxs import ZMQ::LibZMQ3; import ZMQ::Constants qw(:all); ($zmq_major, $zmq_minor, $zmq_patch) = ZMQ::LibZMQ3::zmq_version(); # *zmq_sendmsg [native] # (socket,msgobj,flags) # *zmq_recvmsg [native] # (socket,flags) -> msgobj *zmq_sendstr = sub { # (socket,string,flags) my $rv = zmq_send($_[0], $_[1], length $_[1], $_[2]||0); $rv == -1 ? undef : $rv; }; } elsif (eval { require ZMQ::LibZMQ2 && require ZMQ::Constants }) { $zmq_mod_name = 'ZMQ::LibZMQ2'; # new interface module to zmq v2 import ZMQ::LibZMQ2; import ZMQ::Constants qw(:all); ($zmq_major, $zmq_minor, $zmq_patch) = ZMQ::LibZMQ2::zmq_version(); # zmq v2/v3 incompatibile renaming *zmq_sendmsg = \&ZMQ::LibZMQ2::zmq_send; # (socket,msgobj,flags) *zmq_recvmsg = \&ZMQ::LibZMQ2::zmq_recv; # (socket,flags) -> msgobj *zmq_sendstr = sub { # (socket,string,flags) my $rv = zmq_send(@_); $rv == -1 ? undef : $rv; }; } elsif (eval { require ZeroMQ::Constants && require ZeroMQ::Raw }) { $zmq_mod_name = 'ZeroMQ'; # old interface module to zmq v2 import ZeroMQ::Raw; import ZeroMQ::Constants qw(:all); ($zmq_major, $zmq_minor, $zmq_patch) = ZeroMQ::version(); # zmq v2/v3 incompatibile renaming *zmq_sendmsg = \&ZeroMQ::Raw::zmq_send; # (socket,msgobj,flags) *zmq_recvmsg = \&ZeroMQ::Raw::zmq_recv; # (socket,flags) -> msgobj *zmq_sendstr = sub { # (socket,string,flags) my $rv = zmq_send(@_); $rv == -1 ? undef : $rv; }; } else { die "Perl modules ZMQ::LibZMQ3 or ZMQ::LibZMQ2 or ZeroMQ not available\n"; } $zmq_mod_version = $zmq_mod_name->VERSION; $zmq_lib_version = join('.', $zmq_major, $zmq_minor, $zmq_patch); 1; } sub zmq_version { sprintf("%s %s, lib %s", $zmq_mod_name, $zmq_mod_version, $zmq_lib_version); }; sub zmq_recvstr { # (socket,buffer,offset) -> (len,more) my $sock = $_[0]; my $offset = $_[2] || 0; my $zm = zmq_recvmsg($sock); # a copy of a received msg obj if (!$zm) { substr($_[1],$offset) = ''; return } ($offset ? substr($_[1],$offset) : $_[1]) = zmq_msg_data($zm); my $len = length($_[1]) - $offset; zmq_msg_close($zm); return $len if !wantarray; my $more = zmq_getsockopt($sock, ZMQ_RCVMORE); if ($more == -1) { substr($_[1],$offset) = ''; return } ($len, $more); }; my $wakeuptime = 1; # -w, sleep time in seconds, may be fractional my $repeatcount; # -c, repeat count (when defined) my $zmq_poll_units = 1000; # milliseconds since zmq v3 $zmq_poll_units *= 1000 if $zmq_lib_version =~ /^[012]\./; # microseconds sub fmt_age($$$) { my($t,$state_bar,$idling) = @_; $t = int($t); my $char = $idling ? '.' : '='; my $bar_l = $idling ? $t : length($state_bar); my $bar = substr( ($char x 9 . ':') x 3 . $char x 5, 0,$bar_l); if (!$idling) { $state_bar = substr($state_bar,0,length($bar)-2) . substr($state_bar,-1,1) . '>' if length($state_bar) > length($bar); for my $j (0 .. length($bar)-1) { substr($bar,$j,1) = substr($state_bar,$j,1) if substr($bar,$j,1) eq '=' && substr($state_bar,$j,1) ne ' '; } } 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; my $str = sprintf("%d:%02d:%02d", $h,$m,$s); $str = (!$d ? " " : sprintf("%dd",$d)) . $str; $str . ' ' . $bar; }; sub usage() { print <<'EOD'; States legend: A accepted a connection b begin with a protocol for accepting a request m 'MAIL FROM' smtp command started a new transaction in the same session d transferring data from MTA to amavisd = content checking just started G generating and verifying unique mail_id D decoding of mail parts V virus scanning S spam scanning P pen pals database lookup and updates r preparing results Q quarantining and preparing/sending notifications F forwarding mail to MTA . content checking just finished sp space indicates idle (elapsed time bar is showing dots) EOD print "Usage: $0 [-c <count>] [-w <wait-interval>]\n"; } my($zmq_ctx, $zmq_sock); my %process; # associative array on pid my $any_events = 0; sub process_message { my($msgstr, $msgstr_l, $more, $val, $p); for (;;) { ($msgstr_l,$more) = zmq_recvstr($zmq_sock,$msgstr); defined $msgstr_l or die "zmq_recvstr failed: $!"; $any_events = 1; local($1); if (!defined $msgstr) { # should not happen (except on a failure of zmq_recvmsg) } elsif ($msgstr =~ /^am\.st \d+\s+/s) { my($subscription_chan, $pid, $time, $state, $task_id) = split(' ',$msgstr); if ($state eq 'FLUSH') { %process = (); # flush all kept state (e.g. on a restart) printf STDERR ("state flushed (restart)\n"); } elsif ($state eq 'exiting' || $state eq 'purged') { delete $process{$pid}; # may or may not exist } else { $state = ' ' if $state eq '-'; $p = $process{$pid}; if ($p) { $p->{state} = $state; $p->{task_id} = $task_id; } else { # new process appeared $process{$pid} = $p = { state => $state, task_id => $task_id, timestamp => undef, base_timestamp => undef, last_displ_timestamp => undef, state_bars => undef, }; } my $now = Time::HiRes::time; if ($time > 1e9) { # Unix time in seconds with fraction (> Y2000) $p->{base_timestamp} = $p->{timestamp} = $time; $p->{state_bars} = ''; # reset for a new task } elsif (!$p->{base_timestamp}) { # delta time but no base $p->{timestamp} = $now; $p->{base_timestamp} = $p->{timestamp} - $time/1000; # estimate } else { # delta time in ms since base_timestamp $p->{timestamp} = $p->{base_timestamp} + $time/1000; } $p->{tick} = $now; } } elsif ($msgstr =~ /^am\.proc\.(busy|idle) /) { my($subscription_chan, @pid_list) = split(' ',$msgstr); my $now = Time::HiRes::time; for my $pid (@pid_list) { if ($process{$pid}) { $p->{tick} = $now; } else { $process{$pid} = $p = { state => $1 eq 'busy' ? '?' : ' ', base_timestamp => $now, timestamp => $now, tick => $now, }; } } } else { print STDERR "Unrecognized message received: $msgstr\n"; } last if !$more; } 1; } use vars qw($peak_active $last_peak_reading_time); BEGIN { $peak_active = 0 } sub display_state() { my $num_idling = 0; my $num_active = 0; my $now = Time::HiRes::time; for my $pid (sort { $a <=> $b } keys %process) { my $p = $process{$pid}; my $idling = !defined $p->{task_id} && $p->{state} =~ /^[. ]\z/s; my $age = $now - $p->{base_timestamp}; if ($idling) { $num_idling++; $p->{state_bars} = ''; next; # suppress reporting idle processes (or comment-out) } else { $num_active++; my $len = int($age + 0.5); $len = 1 if $len < 1; my $str = $p->{state_bars}; $str = '' if !defined $str; if ($len > length $str) { # replicate last character to desired size my $ch = $str eq '' ? '=' : substr($str,-1,1); $str .= $ch x ($len - length $str); } substr($str,$len-1,1) = $p->{state}; $p->{state_bars} = $str; } printf STDERR ("PID %5d: %-11s %s\n", $pid, $p->{task_id} || $p->{state}, fmt_age($age, $p->{state_bars}, $idling) ); } $now = Time::HiRes::time; if ($num_active > $peak_active) { $peak_active = $num_active; } elsif ($peak_active >= 0.1) { # exponential decay of a peak indicator my $halflife = 8; # seconds my $weight = exp(-(($now - $last_peak_reading_time) / $halflife) * log(2)); $peak_active *= $weight; $peak_active = $num_active if $num_active > $peak_active; } $last_peak_reading_time = $now; my $bar = ('*' x $num_active) . ('.' x $num_idling); my $ipeak_active = int($peak_active+0.5); if ($ipeak_active > $num_active) { my $padding = $ipeak_active - ($num_active + $num_idling); $bar .= ' ' x $padding if $padding > 0; substr($bar, $num_active, $ipeak_active-$num_active) = ':' x ($ipeak_active-$num_active); } printf STDERR ("%d active, %d idling processes\n", $num_active, $num_idling); printf STDERR ("%s\n", $bar); } # main program starts here my $normal_termination = 0; $SIG{INT} = sub { die "\n" }; # do the END code block when interrupted $SIG{TERM} = sub { die "\n" }; # do the END code block when killed while (@ARGV) { my $opt = shift @ARGV; my $val = shift @ARGV; if ($opt eq '-w' && $val =~ /^\+?\d+(?:\.\d*)?\z/) { $wakeuptime = $val } elsif ($opt eq '-c' && $val =~ /^[+-]?\d+\z/) { $repeatcount = $val } else { usage(); exit 1 } } $zmq_ctx = zmq_init(); $zmq_ctx or die "Can't create ZMQ context: $!"; $zmq_sock = zmq_socket($zmq_ctx,ZMQ_SUB); $zmq_sock or die "Can't create ZMQ socket: $!"; my $sock_ipv4only = 1; # a ZMQ default if (defined &ZMQ_IPV4ONLY && $outer_sock_specs =~ /:[0-9a-f]*:/i) { zmq_setsockopt($zmq_sock, ZMQ_IPV4ONLY(), 0) != -1 or die "zmq_setsockopt failed: $!"; $sock_ipv4only = 0; } zmq_setsockopt($zmq_sock, ZMQ_SUBSCRIBE, 'am.st ') != -1 or die "zmq_setsockopt SUBSCRIBE failed: $!"; zmq_setsockopt($zmq_sock, ZMQ_SUBSCRIBE, 'am.proc.') != -1 or die "zmq_setsockopt SUBSCRIBE failed: $!"; zmq_connect($zmq_sock, $outer_sock_specs) != -1 or die "zmq_connect to $outer_sock_specs failed: $!"; print <<'EOD'; process-id task-id elapsed in elapsed-bar (dots indicate idle) or state idle or busy EOD my $last_display_time; for (;;) { if (defined $repeatcount) { last if $repeatcount <= 0; $repeatcount--; } $| = 0; print "\n"; my $now = Time::HiRes::time; my $redraw_at = defined $last_display_time ? $last_display_time + $wakeuptime : $now + 0.2; for (;;) { my $timeout = $redraw_at - $now; $timeout = 0 if $timeout < 0; $any_events = 0; zmq_poll( [ { socket => $zmq_sock, events => ZMQ_POLLIN, callback => \&process_message, }, ], $timeout * $zmq_poll_units ) != -1 or die "zmq_poll failed: $!"; $now = Time::HiRes::time; last if $now >= $redraw_at; last if $any_events && $now > $last_display_time + 0.2; } while (my($pid,$p) = each %process) { # remove stale entries delete $process{$pid} if $p && $now - $p->{tick} > 10*60; } display_state(); $last_display_time = Time::HiRes::time; $| = 1; # flush STDOUT } # forever $normal_termination = 1; END { # ignoring status zmq_close($zmq_sock) if $zmq_sock; zmq_term($zmq_ctx) if $zmq_ctx; print "exited\n" if !$normal_termination; close(STDOUT) or die "Error closing STDOUT: $!"; close(STDERR) or die "Error closing STDERR: $!"; }
| ver. 1.4 |
Github
|
.
| PHP 7.4.3-4ubuntu2.24 | Генерация страницы: 0 |
proxy
|
phpinfo
|
Настройка