Commit b7131009 authored by Dan Fandrich's avatar Dan Fandrich
Browse files

Changed the test harness to attempt to gracefully shut down servers

before resorting to the kill -9 hammer.

Added test harness infrastructure to support scp/sftp tests, using
OpenSSH as the server.
parent 3d528e1b
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -10,6 +10,12 @@ Dan F (23 March 2007)
- Added --pubkey option to curl and made --key also work for SCP/SFTP,
  plus made --pass work on an SSH private key as well.

- Changed the test harness to attempt to gracefully shut down servers
  before resorting to the kill -9 hammer.

- Added test harness infrastructure to support scp/sftp tests, using
  OpenSSH as the server.

Yang Tse (20 March 2007)
- Fixed: When a signal was caught awaiting for an event using Curl_select()
  or Curl_poll() with a non-zero timeout both functions would restart the
+4 −0
Original line number Diff line number Diff line
@@ -126,6 +126,8 @@ http
http-ipv6
https
none
scp
sftp

Give only one per line.  This subsection is mandatory.
</server>
@@ -212,9 +214,11 @@ Available substitute variables include:
%FTP2PORT  - Port number of the FTP server 2
%TFTPPORT  - Port number of the TFTP server
%TFTP6PORT - IPv6 port number of the TFTP server
%SSHPORT   - Port number of the SCP/SFTP server
%SRCDIR    - Full path to the source dir
%PWD       - Current directory
%CURL      - Path to the curl executable
%USER      - Login ID of the user running the test
</command>

<file name="log/filename">
+117 −4
Original line number Diff line number Diff line
@@ -48,6 +48,7 @@ my $FTPSPORT; # FTPS server port
my $FTP6PORT; # FTP IPv6 server port
my $TFTPPORT; # TFTP
my $TFTP6PORT; # TFTP
my $SSHPORT; # SCP/SFTP

my $CURL="../src/curl"; # what curl executable to run on the tests
my $DBGCURL=$CURL; #"../src/.libs/curl";  # alternative for debugging
@@ -79,6 +80,7 @@ my $FTP2PIDFILE=".ftp2.pid";
my $FTPSPIDFILE=".ftps.pid";
my $TFTPPIDFILE=".tftpd.pid";
my $TFTP6PIDFILE=".tftp6.pid";
my $SSHPIDFILE=".ssh.pid";

# invoke perl like this:
my $perl="perl -I$srcdir";
@@ -197,6 +199,15 @@ sub logmsg {

chomp($pwd = `pwd`);

# get the name of the current user
my $USER = $ENV{USER};	# Linux
if (!$USER) {
    $USER = $ENV{USERNAME};	# Windows
    if (!$USER) {
        $USER = $ENV{LOGNAME};	# Some UNIX (I think)
    }
}

# enable memory debugging if curl is compiled with it
$ENV{'CURL_MEMDEBUG'} = $memdump;
$ENV{'HOME'}=$pwd;
@@ -230,6 +241,16 @@ $ENV{'SSL_CERT_DIR'}=undef;
$ENV{'SSL_CERT_PATH'}=undef;
$ENV{'CURL_CA_BUNDLE'}=undef;

#######################################################################
# Check if a given child process has just died. Reaps it if so.
#
sub checkdied {
    use POSIX ":sys_wait_h";
    my $pid = $_[0];
    my $rc = waitpid($pid, &WNOHANG);
    return $rc == $pid;
}

#######################################################################
# Start a new thread/process and run the given command line in there.
# Return the pids (yes plural) of the new child process to the parent.
@@ -277,6 +298,15 @@ sub startnew {
                last;
            }
        }
        if (checkdied($child)) {
            logmsg "startnew: Warning: child process has died\n" if($verbose);
            # We can't just abort waiting for the server with a
            # return (-1,-1);
            # because the server might have forked and could still start
            # up normally. Instead, just reduce the amount of time we remain
            # waiting.
            $count >>= 2;
        }
        sleep(1);
    }

@@ -411,8 +441,10 @@ sub stopserver {
        return; # whad'da'ya wanna'da with no pid ?
    }

    # it might be more than one 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($_);
@@ -421,8 +453,25 @@ sub stopserver {
                if($verbose) {
                    logmsg "RUN: Test server pid $1 signalled to die\n";
                }
                kill(9, $1); # die!
                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!
        }
    }
}
@@ -520,6 +569,17 @@ sub verifyftp {
    return $pid;
}

