Make runtest more resilient to setup problems
[exim.git] / test / runtest
index d5a161234dc855aa534f5c6660987ce8f3e87a5f..4f1d18edb8634726f7a6aa72f246452a06fcfe63 100755 (executable)
@@ -1,7 +1,5 @@
 #! /usr/bin/perl -w
 
-# $Cambridge: exim/test/runtest,v 1.4 2006/02/10 16:29:20 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 #
@@ -23,9 +21,9 @@ use Socket;
 
 # Start by initializing some global variables
 
-$testversion = "4.61 (06-Feb-06)";
+$testversion = "4.72 (02-Jun-10)";
 
-$cf = "bin/cf";
+$cf = "bin/cf -exact";
 $cr = "\r";
 $debug = 0;
 $force_update = 0;
@@ -98,6 +96,7 @@ sub inthandler { print "\n"; tests_exit(-1, "Caught SIGINT"); }
 
 sub do_substitute{
 s?\bCALLER\b?$parm_caller?g;
+s?\bCALLERGROUP\b?$parm_caller_group?g;
 s?\bCALLER_UID\b?$parm_caller_uid?g;
 s?\bCALLER_GID\b?$parm_caller_gid?g;
 s?\bCLAMSOCKET\b?$parm_clamsocket?g;
@@ -298,6 +297,7 @@ $spid = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
 
 while(<IN>)
   {
+RESET_AFTER_EXTRA_LINE_READ:
   # Check for "*** truncated ***"
   $yield = 1 if /\*\*\* truncated \*\*\*/;
 
@@ -329,7 +329,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
@@ -337,6 +338,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/;
 
@@ -358,6 +362,10 @@ while(<IN>)
   # Random local part in callout cache testing
   s/myhost.test.ex-\d+-testing/myhost.test.ex-dddddddd-testing/;
 
+  # File descriptor numbers may vary
+  s/^writing data block fd=\d+/writing data block fd=dddd/;
+  s/running as transport filter: write=\d+ read=\d+/running as transport filter: write=dddd read=dddd/;
+
 
   # ======== Dumpdb output ========
   # This must be before the general date/date munging.
@@ -413,7 +421,8 @@ while(<IN>)
     my($next) = $3 - $2;
     $_ = "  first failed=dddd last try=dddd next try=+$next $4\n";
     }
-  s/^now=\d+ received_time=\d+ diff=\d+ timeout=(\d+)/now=tttt received_time=tttt diff=tttt timeout=$1/;
+  s/^(\s*)now=\d+ first_failed=\d+ next_try=\d+ expired=(\d)/$1now=tttt first_failed=tttt next_try=tttt expired=$2/;
+  s/^(\s*)received_time=\d+ diff=\d+ timeout=(\d+)/$1received_time=tttt diff=tttt timeout=$2/;
 
   # Time to retry may vary
   s/time to retry = \S+/time to retry = tttt/;
@@ -423,8 +432,15 @@ while(<IN>)
   # Date/time in exim -bV output
   s/\d\d-[A-Z][a-z]{2}-\d{4}\s\d\d:\d\d:\d\d/07-Mar-2000 12:21:52/g;
 
+  # Time on queue tolerance
+  s/QT=1s/QT=0s/;
 
-  # ======== Caller's login, uid, gid, home ========
+  # Eximstats heading
+  s/Exim\sstatistics\sfrom\s\d{4}-\d\d-\d\d\s\d\d:\d\d:\d\d\sto\s
+    \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, 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!
@@ -436,6 +452,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.
@@ -450,9 +468,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/;
@@ -469,10 +488,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/;
@@ -488,6 +515,10 @@ while(<IN>)
   # Pid in temp file in appendfile transport
   s"test-mail/temp\.\d+\."test-mail/temp.pppp.";
 
+  # Optional pid in log lines
+  s/^(\d{4}-\d\d-\d\d\s\d\d:\d\d:\d\d)(\s[+-]\d\d\d\d|)(\s\[\d+\])/
+    "$1$2 [" . new_value($3, "%s", \$next_pid) . "]"/gxe;
+
   # Detect a daemon stderr line with a pid and save the pid for subsequent
   # removal from following lines.
   $spid = $1 if /^(\s*\d+) (?:listening|LOG: MAIN|(?:daemon_smtp_port|local_interfaces) overridden by)/;
@@ -504,8 +535,8 @@ while(<IN>)
   s/\(port=(\d+)/"(port=" . new_value($1, "%s", \$next_port)/e;
 
   # This handles "connection from" and the like, when the port is given
-  if (!/listening for SMTP on/ && !/Connecting to/ && !/=>/ && !/\*>/ &&
-      !/Connection refused/)
+  if (!/listening for SMTP on/ && !/Connecting to/ && !/=>/ && !/->/
+      && !/\*>/ && !/Connection refused/)
     {
     s/\[([a-z\d:]+|\d+(?:\.\d+){3})\]:(\d+)/"[".$1."]:".new_value($2,"%s",\$next_port)/ie;
     }
@@ -518,6 +549,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/;
@@ -526,6 +560,9 @@ while(<IN>)
   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/\b\Q$parm_ipv4r\E\b/ip4-reverse/g;
+  s/\b\Q$parm_ipv6r\E\b/ip6-reverse/g;
+  s/^(\s+host\s\S+\s+\[\S+\]) +$/$1 /;
 
 
   # ======== Test network IP addresses ========
@@ -566,7 +603,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/;
@@ -579,10 +616,12 @@ while(<IN>)
   s/\+0100,\d+;/+0100,ddd;/;
   s/\(\d+ bytes written\)/(ddd bytes written)/;
   s/added '\d+ 1'/added 'ddd 1'/;
+  s/Received\s+\d+/Received               nnn/;
+  s/Delivered\s+\d+/Delivered              nnn/;
 
 
   # ======== Values in spool space failure message ========
-  s/space=\d+ inodes=\d+/space=xxxxx inodes=xxxxx/;
+  s/space=\d+ inodes=[+-]?\d+/space=xxxxx inodes=xxxxx/;
 
 
   # ======== Filter sizes ========
@@ -613,7 +652,7 @@ while(<IN>)
   s/\b\d+\.H\d+P\d+\b/dddddddddd.HddddddPddddd/;
 
   # Maildirsize data
-  if (/^\d+S,\d+C\s*$/)
+  while (/^\d+S,\d+C\s*$/)
     {
     print MUNGED;
     while (<IN>)
@@ -623,6 +662,7 @@ while(<IN>)
       }
     last if !defined $_;
     }
+  last if !defined $_;
 
 
   # ======== Output from the "fd" program about open descriptors ========
@@ -648,9 +688,11 @@ while(<IN>)
 
   if ($is_stdout)
     {
-    # Skip translate_ip_address in -bP output because it ain't always there
+    # Skip translate_ip_address and use_classresources in -bP output because
+    # they aren't always there.
 
     next if /translate_ip_address =/;
+    next if /use_classresources/;
 
     # In certain filter tests, remove initial filter lines because they just
     # clog up by repetition.
@@ -686,6 +728,44 @@ 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 /^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 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.
 
@@ -693,6 +773,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\)/;
@@ -853,7 +936,7 @@ my($rf,$rsf,$mf,$sf,$sortfile) = @_;
 
 if (! -e $sf)
   {
-  return 0 if (! -s $rf && ! -s $rsf);
+  return 0 if (! -s $rf && (! defined $rsf || ! -s $rsf));
 
   print "\n";
   print "** $rf is not empty\n" if (-s $rf);
@@ -875,7 +958,7 @@ if (! -e $sf)
       print "\n";
       print "------------ $f -----------\n"
         if (defined $rf && -s $rf && defined $rsf && -s $rsf);
-      system("$more $f");
+      system("$more '$f'");
       }
     }
 
