1*0Sstevel@tonic-gate#!./perl -w 2*0Sstevel@tonic-gate 3*0Sstevel@tonic-gatemy $child; 4*0Sstevel@tonic-gatemy $can_fork; 5*0Sstevel@tonic-gatemy $has_perlio; 6*0Sstevel@tonic-gate 7*0Sstevel@tonic-gateBEGIN { 8*0Sstevel@tonic-gate chdir 't' if -d 't'; 9*0Sstevel@tonic-gate @INC = '../lib'; 10*0Sstevel@tonic-gate require Config; import Config; 11*0Sstevel@tonic-gate $can_fork = $Config{'d_fork'} 12*0Sstevel@tonic-gate || ($^O eq 'MSWin32' && $Config{useithreads} 13*0Sstevel@tonic-gate && $Config{ccflags} =~ /-DPERL_IMPLICIT_SYS\b/); 14*0Sstevel@tonic-gate 15*0Sstevel@tonic-gate 16*0Sstevel@tonic-gate if ($^O eq "hpux" or $Config{'extensions'} !~ /\bSocket\b/ && 17*0Sstevel@tonic-gate !(($^O eq 'VMS') && $Config{d_socket})) { 18*0Sstevel@tonic-gate print "1..0\n"; 19*0Sstevel@tonic-gate exit 0; 20*0Sstevel@tonic-gate } 21*0Sstevel@tonic-gate 22*0Sstevel@tonic-gate # Too many things in this test will hang forever if something is wrong, 23*0Sstevel@tonic-gate # so we need a self destruct timer. And IO can hang despite an alarm. 24*0Sstevel@tonic-gate 25*0Sstevel@tonic-gate # This is convoluted, but we must fork before Test::More, else child's 26*0Sstevel@tonic-gate # Test::More thinks that it ran no tests, and prints a message to that 27*0Sstevel@tonic-gate # effect 28*0Sstevel@tonic-gate if( $can_fork) { 29*0Sstevel@tonic-gate my $parent = $$; 30*0Sstevel@tonic-gate $child = fork; 31*0Sstevel@tonic-gate die "Fork failed" unless defined $child; 32*0Sstevel@tonic-gate if (!$child) { 33*0Sstevel@tonic-gate $SIG{INT} = sub {exit 0}; # You have 60 seconds. Your time starts now. 34*0Sstevel@tonic-gate my $must_finish_by = time + 60; 35*0Sstevel@tonic-gate my $remaining; 36*0Sstevel@tonic-gate while (($remaining = $must_finish_by - time) > 0) { 37*0Sstevel@tonic-gate sleep $remaining; 38*0Sstevel@tonic-gate } 39*0Sstevel@tonic-gate warn "Something unexpectedly hung during testing"; 40*0Sstevel@tonic-gate kill "INT", $parent or die "Kill failed: $!"; 41*0Sstevel@tonic-gate exit 1; 42*0Sstevel@tonic-gate } 43*0Sstevel@tonic-gate } 44*0Sstevel@tonic-gate unless ($has_perlio = find PerlIO::Layer 'perlio') { 45*0Sstevel@tonic-gate print <<EOF; 46*0Sstevel@tonic-gate# Since you don't have perlio you might get failures with UTF-8 locales. 47*0Sstevel@tonic-gateEOF 48*0Sstevel@tonic-gate } 49*0Sstevel@tonic-gate} 50*0Sstevel@tonic-gate 51*0Sstevel@tonic-gateuse Socket; 52*0Sstevel@tonic-gateuse Test::More; 53*0Sstevel@tonic-gateuse strict; 54*0Sstevel@tonic-gateuse warnings; 55*0Sstevel@tonic-gateuse Errno; 56*0Sstevel@tonic-gate 57*0Sstevel@tonic-gatemy $skip_reason; 58*0Sstevel@tonic-gate 59*0Sstevel@tonic-gateif( !$Config{d_alarm} ) { 60*0Sstevel@tonic-gate plan skip_all => "alarm() not implemented on this platform"; 61*0Sstevel@tonic-gate} elsif( !$can_fork ) { 62*0Sstevel@tonic-gate plan skip_all => "fork() not implemented on this platform"; 63*0Sstevel@tonic-gate} else { 64*0Sstevel@tonic-gate # This should fail but not die if there is real socketpair 65*0Sstevel@tonic-gate eval {socketpair LEFT, RIGHT, -1, -1, -1}; 66*0Sstevel@tonic-gate if ($@ =~ /^Unsupported socket function "socketpair" called/ || 67*0Sstevel@tonic-gate $! =~ /^The operation requested is not supported./) { # Stratus VOS 68*0Sstevel@tonic-gate plan skip_all => 'No socketpair (real or emulated)'; 69*0Sstevel@tonic-gate } else { 70*0Sstevel@tonic-gate eval {AF_UNIX}; 71*0Sstevel@tonic-gate if ($@ =~ /^Your vendor has not defined Socket macro AF_UNIX/) { 72*0Sstevel@tonic-gate plan skip_all => 'No AF_UNIX'; 73*0Sstevel@tonic-gate } else { 74*0Sstevel@tonic-gate plan tests => 45; 75*0Sstevel@tonic-gate } 76*0Sstevel@tonic-gate } 77*0Sstevel@tonic-gate} 78*0Sstevel@tonic-gate 79*0Sstevel@tonic-gate# But we'll install an alarm handler in case any of the races below fail. 80*0Sstevel@tonic-gate$SIG{ALRM} = sub {die "Unexpected alarm during testing"}; 81*0Sstevel@tonic-gate 82*0Sstevel@tonic-gateok (socketpair (LEFT, RIGHT, AF_UNIX, SOCK_STREAM, PF_UNSPEC), 83*0Sstevel@tonic-gate "socketpair (LEFT, RIGHT, AF_UNIX, SOCK_STREAM, PF_UNSPEC)") 84*0Sstevel@tonic-gate or print "# \$\! = $!\n"; 85*0Sstevel@tonic-gate 86*0Sstevel@tonic-gateif ($has_perlio) { 87*0Sstevel@tonic-gate binmode(LEFT, ":bytes"); 88*0Sstevel@tonic-gate binmode(RIGHT, ":bytes"); 89*0Sstevel@tonic-gate} 90*0Sstevel@tonic-gate 91*0Sstevel@tonic-gatemy @left = ("hello ", "world\n"); 92*0Sstevel@tonic-gatemy @right = ("perl ", "rules!"); # Not like I'm trying to bias any survey here. 93*0Sstevel@tonic-gate 94*0Sstevel@tonic-gateforeach (@left) { 95*0Sstevel@tonic-gate # is (syswrite (LEFT, $_), length $_, "write " . _qq ($_) . " to left"); 96*0Sstevel@tonic-gate is (syswrite (LEFT, $_), length $_, "syswrite to left"); 97*0Sstevel@tonic-gate} 98*0Sstevel@tonic-gateforeach (@right) { 99*0Sstevel@tonic-gate # is (syswrite (RIGHT, $_), length $_, "write " . _qq ($_) . " to right"); 100*0Sstevel@tonic-gate is (syswrite (RIGHT, $_), length $_, "syswrite to right"); 101*0Sstevel@tonic-gate} 102*0Sstevel@tonic-gate 103*0Sstevel@tonic-gate# stream socket, so our writes will become joined: 104*0Sstevel@tonic-gatemy ($buffer, $expect); 105*0Sstevel@tonic-gate$expect = join '', @right; 106*0Sstevel@tonic-gateundef $buffer; 107*0Sstevel@tonic-gateis (read (LEFT, $buffer, length $expect), length $expect, "read on left"); 108*0Sstevel@tonic-gateis ($buffer, $expect, "content what we expected?"); 109*0Sstevel@tonic-gate$expect = join '', @left; 110*0Sstevel@tonic-gateundef $buffer; 111*0Sstevel@tonic-gateis (read (RIGHT, $buffer, length $expect), length $expect, "read on right"); 112*0Sstevel@tonic-gateis ($buffer, $expect, "content what we expected?"); 113*0Sstevel@tonic-gate 114*0Sstevel@tonic-gateok (shutdown(LEFT, SHUT_WR), "shutdown left for writing"); 115*0Sstevel@tonic-gate# This will hang forever if eof is buggy, and alarm doesn't interrupt system 116*0Sstevel@tonic-gate# Calls. Hence the child process minder. 117*0Sstevel@tonic-gateSKIP: { 118*0Sstevel@tonic-gate skip "SCO Unixware / OSR have a bug with shutdown",2 if $^O =~ /^(?:svr|sco)/; 119*0Sstevel@tonic-gate local $SIG{ALRM} = sub { warn "EOF on right took over 3 seconds" }; 120*0Sstevel@tonic-gate local $TODO = "Known problems with unix sockets on $^O" 121*0Sstevel@tonic-gate if $^O eq 'hpux' || $^O eq 'super-ux'; 122*0Sstevel@tonic-gate alarm 3; 123*0Sstevel@tonic-gate $! = 0; 124*0Sstevel@tonic-gate ok (eof RIGHT, "right is at EOF"); 125*0Sstevel@tonic-gate local $TODO = "Known problems with unix sockets on $^O" 126*0Sstevel@tonic-gate if $^O eq 'unicos' || $^O eq 'unicosmk'; 127*0Sstevel@tonic-gate is ($!, '', 'and $! should report no error'); 128*0Sstevel@tonic-gate alarm 60; 129*0Sstevel@tonic-gate} 130*0Sstevel@tonic-gate 131*0Sstevel@tonic-gatemy $err = $!; 132*0Sstevel@tonic-gate$SIG{PIPE} = 'IGNORE'; 133*0Sstevel@tonic-gate{ 134*0Sstevel@tonic-gate local $SIG{ALRM} 135*0Sstevel@tonic-gate = sub { warn "syswrite to left didn't fail within 3 seconds" }; 136*0Sstevel@tonic-gate alarm 3; 137*0Sstevel@tonic-gate # Split the system call from the is() - is() does IO so 138*0Sstevel@tonic-gate # (say) a flush may do a seek which on a pipe may disturb errno 139*0Sstevel@tonic-gate my $ans = syswrite (LEFT, "void"); 140*0Sstevel@tonic-gate $err = $!; 141*0Sstevel@tonic-gate is ($ans, undef, "syswrite to shutdown left should fail"); 142*0Sstevel@tonic-gate alarm 60; 143*0Sstevel@tonic-gate} 144*0Sstevel@tonic-gate{ 145*0Sstevel@tonic-gate # This may need skipping on some OSes - restoring value saved above 146*0Sstevel@tonic-gate # should help 147*0Sstevel@tonic-gate $! = $err; 148*0Sstevel@tonic-gate ok (($!{EPIPE} or $!{ESHUTDOWN}), '$! should be EPIPE or ESHUTDOWN') 149*0Sstevel@tonic-gate or printf "\$\!=%d(%s)\n", $err, $err; 150*0Sstevel@tonic-gate} 151*0Sstevel@tonic-gate 152*0Sstevel@tonic-gatemy @gripping = (chr 255, chr 127); 153*0Sstevel@tonic-gateforeach (@gripping) { 154*0Sstevel@tonic-gate is (syswrite (RIGHT, $_), length $_, "syswrite to right"); 155*0Sstevel@tonic-gate} 156*0Sstevel@tonic-gate 157*0Sstevel@tonic-gateok (!eof LEFT, "left is not at EOF"); 158*0Sstevel@tonic-gate 159*0Sstevel@tonic-gate$expect = join '', @gripping; 160*0Sstevel@tonic-gateundef $buffer; 161*0Sstevel@tonic-gateis (read (LEFT, $buffer, length $expect), length $expect, "read on left"); 162*0Sstevel@tonic-gateis ($buffer, $expect, "content what we expected?"); 163*0Sstevel@tonic-gate 164*0Sstevel@tonic-gateok (close LEFT, "close left"); 165*0Sstevel@tonic-gateok (close RIGHT, "close right"); 166*0Sstevel@tonic-gate 167*0Sstevel@tonic-gate 168*0Sstevel@tonic-gate# And now datagrams 169*0Sstevel@tonic-gate# I suspect we also need a self destruct time-bomb for these, as I don't see any 170*0Sstevel@tonic-gate# guarantee that the stack won't drop a UDP packet, even if it is for localhost. 171*0Sstevel@tonic-gate 172*0Sstevel@tonic-gateSKIP: { 173*0Sstevel@tonic-gate skip "No usable SOCK_DGRAM for socketpair", 24 if ($^O =~ /^(MSWin32|os2)\z/); 174*0Sstevel@tonic-gate local $TODO = "socketpair not supported on $^O" if $^O eq 'nto'; 175*0Sstevel@tonic-gate 176*0Sstevel@tonic-gateok (socketpair (LEFT, RIGHT, AF_UNIX, SOCK_DGRAM, PF_UNSPEC), 177*0Sstevel@tonic-gate "socketpair (LEFT, RIGHT, AF_UNIX, SOCK_DGRAM, PF_UNSPEC)") 178*0Sstevel@tonic-gate or print "# \$\! = $!\n"; 179*0Sstevel@tonic-gate 180*0Sstevel@tonic-gateif ($has_perlio) { 181*0Sstevel@tonic-gate binmode(LEFT, ":bytes"); 182*0Sstevel@tonic-gate binmode(RIGHT, ":bytes"); 183*0Sstevel@tonic-gate} 184*0Sstevel@tonic-gate 185*0Sstevel@tonic-gateforeach (@left) { 186*0Sstevel@tonic-gate # is (syswrite (LEFT, $_), length $_, "write " . _qq ($_) . " to left"); 187*0Sstevel@tonic-gate is (syswrite (LEFT, $_), length $_, "syswrite to left"); 188*0Sstevel@tonic-gate} 189*0Sstevel@tonic-gateforeach (@right) { 190*0Sstevel@tonic-gate # is (syswrite (RIGHT, $_), length $_, "write " . _qq ($_) . " to right"); 191*0Sstevel@tonic-gate is (syswrite (RIGHT, $_), length $_, "syswrite to right"); 192*0Sstevel@tonic-gate} 193*0Sstevel@tonic-gate 194*0Sstevel@tonic-gate# stream socket, so our writes will become joined: 195*0Sstevel@tonic-gatemy ($total); 196*0Sstevel@tonic-gate$total = join '', @right; 197*0Sstevel@tonic-gateforeach $expect (@right) { 198*0Sstevel@tonic-gate undef $buffer; 199*0Sstevel@tonic-gate is (sysread (LEFT, $buffer, length $total), length $expect, "read on left"); 200*0Sstevel@tonic-gate is ($buffer, $expect, "content what we expected?"); 201*0Sstevel@tonic-gate} 202*0Sstevel@tonic-gate$total = join '', @left; 203*0Sstevel@tonic-gateforeach $expect (@left) { 204*0Sstevel@tonic-gate undef $buffer; 205*0Sstevel@tonic-gate is (sysread (RIGHT, $buffer, length $total), length $expect, "read on right"); 206*0Sstevel@tonic-gate is ($buffer, $expect, "content what we expected?"); 207*0Sstevel@tonic-gate} 208*0Sstevel@tonic-gate 209*0Sstevel@tonic-gateok (shutdown(LEFT, 1), "shutdown left for writing"); 210*0Sstevel@tonic-gate 211*0Sstevel@tonic-gate# eof uses buffering. eof is indicated by a sysread of zero. 212*0Sstevel@tonic-gate# but for a datagram socket there's no way it can know nothing will ever be 213*0Sstevel@tonic-gate# sent 214*0Sstevel@tonic-gateSKIP: { 215*0Sstevel@tonic-gate skip "$^O does length 0 udp reads", 2 if ($^O eq 'os390'); 216*0Sstevel@tonic-gate 217*0Sstevel@tonic-gate my $alarmed = 0; 218*0Sstevel@tonic-gate local $SIG{ALRM} = sub { $alarmed = 1; }; 219*0Sstevel@tonic-gate print "# Approximate forever as 3 seconds. Wait 'forever'...\n"; 220*0Sstevel@tonic-gate alarm 3; 221*0Sstevel@tonic-gate undef $buffer; 222*0Sstevel@tonic-gate is (sysread (RIGHT, $buffer, 1), undef, 223*0Sstevel@tonic-gate "read on right should be interrupted"); 224*0Sstevel@tonic-gate is ($alarmed, 1, "alarm should have fired"); 225*0Sstevel@tonic-gate} 226*0Sstevel@tonic-gate 227*0Sstevel@tonic-gatealarm 30; 228*0Sstevel@tonic-gate 229*0Sstevel@tonic-gate#ok (eof RIGHT, "right is at EOF"); 230*0Sstevel@tonic-gate 231*0Sstevel@tonic-gateforeach (@gripping) { 232*0Sstevel@tonic-gate is (syswrite (RIGHT, $_), length $_, "syswrite to right"); 233*0Sstevel@tonic-gate} 234*0Sstevel@tonic-gate 235*0Sstevel@tonic-gate$total = join '', @gripping; 236*0Sstevel@tonic-gateforeach $expect (@gripping) { 237*0Sstevel@tonic-gate undef $buffer; 238*0Sstevel@tonic-gate is (sysread (LEFT, $buffer, length $total), length $expect, "read on left"); 239*0Sstevel@tonic-gate is ($buffer, $expect, "content what we expected?"); 240*0Sstevel@tonic-gate} 241*0Sstevel@tonic-gate 242*0Sstevel@tonic-gateok (close LEFT, "close left"); 243*0Sstevel@tonic-gateok (close RIGHT, "close right"); 244*0Sstevel@tonic-gate 245*0Sstevel@tonic-gate} # end of DGRAM SKIP 246*0Sstevel@tonic-gate 247*0Sstevel@tonic-gatekill "INT", $child or warn "Failed to kill child process $child: $!"; 248*0Sstevel@tonic-gateexit 0; 249