$ENV{USER} = getpwuid($>) if not exists $ENV{USER};
my ($parm_configure_owner, $parm_configure_group);
-my ($parm_ipv4, $parm_ipv6);
+my ($parm_ipv4, $parm_ipv6, $parm_ipv6_stripped);
my $parm_hostname;
###############################################################################
# time used was fixed when I first started running automatic Exim tests.
# Date/time in header lines and SMTP responses
- s/[A-Z][a-z]{2},\s\d\d?\s[A-Z][a-z]{2}\s\d\d\d\d\s\d\d\:\d\d:\d\d\s[-+]\d{4}
+ s/[A-Z][a-z]{2},\s\d\d?\s[A-Z][a-z]{2}\s\d{4}\s\d\d\:\d\d:\d\d\s[-+]\d{4}
/Tue, 2 Mar 1999 09:44:33 +0000/gx;
+ # and in a French locale
+ s/\S{4},\s\d\d?\s[^,]+\s\d{4}\s\d\d\:\d\d:\d\d\s[-+]\d{4}
+ /dim., 10 f\xE9vr 2019 20:05:49 +0000/gx;
# Date/time in logs and in one instance of a filter test
s/^\d{4}-\d\d-\d\d\s\d\d:\d\d:\d\d(\s[+-]\d\d\d\d)?\s/1999-03-02 09:44:33 /gx;
s/(could not connect to .*: Connection) reset by peer$/$1 refused/;
# ======== TLS certificate algorithms ========
+ #
+ # In Received: headers, convert RFC 8314 style ciphersuite to
+ # the older (comment) style, keeping only the Auth element
+ # (discarding kex, cipher, mac). For TLS 1.3 there is no kex
+ # element (and no _WITH); insert a spurious "RSA".
+
+ s/^\s+by .+ with .+ \K tls TLS_.*?([^_]+)_WITH.+$/(TLS1.x:ke-$1-AES256-SHAnnn:xxx)/;
+ s/^\s+by .+ with .+ \K tls TLS_.+$/(TLS1.x:ke-RSA-AES256-SHAnnn:xxx)/;
+
# 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.
s/(?<!ke-)((EC)?DHE-)?(RSA|ECDSA)-(AES256|CHACHA20)-(GCM-SHA384|POLY1305):256/ke-$3-AES256-SHAnnn:xxx/g;
# GnuTLS have seen:
+ # TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256
+ # TLS1.3:ECDHE_SECP256R1__RSA_PSS_RSAE_SHA256__AES_256_GCM__AEAD:256
+ # TLS1.3:ECDHE_X25519__RSA_PSS_RSAE_SHA256__AES_256_GCM:256
+ # TLS1.3:ECDHE_PSK_SECP256R1__AES_256_GCM__AEAD:256
+ #
# TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256
# TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128
# TLS1.2:RSA_AES_256_CBC_SHA1:256 (canonical)
# TLS1.2:DHE_RSA_AES_128_CBC_SHA1:128
+ # TLS1.2:ECDHE_SECP256R1__RSA_SHA256__AES_256_GCM:256
+ # TLS1.2:ECDHE_SECP256R1__RSA_SHA256__AES_128_CBC__SHA256:128
+ # TLS1.2:ECDHE_SECP256R1__ECDSA_SHA512__AES_256_GCM:256
+ # TLS1.2:ECDHE_SECP256R1__AES_256_GCM:256 (3.6.7 resumption)
+ # TLS1.2:ECDHE_RSA_SECP256R1__AES_256_GCM:256 (! 3.5.18 !)
+ # TLS1.2:RSA__CAMELLIA_256_GCM:256 (leave the cipher name)
+ # TLS1.2-PKIX:RSA__AES_128_GCM__AEAD:128 (the -PKIX seems to be a 3.1.20 thing)
+ # TLS1.2-PKIX:ECDHE_RSA_SECP521R1__AES_256_GCM__AEAD:256
#
# 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:RSA_AES_256_CBC_SHA1:256
# X=TLS1.0:DHE_RSA_AES_256_CBC_SHA1:256
+ # X=TLS1.0-PKIX:RSA__AES_256_CBC__SHA1:256
# and as stand-alone cipher:
# ECDHE-RSA-AES256-SHA
# 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-AES256-SHA1:256/g;
- s/TLS1.[012]:((EC)?DHE_)?(RSA|ECDSA)_AES_(256|128)_(CBC|GCM)_SHA(1|256|384):(256|128)/TLS1.x:ke-$3-AES256-SHAnnn:xxx/g;
+ s/TLS1.[0123](-PKIX)?: # TLS version
+ ((EC)?DHE(_((?<psk>PSK)_)?((?<auth>RSA|ECDSA)_)?
+ (SECP(256|521)R1|X25519))?__?)? # key-exchange
+ ((?<auth>RSA|ECDSA)((_PSS_RSAE)?_SHA(512|256))?__?)? # authentication
+ AES_(256|128)_(CBC|GCM) # cipher
+ (__?AEAD)? # pseudo-MAC
+ (__?SHA(1|256|384))? # PRF
+ :(256|128) # cipher strength
+ /"TLS1.x:ke-"
+ . (defined($+{psk}) ? $+{psk} : "")
+ . (defined($+{auth}) ? $+{auth} : "")
+ . "-AES256-SHAnnn:xxx"/gex;
+ s/TLS1.2:RSA__CAMELLIA_256_GCM(_SHA384)?:256/TLS1.2:RSA_CAMELLIA_256_GCM-SHAnnn:256/g;
s/\b(ECDHE-(RSA|ECDSA)-AES256-SHA|DHE-RSA-AES256-SHA256)\b/ke-$2-AES256-SHAnnn/g;
# GnuTLS library error message changes
- s/No certificate was found/The peer did not send any certificate/g;
+ s/(No certificate was found|Certificate is required)/The peer did not send any certificate/g;
#(dodgy test?) s/\(certificate verification failed\): invalid/\(gnutls_handshake\): The peer did not send any certificate./g;
s/\(gnutls_priority_set\): No or insufficient priorities were set/\(gnutls_handshake\): Could not negotiate a supported cipher suite/g;
s/\(gnutls_handshake\): \KNo supported cipher suites have been found.$/Could not negotiate a supported cipher suite./;
s/waiting for children of \d+/waiting for children of pppp/;
s/waiting for (\S+) \(\d+\)/waiting for $1 (pppp)/;
- # The spool header file name varies with PID
- s%^(Writing spool header file: .*/hdr).[0-9]{1,5}%$1.pppp%;
-
# ======== Port numbers ========
# Incoming port numbers may vary, but not in daemon startup line.
}
# Port in host address in spool file output from -Mvh
- s/^-host_address (.*)\.\d+/-host_address $1.9999/;
+ s/^(--?host_address) (.*)\.\d+/$1 $2.9999/;
if ($dynamic_socket and $dynamic_socket->opened and my $port = $dynamic_socket->sockport) {
s/^Connecting to 127\.0\.0\.1 port \K$port/<dynamic port>/;
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/(^|\W)\K\Q$parm_ipv6\E/ip6:ip6:ip6:ip6:ip6:ip6:ip6:ip6/g;
+ s/(^|\W)\K\Q$parm_ipv6_stripped\E/ip6:ip6:ip6:ip6:ip6:ip6:ip6:ip6/g;
s/\b\Q$parm_ipv4r\E\b/ip4-reverse/g;
s/(^|\W)\K\Q$parm_ipv6r\E/ip6-reverse/g;
s/^(\s+host\s\S+\s+\[\S+\]) +$/$1 /;
s/^waiting for server to shut down\.+ done$/waiting for server to shut down.... done/;
s/^\/.*postgres /POSTGRES /;
+ # DMARC is not always supported by the build
+ next if /^dmarc_tld_file =/;
+
# ARC is not always supported by the build
next if /^arc_sign =/;
+
+ # TLS resumption is not always supported by the build
+ next if /^tls_resumption_hosts =/;
+ next if /^-tls_resumption/;
}
# ======== stderr ========
next if /name=localhost address=::1/;
# drop pdkim debugging header
- next if /^PDKIM( <<<<<<<<<<<<<<<<<<<<<<<<<<<<<+|: no signatures)$/;
+ next if /^DKIM( <<<<<<<<<<<<<<<<<<<<<<<<<<<<<+|: no signatures)$/;
# Various other IPv6 lines must be omitted too
next if /get\[host\|ipnode\]byname\[2\]\(af=inet6\)/;
next if /DNS lookup of \S+ \(AAAA\) using fakens/;
next if / in dns_ipv4_lookup?/;
+ next if / writing neg-cache entry for .*AAAA/;
if (/DNS lookup of \S+ \(AAAA\) gave NO_DATA/)
{
# remote port numbers vary
s/(Connection request from 127.0.0.1 port) \d{1,5}/$1 sssss/;
+ # Platform-dependent error strings
+ s/Operation timed out/Connection timed out/;
+
+ # Platform-dependent resolver option bits
+ s/^ (?:writing|update) neg-cache entry for [^,]+-\K[0-9a-f]+, ttl/xxxx, ttl/;
+
+ # timing variance, run-to-run
+ s/^time on queue = \K1s/0s/;
+
# Skip hosts_require_dane checks when the options
# are unset, because dane ain't always there.
next if /in\shosts_require_dane\?\sno\s\(option\sunset\)/x;
- # DISABLE_OCSP
+ # DISABLE_OCSP
next if /in hosts_requ(est|ire)_ocsp\? (no|yes)/;
# SUPPORT_PROXY
next if /host in hosts_proxy\?/;
+ # PIPE_CONNECT
+ next if / in (pipelining_connect_advertise_hosts|hosts_pipe_connect)?\? no /;
+
# Experimental_International
next if / in smtputf8_advertise_hosts\? no \(option unset\)/;
# TCP Fast Open
next if /^(ppppp )?setsockopt FASTOPEN: Network Error/;
- # Experimental_PIPE_CONNECT
- next if / in (pipelining_connect_advertise_hosts|hosts_pipe_connect)?\? no /;
-
# Environment cleaning
next if /\w+ in keep_environment\? (yes|no)/;
}
# Not all platforms build with DKIM enabled
- next if /^PDKIM >> Body data for hash, canonicalized/;
+ next if /^DKIM >> Body data for hash, canonicalized/;
+
+ # Not all platforms build with SPF enabled
+ next if /^(spf_conn_init|SPF_dns_exim_new|spf_compile\.c)/;
# Not all platforms have sendfile support
next if /^cannot use sendfile for body: no support$/;
# Parts of DKIM-specific debug output depend on the time/date
next if /^date:\w+,\{SP\}/;
- next if /^PDKIM \[[^[]+\] (Header hash|b) computed:/;
+ next if /^DKIM \[[^[]+\] (Header hash|b) computed:/;
# Not all platforms support TCP Fast Open, and the compile omits the check
- if (s/\S+ in hosts_try_fastopen\? no \(option unset\)\n$//)
+ if (s/\S+ in hosts_try_fastopen\? (no \(option unset\)|yes \(matched "\*"\))\n$//)
{
$_ .= <IN>;
s/ \.\.\. >>> / ... /;
+ if (s/ non-TFO mode connection attempt to 224.0.0.0, 0 data\b$//) { $_ .= <IN>; }
s/Address family not supported by protocol family/Network Error/;
s/Network is unreachable/Network Error/;
}
next if /^(ppppp )?setsockopt FASTOPEN: Protocol not available$/;
s/^(Connecting to .* \.\.\. sending) \d+ (nonTFO early-data)$/$1 dd $2/;
+ if (/^([0-9: ]* # possible timestamp
+ Connecting\ to\ [^ ]+\ [^ ]+(\ from\ [^ ]+)?)\ \.\.\.
+ \ .*TFO\ mode\x20
+ (sendto,\ no\ data:\ EINPROGRESS # Linux
+ |connection\ attempt\ to\ [^,]+,\ 0\ data) # MacOS & no-support
+ $/x)
+ {
+ $_ = $1 . " ... " . <IN>;
+ s/^(.* \.\.\.) [0-9: ]*connected$/$1 connected/;
+
+ if (/^Connecting to .* \.\.\. connected$/)
+ {
+ $_ .= <IN>;
+ if (/^(Connecting to .* \.\.\. )connected\n\s+SMTP(\(close\)>>|\(Connection refused\)<<)$/)
+ {
+ $_ = $1 . "failed: Connection refused\n" . <IN>;
+ s/^(Connecting .*)\n\s+SMTP\(close\)>>$/$1/;
+ }
+ elsif (/^(Connecting to .* \.\.\. connected\n)read response data: size=/)
+ { $_ = $1; }
+
+ # Date/time in SMTP banner
+ s/[A-Z][a-z]{2},\s\d\d?\s[A-Z][a-z]{2}\s\d{4}\s\d\d\:\d\d:\d\d\s[-+]\d{4}
+ /Tue, 2 Mar 1999 09:44:33 +0000/gx;
+ }
+ }
+
# Specific pointer values reported for DB operations change from run to run
s/^(\s*returned from EXIM_DBOPEN: )(0x)?[0-9a-f]+/${1}0xAAAAAAAA/;
s/^(\s*EXIM_DBCLOSE.)(0x)?[0-9a-f]+/${1}0xAAAAAAAA/;
# Not all builds include DMARC
next if /^DMARC: no (dmarc_tld_file|sender_host_address)$/ ;
+ # TLS resumption is not always supported by the build
+ next if /in tls_resumption_hosts\?/;
+
+ # Platform differences in errno strings
+ s/ SMTP\(Operation timed out\)<</ SMTP(Connection timed out)<</;
+
# When Exim is checking the size of directories for maildir, it uses
# the check_dir_size() function to scan directories. Of course, the order
# of the files that are obtained using readdir() varies from system to
s/(TLS error on connection [^:]*: error:)[0-9A-F]{8}(:system library):(?:fopen|func\(4095\)):(No such file or directory)$/$1xxxxxxxx$2:fopen:$3/;
s/(DANE attempt failed.*error:)[0-9A-F]{8}(:SSL routines:)(ssl3_get_server_certificate|tls_process_server_certificate|CONNECT_CR_CERT)(?=:certificate verify failed$)/$1xxxxxxxx$2ssl3_get_server_certificate/;
s/(DKIM: validation error: )error:[0-9A-F]{8}:rsa routines:(?:(?i)int_rsa_verify|CRYPTO_internal):(?:bad signature|algorithm mismatch)$/$1Public key signature verification has failed./;
+ s/ARC: AMS signing: privkey PEM-block import: error:\K[0-9A-F]{8}:(PEM routines):get_name:(no start line)/0906D06C:$1:PEM_read_bio:$2/;
+
+ # gnutls version variances
+ if (/TLS error on connection \(recv\): .* (Decode error|peer did not send any certificate)/)
+ {
+ my $prev = $_;
+ $_ = <IN>;
+ if (/error on first read/)
+ {
+ s/TLS session: \Kerror on first read:/(gnutls_handshake): A TLS fatal alert has been received.:/;
+ goto RESET_AFTER_EXTRA_LINE_READ;
+ }
+ else
+ { $_ = $prev; }
+ }
+ # translate gnutls error into the openssl one
+ s/ARC: AMS signing: privkey PEM-block import: \KThe requested data were not available.$/error:0906D06C:PEM routines:PEM_read_bio:no start line/;
# DKIM timestamps
if ( /(DKIM: d=.*) t=([0-9]*) x=([0-9]*) / )
my ($prefix, $t_diff) = ($1, $3 - $2);
s/DKIM: d=.* t=[0-9]* x=[0-9]* /${prefix} t=T x=T+${t_diff} /;
}
+
+ # port numbers
+ s/(?:\[[^\]]*\]:|port )\K$parm_port_d/PORT_D/;
+ s/(?:\[[^\]]*\]:|port )\K$parm_port_d2/PORT_D2/;
+ s/(?:\[[^\]]*\]:|port )\K$parm_port_d3/PORT_D3/;
+ s/(?:\[[^\]]*\]:|port )\K$parm_port_d4/PORT_D4/;
+ s/(?:\[[^\]]*\]:|port )\K$parm_port_s/PORT_S/;
+ s/(?:\[[^\]]*\]:|port )\K$parm_port_n/PORT_N/;
+ s/I=\[[^\]]*\]:\K\d+/ppppp/;
+
}
# ======== mail ========
s! DN="[^,"]*\K,!/!;
',
'rejectlog' => 's/ X=TLS\S+ / X=TLS_proto_and_cipher /',
- 'mail' => 's/ \(TLS[^)]*\)/ (TLS_proto_and_cipher)/',
+ 'mail' => 's/^\s+by .+ with .+ \K tls TLS_.+$/(TLS_proto_and_cipher)/;
+ s/ \(TLS[^)]*\)/ (TLS_proto_and_cipher)/;
+ ',
},
'debug_pid' =>
# The "munge" command selects one of a hardwired set of test-result modifications
-# to be made before result compares are run agains the golden set. This lets
+# to be made before result compares are run against the golden set. This lets
# us account for test-system dependent things which only affect a few, but known,
# test-cases.
# Currently only the last munge takes effect.
my $git = `git describe --dirty=-XX --match 'exim-4*'`;
if (defined $git and $? == 0) {
chomp $git;
- $version =~ s/^\d+\K\./_/;
$git =~ s/^exim-//i;
$git =~ s/.*-\Kg([[:xdigit:]]+(?:-XX)?)/$1/;
print <<___
if 0020 & (stat "$parm_cwd/test-config")[2]
and $parm_configure_group != $);
-die "aux-fixed file is world-writeable; best to strip them all, recursively\n"
+die "aux-fixed file is group-writeable; best to strip them all, recursively\n"
if 0020 & (stat "aux-fixed/0037.f-1")[2];
# Find this host's IP addresses - there may be many, of course, but we keep
# one of each type (IPv4 and IPv6).
+#XXX it would be good to avoid non-UP interfaces
open(IFCONFIG, '-|', (grep { -x "$_/ip" } split /:/, $ENV{PATH}) ? 'ip address' : 'ifconfig -a')
or die "** Cannot run 'ip address' or 'ifconfig -a'\n";
while (not ($parm_ipv4 and $parm_ipv6) and defined($_ = <IFCONFIG>))
{
+ if (/^(?:[0-9]+: )?([a-z0-9]+): /) { $ifname = $1; }
+
if (not $parm_ipv4 and /^\s*inet(?:\saddr)?:?\s?(\d+\.\d+\.\d+\.\d+)(?:\/\d+)?\s/i)
{
- next if $1 =~ /^(?:127|10)\./;
+ # It would ne nice to be able to vary the /16 used for manyhome; we could take
+ # an option to runtest used here - but we'd also have to pass it on to fakens.
+ # Possibly an environment variable?
+ next if $1 eq '0.0.0.0' or $1 =~ /^(?:127|10\.250)\./;
$parm_ipv4 = $1;
}
- if (not $parm_ipv6 and /^\s*inet6(?:\saddr)?:?\s?([abcdef\d:]+)(?:\/\d+)/i)
+ if (not $parm_ipv6 and /^\s*inet6(?:\saddr)?:?\s?([abcdef\d:]+)(?:%[^ \/]+)?(?:\/\d+)?/i)
{
- next if $1 eq '::1' or $1 =~ /^fe80/i;
+ next if $1 eq '::' or $1 eq '::1' or $1 =~ /^ff00/i or $1 =~ /^fe80::1/i;
$parm_ipv6 = $1;
+ if ($1 =~ /^fe80/i) { $parm_ipv6 .= '%' . $ifname; }
}
}
close(IFCONFIG);
print "IPv4 address is $parm_ipv4\n";
print "IPv6 address is $parm_ipv6\n";
+$parm_ipv6 =~ /^[^%\/]*/;
+# drop any %scope from the ipv6, for some uses
+($parm_ipv6_stripped = $parm_ipv6) =~ s/%.*//g;
# For munging test output, we need the reversed IP addresses.
$parm_ipv6r = $parm_ipv6; # Appropriate if not in use
if ($parm_ipv6 =~ /^[\da-f]/)
{
- my(@comps) = split /:/, $parm_ipv6;
+ my(@comps) = split /:/, $parm_ipv6_stripped;
my(@nibbles);
foreach $comp (@comps)
{
"; for queries that it cannot answer\n\n" .
"PASS ON NOT FOUND\n\n";
print OUT "$shortname A $parm_ipv4\n" if $have_ipv4;
- print OUT "$shortname AAAA $parm_ipv6\n" if $have_ipv6;
+ print OUT "$shortname AAAA $parm_ipv6_stripped\n" if $have_ipv6;
print OUT "\n; End\n";
close(OUT);
}
if ($have_ipv4 && $parm_ipv4 ne "127.0.0.1")
{
my(@components) = $parm_ipv4 =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)/;
- open(OUT, ">$parm_cwd/dnszones/db.ip4.$components[0]") ||
- tests_exit(-1,
- "Failed to open $parm_cwd/dnszones/db.ip4.$components[0]: $!");
- print OUT "; This is a dynamically constructed fake zone file.\n" .
- "; The zone is $components[0].in-addr.arpa.\n\n" .
- "$components[3].$components[2].$components[1] PTR $parm_hostname.\n\n" .
- "; End\n";
- close(OUT);
+
+ if ($components[0]=='10')
+ {
+ open(OUT, ">>$parm_cwd/dnszones/db.ip4.$components[0]") ||
+ tests_exit(-1, "Failed to open $parm_cwd/dnszones/db.ip4.$components[0]: $!");
+ print OUT "$components[3].$components[2].$components[1] PTR $parm_hostname.\n\n";
+ close(OUT);
+ }
+ else
+ {
+ open(OUT, ">$parm_cwd/dnszones/db.ip4.$components[0]") ||
+ tests_exit(-1,
+ "Failed to open $parm_cwd/dnszones/db.ip4.$components[0]: $!");
+ print OUT "; This is a dynamically constructed fake zone file.\n" .
+ "; The zone is $components[0].in-addr.arpa.\n\n" .
+ "$components[3].$components[2].$components[1] PTR $parm_hostname.\n\n" .
+ "; End\n";
+ close(OUT);
+ }
}
-if ($have_ipv6 && $parm_ipv6 ne "::1")
+if ($have_ipv6 && $parm_ipv6_stripped ne "::1")
{
- my($exp_v6) = $parm_ipv6;
+ my($exp_v6) = $parm_ipv6_stripped;
$exp_v6 =~ s/[^:]//g;
- if ( $parm_ipv6 =~ /^([^:].+)::$/ ) {
+ if ( $parm_ipv6_stripped =~ /^([^:].+)::$/ ) {
$exp_v6 = $1 . ':0' x (9-length($exp_v6));
- } elsif ( $parm_ipv6 =~ /^(.+)::(.+)$/ ) {
+ } elsif ( $parm_ipv6_stripped =~ /^(.+)::(.+)$/ ) {
$exp_v6 = $1 . ':0' x (8-length($exp_v6)) . ':' . $2;
- } elsif ( $parm_ipv6 =~ /^::(.+[^:])$/ ) {
+ } elsif ( $parm_ipv6_stripped =~ /^::(.+[^:])$/ ) {
$exp_v6 = '0:' x (9-length($exp_v6)) . $1;
} else {
- $exp_v6 = $parm_ipv6;
+ $exp_v6 = $parm_ipv6_stripped;
}
my(@components) = split /:/, $exp_v6;
my(@nibbles) = reverse (split /\s*/, shift @components);
print "==================>\n";
system("tail -20 test-stdout");
print "===================\n";
+
print "stderr tail:\n";
print "==================>\n";
- system("tail -20 test-stderr");
+ system("tail -30 test-stderr");
print "===================\n";
+
+ print "stdout-server tail:\n";
+ print "==================>\n";
+ system("tail -20 test-stdout-server");
+ print "===================\n";
+
+ print "stderr-server tail:\n";
+ print "==================>\n";
+ system("tail -30 test-stderr-server");
+ print "===================\n";
+
print "... continue forced\n";
}