@@ -989,7 +1072,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
 
@@ -1009,7 +1092,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); }
 
@@ -1381,7 +1464,7 @@ if (/^exim_lock\s+(.*)$/)
   # This gives the process time to get started; otherwise the next
   # process may not find it there when it expects it.
 
-  select(undef, undef, undef, 0.01);
+  select(undef, undef, undef, 0.1);
   return 3;
   }
 
@@ -1399,6 +1482,30 @@ if (/^exinext\s+(.*)/)
   }
 
 
+# The "exigrep" command runs exigrep on the current mainlog
+
+if (/^exigrep\s+(.*)/)
+  {
+  run_system("(./eximdir/exigrep " .
+    "$1 $parm_cwd/spool/log/mainlog;" .
+    "echo exigrep exit code = \$?)" .
+    ">>test-stdout");
+  return 1;
+  }
+
+
+# The "eximstats" command runs eximstats on the current mainlog
+
+if (/^eximstats\s+(.*)/)
+  {
+  run_system("(./eximdir/eximstats " .
+    "$1 $parm_cwd/spool/log/mainlog;" .
+    "echo eximstats exit code = \$?)" .
+    ">>test-stdout");
+  return 1;
+  }
+
+
 # The "gnutls" command makes a copy of saved GnuTLS parameter data in the
 # spool directory, to save Exim from re-creating it each time.
 
@@ -1503,7 +1610,7 @@ if (/^server\s+(.*)$/)
   # This gives the server time to get started; otherwise the next
   # process may not find it there when it expects it.
 
