Test harness process control enhancements

This commit is contained in:
Yang Tse 2009-12-16 15:16:06 +00:00
parent 3111701c38
commit 3802d027cd
4 changed files with 166 additions and 93 deletions

View File

@ -48,7 +48,7 @@ sub pidfromfile {
# the process is not alive, will return the negative value of the pid.
#
sub processexists {
# use POSIX ":sys_wait_h";
use POSIX ":sys_wait_h";
my $pidfile = $_[0];
# fetch pid from pidfile
@ -61,7 +61,7 @@ sub processexists {
}
else {
# reap it if this has not already been done
# waitpid($pid, &WNOHANG);
waitpid($pid, &WNOHANG);
# get rid of the certainly invalid pidfile
unlink($pidfile) if($pid == pidfromfile($pidfile));
return -$pid; # negative means dead process
@ -70,36 +70,125 @@ sub processexists {
return 0;
}
#######################################################################
# killpid attempts to gracefully stop processes in the given pid list
# with a SIGTERM signal and SIGKILLs those which haven't died on time.
#
sub killpid {
use POSIX ":sys_wait_h";
my ($verbose, $pidlist) = @_;
# The 'pidlist' argument is a string of whitespace separated pids.
return if(not defined $pidlist);
# For each pid which is alive send it a SIGTERM to gracefully
# stop it, otherwise reap it if this has not been done yet.
my @signalled;
my $prev = 0;
my @pids = sort({$a <=> $b} split(/\s+/, $pidlist));
foreach my $tmp (@pids) {
chomp $tmp;
if($tmp =~ /^(\d+)$/) {
my $pid = $1;
if(($pid > 0) && ($prev != $pid)) {
$prev = $pid;
if(kill(0, $pid)) {
print("RUN: Process with pid $pid signalled to die\n")
if($verbose);
kill("TERM", $pid);
push @signalled, $pid;
}
else {
print("RUN: Process with pid $pid already dead\n")
if($verbose);
waitpid($pid, &WNOHANG);
}
}
}
}
return if(not scalar(@signalled));
# Allow all signalled processes five seconds to gracefully die.
my $quarters = 20;
while($quarters--) {
for(my $i = scalar(@signalled) - 1; $i >= 0; $i--) {
my $pid = $signalled[$i];
if(!kill(0, $pid)) {
print("RUN: Process with pid $pid gracefully died\n")
if($verbose);
waitpid($pid, &WNOHANG);
splice @signalled, $i, 1;
}
}
return if(not scalar(@signalled));
select(undef, undef, undef, 0.25);
}
# Mercilessly SIGKILL processes still alive.
foreach my $pid (@signalled) {
print("RUN: Process with pid $pid forced to die with SIGKILL\n")
if($verbose);
kill("KILL", $pid);
waitpid($pid, 0);
}
}
#############################################################################
# Kill a specific slave
#
sub ftpkillslave {
my ($id, $ext, $verbose)=@_;
my $base;
my $pidlist;
my @pidfiles;
for $base (('filt', 'data')) {
my $f = ".sock$base$id$ext.pid";
my $pid = processexists($f);
if($pid > 0) {
printf ("* kill pid for %s => %d\n", "ftp-$base$id$ext", $pid) if($verbose);
kill (9, $pid); # die!
waitpid($pid, 0);
printf ("* kill pid for %s => %d\n", "ftp-$base$id$ext", $pid)
if($verbose);
$pidlist .= "$pid ";
}
unlink($f);
push @pidfiles, $f;
}
killpid($verbose, $pidlist);
foreach my $pidfile (@pidfiles) {
unlink($pidfile);
}
}
#############################################################################
# Make sure no FTP leftovers are still running. Kill all slave processes.
# This uses pidfiles since it might be used by other processes.
#
sub ftpkillslaves {
my ($versbose) = @_;
for $ext (("", "ipv6")) {
for $id (("", "2")) {
ftpkillslave ($id, $ext, $verbose);
my ($verbose) = @_;
my $pidlist;
my @pidfiles;
for $ext (('', 'ipv6')) {
for $id (('', '2')) {
for $base (('filt', 'data')) {
my $f = ".sock$base$id$ext.pid";
my $pid = processexists($f);
if($pid > 0) {
printf ("* kill pid for %s => %d\n", "ftp-$base$id$ext",
$pid) if($verbose);
$pidlist .= "$pid ";
}
push @pidfiles, $f;
}
}
}
killpid($verbose, $pidlist);
foreach my $pidfile (@pidfiles) {
unlink($pidfile);
}
}

View File

@ -39,25 +39,55 @@
use strict;
use IPC::Open2;
#use Time::HiRes qw( gettimeofday ); # not available in perl 5.6
require "getpart.pm";
require "ftp.pm";
BEGIN {
if($] >= 5.006) {
use Time::HiRes qw( gettimeofday );
}
}
my $ftpdnum="";
# open and close each time to allow removal at any time
my $logfilename = 'log/logfile.log'; # Override this for each test server
#######################################################################
# getlogfilename returns a log file name depending on given arguments.
#
sub getlogfilename {
my ($proto, $ipversion, $ssl, $instance, $sockfilter) = @_;
my $filename;
# For now, simply mimic old behavior.
$filename = "log/ftpd$ftpdnum.log";
return $filename;
}
#######################################################################
# logmsg is general message logging subroutine for our test servers.
#
sub logmsg {
# if later than perl 5.6 is used
# my ($seconds, $microseconds) = gettimeofday;
my $seconds = time();
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
localtime($seconds);
open(FTPLOG, ">>log/ftpd$ftpdnum.log");
printf FTPLOG ("%02d:%02d:%02d ", $hour, $min, $sec);
print FTPLOG @_;
close(FTPLOG);
my $now;
if($] >= 5.006) {
my ($seconds, $usec) = gettimeofday();
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
localtime($seconds);
$now = sprintf("%02d:%02d:%02d.%06d ", $hour, $min, $sec, $usec);
}
else {
my $seconds = time();
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
localtime($seconds);
$now = sprintf("%02d:%02d:%02d ", $hour, $min, $sec);
}
if(open(LOGFILEFH, ">>$logfilename")) {
print LOGFILEFH $now;
print LOGFILEFH @_;
close(LOGFILEFH);
}
}
sub ftpmsg {
@ -138,7 +168,7 @@ if($proto !~ /^(ftp|imap|pop3|smtp)\z/) {
sub catch_zap {
my $signame = shift;
ftpkillslaves(1);
ftpkillslaves($verbose);
unlink($pidfile);
if($serverlogslocked) {
$serverlogslocked = 0;
@ -167,8 +197,7 @@ sub sysread_or_die {
($fcaller, $lcaller) = (caller)[1,2];
logmsg "Failed to read input\n";
logmsg "Error: ftp$ftpdnum$ext sysread error: $!\n";
kill(9, $sfpid);
waitpid($sfpid, 0);
killpid($verbose, $sfpid);
logmsg "Exited from sysread_or_die() at $fcaller " .
"line $lcaller. ftp$ftpdnum$ext sysread error: $!\n";
unlink($pidfile);
@ -182,8 +211,7 @@ sub sysread_or_die {
($fcaller, $lcaller) = (caller)[1,2];
logmsg "Failed to read input\n";
logmsg "Error: ftp$ftpdnum$ext read zero\n";
kill(9, $sfpid);
waitpid($sfpid, 0);
killpid($verbose, $sfpid);
logmsg "Exited from sysread_or_die() at $fcaller " .
"line $lcaller. ftp$ftpdnum$ext read zero\n";
unlink($pidfile);
@ -209,8 +237,7 @@ sub startsf {
if($pong !~ /^PONG/) {
logmsg "Failed sockfilt command: $cmd\n";
kill(9, $sfpid);
waitpid($sfpid, 0);
killpid($verbose, $sfpid);
unlink($pidfile);
if($serverlogslocked) {
$serverlogslocked = 0;
@ -220,6 +247,8 @@ sub startsf {
}
}
$logfilename = getlogfilename();
startsf();
logmsg sprintf("%s server listens on port IPv%d/$port\n", uc($proto),
@ -716,8 +745,7 @@ sub PASV_command {
my $prev = processexists($pidf);
if($prev > 0) {
print "kill existing server: $prev\n" if($verbose);
kill(9, $prev);
waitpid($prev, 0);
killpid($verbose, $prev);
}
# We fire up a new sockfilt to do the data transfer for us.
@ -730,8 +758,7 @@ sub PASV_command {
sysread_or_die(\*DREAD, \$pong, 5);
if($pong !~ /^PONG/) {
kill(9, $slavepid);
waitpid($slavepid, 0);
killpid($verbose, $slavepid);
sendcontrol "500 no free ports!\r\n";
logmsg "failed to run sockfilt for data connection\n";
return 0;
@ -865,8 +892,7 @@ sub PORT_command {
if($pong !~ /^PONG/) {
logmsg "Failed sockfilt for data connection\n";
kill(9, $slavepid);
waitpid($slavepid, 0);
killpid($verbose, $slavepid);
}
logmsg "====> Client DATA connect to port $port\n";
@ -986,8 +1012,7 @@ while(1) {
# flush data:
$| = 1;
kill(9, $slavepid) if($slavepid);
waitpid($slavepid, 0) if($slavepid);
killpid($verbose, $slavepid);
$slavepid=0;
&customize(); # read test control instructions

View File

@ -61,7 +61,6 @@ BEGIN {
}
use strict;
#use Time::HiRes qw( gettimeofday );
#use warnings;
use Cwd;
@ -240,19 +239,12 @@ my $torture;
my $tortnum;
my $tortalloc;
# open and close each time to allow removal at any time
#######################################################################
# logmsg is our general message logging subroutine.
#
sub logmsg {
# uncomment the Time::HiRes usage for this
# my ($seconds, $microseconds) = gettimeofday;
# my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
# localtime($seconds);
my $t;
if(1) {
# $t = sprintf ("%02d:%02d:%02d.%06d ", $hour, $min, $sec,
# $microseconds);
}
for(@_) {
print "${t}$_";
print "$_";
}
}
@ -272,7 +264,7 @@ $ENV{'HOME'}=$pwd;
sub catch_zap {
my $signame = shift;
logmsg "runtests.pl received SIG$signame, exiting\n";
stopservers(1);
stopservers($verbose);
die "Somebody sent me a SIG$signame";
}
$SIG{INT} = \&catch_zap;
@ -550,45 +542,9 @@ sub torture {
# stop the given test server (pid)
#
sub stopserver {
my ($pid) = @_;
my ($pidlist) = @_;
if(not defined $pid || $pid <= 0) {
return; # whad'da'ya wanna'da with no pid ?
}
# It might be more than one pid
# Send each one a SIGTERM to gracefully kill it
my @killed;
my @pids = split(/\s+/, $pid);
for (@pids) {
chomp($_);
if($_ =~ /^(\d+)$/) {
if(($1 > 0) && kill(0, $1)) {
if($verbose) {
logmsg "RUN: Test server pid $1 signalled to die\n";
}
kill(15, $1); # die!
push @killed, $1;
}
}
}
# Give each process killed up to a few seconds to die, then send
# a SIGKILL to finish it off for good.
for (@killed) {
my $count = 5; # wait for this many seconds for server to die
while($count--) {
if (!kill(0, $_) || checkdied($_)) {
last;
}
sleep(1);
}
if ($count < 0) {
logmsg "RUN: forcing pid $_ to die with SIGKILL\n";
kill(9, $_); # die!
}
}
killpid($verbose, $pidlist);
}
#######################################################################
@ -2554,23 +2510,26 @@ sub singletest {
# Stop all running test servers
sub stopservers {
my ($verbose)=@_;
my $pidlist;
for(keys %run) {
my $server = $_;
my $pids=$run{$server};
my $pid;
my $prev;
foreach $pid (split(" ", $pids)) {
foreach $pid (split(/\s+/, $pids)) {
if($pid != $prev) {
# no need to kill same pid twice!
logmsg sprintf("* kill pid for %s => %d\n",
$server, $pid) if($verbose);
stopserver($pid);
$pidlist .= "$pid ";
$prev = $pid;
}
$prev = $pid;
}
delete $run{$server};
}
killpid($verbose, $pidlist);
ftpkillslaves($verbose);
}

View File

@ -86,7 +86,7 @@ void logmsg(const char *msg, ...)
vsnprintf(buffer, sizeof(buffer), msg, ap);
va_end(ap);
logfp = fopen(serverlogfile, "a");
logfp = fopen(serverlogfile, "ab");
if(logfp) {
fprintf(logfp, "%s %s\n", timebuf, buffer);
fclose(logfp);
@ -221,7 +221,7 @@ int write_pidfile(const char *filename)
long pid;
pid = (long)getpid();
pidfile = fopen(filename, "w");
pidfile = fopen(filename, "wb");
if(!pidfile) {
logmsg("Couldn't write pid file: %s %s", filename, strerror(ERRNO));
return 0; /* fail */