File Coverage

blib/lib/Proc/Background/Unix.pm
Criterion Covered Total %
statement 87 106 82.0
branch 52 76 68.4
condition 11 23 47.8
subroutine 9 12 75.0
pod n/a
total 159 217 73.2


line stmt bran cond sub pod time code
1             package Proc::Background::Unix;
2             $Proc::Background::Unix::VERSION = '1.30';
3             # ABSTRACT: Unix-specific implementation of process create/wait/kill
4             require 5.004_04;
5              
6 21     21   179 use strict;
  21         42  
  21         1484  
7 21     21   131 use Exporter;
  21         39  
  21         846  
8 21     21   119 use Carp;
  21         42  
  21         1317  
9 21     21   11786 use POSIX qw( :errno_h :sys_wait_h );
  21         203066  
  21         170  
10              
11             # Test for existence of FD_CLOEXEC, needed for child-error-through-pipe trick
12             my ($FD_CLOEXEC);
13             eval {
14             require Fcntl;
15             $FD_CLOEXEC= Fcntl::FD_CLOEXEC();
16             };
17              
18             # For un-explained mysterious reasons, Time::HiRes::alarm seem to misbehave on 5.10 and earlier
19             # but core alarm works fine.
20             my $alarm= ($] >= 5.012)? do { require Time::HiRes; \&Time::HiRes::alarm; }
21             : sub {
22             # round up to whole seconds
23             CORE::alarm(POSIX::ceil($_[0]));
24             };
25              
26             @Proc::Background::Unix::ISA = qw(Exporter);
27              
28             # Start the background process. If it is started sucessfully, then record
29             # the process id in $self->{_os_obj}.
30             sub _start {
31 108     108   495 my ($self, $options)= @_;
32              
33             # There are three main scenarios for how-to-exec:
34             # * single-string command, to be handled by shell
35             # * arrayref command, to be handled by execve
36             # * arrayref command with 'exe' (fake argv0)
37             # and one that isn't logical:
38             # * single-string command with exe
39             # throw an error for that last one rather than trying something awkward
40             # like splitting the command string.
41              
42 108         266 my @argv;
43 108         338 my $cmd= $self->{_command};
44 108         829 my $exe= $self->{_exe};
45              
46 108 100       1278 if (ref $cmd eq 'ARRAY') {
    50          
47 103         475 @argv= @$cmd;
48 103 50       1104 ($exe, my $err) = Proc::Background::_resolve_path(defined $exe? $exe : $argv[0]);
49 103 100       488 return $self->_fatal($err) unless defined $exe;
50 101         368 $self->{_exe}= $exe;
51             } elsif (defined $exe) {
52 0         0 croak "Can't combine 'exe' option with single-string 'command', use arrayref 'command' instead.";
53             }
54              
55 106 100       444 if (defined $options->{cwd}) {
56             -d $options->{cwd}
57 7 100       167 or return $self->_fatal("directory does not exist: '$options->{cwd}'");
58             }
59              
60 104         271 my ($new_stdin, $new_stdout, $new_stderr);
61             $new_stdin= _resolve_file_handle($options->{stdin}, '<', \*STDIN)
62 104 100       418 if exists $options->{stdin};
63             $new_stdout= _resolve_file_handle($options->{stdout}, '>>', \*STDOUT)
64 104 100       418 if exists $options->{stdout};
65             $new_stderr= _resolve_file_handle($options->{stderr}, '>>', \*STDERR)
66 104 100       577 if exists $options->{stderr};
67              
68             # Fork a child process.
69 104         311 my ($pipe_r, $pipe_w);
70 104 50       362 if (defined $FD_CLOEXEC) {
71             # use a pipe for the child to report exec() errors
72 104 50       7025 pipe $pipe_r, $pipe_w or return $self->_fatal("pipe: $!");
73             # This pipe needs to be in the non-preserved range that doesn't exist after exec().
74             # In the edge case where a pipe received a FD less than $^F, the CLOEXEC flag isn't set.
75             # Try again on higher descriptors, then close the lower ones.
76 104         450 my @rejects;
77 104   33     2079 while (fileno $pipe_r <= $^F or fileno $pipe_w <= $^F) {
78 0         0 push @rejects, $pipe_r, $pipe_w;
79 0 0       0 pipe $pipe_r, $pipe_w or return $self->_fatal("pipe: $!");
80             }
81             }
82 104         327 my $pid;
83             {
84 104 100       195 if ($pid = fork()) {
  104 50       117629  
    0          
85             # parent
86 87         3450 $self->{_os_obj} = $pid;
87 87         1722 $self->{_pid} = $pid;
88 87 50       766 if (defined $pipe_r) {
89 87         3808 close $pipe_w;
90             # wait for child to reply or close the pipe
91 87     0   12204 local $SIG{PIPE}= sub {};
92 87         1730 my $msg= '';
93 87         31815570 while (0 < read $pipe_r, $msg, 1024, length $msg) {}
94 87         2840 close $pipe_r;
95             # If child wrote anything to the pipe, it failed to exec.
96             # Reap it before dying.
97 87 50       3169 if (length $msg) {
98 0         0 waitpid $pid, 0;
99 0         0 return $self->_fatal($msg);
100             }
101             }
102 87         441 last;
103             } elsif (defined $pid) {
104             # child
105             # Make absolutely sure nothing in this block interacts with the rest of the
106             # process state, and that flow control never skips the _exit().
107 17         953 eval {
108 17         1142 local $SIG{__DIE__}= undef;
109 17         264 eval {
110             chdir($options->{cwd}) or die "chdir($options->{cwd}): $!\n"
111 17 100 50     742 if defined $options->{cwd};
112              
113 17 100 50     961 open STDIN, '<&', $new_stdin or die "Can't redirect STDIN: $!\n"
114             if defined $new_stdin;
115 17 100 50     680 open STDOUT, '>&', $new_stdout or die "Can't redirect STDOUT: $!\n"
116             if defined $new_stdout;
117 17 100 50     239 open STDERR, '>&', $new_stderr or die "Can't redirect STDERR: $!\n"
118             if defined $new_stderr;
119              
120 17 100       381 if (defined $exe) {
121 16 0       154 exec { $exe } @argv or die "$0: exec failed: $!\n";
  16         0  
122             } else {
123 1 0       0 exec $cmd or die "$0: exec failed: $!\n";
124             }
125             };
126 0 0       0 if (defined $pipe_w) {
127 0         0 print $pipe_w $@;
128 0         0 close $pipe_w; # force it to flush. Nothing else needs closed because we are about to _exit
129             } else {
130 0         0 print STDERR $@;
131             }
132             };
133 0         0 POSIX::_exit(1);
134             } elsif ($! == EAGAIN) {
135 0         0 sleep 5;
136 0         0 redo;
137             } else {
138 0         0 return $self->_fatal("fork: $!");
139             }
140             }
141              
142 87         5277 $self;
143             }
144              
145             sub _resolve_file_handle {
146 32     32   151 my ($thing, $mode, $default)= @_;
147 32 100       142 if (!defined $thing) {
    100          
148 11 50       419 open my $fh, $mode, '/dev/null' or croak "open(/dev/null): $!";
149 11         60 return $fh;
150             } elsif (ref $thing) {
151             # use 'undef' to mean no-change
152 19 100       89 return (fileno($thing) == fileno($default))? undef : $thing;
153             } else {
154 2 50       110 open my $fh, $mode, $thing or croak "open($thing): $!";
155 2         24 return $fh;
156             }
157             }
158              
159             # Wait for the child.
160             # (0, exit_value) : sucessfully waited on.
161             # (1, undef) : process already reaped and exit value lost.
162             # (2, undef) : process still running.
163             sub _waitpid {
164 204     204   762 my ($self, $blocking, $wait_seconds) = @_;
165              
166             {
167             # Try to wait on the process.
168             # Implement the optional timeout with the 'alarm' call.
169 204         410 my $result= 0;
  204         515  
170 204 100 100     1291 if ($blocking && $wait_seconds) {
171 57     14   1971 local $SIG{ALRM}= sub { die "alarm\n" };
  14         981  
172 57         951 $alarm->($wait_seconds);
173 57         463 eval { $result= waitpid($self->{_os_obj}, 0); };
  57         14564491  
174 57         2707 $alarm->(0);
175             }
176             else {
177 147 100       22102296 $result= waitpid($self->{_os_obj}, $blocking? 0 : WNOHANG);
178             }
179              
180             # Process finished. Grab the exit value.
181 204 100 33     1895 if ($result == $self->{_os_obj}) {
    50          
    50          
182 87         326 delete $self->{_suspended};
183 87         2189 return (0, $?);
184             }
185             # Process already reaped. We don't know the exist status.
186             elsif ($result == -1 and $! == ECHILD) {
187 0         0 return (1, 0);
188             }
189             # Process still running.
190             elsif ($result == 0) {
191 117         946 return (2, 0);
192             }
193             # If we reach here, then waitpid caught a signal, so let's retry it.
194 0         0 redo;
195             }
196 0         0 return 0;
197             }
198              
199             sub _suspend {
200 0     0   0 kill STOP => $_[0]->{_os_obj};
201             }
202              
203             sub _resume {
204 0     0   0 kill CONT => $_[0]->{_os_obj};
205             }
206              
207             sub _terminate {
208 26     26   127 my $self = shift;
209 26 50 33     1042 my @kill_sequence= @_ && ref $_[0] eq 'ARRAY'? @{ $_[0] } : qw( TERM 2 TERM 8 KILL 3 KILL 7 );
  0         0  
210             # Try to kill the process with different signals. Calling alive() will
211             # collect the exit status of the program.
212 26   33     289 while (@kill_sequence and $self->alive) {
213 26         94 my $sig= shift @kill_sequence;
214 26         115 my $delay= shift @kill_sequence;
215 26         1555 kill($sig, $self->{_os_obj});
216 26 50       203 next unless defined $delay;
217 26 50       139 last if $self->_reap(1, $delay); # block before sending next signal
218             }
219             }
220              
221             1;
222              
223             __END__