Linux biogene 3.16.0-11-amd64 #1 SMP Debian 3.16.84-1 (2020-06-09) x86_64
Apache
: 46.101.124.208 | : 18.224.56.167
Cant Read [ /etc/named.conf ]
5.6.40-0+deb8u12
www-data
www.github.com/MadExploits
Terminal
AUTO ROOT
Adminer
Backdoor Destroyer
Linux Exploit
Lock Shell
Lock File
Create User
CREATE RDP
PHP Mailer
BACKCONNECT
UNLOCK SHELL
HASH IDENTIFIER
CPANEL RESET
CREATE WP USER
README
+ Create Folder
+ Create File
/
usr /
sbin /
[ HOME SHELL ]
Name
Size
Permission
Action
a2disconf
13.84
KB
-rwxr-xr-x
a2dismod
13.84
KB
-rwxr-xr-x
a2dissite
13.84
KB
-rwxr-xr-x
a2enconf
13.84
KB
-rwxr-xr-x
a2enmod
13.84
KB
-rwxr-xr-x
a2ensite
13.84
KB
-rwxr-xr-x
a2query
9.66
KB
-rwxr-xr-x
accessdb
10.13
KB
-rwxr-xr-x
accton
10.2
KB
-rwxr-xr-x
acpid
46.8
KB
-rwxr-xr-x
add-shell
695
B
-rwxr-xr-x
addgnupghome
3.05
KB
-rwxr-xr-x
addgroup
33.66
KB
-rwxr-xr-x
adduser
33.66
KB
-rwxr-xr-x
amavis-mc
18.73
KB
-rwxr-xr-x
amavis-services
33.4
KB
-rwxr-xr-x
amavisd-agent
13.11
KB
-rwxr-xr-x
amavisd-nanny
11.72
KB
-rwxr-xr-x
amavisd-new
1.46
MB
-rwxr-xr-x
amavisd-new-cronjob
825
B
-rwxr-xr-x
amavisd-release
12.32
KB
-rwxr-xr-x
amavisd-signer
36.85
KB
-rwxr-xr-x
amavisd-snmp-subagent
52.97
KB
-rwxr-xr-x
amavisd-snmp-subagent-zmq
52.42
KB
-rwxr-xr-x
amavisd-status
14
KB
-rwxr-xr-x
amavisd-submit
9.09
KB
-rwxr-xr-x
apache2
650.93
KB
-rwxr-xr-x
apache2ctl
6.25
KB
-rwxr-xr-x
apachectl
6.25
KB
-rwxr-xr-x
applygnupgdefaults
2.21
KB
-rwxr-xr-x
arp
54.38
KB
-rwxr-xr-x
arpd
39.11
KB
-rwxr-xr-x
arping
30.48
KB
-rwxr-xr-x
aspell-autobuildhash
13.09
KB
-rwxr-xr-x
atd
21.88
KB
-rwxr-xr-x
biosdecode
18.76
KB
-rwxr-xr-x
check_forensic
952
B
-rwxr-xr-x
chgpasswd
57.3
KB
-rwxr-xr-x
chpasswd
49.36
KB
-rwxr-xr-x
chroot
34.6
KB
-rwxr-xr-x
cpgr
51.48
KB
-rwxr-xr-x
cppw
51.48
KB
-rwxr-xr-x
cron
43.36
KB
-rwxr-xr-x
dbconfig-generate-include
11.63
KB
-rwxr-xr-x
dbconfig-load-include
5.57
KB
-rwxr-xr-x
delgroup
15.43
KB
-rwxr-xr-x
deluser
15.43
KB
-rwxr-xr-x
dmidecode
98.43
KB
-rwxr-xr-x
dovecot
82.05
KB
-rwxr-xr-x
dpkg-divert
135.23
KB
-rwxr-xr-x
dpkg-preconfigure
3.52
KB
-rwxr-xr-x
dpkg-reconfigure
4.23
KB
-rwxr-xr-x
dpkg-statoverride
58.84
KB
-rwxr-xr-x
dump-acct
18.46
KB
-rwxr-xr-x
dump-utmp
14.4
KB
-rwxr-xr-x
e2freefrag
10.18
KB
-rwxr-xr-x
e4defrag
26.47
KB
-rwxr-xr-x
fdformat
10.21
KB
-rwxr-xr-x
filefrag
14.25
KB
-rwxr-xr-x
groupadd
57.36
KB
-rwxr-xr-x
groupdel
53.23
KB
-rwxr-xr-x
groupmod
67.73
KB
-rwxr-xr-x
grpck
53.35
KB
-rwxr-xr-x
grpconv
49.23
KB
-rwxr-xr-x
grpunconv
49.23
KB
-rwxr-xr-x
grub-bios-setup
751.6
KB
-rwxr-xr-x
grub-install
943.7
KB
-rwxr-xr-x
grub-macbless
739.26
KB
-rwxr-xr-x
grub-mkconfig
7.78
KB
-rwxr-xr-x
grub-mkdevicemap
188.83
KB
-rwxr-xr-x
grub-probe
749.2
KB
-rwxr-xr-x
grub-reboot
4.01
KB
-rwxr-xr-x
grub-set-default
3.48
KB
-rwxr-xr-x
httxt2dbm
9.96
KB
-rwxr-xr-x
iconvconfig
30.46
KB
-rwxr-xr-x
install-sgmlcatalog
4.44
KB
-rwxr-xr-x
invoke-rc.d
15.01
KB
-rwxr-xr-x
ip6tables-apply
6.85
KB
-rwxr-xr-x
iptables-apply
6.85
KB
-rwxr-xr-x
irqbalance
43.07
KB
-rwxr-xr-x
ispell-autobuildhash
15.25
KB
-rwxr-xr-x
laptop-detect
2.53
KB
-rwxr-xr-x
ldattach
22.35
KB
-rwxr-xr-x
locale-gen
1.5
KB
-rwxr-xr-x
logrotate
62.92
KB
-rwxr-xr-x
logwatch
57.19
KB
-rwxr-xr-x
make-ssl-cert
3.69
KB
-rwxr-xr-x
mkinitramfs
9.15
KB
-rwxr-xr-x
mklost+found
10.2
KB
-rwxr-xr-x
mount.davfs
123.61
KB
-rwsr-xr-x
mysqld
11.32
MB
-rwxr-xr-x
newusers
77.86
KB
-rwxr-xr-x
nfacct
14.2
KB
-rwxr-xr-x
nfnl_osf
14.18
KB
-rwxr-xr-x
nologin
5.87
KB
-rwxr-xr-x
ownership
10.13
KB
-rwxr-xr-x
p0f-analyzer
23.34
KB
-rwxr-xr-x
pam-auth-update
19.02
KB
-rwxr-xr-x
pam_getenv
2.82
KB
-rwxr-xr-x
pam_timestamp_check
10.2
KB
-rwxr-xr-x
paperconfig
4.07
KB
-rwxr-xr-x
php5dismod
6.21
KB
-rwxr-xr-x
php5enmod
6.21
KB
-rwxr-xr-x
php5query
5.05
KB
-rwxr-xr-x
pma-configure
299
B
-rwxr-xr-x
pma-secure
157
B
-rwxr-xr-x
postalias
17.87
KB
-rwxr-xr-x
postcat
17.94
KB
-rwxr-xr-x
postconf
163.38
KB
-rwxr-xr-x
postdrop
13.99
KB
-r-xr-sr-x
postfix
13.95
KB
-rwxr-xr-x
postfix-add-filter
4.88
KB
-rwxr-xr-x
postfix-add-policy
3.81
KB
-rwxr-xr-x
postkick
9.87
KB
-rwxr-xr-x
postlock
9.87
KB
-rwxr-xr-x
postlog
10.05
KB
-rwxr-xr-x
postmap
17.87
KB
-rwxr-xr-x
postmulti
30.26
KB
-rwxr-xr-x
postqueue
13.95
KB
-r-xr-sr-x
postsuper
26.26
KB
-rwxr-xr-x
posttls-finger
33.95
KB
-rwxr-xr-x
pwck
49.3
KB
-rwxr-xr-x
pwconv
45.17
KB
-rwxr-xr-x
pwunconv
36.36
KB
-rwxr-xr-x
qmqp-sink
13.87
KB
-rwxr-xr-x
qmqp-source
17.88
KB
-rwxr-xr-x
qshape
12.55
KB
-rwxr-xr-x
readprofile
14.33
KB
-rwxr-xr-x
remove-default-ispell
2.86
KB
-rwxr-xr-x
remove-default-wordlist
2.86
KB
-rwxr-xr-x
remove-shell
749
B
-rwxr-xr-x
rmail
13.87
KB
-rwxr-xr-x
rmt
54.91
KB
-rwxr-xr-x
rmt-tar
54.91
KB
-rwxr-xr-x
rsyslogd
563.81
KB
-rwxr-xr-x
rtcwake
26.42
KB
-rwxr-xr-x
sa
35.39
KB
-rwxr-xr-x
safe_finger
10.3
KB
-rwxr-xr-x
select-default-ispell
1.83
KB
-rwxr-xr-x
select-default-wordlist
1.82
KB
-rwxr-xr-x
sendmail
26.05
KB
-rwxr-xr-x
service
9.37
KB
-rwxr-xr-x
setvesablank
10.07
KB
-rwxr-xr-x
slapacl
1.21
MB
-rwxr-xr-x
slapadd
1.21
MB
-rwxr-xr-x
slapauth
1.21
MB
-rwxr-xr-x
slapcat
1.21
MB
-rwxr-xr-x
slapd
1.21
MB
-rwxr-xr-x
slapdn
1.21
MB
-rwxr-xr-x
slapindex
1.21
MB
-rwxr-xr-x
slappasswd
1.21
MB
-rwxr-xr-x
slapschema
1.21
MB
-rwxr-xr-x
slaptest
1.21
MB
-rwxr-xr-x
smtp-sink
30.8
KB
-rwxr-xr-x
smtp-source
21.89
KB
-rwxr-xr-x
spamd
127.49
KB
-rwxr-xr-x
split-logfile
2.36
KB
-rwxr-xr-x
sshd
760.63
KB
-rwxr-xr-x
tarcat
936
B
-rwxr-xr-x
tcpd
10.12
KB
-rwxr-xr-x
tcpdchk
22.42
KB
-rwxr-xr-x
tcpdmatch
18.33
KB
-rwxr-xr-x
tcptraceroute
1.45
KB
-rwxr-xr-x
tcptraceroute.db
1.45
KB
-rwxr-xr-x
traceroute
67.37
KB
-rwxr-xr-x
try-from
10.06
KB
-rwxr-xr-x
tunelp
14.22
KB
-rwxr-xr-x
tzconfig
106
B
-rwxr-xr-x
umount.davfs
10.21
KB
-rwxr-xr-x
update-alternatives
46.43
KB
-rwxr-xr-x
update-ca-certificates
4.85
KB
-rwxr-xr-x
update-catalog
9.15
KB
-rwxr-xr-x
update-default-aspell
1
KB
-rwxr-xr-x
update-default-ispell
9.68
KB
-rwxr-xr-x
update-default-wordlist
7.5
KB
-rwxr-xr-x
update-dictcommon-aspell
1
KB
-rwxr-xr-x
update-dictcommon-hunspell
782
B
-rwxr-xr-x
update-grub
64
B
-rwxr-xr-x
update-grub2
64
B
-rwxr-xr-x
update-icon-caches
617
B
-rwxr-xr-x
update-info-dir
1.65
KB
-rwxr-xr-x
update-initramfs
8.76
KB
-rwxr-xr-x
update-locale
2.99
KB
-rwxr-xr-x
update-mime
8.84
KB
-rwxr-xr-x
update-passwd
30.31
KB
-rwxr-xr-x
update-pciids
2.84
KB
-rwxr-xr-x
update-python-modules
19.33
KB
-rwxr-xr-x
update-rc.d
11.72
KB
-rwxr-xr-x
update-xmlcatalog
16.28
KB
-rwxr-xr-x
upgrade-from-grub-legacy
1.49
KB
-rwxr-xr-x
useradd
118.69
KB
-rwxr-xr-x
userdel
81.92
KB
-rwxr-xr-x
usermod
122.5
KB
-rwxr-xr-x
validlocale
1.73
KB
-rwxr-xr-x
vcstime
6.06
KB
-rwxr-xr-x
vigr
59.73
KB
-rwxr-xr-x
vipw
59.73
KB
-rwxr-xr-x
visudo
2.2
MB
-rwxr-xr-x
vpddecode
14.26
KB
-rwxr-xr-x
vsftpd
156.32
KB
-rwxr-xr-x
zabbix_agentd
527.59
KB
-rwxr-xr-x
zic
42.36
KB
-rwxr-xr-x
Delete
Unzip
Zip
${this.title}
Close
Code Editor : amavis-mc
#!/usr/bin/perl -T #------------------------------------------------------------------------------ # This is amavis-mc, a master (of ceremonies) processes to supervise # supporting service processes (such as amavis-services) used by amavisd-new. # # Author: Mark Martinec <Mark.Martinec@ijs.si> # # Copyright (c) 2012-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/ #------------------------------------------------------------------------------ use strict; use re 'taint'; use warnings; use warnings FATAL => qw(utf8 void); no warnings 'uninitialized'; use vars qw($VERSION); $VERSION = 2.008002; use vars qw($myproduct_name $myversion_id $myversion_date $myversion); BEGIN { $myproduct_name = 'amavis-mc'; $myversion_id = '2.9.0'; $myversion_date = '20140506'; $myversion = "$myproduct_name-$myversion_id ($myversion_date)"; } use Errno qw(ESRCH ENOENT); use POSIX qw(:sys_wait_h WIFEXITED WIFSIGNALED WIFSTOPPED WEXITSTATUS WTERMSIG WSTOPSIG); use Time::HiRes qw(time); use IO::File qw(O_RDONLY O_WRONLY O_RDWR O_APPEND O_CREAT O_EXCL); use Unix::Syslog qw(:macros :subs); use vars qw(@path @services $daemon_user $daemon_group $pid_file $log_level $syslog_ident $syslog_facility); ### USER CONFIGURABLE: $daemon_user = 'amavis'; $daemon_group = 'amavis'; $pid_file = '/var/run/amavis/amavis-mc.pid'; $log_level = 0; $syslog_ident = 'amavis-mc'; $syslog_facility = LOG_MAIL; @path = qw(/usr/local/sbin /usr/local/bin /usr/sbin /sbin /usr/bin /bin); @services = ( { cmd => 'amavis-services msg-forwarder' }, { cmd => 'amavis-services childproc-minder' }, { cmd => 'amavis-services snmp-responder' }, ); ### END OF USER CONFIGURABLE my($interrupted, $syslog_open, $pid_file_created, @pids_exited, %pid2service); # Return untainted copy of a string (argument can be a string or a string ref) # sub untaint($) { return undef if !defined $_[0]; # must return undef even in a list context! no re 'taint'; local $1; # avoid Perl taint bug: tainted global $1 propagates taintedness (ref($_[0]) ? ${$_[0]} : $_[0]) =~ /^(.*)\z/s; $1; } # is message log level below the current log level (i.e. eligible for logging)? # sub ll($) { my($level) = @_; $level <= $log_level; } sub do_log($$;@) { # my($level,$errmsg,@args) = @_; my $level = shift; if ($level <= $log_level) { my $errmsg = shift; # treat $errmsg as sprintf format string if additional arguments provided $errmsg = sprintf($errmsg,@_) if @_; if (!$syslog_open) { $errmsg .= "\n"; print STDERR $errmsg; # print ignoring I/O status, except SIGPIPE } else { my $prio = $level >= 3 ? LOG_DEBUG # most frequent first : $level >= 1 ? LOG_INFO : $level >= 0 ? LOG_NOTICE : $level >= -1 ? LOG_WARNING : LOG_ERR; syslog($prio, "%s", $errmsg); } } } sub find_program_path($$) { my($fv_list, $path_list_ref) = @_; $fv_list = [$fv_list] if !ref $fv_list; my $found; for my $fv (@$fv_list) { # search through alternatives my(@fv_cmd) = split(' ',$fv); my $cmd = $fv_cmd[0]; if (!@fv_cmd) { # empty, not available } elsif ($cmd =~ m{^/}s) { # absolute path my $errn = stat($cmd) ? 0 : 0+$!; if ($errn == ENOENT) { # file does not exist } elsif ($errn) { do_log(-1, "find_program_path: %s inaccessible: %s", $cmd,$!); } elsif (-d _) { do_log(0, "find_program_path: %s is a directory", $cmd); } elsif (!-x _) { do_log(0, "find_program_path: %s is not executable", $cmd); } else { $found = join(' ', @fv_cmd); } } elsif ($cmd =~ m{/}s) { # relative path die "find_program_path: relative paths not implemented: @fv_cmd\n"; } else { # walk through the specified PATH for my $p (@$path_list_ref) { my $errn = stat("$p/$cmd") ? 0 : 0+$!; if ($errn == ENOENT) { # file does not exist } elsif ($errn) { do_log(-1, "find_program_path: %s/%s inaccessible: %s", $p,$cmd,$!); } elsif (-d _) { do_log(0, "find_program_path: %s/%s is a directory", $p,$cmd); } elsif (!-x _) { do_log(0, "find_program_path: %s/%s is not executable", $p,$cmd); } else { $found = $p . '/' . join(' ', @fv_cmd); last; } } } last if defined $found; } $found; } # drop privileges # sub drop_priv($$) { my($desired_user,$desired_group) = @_; local($1); my($username,$passwd,$uid,$gid) = $desired_user=~/^(\d+)$/ ? (undef,undef,$1,undef) :getpwnam($desired_user); defined $uid or die "drop_priv: No such username: $desired_user\n"; if (!defined($desired_group) || $desired_group eq '') { $desired_group = $gid; # for logging purposes } else { $gid = $desired_group=~/^(\d+)$/ ? $1 : getgrnam($desired_group) } defined $gid or die "drop_priv: No such group: $desired_group\n"; $( = $gid; $) = "$gid $gid"; # real and effective GID POSIX::setgid($gid) or die "drop_priv: Can't setgid to $gid: $!"; POSIX::setuid($uid) or die "drop_priv: Can't setuid to $uid: $!"; $> = $uid; $< = $uid; # just in case # print STDERR "desired user=$desired_user ($uid), current: EUID: $> ($<)\n"; # print STDERR "desired group=$desired_group ($gid), current: EGID: $) ($()\n"; $> != 0 or die "drop_priv: Still running as root, aborting\n"; $< != 0 or die "Effective UID changed, but Real UID is 0, aborting\n"; } sub daemonize() { closelog() if $syslog_open; $syslog_open = 0; STDOUT->autoflush(1); STDERR->autoflush(1); close(STDIN) or die "Can't close STDIN: $!"; my $pid; # 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 } chdir('/') or die "Can't chdir to '/': $!"; # a daemonized child process, live long and prosper... do_log(2, "Daemonized as process [%s]", $$); openlog($syslog_ident, LOG_PID | LOG_NDELAY, $syslog_facility); $syslog_open = 1; { # suppress unnecessary warning: # "Filehandle STDIN reopened as STDOUT only for output" # See https://rt.perl.org/rt3/Public/Bug/Display.html?id=23838 no warnings 'io'; close(STDOUT) or die "Can't close STDOUT: $!"; 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: $!"; } } # Run specified command as a subprocess. # Return a process id of a child process. # sub spawn_command($@) { my($cmd, @args) = @_; my $cmd_text = join(' ', $cmd, @args); my $pid; eval { # Note that fork(2) returns ENOMEM on lack of swap space, and EAGAIN when # process limit is reached; we want it to fail in both cases and not obey # the EAGAIN and keep retrying, as perl open() does. $pid = fork(); 1; } or do { my $eval_stat = $@ ne '' ? $@ : "errno=$!"; chomp $eval_stat; die "spawn_command (forking): $eval_stat"; }; defined($pid) or die "spawn_command: can't fork: $!"; if (!$pid) { # child alarm(0); my $interrupt = ''; my $h1 = sub { $interrupt = $_[0] }; my $h2 = sub { die "Received signal ".$_[0] }; @SIG{qw(INT HUP TERM TSTP QUIT USR1 USR2)} = ($h1) x 7; my $err; eval { # die must be caught, otherwise we end up with two running daemons local(@SIG{qw(INT HUP TERM TSTP QUIT USR1 USR2)}) = ($h2) x 7; if ($interrupt ne '') { my $i = $interrupt; $interrupt = ''; die $i } close STDIN; # ignoring errors close STDOUT; # ignoring errors # BEWARE of Perl older that 5.6.0: sockets and pipes were not FD_CLOEXEC exec {$cmd} ($cmd,@args); die "spawn_command: failed to exec $cmd_text: $!"; 0; # paranoia } or do { $err = $@ ne '' ? $@ : "errno=$!"; chomp $err; }; eval { local(@SIG{qw(INT HUP TERM TSTP QUIT USR1 USR2)}) = ($h2) x 7; if ($interrupt ne '') { my $i = $interrupt; $interrupt = ''; die $i } # we're in trouble if stderr was attached to a terminal, but no longer is eval { do_log(-1,"spawn_command: child process [%s]: %s", $$,$err) }; } or 1; # ignore failures, make perlcritic happy { # no warnings; POSIX::_exit(6); # avoid END and destructor processing kill('KILL',$$); exit 1; # still kicking? die! } } # parent do_log(5,"spawn_command: [%s] %s", $pid, $cmd_text); $pid; # return the PID of a subprocess } sub usage() { my $me = $0; local $1; $me =~ s{([^/]*)\z}{$1}s; "Usage: $me (-h | -V | [-f] [-P pid_file] [-d log_level])"; } # map process termination status number to an informative string, and # append optional message (dual-valued errno or a string or a number), # returning the resulting string # sub exit_status_str($;$) { my($stat,$errno) = @_; my $str; if (!defined($stat)) { $str = '(no status)'; } elsif (WIFEXITED($stat)) { $str = sprintf('exit %d', WEXITSTATUS($stat)); } elsif (WIFSTOPPED($stat)) { $str = sprintf('stopped, signal %d', WSTOPSIG($stat)); } else { my $sig = WTERMSIG($stat); $str = sprintf('%s, signal %d (%04x)', $sig == 1 ? 'HANGUP' : $sig == 2 ? 'INTERRUPTED' : $sig == 6 ? 'ABORTED' : $sig == 9 ? 'KILLED' : $sig == 15 ? 'TERMINATED' : 'DIED', $sig, $stat); } if (defined $errno) { # deal with dual-valued and plain variables $str .= ', '.$errno if (0+$errno) != 0 || ($errno ne '' && $errno ne '0'); } $str; } # check errno to be 0 and a process exit status to be in the list of success # status codes, returning true if both are ok, and false otherwise # sub proc_status_ok($;$@) { my($exit_status,$errno,@success) = @_; my $ok = 0; if ((!defined $errno || $errno == 0) && WIFEXITED($exit_status)) { my $j = WEXITSTATUS($exit_status); if (!@success) { $ok = $j==0 } # empty list implies only status 0 is good elsif (grep($_==$j, @success)) { $ok = 1 } } $ok; } sub report_terminations($) { my($pids_exited_list) = @_; # note: child_handler may be growing the list at its tail during the loop while (@$pids_exited_list) { my $pid_stat = shift(@$pids_exited_list); next if !$pid_stat; # just in case my($pid,$status,$timestamp) = @$pid_stat; my $serv = delete $pid2service{$pid}; if (!$serv) { do_log(-1,'Unknown process [%d] exited: %s', $pid, exit_status_str($status,0)); } else { $serv->{status} = $status; $serv->{terminated_at} = $timestamp; my $ll = proc_status_ok($status,0) ? 0 : -1; do_log($ll, 'Process [%d] exited (%s) after %.1f s: %s', $pid, $serv->{cmd}, $serv->{terminated_at} - $serv->{started_at}, exit_status_str($status,0)); } } } sub child_handler { my $signal = $_[0]; for (;;) { my $child_pid = waitpid(-1,WNOHANG); # PID may be negative on Windows last if !$child_pid || $child_pid == -1; push(@pids_exited, [$child_pid, $?, time]); } $SIG{CHLD} = \&child_handler; }; # main program starts here delete @ENV{'PATH', 'IFS', 'CDPATH', 'ENV', 'BASH_ENV'}; $ENV{PATH} = join(':',@path) if @path; my $foreground = 0; my(@argv) = @ARGV; # preserve @ARGV, may modify @argv while (@argv >= 2 && $argv[0] =~ /^-[dP]\z/ || @argv >= 1 && $argv[0] =~ /^-/) { 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() . "\n"; } elsif ($opt eq '-V') { # -V (version) die "$myversion\n"; } elsif ($opt eq '-f') { $foreground = 1; } elsif ($opt eq '-d') { # -d log_level $val =~ /^\d+\z/ or die "Bad value for option -d: $val\n"; $log_level = untaint($val); } elsif ($opt eq '-P') { # -P pid_file $pid_file = untaint($val); } else { die "Error in command line options: $opt\n\n" . usage() . "\n"; } } !@argv or die sprintf("Error parsing a command line %s\n\n%s\n", join(' ',@ARGV), usage()); $SIG{'__DIE__' } = sub { if (!$^S) { my($m) = @_; chomp($m); do_log(-1,"_DIE: %s", $m) } }; $SIG{'__WARN__'} = sub { my($m) = @_; chomp($m); do_log(0,"_WARN: %s",$m) }; if ($foreground) { do_log(0,"amavis master process starting in foreground, perl %s", $] ); } else { # daemonize openlog($syslog_ident, LOG_PID | LOG_NDELAY, $syslog_facility); $syslog_open = 1; do_log(2,"to be daemonized"); daemonize(); srand(); do_log(0,'amavis master process starting. '. 'daemonized as PID [%s], perl %s', $$, $] ); } if (defined $daemon_user) { drop_priv($daemon_user,$daemon_group); } if (defined $pid_file && $pid_file ne '') { my $pid_file_fh = IO::File->new; $pid_file_fh->open($pid_file, O_CREAT|O_WRONLY, 0640) or die "Can't create PID file $pid_file: $!"; $pid_file_fh->print($$."\n") or die "Can't write to PID file $pid_file: $!"; $pid_file_fh->close or die "Can't close PID file $pid_file: $!"; $pid_file_created = 1; } # initialize for my $serv (@services) { $serv->{started_cnt} = 0; $serv->{pid} = $serv->{status} = undef; $serv->{started_at} = $serv->{terminated_at} = undef; my $found = find_program_path($serv->{cmd}, \@path); defined $found or die sprintf("Can't find program %s in path %s\n", $serv->{cmd}, join(':',@path)); $serv->{cmd} = $found; } $SIG{CHLD} = \&child_handler; eval { # catch TERM and INT signals for a controlled shutdown my $h = sub { $interrupted = $_[0]; die "\n" }; local $SIG{INT} = $h; local $SIG{TERM} = $h; for (;;) { last if defined $interrupted; for my $serv (@services) { next if $serv->{disabled}; report_terminations(\@pids_exited) if @pids_exited; if (defined $serv->{status}) { # process has terminated, clean up $serv->{pid} = undef; $serv->{started_at} = undef; } last if defined $interrupted; if (!defined $serv->{pid}) { # service not running if ($serv->{started_cnt} >= 5) { do_log(-1,'Exceeded restart count, giving up on (%s)', $serv->{cmd}); $serv->{disabled} = 1; } elsif (defined $serv->{terminated_at} && time - $serv->{terminated_at} < 1) { # postpone a restart for at least a second do_log(5, 'Postponing a restart (%s)', $serv->{cmd}); } else { my($cmd,@args) = split(' ',$serv->{cmd}); $serv->{started_cnt}++; $serv->{status} = $serv->{terminated_at} = undef; $serv->{started_at} = time; my $pid = $serv->{pid} = spawn_command($cmd,@args); do_log(0, 'Process [%d] started: %s', $pid, $serv->{cmd}); # to avoid race the signal handler must not be updating %pid2service $pid2service{$pid} = $serv; } } } sleep 5; # sleep may be aborted prematurely by a signal } # until interrupted }; do_log(0, 'Master process shutting down'); for my $sig ('TERM', 'KILL') { # terminate or kill child processes for my $serv (@services) { my $pid = $serv->{pid}; next if !$pid; my $n = kill($sig,$pid); if ($n == 0 && $! == ESRCH) { # process already gone } elsif ($n == 0) { do_log(-1, "Can't send SIG%s to process [%s]: %s", $sig, $pid, $!); } else { do_log(0, "%s process [%s] (%s)", $sig eq 'TERM' ? 'Terminating' : 'Killing', $pid, $serv->{cmd}); } } my $deadline = time + 10; # 10 second grace period while (time < $deadline) { report_terminations(\@pids_exited); sleep 1; # sleep may be aborted prematurely by a signal # stop waiting if all gone last if !grep { $_->{pid} && kill(0, $_->{pid}) } @services; } report_terminations(\@pids_exited); } END { do_log(0,'Master process exiting: %s', $interrupted) if defined $interrupted; if ($pid_file_created) { unlink($pid_file) or do_log(-1, "Can't delete a PID file %s: %s", $pid_file, $!); } if ($syslog_open) { closelog(); $syslog_open = 0 } }
Close