-  select(undef, undef, undef, 0.01);
+  select(undef, undef, undef, 0.5);
   return 3;
   }
 
@@ -1583,12 +1690,12 @@ if (/^(cat)?write\s+(\S+)(?:\s+(.*))?\s*$/)
 # command in the variable $cmd. Shared code to run this command and handle its
 # input and output follows.
 
-# The "client" and "client-ssl" commands run a script-driven program that plays
-# the part of an email client. We also have the availability of running Perl
-# for doing one-off special things. Note that all these commands expect stdin
-# data to be supplied.
+# The "client", "client-gnutls", and "client-ssl" commands run a script-driven
+# program that plays the part of an email client. We also have the availability
+# of running Perl for doing one-off special things. Note that all these
+# commands expect stdin data to be supplied.
 
-if (/^client/ || /^client-ssl/ || /^(sudo\s+)?perl\b/)
+if (/^client/ || /^(sudo\s+)?perl\b/)
   {
   s"client"./bin/client";
   $cmd = "$_ >>test-stdout 2>>test-stderr";
@@ -1680,9 +1787,11 @@ elsif (/^([A-Z_]+=\S+\s+)?(\d+)?\s*(sudo\s+)?exim(_\S+)?\s+(.*)$/)
 
     # Before running the command, convert the -bd option into -bdf so that an
     # Exim daemon doesn't double fork. This means that when we wait close
-    # DAEMONCMD, it waits for the correct process.
+    # DAEMONCMD, it waits for the correct process. Also, ensure that the pid
+    # file is written to the spool directory, in case the Exim binary was
+    # built with PID_FILE_PATH pointing somewhere else.
 
-    $cmd =~ s/\s-bd\s/ -bdf /;
+    $cmd =~ s!\s-bd\s! -bdf -oP $parm_cwd/spool/exim-daemon.pid !;
     print ">> |${cmd}-server\n" if ($debug);
     open DAEMONCMD, "|${cmd}-server" || tests_exit(-1, "Failed to run $cmd");
     DAEMONCMD->autoflush(1);
@@ -1923,7 +2032,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>)
@@ -1938,6 +2053,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)
   {
@@ -1945,7 +2067,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";
@@ -1958,7 +2080,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: (.*)/)
@@ -1969,7 +2094,7 @@ while (<EXIMINFO>)
     %parm_support = @temp;
     }
 
