xref: /openbsd-src/gnu/usr.bin/perl/t/op/fork.t (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
1#!./perl
2
3# tests for both real and emulated fork()
4
5BEGIN {
6    chdir 't' if -d 't';
7    @INC = '../lib';
8    require Config; import Config;
9    unless ($Config{'d_fork'} or $Config{'d_pseudofork'}) {
10	print "1..0 # Skip: no fork\n";
11	exit 0;
12    }
13    $ENV{PERL5LIB} = "../lib";
14}
15
16if ($^O eq 'mpeix') {
17    print "1..0 # Skip: fork/status problems on MPE/iX\n";
18    exit 0;
19}
20
21$|=1;
22
23undef $/;
24@prgs = split "\n########\n", <DATA>;
25print "1..", scalar @prgs, "\n";
26
27$tmpfile = "forktmp000";
281 while -f ++$tmpfile;
29END { close TEST; unlink $tmpfile if $tmpfile; }
30
31$CAT = (($^O eq 'MSWin32') ? '.\perl -e "print <>"' : (($^O eq 'NetWare') ? 'perl -e "print <>"' : 'cat'));
32
33for (@prgs){
34    my $switch;
35    if (s/^\s*(-\w.*)//){
36	$switch = $1;
37    }
38    my($prog,$expected) = split(/\nEXPECT\n/, $_);
39    $expected =~ s/\n+$//;
40    # results can be in any order, so sort 'em
41    my @expected = sort split /\n/, $expected;
42    open TEST, ">$tmpfile" or die "Cannot open $tmpfile: $!";
43    print TEST $prog, "\n";
44    close TEST or die "Cannot close $tmpfile: $!";
45    my $results;
46    if ($^O eq 'MSWin32') {
47      $results = `.\\perl -I../lib $switch $tmpfile 2>&1`;
48    }
49    elsif ($^O eq 'NetWare') {
50      $results = `perl -I../lib $switch $tmpfile 2>&1`;
51    }
52    else {
53      $results = `./perl $switch $tmpfile 2>&1`;
54    }
55    $status = $?;
56    $results =~ s/\n+$//;
57    $results =~ s/at\s+forktmp\d+\s+line/at - line/g;
58    $results =~ s/of\s+forktmp\d+\s+aborted/of - aborted/g;
59# bison says 'parse error' instead of 'syntax error',
60# various yaccs may or may not capitalize 'syntax'.
61    $results =~ s/^(syntax|parse) error/syntax error/mig;
62    $results =~ s/^\n*Process terminated by SIG\w+\n?//mg
63	if $^O eq 'os2';
64    my @results = sort split /\n/, $results;
65    if ( "@results" ne "@expected" ) {
66	print STDERR "PROG: $switch\n$prog\n";
67	print STDERR "EXPECTED:\n$expected\n";
68	print STDERR "GOT:\n$results\n";
69	print "not ";
70    }
71    print "ok ", ++$i, "\n";
72}
73
74__END__
75$| = 1;
76if ($cid = fork) {
77    sleep 1;
78    if ($result = (kill 9, $cid)) {
79	print "ok 2\n";
80    }
81    else {
82	print "not ok 2 $result\n";
83    }
84    sleep 1 if $^O eq 'MSWin32';	# avoid WinNT race bug
85}
86else {
87    print "ok 1\n";
88    sleep 10;
89}
90EXPECT
91ok 1
92ok 2
93########
94$| = 1;
95if ($cid = fork) {
96    sleep 1;
97    print "not " unless kill 'INT', $cid;
98    print "ok 2\n";
99}
100else {
101    # XXX On Windows the default signal handler kills the
102    # XXX whole process, not just the thread (pseudo-process)
103    $SIG{INT} = sub { exit };
104    print "ok 1\n";
105    sleep 5;
106    die;
107}
108EXPECT
109ok 1
110ok 2
111########
112$| = 1;
113sub forkit {
114    print "iteration $i start\n";
115    my $x = fork;
116    if (defined $x) {
117	if ($x) {
118	    print "iteration $i parent\n";
119	}
120	else {
121	    print "iteration $i child\n";
122	}
123    }
124    else {
125	print "pid $$ failed to fork\n";
126    }
127}
128while ($i++ < 3) { do { forkit(); }; }
129EXPECT
130iteration 1 start
131iteration 1 parent
132iteration 1 child
133iteration 2 start
134iteration 2 parent
135iteration 2 child
136iteration 2 start
137iteration 2 parent
138iteration 2 child
139iteration 3 start
140iteration 3 parent
141iteration 3 child
142iteration 3 start
143iteration 3 parent
144iteration 3 child
145iteration 3 start
146iteration 3 parent
147iteration 3 child
148iteration 3 start
149iteration 3 parent
150iteration 3 child
151########
152$| = 1;
153fork()
154 ? (print("parent\n"),sleep(1))
155 : (print("child\n"),exit) ;
156EXPECT
157parent
158child
159########
160$| = 1;
161fork()
162 ? (print("parent\n"),exit)
163 : (print("child\n"),sleep(1)) ;
164EXPECT
165parent
166child
167########
168$| = 1;
169@a = (1..3);
170for (@a) {
171    if (fork) {
172	print "parent $_\n";
173	$_ = "[$_]";
174    }
175    else {
176	print "child $_\n";
177	$_ = "-$_-";
178    }
179}
180print "@a\n";
181EXPECT
182parent 1
183child 1
184parent 2
185child 2
186parent 2
187child 2
188parent 3
189child 3
190parent 3
191child 3
192parent 3
193child 3
194parent 3
195child 3
196[1] [2] [3]
197-1- [2] [3]
198[1] -2- [3]
199[1] [2] -3-
200-1- -2- [3]
201-1- [2] -3-
202[1] -2- -3-
203-1- -2- -3-
204########
205$| = 1;
206foreach my $c (1,2,3) {
207    if (fork) {
208	print "parent $c\n";
209    }
210    else {
211	print "child $c\n";
212	exit;
213    }
214}
215while (wait() != -1) { print "waited\n" }
216EXPECT
217child 1
218child 2
219child 3
220parent 1
221parent 2
222parent 3
223waited
224waited
225waited
226########
227use Config;
228$| = 1;
229$\ = "\n";
230fork()
231 ? print($Config{osname} eq $^O)
232 : print($Config{osname} eq $^O) ;
233EXPECT
2341
2351
236########
237$| = 1;
238$\ = "\n";
239fork()
240 ? do { require Config; print($Config::Config{osname} eq $^O); }
241 : do { require Config; print($Config::Config{osname} eq $^O); }
242EXPECT
2431
2441
245########
246$| = 1;
247use Cwd;
248my $cwd = cwd(); # Make sure we load Win32.pm while "../lib" still works.
249$\ = "\n";
250my $dir;
251if (fork) {
252    $dir = "f$$.tst";
253    mkdir $dir, 0755;
254    chdir $dir;
255    print cwd() =~ /\Q$dir/i ? "ok 1 parent" : "not ok 1 parent";
256    chdir "..";
257    rmdir $dir;
258}
259else {
260    sleep 2;
261    $dir = "f$$.tst";
262    mkdir $dir, 0755;
263    chdir $dir;
264    print cwd() =~ /\Q$dir/i ? "ok 1 child" : "not ok 1 child";
265    chdir "..";
266    rmdir $dir;
267}
268EXPECT
269ok 1 parent
270ok 1 child
271########
272$| = 1;
273$\ = "\n";
274my $getenv;
275if ($^O eq 'MSWin32' || $^O eq 'NetWare') {
276    $getenv = qq[$^X -e "print \$ENV{TST}"];
277}
278else {
279    $getenv = qq[$^X -e 'print \$ENV{TST}'];
280}
281$ENV{TST} = 'foo';
282if (fork) {
283    sleep 1;
284    print "parent before: " . `$getenv`;
285    $ENV{TST} = 'bar';
286    print "parent after: " . `$getenv`;
287}
288else {
289    print "child before: " . `$getenv`;
290    $ENV{TST} = 'baz';
291    print "child after: " . `$getenv`;
292}
293EXPECT
294child before: foo
295child after: baz
296parent before: foo
297parent after: bar
298########
299$| = 1;
300$\ = "\n";
301if ($pid = fork) {
302    waitpid($pid,0);
303    print "parent got $?"
304}
305else {
306    exit(42);
307}
308EXPECT
309parent got 10752
310########
311$| = 1;
312$\ = "\n";
313my $echo = 'echo';
314if ($pid = fork) {
315    waitpid($pid,0);
316    print "parent got $?"
317}
318else {
319    exec("$echo foo");
320}
321EXPECT
322foo
323parent got 0
324########
325if (fork) {
326    die "parent died";
327}
328else {
329    die "child died";
330}
331EXPECT
332parent died at - line 2.
333child died at - line 5.
334########
335if ($pid = fork) {
336    eval { die "parent died" };
337    print $@;
338}
339else {
340    eval { die "child died" };
341    print $@;
342}
343EXPECT
344parent died at - line 2.
345child died at - line 6.
346########
347if (eval q{$pid = fork}) {
348    eval q{ die "parent died" };
349    print $@;
350}
351else {
352    eval q{ die "child died" };
353    print $@;
354}
355EXPECT
356parent died at (eval 2) line 1.
357child died at (eval 2) line 1.
358########
359BEGIN {
360    $| = 1;
361    fork and exit;
362    print "inner\n";
363}
364# XXX In emulated fork(), the child will not execute anything after
365# the BEGIN block, due to difficulties in recreating the parse stacks
366# and restarting yyparse() midstream in the child.  This can potentially
367# be overcome by treating what's after the BEGIN{} as a brand new parse.
368#print "outer\n"
369EXPECT
370inner
371########
372sub pipe_to_fork ($$) {
373    my $parent = shift;
374    my $child = shift;
375    pipe($child, $parent) or die;
376    my $pid = fork();
377    die "fork() failed: $!" unless defined $pid;
378    close($pid ? $child : $parent);
379    $pid;
380}
381
382if (pipe_to_fork('PARENT','CHILD')) {
383    # parent
384    print PARENT "pipe_to_fork\n";
385    close PARENT;
386}
387else {
388    # child
389    while (<CHILD>) { print; }
390    close CHILD;
391    exit;
392}
393
394sub pipe_from_fork ($$) {
395    my $parent = shift;
396    my $child = shift;
397    pipe($parent, $child) or die;
398    my $pid = fork();
399    die "fork() failed: $!" unless defined $pid;
400    close($pid ? $child : $parent);
401    $pid;
402}
403
404if (pipe_from_fork('PARENT','CHILD')) {
405    # parent
406    while (<PARENT>) { print; }
407    close PARENT;
408}
409else {
410    # child
411    print CHILD "pipe_from_fork\n";
412    close CHILD;
413    exit;
414}
415EXPECT
416pipe_from_fork
417pipe_to_fork
418########
419$|=1;
420if ($pid = fork()) {
421    print "forked first kid\n";
422    print "waitpid() returned ok\n" if waitpid($pid,0) == $pid;
423}
424else {
425    print "first child\n";
426    exit(0);
427}
428if ($pid = fork()) {
429    print "forked second kid\n";
430    print "wait() returned ok\n" if wait() == $pid;
431}
432else {
433    print "second child\n";
434    exit(0);
435}
436EXPECT
437forked first kid
438first child
439waitpid() returned ok
440forked second kid
441second child
442wait() returned ok
443########
444pipe(RDR,WTR) or die $!;
445my $pid = fork;
446die "fork: $!" if !defined $pid;
447if ($pid == 0) {
448    my $rand_child = rand;
449    close RDR;
450    print WTR $rand_child, "\n";
451    close WTR;
452} else {
453    my $rand_parent = rand;
454    close WTR;
455    chomp(my $rand_child  = <RDR>);
456    close RDR;
457    print $rand_child ne $rand_parent, "\n";
458}
459EXPECT
4601
461########
462# [perl #39145] Perl_dounwind() crashing with Win32's fork() emulation
463sub { @_ = 3; fork ? die "1\n" : die "1\n" }->(2);
464EXPECT
4651
4661
467