diff --git a/lib/Plack/App/WrapCGI.pm b/lib/Plack/App/WrapCGI.pm index 306a9c832..1ce526ebb 100644 --- a/lib/Plack/App/WrapCGI.pm +++ b/lib/Plack/App/WrapCGI.pm @@ -30,6 +30,7 @@ sub prepare_app { pipe( my $stdoutr, my $stdoutw ); pipe( my $stdinr, my $stdinw ); + local $SIG{CHLD} = 'DEFAULT'; my $pid = fork(); Carp::croak("fork failed: $!") unless defined $pid; @@ -64,13 +65,17 @@ sub prepare_app { # close STDIN so child will stop waiting close $stdinw; - my $res = ''; - while (waitpid($pid, WNOHANG) <= 0) { + my $res = ''; my $waited_pid; + while (($waited_pid = waitpid($pid, WNOHANG)) == 0) { $res .= slurp_fh($stdoutr); } $res .= slurp_fh($stdoutr); - if (POSIX::WIFEXITED($?)) { + # -1 means that the child went away, and something else + # (probably some global SIGCHLD handler) took care of it; + # yes, we just reset $SIG{CHLD} above, but you can never + # be too sure + if (POSIX::WIFEXITED($?) || $waited_pid == -1) { return CGI::Parse::PSGI::parse_cgi_output(\$res); } else { Carp::croak("Error at run_on_shell CGI: $!"); diff --git a/t/Plack-Middleware/wrapcgi_exec.t b/t/Plack-Middleware/wrapcgi_exec.t index 5703c620e..8dbb1d53c 100644 --- a/t/Plack-Middleware/wrapcgi_exec.t +++ b/t/Plack-Middleware/wrapcgi_exec.t @@ -136,5 +136,31 @@ print \$q->header(-type => "text/plain"), \$result; undef $tmp; }; +# test that SIGCHLD handlers don't interfere +{ + my $tmp = File::Temp->new(CLEANUP => 1); + print $tmp <<"..."; +#!$^X +use CGI; +my \$q = CGI->new; +print \$q->header, "Hello"; +... + close $tmp; + + chmod(oct("0700"), $tmp->filename) or die "Cannot chmod"; + + local $SIG{CHLD} = 'IGNORE'; + my $app_exec = Plack::App::WrapCGI->new(script => "$tmp", execute => 1)->to_app; + test_psgi app => $app_exec, client => sub { + my $cb = shift; + + my $res = $cb->(GET "http://localhost/"); + is $res->code, 200; + is $res->content, "Hello"; + }; + + undef $tmp; +}; + done_testing;