-  elsif (/^Lookups: (.*)/)
+  elsif (/^Lookups \(built-in\): (.*)/)
     {
     print;
     @temp = split /(\s+)/, $1;
@@ -2019,6 +2144,7 @@ while (<EXIMINFO>)
 close(EXIMINFO);
 print "-" x 78, "\n";
 
+unlink("$parm_cwd/test-config");
 
 ##################################################
 #    Check for SpamAssassin and ClamAV           #
@@ -2031,7 +2157,6 @@ if (defined $parm_support{'Content_Scanning'})
   {
   if (system("spamc -h 2>/dev/null >/dev/null") == 0)
     {
-    $parm_running{'SpamAssassin'} = ' ';
     print "The spamc command works:\n";
 
     # This test for an active SpamAssassin is courtesy of John Jetmore.
@@ -2109,30 +2234,74 @@ if (defined $parm_support{'Content_Scanning'})
         }
       }
 
+    # Read the ClamAV configuration file and find the socket interface.
+
     if ($clamconf ne "")
       {
+      my $socket_domain;
       open(IN, "$clamconf") || die "\n** Unable to open $clamconf: $!\n";
       while (<IN>)
         {
         if (/^LocalSocket\s+(.*)/)
           {
           $parm_clamsocket = $1;
+          $socket_domain = AF_UNIX;
           last;
           }
+        if (/^TCPSocket\s+(\d+)/)
+          {
+          if (defined $parm_clamsocket)
+            {
+            $parm_clamsocket .= " $1";
+            $socket_domain = AF_INET;
+            last;
+            }
+          else
+            {
+            $parm_clamsocket = " $1";
+            }
+          }
+        elsif (/^TCPAddr\s+(\S+)/)
+          {
+          if (defined $parm_clamsocket)
+            {
+            $parm_clamsocket = $1 . $parm_clamsocket;
+            $socket_domain = AF_INET;
+            last;
+            }
+          else
+            {
+            $parm_clamsocket = $1;
+            }
+          }
         }
       close(IN);
-      if (-e $parm_clamsocket)
+
+      if (defined $socket_domain)
         {
         print ":\n  The clamd socket is $parm_clamsocket\n";
         # This test for an active ClamAV is courtesy of Daniel Tiefnig.
         eval
           {
-          my $sun = sockaddr_un($parm_clamsocket) or die "** Failed packing '$parm_clamsocket'\n";
-          socket(SOCK, AF_UNIX, SOCK_STREAM, 0) or die "** Unable to open socket '$parm_clamsocket'\n";
-
+          my $socket;
+          if ($socket_domain == AF_UNIX)
+            {
+            $socket = sockaddr_un($parm_clamsocket) or die "** Failed packing '$parm_clamsocket'\n";
+            }
+          elsif ($socket_domain == AF_INET)
+            {
+            my ($ca_host, $ca_port) = split(/\s+/,$parm_clamsocket);
+            my $ca_hostent = gethostbyname($ca_host) or die "** Failed to get raw address for host '$ca_host'\n";
+            $socket = sockaddr_in($ca_port, $ca_hostent) or die "** Failed packing '$parm_clamsocket'\n";
+            }
+          else
+            {
+            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";
           local $SIG{ALRM} = sub { die "** Timeout while connecting to socket '$parm_clamsocket'\n"; };
           alarm(5);
-          connect(SOCK, $sun) 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;
@@ -2149,7 +2318,7 @@ if (defined $parm_support{'Content_Scanning'})
 
         if($@)
           {
-          warn $@;
+          print "  $@";
           print "  Assume ClamAV is not running\n";
           }
         else
@@ -2160,7 +2329,7 @@ if (defined $parm_support{'Content_Scanning'})
         }
       else
         {
-        print ", but the socket for clamd does not exist\n";
+        print ", but the socket for clamd could not be determined\n";
         print "Assume ClamAV is not running\n";
         }
       }
@@ -2246,18 +2415,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/)
@@ -2357,6 +2531,24 @@ else
 print "IPv4 address is $parm_ipv4\n";
 print "IPv6 address is $parm_ipv6\n";
 
+# For munging test output, we need the reversed IP addresses.
+
+$parm_ipv4r = ($parm_ipv4 !~ /^\d/)? "" :
+  join(".", reverse(split /\./, $parm_ipv4));
+
+$parm_ipv6r = $parm_ipv6;             # Appropriate if not in use
+if ($parm_ipv6 =~ /^[\da-f]/)
+  {
+  my(@comps) = split /:/, $parm_ipv6;
+  my(@nibbles);
+  foreach $comp (@comps)
+    {
+    push @nibbles, sprintf("%lx", hex($comp) >> 8);
+    push @nibbles, sprintf("%lx", hex($comp) & 0xff);
+    }
+  $parm_ipv6r = join(".", reverse(@nibbles));
+  }
+
 # Find the host name, fully qualified.
 
 chomp($temp = `hostname`);
@@ -2458,6 +2650,16 @@ if (system("cp $parm_exim_dir/exinext eximdir") != 0)
   tests_exit(-1, "Failed to make a copy of exinext: $!");
   }
 
+if (system("cp $parm_exim_dir/exigrep eximdir") != 0)
+  {
+  tests_exit(-1, "Failed to make a copy of exigrep: $!");
+  }
+
+if (system("cp $parm_exim_dir/eximstats eximdir") != 0)
+  {
+  tests_exit(-1, "Failed to make a copy of eximstats: $!");
+  }
+
 
 ##################################################
 #    Check that the Exim user can access stuff   #
@@ -2468,6 +2670,12 @@ if (system("cp $parm_exim_dir/exinext 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)
@@ -2512,12 +2720,25 @@ opendir(DIR, "scripts") || tests_exit(-1, "Failed to opendir(\"scripts\"): $!");
 @test_dirs = sort readdir(DIR);
 closedir(DIR);
 
+# Remove . and .. and CVS from the list.
+
+for ($i = 0; $i < @test_dirs; $i++)
+  {
+  my($d) = $test_dirs[$i];
+  if ($d eq "." || $d eq ".." || $d eq "CVS")
+    {
+    splice @test_dirs, $i, 1;
+    $i--;
+    }
+  }
+
+# Scan for relevant tests
+
 for ($i = 0; $i < @test_dirs; $i++)
   {
   my($testdir) = $test_dirs[$i];
   my($wantthis) = 1;
 
-  next if $testdir eq "." || $testdir eq "..";
   print ">>Checking $testdir\n" if $debug;
 
   # Skip this directory if the first test is equal or greater than the first
@@ -2814,6 +3035,7 @@ foreach $test (@test_list)
 
   undef %cache;
   $next_msgid = "aX";
+  $next_pid = 1234;
   $next_port = 1111;
   $message_skip = 0;
   $msglog_skip = 0;
@@ -3027,4 +3249,4 @@ tests_exit(-1, "No runnable tests selected") if @test_list == 0;
 tests_exit(0);
 
 # End of runtest script
-
+# vim: set sw=2 :