#######################################################################
# STUB for verifying scp/sftp

sub verifyssh {
    my ($proto, $ip, $port) = @_;
    open(FILE, "<" . $SSHPIDFILE);
    my $pid=0+<FILE>;
    close(FILE);
    return $pid;
}

#######################################################################
# Verify that the server that runs on $ip, $port is our server.
# Retry during 5 seconds before giving up.
@@ -529,7 +589,8 @@ my %protofunc = ('http' => \&verifyhttp,
                 'https' => \&verifyhttp,
                 'ftp' => \&verifyftp,
                 'ftps' => \&verifyftp,
                 'tftp' => \&verifyftp);
                 'tftp' => \&verifyftp,
                 'ssh' => \&verifyssh);

sub verifyserver {
    my ($proto, $ip, $port) = @_;
@@ -852,6 +913,44 @@ sub runtftpserver {
}


#######################################################################
# Start the scp/sftp server
#
sub runsshserver {
    my ($id, $verbose, $ipv6) = @_;
    my $ip=$HOSTIP;
    my $port = $SSHPORT;
    my $pidfile = $SSHPIDFILE;

    my $pid = checkserver($pidfile);
    if($pid > 0) {
        stopserver($pid);
    }

    my $flag=$debugprotocol?"-v ":"";
    my $cmd="$perl $srcdir/sshserver.pl $flag-u $USER -d $srcdir $port";
    my ($sshpid, $pid2) =
        startnew($cmd, $pidfile); # start the server in a new process

    if(!$sshpid || !kill(0, $sshpid)) {
        # it is NOT alive
        logmsg "RUN: failed to start the SSH server!\n";
        # failed to talk to it properly. Kill the server and return failure
        stopserver("$sshpid $pid2");
        return -1;
    }

    if (!verifyserver('ssh',$ip,$port)) {
        logmsg "RUN: SSH server failed verification\n";
        return (0,0);
    }
    if($verbose) {
        logmsg "RUN: SSH server is now running PID $sshpid\n";
    }

    return ($pid2, $sshpid);
}

#######################################################################
# Remove all files in the specified directory
#
@@ -1167,9 +1266,10 @@ sub checksystem {
    if($tftp_ipv6) {
        logmsg sprintf("* TFTP IPv6 port: %d\n", $TFTP6PORT);
    }
    logmsg sprintf("* SCP/SFTP port:  %d\n", $SSHPORT);

    if($ssl_version) {
        logmsg sprintf("* SSL library:    %s\n", $ssllib?"yassl":"<unknown>");
        logmsg sprintf("* SSL library:    %s\n", $ssllib);
    }

    $has_textaware = ($^O eq 'MSWin32') || ($^O eq 'msys');
@@ -1197,7 +1297,9 @@ sub subVariables {
  $$thing =~ s/%PWD/$pwd/g;
  $$thing =~ s/%TFTPPORT/$TFTPPORT/g;
  $$thing =~ s/%TFTP6PORT/$TFTP6PORT/g;
  $$thing =~ s/%SSHPORT/$SSHPORT/g;
  $$thing =~ s/%CURL/$CURL/g;
  $$thing =~ s/%USER/$USER/g;

  # The purpose of FTPTIME2 and FTPTIME3 is to provide times that can be
  # used for time-out tests and that whould work on most hosts as these
@@ -2058,6 +2160,16 @@ sub startservers {
                $run{'tftp-ipv6'}="$pid $pid2";
            }
        }
        elsif($what eq "sftp" || $what eq "scp") {
            if(!$run{'ssh'}) {
                ($pid, $pid2) = runsshserver("", $verbose);
                if($pid <= 0) {
                    return "failed starting SSH server";
                }
                printf ("* pid ssh => %d %d\n", $pid, $pid2) if($verbose);
                $run{'ssh'}="$pid $pid2";
            }
        }
        elsif($what eq "none") {
            logmsg "* starts no server\n" if ($verbose);
        }
@@ -2244,6 +2356,7 @@ $FTP2PORT = $base + 5; # FTP server 2 port
$FTP6PORT =  $base + 6; # FTP IPv6 port
$TFTPPORT =  $base + 7; # TFTP (UDP) port
$TFTP6PORT =  $base + 8; # TFTP IPv6 (UDP) port
$SSHPORT =   $base + 9; # SSH (SCP/SFTP) port

#######################################################################
# clear and create logging directory:

tests/sshserver.pl

0 → 100644
+138 −0
Original line number Diff line number Diff line
#/usr/bin/env perl
# $Id$
# Start sshd for use in the SCP and SFTP curl test harness tests

# Options:
# -u user
# -v
# target_port

use strict;
use File::Spec;

my $verbose=0; # set to 1 for debugging

my $port = 8999;        # just our default, weird enough

my $path = `pwd`;
chomp $path;

my $exeext;
if ($^O eq 'MSWin32' || $^O eq 'cygwin' || $^O eq 'msys' || $^O eq 'dos' || $^O eq 'os2') {
    $exeext = '.exe';
}

# Where to look for sftp-server
my @sftppath=qw(/usr/lib/openssh /usr/libexec/openssh /usr/libexec /usr/local/libexec /opt/local/libexec /usr/lib/ssh /usr/libexec/ssh /usr/sbin /usr/lib /usr/lib/ssh/openssh /usr/lib64/ssh);

my $username = $ENV{USER};

# Find a file somewhere in the given path
sub searchpath {
  my $fn = $_[0] . $exeext;
  shift;
  my @path = @_;
  foreach (@path) {
	my $file = File::Spec->catfile($_, $fn);
	if (-e $file) {
		return $file;
	}
  }
}

# Parse options
do {
    if($ARGV[0] eq "-v") {
        $verbose=1;
    }
    elsif($ARGV[0] eq "-u") {
        $username=$ARGV[1];
        shift @ARGV;
    }
    elsif($ARGV[0] =~ /^(\d+)$/) {
        $port = $1;
    }
} while(shift @ARGV);

my $conffile="curl_sshd_config";	# sshd configuration data

# Search the PATH for sshd.  sshd insists on being called with an absolute
# path for some reason.
my $sshd = searchpath("sshd", File::Spec->path());
if (!$sshd) {
	print "sshd is not available\n";
	exit 1;
}
if ($verbose) {
	print STDERR "SSH server found at $sshd\n";
}

my $sftp = searchpath("sftp-server", @sftppath);
if (!$sftp) {
	print "Could not find sftp-server plugin\n";
	exit 1;
}
if ($verbose) {
	print STDERR "SFTP server plugin found at $sftp\n";
}

if (! -e "curl_client_key.pub") {
	if ($verbose) {
		print STDERR "Generating host and client keys...\n";
	}
	# Make sure all files are gone so ssh-keygen doesn't complain
	unlink("curl_host_dsa_key", "curl_client_key","curl_host_dsa_key.pub", "curl_client_key.pub"); 
	system "ssh-keygen -q -t dsa -f curl_host_dsa_key -C 'curl test server' -N ''" and die "Could not generate key";
        system "ssh-keygen -q -t dsa -f curl_client_key -C 'curl test client' -N ''" and die "Could not generate key";
}

open(FILE, ">$conffile") || die "Could not write $conffile";
print FILE <<EOF
# This is a generated file!  Do not edit!
# OpenSSH sshd configuration file for curl testing
AllowUsers $username
AuthorizedKeysFile $path/curl_client_key.pub
HostKey $path/curl_host_dsa_key
PidFile $path/.ssh.pid
Port $port
ListenAddress localhost
Protocol 2
AllowTcpForwarding no
HostbasedAuthentication no
IgnoreRhosts yes
IgnoreUserKnownHosts yes
KeepAlive no
PasswordAuthentication no
PermitEmptyPasswords no
PermitRootLogin no
PrintLastLog no
PrintMotd no
StrictModes no
Subsystem sftp $sftp
UseLogin no
X11Forwarding no
# Newer OpenSSH options
UsePam no
UseDNS no
ChallengeResponseAuthentication no
EOF
;
close FILE;

if (system "$sshd -t -q -f $conffile") {
	# This is likely due to missing support for UsePam
	print "$sshd is too old and is not supported\n";
	unlink $conffile;
	exit 1;
}

# Start the server
my $rc = system "$sshd -e -f $conffile > log/ssh.log 2>&1";
$rc >>= 8;
if($rc) {
    print STDERR "$sshd exited with $rc!\n";
}

unlink $conffile;

exit $rc;