X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/4311097ecf0db28101920522340c59469582529e..817d9f576cdfbc27cf0536be348645baf27d7836:/test/runtest diff --git a/test/runtest b/test/runtest index 4e511dc36..b88e71e60 100755 --- a/test/runtest +++ b/test/runtest @@ -1,7 +1,5 @@ #! /usr/bin/perl -w -# $Cambridge: exim/test/runtest,v 1.24 2007/02/08 15:16:19 ph10 Exp $ - ############################################################################### # This is the controlling script for the "new" test suite for Exim. It should # # be possible to export this suite for running on a wide variety of hosts, in # @@ -15,17 +13,27 @@ # Placed in the Exim CVS: 06 February 2006 # ############################################################################### +#use strict; require Cwd; use Errno; use FileHandle; use Socket; +use Time::Local; # Start by initializing some global variables -$testversion = "4.67 (15-Jan-07)"; +$testversion = "4.80 (08-May-12)"; + +# This gets embedded in the D-H params filename, and the value comes +# from asking GnuTLS for "normal", but there appears to be no way to +# use certtool/... to ask what that value currently is. *sigh* +# We also clamp it because of NSS interop, see addition of tls_dh_max_bits. +# This value is correct as of GnuTLS 2.12.18 as clamped by tls_dh_max_bits. +# normal = 2432 tls_dh_max_bits = 2236 +$gnutls_dh_bits_normal = 2236; -$cf = "bin/cf"; +$cf = "bin/cf -exact"; $cr = "\r"; $debug = 0; $force_update = 0; @@ -67,6 +75,9 @@ $parm_port_d2 = 1226; # Additional for daemon $parm_port_d3 = 1227; # Additional for daemon $parm_port_d4 = 1228; # Additional for daemon +# Manually set locale +$ENV{'LC_ALL'} = 'C'; + ############################################################################### @@ -120,6 +131,12 @@ s?\bV6NET:?$parm_ipv6_test_net:?g; } +################################################## +# Any state to be preserved across tests # +################################################## + +my $TEST_STATE = {}; + ################################################## # Subroutine to tidy up and exit # @@ -142,6 +159,13 @@ my($spool); # than SIGTERM to stop it outputting "Terminated" to the terminal when not in # the background. +if (exists $TEST_STATE->{exim_pid}) + { + $pid = $TEST_STATE->{exim_pid}; + print "Tidyup: killing wait-mode daemon pid=$pid\n"; + system("sudo kill -SIGINT $pid"); + } + if (opendir(DIR, "spool")) { my(@spools) = sort readdir(DIR); @@ -168,6 +192,8 @@ system("sudo /bin/rm -rf ./spool test-* ./dnszones/*") if ($rc == 0 && !$save_output); system("sudo /bin/rm -rf ./eximdir/*"); + +print "\nYou were in test $test at the end there.\n\n" if defined $test; exit $rc if ($rc >= 0); die "** runtest error: $_[1]\n"; } @@ -198,14 +224,26 @@ return $newid; } -# This is used while munging the output from exim_dumpdb. We cheat by assuming -# that the date always the same, and just return the number of seconds since -# midnight. +# This is used while munging the output from exim_dumpdb. +# May go wrong across DST changes. sub date_seconds { my($day,$month,$year,$hour,$min,$sec) = $_[0] =~ /^(\d\d)-(\w\w\w)-(\d{4})\s(\d\d):(\d\d):(\d\d)/; -return $hour * 60 * 60 + $min * 60 + $sec; +my($mon); +if ($month =~ /Jan/) {$mon = 0;} +elsif($month =~ /Feb/) {$mon = 1;} +elsif($month =~ /Mar/) {$mon = 2;} +elsif($month =~ /Apr/) {$mon = 3;} +elsif($month =~ /May/) {$mon = 4;} +elsif($month =~ /Jun/) {$mon = 5;} +elsif($month =~ /Jul/) {$mon = 6;} +elsif($month =~ /Aug/) {$mon = 7;} +elsif($month =~ /Sep/) {$mon = 8;} +elsif($month =~ /Oct/) {$mon = 9;} +elsif($month =~ /Nov/) {$mon = 10;} +elsif($month =~ /Dec/) {$mon = 11;} +return timelocal($sec,$min,$hour,$day,$mon,$year); } @@ -299,6 +337,7 @@ $spid = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; while(<IN>) { +RESET_AFTER_EXTRA_LINE_READ: # Check for "*** truncated ***" $yield = 1 if /\*\*\* truncated \*\*\*/; @@ -308,11 +347,15 @@ while(<IN>) # But convert "name=the.local.host address=127.0.0.1" to use "localhost" s/name=the\.local\.host address=127\.0\.0\.1/name=localhost address=127.0.0.1/g; + # The name of the shell may vary + s/\s\Q$parm_shell\E\b/ ENV_SHELL/; + # Replace the path to the testsuite directory s?\Q$parm_cwd\E?TESTSUITE?g; # Replace the Exim version number (may appear in various places) - s/Exim \d+\.\d+[\w-]*/Exim x.yz/i; + # patchexim should have fixed this for us + #s/(Exim) \d+\.\d+[\w_-]*/$1 x.yz/i; # Replace Exim message ids by a unique series s/((?:[^\W_]{6}-){2}[^\W_]{2}) @@ -330,7 +373,8 @@ while(<IN>) s/TlRMTVNTUAACAAAAAAAAAAAoAAABgg[\w+\/]+/TlRMTVNTUAACAAAAAAAAAAAoAAABggAAAEbBRwqFwwIAAAAAAAAAAAAt1sgAAAAA/; # PRVS values - s?prvs=([^/]+)/[\da-f]{10}@?prvs=$1/xxxxxxxxxx@?g; + s?prvs=([^/]+)/[\da-f]{10}@?prvs=$1/xxxxxxxxxx@?g; # Old form + s?prvs=[\da-f]{10}=([^@]+)@?prvs=xxxxxxxxxx=$1@?g; # New form # Error lines on stdout from SSL contain process id values and file names. # They also contain a source file name and line number, which may vary from @@ -338,6 +382,9 @@ while(<IN>) s/^\d+:error:/pppp:error:/; s/:(?:\/[^\s:]+\/)?([^\/\s]+\.c):\d+:/:$1:dddd:/; + # There are differences in error messages between OpenSSL versions + s/SSL_CTX_set_cipher_list/SSL_connect/; + # One error test in expansions mentions base 62 or 36 s/is not a base (36|62) number/is not a base 36\/62 number/; @@ -350,9 +397,6 @@ while(<IN>) # The message for a non-listening FIFO varies s/:[^:]+: while opening named pipe/: Error: while opening named pipe/; - # The name of the shell may vary - s/\s\Q$parm_shell\E\b/ SHELL/; - # Debugging output of lists of hosts may have different sort keys s/sort=\S+/sort=xx/ if /^\S+ (?:\d+\.){3}\d+ mx=\S+ sort=\S+/; @@ -437,7 +481,43 @@ while(<IN>) \d{4}-\d\d-\d\d\s\d\d:\d\d:\d\d/Exim statistics from <time> to <time>/x; - # ======== Caller's login, uid, gid, home ======== + # ======== TLS certificate algorithms ======== + # Test machines might have various different TLS library versions supporting + # different protocols; can't rely upon TLS 1.2's AES256-GCM-SHA384, so we + # treat the standard algorithms the same. + # So far, have seen: + # TLSv1:AES256-SHA:256 + # TLSv1.2:AES256-GCM-SHA384:256 + # TLSv1.2:DHE-RSA-AES256-SHA:256 + # TLS1.2:DHE_RSA_AES_128_CBC_SHA1:128 + # We also need to handle the ciphersuite without the TLS part present, for + # client-ssl's output. We also see some older forced ciphersuites, but + # negotiating TLS 1.2 instead of 1.0. + # Mail headers (...), log-lines X=..., client-ssl output ... + # (and \b doesn't match between ' ' and '(' ) + + s/( (?: (?:\b|\s) [\(=] ) | \s )TLSv1\.2:/$1TLSv1:/xg; + s/\bAES256-GCM-SHA384\b/AES256-SHA/g; + s/\bDHE-RSA-AES256-SHA\b/AES256-SHA/g; + + # GnuTLS have seen: + # TLS1.2:RSA_AES_256_CBC_SHA1:256 (canonical) + # TLS1.2:DHE_RSA_AES_128_CBC_SHA1:128 + # + # X=TLS1.2:DHE_RSA_AES_256_CBC_SHA256:256 + # X=TLS1.2:RSA_AES_256_CBC_SHA1:256 + # X=TLS1.1:RSA_AES_256_CBC_SHA1:256 + # X=TLS1.0:DHE_RSA_AES_256_CBC_SHA1:256 + # and as stand-alone cipher: + # DHE-RSA-AES256-SHA256 + # DHE-RSA-AES256-SHA + # picking latter as canonical simply because regex easier that way. + s/\bDHE_RSA_AES_128_CBC_SHA1:128/RSA_AES_256_CBC_SHA1:256/g; + s/TLS1.[012]:(DHE_)?RSA_AES_256_CBC_SHA(1|256):256/TLS1.x:xxxxRSA_AES_256_CBC_SHAnnn:256/g; + s/\bDHE-RSA-AES256-SHA256\b/DHE-RSA-AES256-SHA/g; + + + # ======== Caller's login, uid, gid, home, gecos ======== s/\Q$parm_caller_home\E/CALLER_HOME/g; # NOTE: these must be done s/\b\Q$parm_caller\E\b/CALLER/g; # in this order! @@ -449,6 +529,8 @@ while(<IN>) s/\buid=$parm_caller_uid\b/uid=CALLER_UID/g; s/\bgid=$parm_caller_gid\b/gid=CALLER_GID/g; + s/\bname=$parm_caller_gecos\b/name=CALLER_GECOS/g; + # When looking at spool files with -Mvh, we will find not only the caller # login, but also the uid and gid. It seems that $) in some Perls gives all # the auxiliary gids as well, so don't bother checking for that. @@ -463,9 +545,10 @@ while(<IN>) # ======== Exim's login ======== - # For bounce messages, this will appear on the U= lines in logs and also - # after Received: and in addresses. In one pipe test it appears after - # "Running as:". It also appears in addresses, and in the names of lock + # For messages received by the daemon, this is in the -H file, which some + # tests inspect. For bounce messages, this will appear on the U= lines in + # logs and also after Received: and in addresses. In one pipe test it appears + # after "Running as:". It also appears in addresses, and in the names of lock # files. s/U=$parm_eximuser/U=EXIMUSER/; @@ -482,10 +565,18 @@ while(<IN>) s/\buid=$parm_exim_uid\b/uid=EXIM_UID/g; s/\bgid=$parm_exim_gid\b/gid=EXIM_GID/g; + s/^$parm_eximuser $parm_exim_uid $parm_exim_gid/EXIMUSER EXIM_UID EXIM_GID/; + # ======== General uids, gids, and pids ======== # Note: this must come after munges for caller's and exim's uid/gid + # These are for systems where long int is 64 + s/\buid=4294967295/uid=-1/; + s/\beuid=4294967295/euid=-1/; + s/\bgid=4294967295/gid=-1/; + s/\begid=4294967295/egid=-1/; + s/\bgid=\d+/gid=gggg/; s/\begid=\d+/egid=gggg/; s/\bpid=\d+/pid=pppp/; @@ -535,6 +626,9 @@ while(<IN>) # The amount of space between "host" and the address in verification output # depends on the length of the host name. We therefore reduce it to one space # for all of them. + # Also, the length of space at the end of the host line is dependent + # on the length of the longest line, so strip it also on otherwise + # un-rewritten lines like localhost s/^\s+host\s(\S+)\s+(\S+)/ host $1 $2/; s/^\s+(host\s\S+\s\S+)\s+(port=.*)/ host $1 $2/; @@ -542,9 +636,10 @@ while(<IN>) s/host\s\Q$parm_ipv4\E\s\[\Q$parm_ipv4\E\]/host ipv4.ipv4.ipv4.ipv4 [ipv4.ipv4.ipv4.ipv4]/; s/host\s\Q$parm_ipv6\E\s\[\Q$parm_ipv6\E\]/host ip6:ip6:ip6:ip6:ip6:ip6:ip6:ip6 [ip6:ip6:ip6:ip6:ip6:ip6:ip6:ip6]/; s/\b\Q$parm_ipv4\E\b/ip4.ip4.ip4.ip4/g; - s/\b\Q$parm_ipv6\E\b/ip6:ip6:ip6:ip6:ip6:ip6:ip6:ip6/g; + s/(^|\W)\K\Q$parm_ipv6\E/ip6:ip6:ip6:ip6:ip6:ip6:ip6:ip6/g; s/\b\Q$parm_ipv4r\E\b/ip4-reverse/g; - s/\b\Q$parm_ipv6r\E\b/ip6-reverse/g; + s/(^|\W)\K\Q$parm_ipv6r\E/ip6-reverse/g; + s/^(\s+host\s\S+\s+\[\S+\]) +$/$1 /; # ======== Test network IP addresses ======== @@ -574,7 +669,14 @@ while(<IN>) # ======== Output from ls ======== # Different operating systems use different spacing on long output - s/ +/ /g if /^[-rwd]{10} /; + #s/ +/ /g if /^[-rwd]{10} /; + # (Bug 1226) SUSv3 allows a trailing printable char for modified access method control. + # Handle only the Gnu and MacOS space, dot, plus and at-sign. A full [[:graph:]] + # unfortunately matches a non-ls linefull of dashes. + # Allow the case where we've already picked out the file protection bits. + if (s/^([-d](?:[-r][-w][-SsTtx]){3})[.+@]?( +|$)/$1$2/) { + s/ +/ /g; + } # ======== Message sizes ========= @@ -585,7 +687,7 @@ while(<IN>) s/([\s,])S=\d+\b/$1S=sss/; s/:S\d+\b/:Ssss/; s/^(\s*\d+m\s+)\d+(\s+[a-z0-9-]{16} <)/$1sss$2/i if $is_stdout; - s/\sSIZE=\d+\b/ SIZE=ssss/ if $is_stderr || $is_stdout; + s/\sSIZE=\d+\b/ SIZE=ssss/; s/\ssize=\d+\b/ size=sss/ if $is_stderr; s/old size = \d+\b/old size = sssss/; s/message size = \d+\b/message size = sss/; @@ -620,7 +722,6 @@ while(<IN>) s/(TLS error on connection (?:from|to) .*? \(SSL_\w+\): error:)(.*)/$1 <<detail omitted>>/; - # ======== Maildir things ======== # timestamp output in maildir processing s/(timestamp=|\(timestamp_only\): )\d+/$1ddddddd/g; @@ -663,6 +764,12 @@ while(<IN>) s/^\d\d\d(?=[PFS*])/ddd/; + # ========= Exim lookups ================== + # Lookups have a char which depends on the number of lookup types compiled in, + # in stderr output. Replace with a "0". Recognising this while avoiding + # other output is fragile; perhaps the debug output should be revised instead. + s%(?<!sqlite)(?<!lsearch\*@)(?<!lsearch\*)(?<!lsearch)[0-?]TESTSUITE/aux-fixed/%0TESTSUITE/aux-fixed/%g; + # ========================================================== # Some munging is specific to the specific file types @@ -710,6 +817,55 @@ while(<IN>) s/\bgethostbyname2?|\bgetipnodebyname/get[host|ipnode]byname[2]/; + # drop gnutls version strings + next if /GnuTLS compile-time version: \d+[\.\d]+$/; + next if /GnuTLS runtime version: \d+[\.\d]+$/; + + # drop openssl version strings + next if /OpenSSL compile-time version: OpenSSL \d+[\.\da-z]+/; + next if /OpenSSL runtime version: OpenSSL \d+[\.\da-z]+/; + + # drop lookups + next if /^Lookups \(built-in\):/; + next if /^Loading lookup modules from/; + next if /^Loaded \d+ lookup modules/; + next if /^Total \d+ lookups/; + + # drop compiler information + next if /^Compiler:/; + + # and the ugly bit + # different libraries will have different numbers (possibly 0) of follow-up + # lines, indenting with more data + if (/^Library version:/) { + while (1) { + $_ = <IN>; + next if /^\s/; + goto RESET_AFTER_EXTRA_LINE_READ; + } + } + + # drop other build-time controls emitted for debugging + next if /^WHITELIST_D_MACROS:/; + next if /^TRUSTED_CONFIG_LIST:/; + + # As of Exim 4.74, we log when a setgid fails; because we invoke Exim + # with -be, privileges will have been dropped, so this will always + # be the case + next if /^changing group to \d+ failed: Operation not permitted/; + + # We might not keep this check; rather than change all the tests, just + # ignore it as long as it succeeds; then we only need to change the + # TLS tests where tls_require_ciphers has been set. + if (m{^changed uid/gid: calling tls_validate_require_cipher}) { + my $discard = <IN>; + next; + } + next if /^tls_validate_require_cipher child \d+ ended: status=0x0/; + + # We invoke Exim with -D, so we hit this new messag as of Exim 4.73: + next if /^macros_trusted overridden to true by whitelisting/; + # We have to omit the localhost ::1 address so that all is well in # the IPv4-only case. @@ -717,6 +873,9 @@ while(<IN>) if (/looked up these IP addresses/); next if /name=localhost address=::1/; + # drop pdkim debugging header + next if /^PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<+$/; + # Various other IPv6 lines must be omitted too next if /using host_fake_gethostbyname for \S+ \(IPv6\)/; @@ -865,7 +1024,7 @@ if ($_[1]) { $_ = "u"; print "... update forced\n"; } # [4] TRUE if this is a log file whose deliveries must be sorted # # Returns: 0 comparison succeeded or differences to be ignored -# 1 comparison failed; files were updated (=> re-compare) +# 1 comparison failed; files may have been updated (=> re-compare) # # Does not return if the user replies "Q" to a prompt. @@ -899,7 +1058,7 @@ if (! -e $sf) print "\n"; print "------------ $f -----------\n" if (defined $rf && -s $rf && defined $rsf && -s $rsf); - system("$more $f"); + system("$more '$f'"); } } @@ -1013,7 +1172,7 @@ if (-e $sf) # Do the comparison - return 0 if (system("$cf $mf $sf >test-cf") == 0); + return 0 if (system("$cf '$mf' '$sf' >test-cf") == 0); # Handle comparison failure @@ -1023,9 +1182,10 @@ if (-e $sf) print "\n"; for (;;) { - interact("Continue, Update & retry, Quit? [Q] ", $force_update); + interact("Continue, Retry, Update & retry, Quit? [Q] ", $force_update); tests_exit(1) if /^q?$/i; return 0 if /^c$/i; + return 1 if /^r$/i; last if (/^u$/i); } } @@ -1033,7 +1193,7 @@ if (-e $sf) # Update or delete the saved file, and give the appropriate return code. if (-s $mf) - { tests_exit(-1, "Failed to cp $mf $sf") if system("cp $mf $sf") != 0; } + { tests_exit(-1, "Failed to cp $mf $sf") if system("cp '$mf' '$sf'") != 0; } else { tests_exit(-1, "Failed to unlink $sf") if !unlink($sf); } @@ -1057,7 +1217,7 @@ return 1; # # Arguments: none # Returns: 0 if the output compared equal -# 1 if files were updated and the test must be re-run +# 1 if re-run needed (files may have been updated) sub check_output{ my($yield) = 0; @@ -1284,17 +1444,21 @@ system("$cmd"); # reference to the subtest number, holding previous value # reference to the expected return code value # reference to where to put the command name (for messages) +# auxilliary information returned from a previous run # # Returns: 0 the commmand was executed inline, no subprocess was run # 1 a non-exim command was run and waited for # 2 an exim command was run and waited for # 3 a command was run and not waited for (daemon, server, exim_lock) # 4 EOF was encountered after an initial return code line +# Optionally alse a second parameter, a hash-ref, with auxilliary information: +# exim_pid: pid of a run process sub run_command{ my($testno) = $_[0]; my($subtestref) = $_[1]; my($commandnameref) = $_[3]; +my($aux_info) = $_[4]; my($yield) = 1; if (/^(\d+)\s*$/) # Handle unusual return code @@ -1452,9 +1616,10 @@ if (/^eximstats\s+(.*)/) if (/^gnutls/) { - run_system "sudo cp -p aux-fixed/gnutls-params spool/gnutls-params;" . - "sudo chown $parm_eximuser:$parm_eximgroup spool/gnutls-params;" . - "sudo chmod 0400 spool/gnutls-params"; + my $gen_fn = "spool/gnutls-params-$gnutls_dh_bits_normal"; + run_system "sudo cp -p aux-fixed/gnutls-params $gen_fn;" . + "sudo chown $parm_eximuser:$parm_eximgroup $gen_fn;" . + "sudo chmod 0400 $gen_fn"; return 1; } @@ -1465,11 +1630,27 @@ if (/^gnutls/) if (/^killdaemon/) { - $pid = `cat $parm_cwd/spool/exim-daemon.*`; - run_system("sudo /bin/kill -SIGINT $pid"); - close DAEMONCMD; # Waits for process - run_system("sudo /bin/rm -f spool/exim-daemon.*"); - return 1; + my $return_extra = {}; + if (exists $aux_info->{exim_pid}) + { + $pid = $aux_info->{exim_pid}; + $return_extra->{exim_pid} = undef; + print ">> killdaemon: recovered pid $pid\n" if $debug; + if ($pid) + { + run_system("sudo /bin/kill -SIGINT $pid"); + wait; + } + } else { + $pid = `cat $parm_cwd/spool/exim-daemon.*`; + if ($pid) + { + run_system("sudo /bin/kill -SIGINT $pid"); + close DAEMONCMD; # Waits for process + } + } + run_system("sudo /bin/rm -f spool/exim-daemon.*"); + return (1, $return_extra); } @@ -1697,6 +1878,10 @@ elsif (/^([A-Z_]+=\S+\s+)?(\d+)?\s*(sudo\s+)?exim(_\S+)?\s+(.*)$/) my($i); for ($i = @msglist; $i > 0; $i--) { $args =~ s/\$msg$i/$msglist[$i-1]/g; } + if ( $args =~ /\$msg\d/ ) + { + tests_exit(-1, "Not enough messages in spool, for test $testno line $lineno\n"); + } } # If -d is specified in $optargs, remove it from $args; i.e. let @@ -1740,6 +1925,38 @@ elsif (/^([A-Z_]+=\S+\s+)?(\d+)?\s*(sudo\s+)?exim(_\S+)?\s+(.*)$/) select(undef, undef, undef, 0.3); # Let the daemon get going return 3; # Don't wait } + elsif ($cmd =~ /\s-DSERVER=wait:(\d+)\s/) + { + my $listen_port = $1; + my $waitmode_sock = new FileHandle; + if ($debug) { printf ">> wait-mode daemon: $cmd\n"; } + run_system("sudo mkdir spool/log 2>/dev/null"); + run_system("sudo chown $parm_eximuser:$parm_eximgroup spool/log"); + + my ($s_ip,$s_port) = ('127.0.0.1', $listen_port); + my $sin = sockaddr_in($s_port, inet_aton($s_ip)) + or die "** Failed packing $s_ip:$s_port\n"; + socket($waitmode_sock, PF_INET, SOCK_STREAM, getprotobyname('tcp')) + or die "** Unable to open socket $s_ip:$s_port: $!\n"; + setsockopt($waitmode_sock, SOL_SOCKET, SO_REUSEADDR, 1) + or die "** Unable to setsockopt(SO_REUSEADDR): $!\n"; + bind($waitmode_sock, $sin) + or die "** Unable to bind socket ($s_port): $!\n"; + listen($waitmode_sock, 5); + my $pid = fork(); + if (not defined $pid) { die "** fork failed: $!\n" } + if (not $pid) { + close(STDIN); + open(STDIN, "<&", $waitmode_sock) or die "** dup sock to stdin failed: $!\n"; + close($waitmode_sock); + print "[$$]>> ${cmd}-server\n" if ($debug); + exec "exec ${cmd}-server"; + exit(1); + } + while (<SCRIPT>) { $lineno++; last if /^\*{4}\s*$/; } # Ignore any input + select(undef, undef, undef, 0.3); # Let the daemon get going + return (3, { exim_pid => $pid }); # Don't wait + } } @@ -1802,6 +2019,13 @@ autoflush STDOUT 1; print "Exim tester $testversion\n"; +################################################## +# Some tests check created file modes # +################################################## + +umask 022; + + ################################################## # Check for the "less" command # ################################################## @@ -1833,7 +2057,7 @@ else # If the first character of the first argument is '/', the argument is taken # as the path to the binary. -$parm_exim = (@ARGV > 0 && $ARGV[0] =~ ?^/?)? shift @ARGV : ""; +$parm_exim = (@ARGV > 0 && $ARGV[0] =~ m?^/?)? shift @ARGV : ""; print "Exim binary is $parm_exim\n" if $parm_exim ne ""; @@ -1973,7 +2197,13 @@ if ($parm_exim eq "") # Find what is in the binary # ################################################## -open(EXIMINFO, "$parm_exim -C confs/0000 -DDIR=$parm_cwd " . +# deal with TRUSTED_CONFIG_LIST restrictions +unlink("$parm_cwd/test-config") if -e "$parm_cwd/test-config"; +symlink("$parm_cwd/confs/0000", "$parm_cwd/test-config") + or die "Unable to link initial config into place: $!\n"; + +print("Probing with config file: $parm_cwd/test-config\n"); +open(EXIMINFO, "$parm_exim -d -C $parm_cwd/test-config -DDIR=$parm_cwd " . "-bP exim_user exim_group|") || die "** Cannot run $parm_exim: $!\n"; while(<EXIMINFO>) @@ -1988,6 +2218,13 @@ if (defined $parm_eximuser) if ($parm_eximuser =~ /^\d+$/) { $parm_exim_uid = $parm_eximuser; } else { $parm_exim_uid = getpwnam($parm_eximuser); } } +else + { + print "Unable to extract exim_user from binary.\n"; + print "Check if Exim refused to run; if so, consider:\n"; + print " TRUSTED_CONFIG_LIST ALT_CONFIG_PREFIX WHITELIST_D_MACROS\n"; + die "Failing to get information from binary.\n"; + } if (defined $parm_eximgroup) { @@ -1995,7 +2232,7 @@ if (defined $parm_eximgroup) else { $parm_exim_gid = getgrnam($parm_eximgroup); } } -open(EXIMINFO, "$parm_exim -bV -C confs/0000 -DDIR=$parm_cwd |") || +open(EXIMINFO, "$parm_exim -bV -C $parm_cwd/test-config -DDIR=$parm_cwd |") || die "** Cannot run $parm_exim: $!\n"; print "-" x 78, "\n"; @@ -2008,7 +2245,10 @@ while (<EXIMINFO>) elsif (/^Size of off_t: (\d+)/) { + print; $have_largefiles = 1 if $1 > 4; + die "** Size of off_t > 32 which seems improbable, not running tests\n" + if ($1 > 32); } elsif (/^Support for: (.*)/) @@ -2019,7 +2259,7 @@ while (<EXIMINFO>) %parm_support = @temp; } - elsif (/^Lookups: (.*)/) + elsif (/^Lookups \(built-in\): (.*)/) { print; @temp = split /(\s+)/, $1; @@ -2069,6 +2309,7 @@ while (<EXIMINFO>) close(EXIMINFO); print "-" x 78, "\n"; +unlink("$parm_cwd/test-config"); ################################################## # Check for SpamAssassin and ClamAV # @@ -2079,6 +2320,8 @@ print "-" x 78, "\n"; if (defined $parm_support{'Content_Scanning'}) { + my $sock = new FileHandle; + if (system("spamc -h 2>/dev/null >/dev/null") == 0) { print "The spamc command works:\n"; @@ -2095,23 +2338,23 @@ if (defined $parm_support{'Content_Scanning'}) { my $sin = sockaddr_in($sport, inet_aton($sint)) or die "** Failed packing $sint:$sport\n"; - socket(SOCK, PF_INET, SOCK_STREAM, getprotobyname('tcp')) + socket($sock, PF_INET, SOCK_STREAM, getprotobyname('tcp')) or die "** Unable to open socket $sint:$sport\n"; local $SIG{ALRM} = sub { die "** Timeout while connecting to socket $sint:$sport\n"; }; alarm(5); - connect(SOCK, $sin) + connect($sock, $sin) or die "** Unable to connect to socket $sint:$sport\n"; alarm(0); - select((select(SOCK), $| = 1)[0]); - print SOCK "bad command\r\n"; + select((select($sock), $| = 1)[0]); + print $sock "bad command\r\n"; $SIG{ALRM} = sub { die "** Timeout while reading from socket $sint:$sport\n"; }; alarm(10); - my $res = <SOCK>; + my $res = <$sock>; alarm(0); $res =~ m|^SPAMD/| @@ -2222,18 +2465,18 @@ if (defined $parm_support{'Content_Scanning'}) { die "** Unknown socket domain '$socket_domain' (should not happen)\n"; } - socket(SOCK, $socket_domain, SOCK_STREAM, 0) or die "** Unable to open socket '$parm_clamsocket'\n"; + socket($sock, $socket_domain, SOCK_STREAM, 0) or die "** Unable to open socket '$parm_clamsocket'\n"; local $SIG{ALRM} = sub { die "** Timeout while connecting to socket '$parm_clamsocket'\n"; }; alarm(5); - connect(SOCK, $socket) or die "** Unable to connect to socket '$parm_clamsocket'\n"; + connect($sock, $socket) or die "** Unable to connect to socket '$parm_clamsocket'\n"; alarm(0); - my $ofh = select SOCK; $| = 1; select $ofh; - print SOCK "PING\n"; + my $ofh = select $sock; $| = 1; select $ofh; + print $sock "PING\n"; $SIG{ALRM} = sub { die "** Timeout while reading from socket '$parm_clamsocket'\n"; }; alarm(10); - my $res = <SOCK>; + my $res = <$sock>; alarm(0); $res =~ /PONG/ or die "** Did not get PONG from socket '$parm_clamsocket'. It said: $res\n"; @@ -2339,18 +2582,23 @@ if (defined $parm_support{'Expand_dlfunc'} && !-e "bin/loaded") # Find the caller of this program. ($parm_caller,$pwpw,$parm_caller_uid,$parm_caller_gid,$pwquota,$pwcomm, - $pwgecos, $parm_caller_home) = getpwuid($>); + $parm_caller_gecos, $parm_caller_home) = getpwuid($>); $pwpw = $pwpw; # Kill Perl warnings $pwquota = $pwquota; $pwcomm = $pwcomm; -$pwgecos = $pwgecos; $parm_caller_group = getgrgid($parm_caller_gid); print "Program caller is $parm_caller, whose group is $parm_caller_group\n"; print "Home directory is $parm_caller_home\n"; +unless (defined $parm_eximgroup) + { + print "Unable to derive \$parm_eximgroup.\n"; + die "** ABANDONING.\n"; + } + print "You need to be in the Exim group to run these tests. Checking ..."; if (`groups` =~ /\b\Q$parm_eximgroup\E\b/) @@ -2480,9 +2728,11 @@ if ($parm_hostname !~ /\./) print "\n*** Host name is not fully qualified: this may cause problems ***\n\n"; } -# Find the user's shell +if ($parm_hostname =~ /[[:upper:]]/) + { + print "\n*** Host name has upper case characters: this may cause problems ***\n\n"; + } -$parm_shell = $ENV{'SHELL'}; ################################################## @@ -2544,7 +2794,7 @@ system("sudo cp eximdir/exim eximdir/exim_exim;" . # Certain of the tests make use of some of Exim's utilities. We do not need # to be root to copy these. -($parm_exim_dir) = $parm_exim =~ ?^(.*)/exim?; +($parm_exim_dir) = $parm_exim =~ m?^(.*)/exim?; $dbm_build_deleted = 0; if (defined $parm_lookups{'dbm'} && @@ -2589,6 +2839,12 @@ if (system("cp $parm_exim_dir/eximstats eximdir") != 0) print "Exim user is $parm_eximuser ($parm_exim_uid)\n"; print "Exim group is $parm_eximgroup ($parm_exim_gid)\n"; + +if ($parm_caller_uid eq $parm_exim_uid) { + tests_exit(-1, "Exim user ($parm_eximuser,$parm_exim_uid) cannot be " + ."the same as caller ($parm_caller,$parm_caller_uid)"); +} + print "The Exim user needs access to the test suite directory. Checking ..."; if (($rc = system("sudo bin/checkaccess $parm_cwd/eximdir/exim $parm_eximuser $parm_eximgroup")) != 0) @@ -2797,6 +3053,10 @@ foreach $basedir ("aux-var", "dnszones") } } +# Set a user's shell, distinguishable from /bin/sh + +symlink("/bin/sh","aux-var/sh"); +$ENV{'SHELL'} = $parm_shell = $parm_cwd . "/aux-var/sh"; ################################################## # Create fake DNS zones for this host # @@ -2836,7 +3096,18 @@ if ($have_ipv4 && $parm_ipv4 ne "127.0.0.1") if ($have_ipv6 && $parm_ipv6 ne "::1") { - my(@components) = split /:/, $parm_ipv6; + my($exp_v6) = $parm_ipv6; + $exp_v6 =~ s/[^:]//g; + if ( $parm_ipv6 =~ /^([^:].+)::$/ ) { + $exp_v6 = $1 . ':0' x (9-length($exp_v6)); + } elsif ( $parm_ipv6 =~ /^(.+)::(.+)$/ ) { + $exp_v6 = $1 . ':0' x (8-length($exp_v6)) . ':' . $2; + } elsif ( $parm_ipv6 =~ /^::(.+[^:])$/ ) { + $exp_v6 = '0:' x (9-length($exp_v6)) . $1; + } else { + $exp_v6 = $parm_ipv6; + } + my(@components) = split /:/, $exp_v6; my(@nibbles) = reverse (split /\s*/, shift @components); my($sep) = ""; @@ -3048,10 +3319,30 @@ foreach $test (@test_list) my($commandname) = ""; my($expectrc) = 0; - my($rc) = run_command($testno, \$subtestno, \$expectrc, \$commandname); + my($rc, $run_extra) = run_command($testno, \$subtestno, \$expectrc, \$commandname, $TEST_STATE); my($cmdrc) = $?; - print ">> rc=$rc cmdrc=$cmdrc\n" if $debug; + if ($debug) { + print ">> rc=$rc cmdrc=$cmdrc\n"; + if (defined $run_extra) { + foreach my $k (keys %$run_extra) { + my $v = defined $run_extra->{$k} ? qq!"$run_extra->{$k}"! : '<undef>'; + print ">> $k -> $v\n"; + } + } + } + $run_extra = {} unless defined $run_extra; + foreach my $k (keys %$run_extra) { + if (exists $TEST_STATE->{$k}) { + my $nv = defined $run_extra->{$k} ? qq!"$run_extra->{$k}"! : 'removed'; + print ">> override of $k; was $TEST_STATE->{$k}, now $nv\n" if $debug; + } + if (defined $run_extra->{$k}) { + $TEST_STATE->{$k} = $run_extra->{$k}; + } elsif (exists $TEST_STATE->{$k}) { + delete $TEST_STATE->{$k}; + } + } # Hit EOF after an initial return code number @@ -3080,10 +3371,10 @@ foreach $test (@test_list) for (;;) { - print "\nshow stdErr, show stdOut, Continue (without file comparison), or Quit? [Q] "; + print "\nshow stdErr, show stdOut, Retry, Continue (without file comparison), or Quit? [Q] "; $_ = <T>; tests_exit(1) if /^q?$/i; - last if /^c$/i; + last if /^[rc]$/i; if (/^e$/i) { system("$more test-stderr"); @@ -3094,6 +3385,7 @@ foreach $test (@test_list) } } + $retry = 1 if /^r$/i; $docheck = 0; } @@ -3116,10 +3408,10 @@ foreach $test (@test_list) for (;;) { - print "\nShow server stdout, Continue, or Quit? [Q] "; + print "\nShow server stdout, Retry, Continue, or Quit? [Q] "; $_ = <T>; tests_exit(1) if /^q?$/i; - last if /^c$/i; + last if /^[rc]$/i; if (/^s$/i) { @@ -3129,6 +3421,7 @@ foreach $test (@test_list) close(S); } } + $retry = 1 if /^r$/i; } } } @@ -3139,6 +3432,13 @@ foreach $test (@test_list) # function returns 0 if all is well, 1 if we should rerun the test (the files # have been updated). It does not return if the user responds Q to a prompt. + if ($retry) + { + $retry = '0'; + print (("#" x 79) . "\n"); + redo; + } + if ($docheck) { if (check_output() != 0) @@ -3162,4 +3462,4 @@ tests_exit(-1, "No runnable tests selected") if @test_list == 0; tests_exit(0); # End of runtest script - +# vim: set sw=2 et :