File Coverage

blib/lib/Proc/SyncExec.pm
Criterion Covered Total %
statement 83 101 82.1
branch 31 64 48.4
condition 10 23 43.4
subroutine 12 13 92.3
pod 5 5 100.0
total 141 206 68.4


line stmt bran cond sub pod time code
1             # $Id: SyncExec.pm,v 1.5 2005/02/04 12:15:57 roderick Exp $
2             #
3             # Copyright (c) 1997 Roderick Schertler. All rights reserved. This
4             # program is free software; you can redistribute it and/or modify it
5             # under the same terms as Perl itself.
6              
7             =head1 NAME
8              
9             Proc::SyncExec - Spawn processes but report exec() errors
10              
11             =head1 SYNOPSIS
12              
13             # Normal-looking piped opens which properly report exec() errors in $!:
14             sync_open WRITER_FH, "|command -with args" or die $!;
15             sync_open READER_FH, "command -with args|" or die $!;
16              
17             # Synchronized fork/exec which reports exec errors in $!:
18             $pid = sync_exec $command, @arg;
19             $pid = sync_exec $code_ref, $cmd, @arg; # run code after fork in kid
20              
21             # fork() which retries if it fails, then croaks() if it still fails.
22             $pid = fork_retry;
23             $pid = fork_retry 100; # retry 100 times rather than 5
24             $pid = fork_retry 100, 2; # sleep 2 rather than 5 seconds between
25              
26             # A couple of interfaces similar to sync_open() but which let you
27             # avoid the shell:
28             $pid = sync_fhpopen_noshell READERFH, 'r', @command;
29             $pid = sync_fhpopen_noshell WRITERFH, 'w', @command;
30             $fh = sync_popen_noshell 'r', @command_which_outputs;
31             $fh = sync_popen_noshell 'w', @command_which_inputs;
32             ($fh, $pid) = sync_popen_noshell 'r', @command_which_outputs;
33             ($fh, $pid)= sync_popen_noshell 'w', @command_which_inputs;
34              
35             =head1 DESCRIPTION
36              
37             This module contains functions for synchronized process spawning with
38             full error return. If the child's exec() call fails the reason for the
39             failure is reported back to the parent.
40              
41             These functions will croak() if they encounter an unexpected system
42             error, such as a pipe() failure or a repeated fork() failure.
43              
44             Nothing is exported by default.
45              
46             =over
47              
48             =cut
49              
50             #';
51              
52             package Proc::SyncExec;
53              
54 7     7   8169 use strict;
  7         14  
  7         266  
55 7     7   42 use vars qw($VERSION @ISA @EXPORT_OK);
  7         14  
  7         532  
56              
57 7     7   35 use Carp qw(croak);
  7         7  
  7         399  
58 7     7   28 use Exporter ();
  7         14  
  7         112  
59 7     7   35 use Fcntl qw(F_SETFD);
  7         7  
  7         322  
60 7     7   8120 use POSIX qw(EINTR);
  7         73724  
  7         42  
61 7     7   17752 use Symbol qw(gensym qualify_to_ref);
  7         9611  
  7         12495  
62              
63             $VERSION = '1.01';
64             @ISA = qw(Exporter);
65             @EXPORT_OK = qw(fork_retry sync_exec sync_fhpopen_noshell
66             sync_popen_noshell sync_open);
67              
68             =item B [I [I]]
69              
70             This function runs fork() until it succeeds or until I
71             (default 5) attempts have been made, sleeping I seconds
72             (default 5) between attempts. If the last fork() fails B
73             croak()s.
74              
75             =cut
76              
77             sub fork_retry {
78 27 50   27 1 355 @_ > 2 and croak "Usage: fork_retry max_retries=5 sleep_between=5";
79 27         92 my ($max_retries, $sleep) = @_;
80 27         45 my ($retries, $kid);
81              
82 27 50 33     139 $max_retries = 5 if !defined $max_retries or $max_retries < 0;
83 27 50 33     114 $sleep = 5 if !defined $sleep or $sleep < 0;
84              
85 27         36 $retries = 0;
86 27         30872 while (!defined($kid = fork)) {
87 0 0       0 croak "Can't fork: $!" if $retries++ >= $max_retries;
88 0         0 sleep $sleep;
89             }
90 27         1529 return $kid;
91             }
92              
93             =item B [I] I...
94              
95             This function is similar to a fork()/exec() sequence but with a few
96             twists.
97              
98             B does not return until after the fork()ed child has already
99             performed its exec(). The synchronization this provides is useful in
100             some unusual circumstances.
101              
102             Normally the pid of the child process is returned. However, if the
103             child fails its exec() B returns undef and sets $! to the
104             reason for the child's exec() failure.
105              
106             Since the @cmd array is passed directly to Perl's exec() Perl might
107             choose to invoke the command via the shell if @cmd contains only one
108             element and it looks like it needs a shell to interpret it. If this
109             happens the return value of B only indicates whether the
110             exec() of the shell worked.
111              
112             The optional initial I argument must be a code reference. If it
113             is present it is run in the child just before exec() is called. You can
114             use this to set up redirections or whatever. If I returns false
115             no exec is performed, instead a failure is returned using the current $!
116             value (or EINTR if $! is 0).
117              
118             If the fork() fails or if there is some other unexpected system error
119             B croak()s rather than returning.
120              
121             =cut
122              
123             sub sync_exec {
124 20 100 66 20 1 12116 my $code = (@_ && ref $_[0] eq 'CODE') ? shift : undef;
125 20 50       138 @_ or croak 'Usage: sync_exec [code] cmd [arg]...';
126 20         148 my @cmd = @_;
127              
128 20         428 my ($reader, $writer) = (gensym, gensym);
129 20 50       5328 pipe $reader, $writer
130             or croak "Can't pipe(): $!";
131 20         107 my $pid = fork_retry;
132 20 100       593 if (!$pid) {
133 5         522 my $ok = 1;
134 5 50       696 $ok = close $reader if $ok;
135 5 50       484 $ok = fcntl $writer, F_SETFD, 1 if $ok;
136 5 100 66     513 $ok = &$code() if $ok && $code;
137 5         306 $^W = 0; # turn off "Can't exec" message
138 5 0 33     0 if (!$ok or !exec @cmd) {
139 0         0 select $writer;
140 0         0 $| = 1;
141 0         0 print $!+0;
142 0         0 POSIX::_exit 1;
143             }
144             }
145 15 50       519 close $writer or croak "Error closing parent's write pipe: $!";
146              
147 15         50 my ($nread, $buf);
148 15         281 while (1) {
149 15         4203563 $nread = sysread $reader, $buf, 16;
150 15 50       194 last if defined $nread;
151 0 0       0 next if $! == EINTR;
152 0         0 croak "Error reading from pipe: $!";
153             }
154 15 50       328 close $reader or croak "Error closing parent's read pipe: $!";
155 15 100       103 if ($nread) {
156 11         20579 while (waitpid($pid, 0) == -1) {
157 0 0       0 next if $! == EINTR;
158 0         0 croak "Error waiting for child: $!";
159             }
160 11         41 $pid = undef;
161 11   50     194 $! = $buf+0 || EINTR;
162             }
163 15         636 return $pid;
164             }
165              
166             =item B I I I [I]...
167              
168             This is a popen() but it never invokes the shell and it uses sync_exec()
169             under the covers. See L.
170              
171             The I is either C<'r'> to read from the process or C<'w'> to write
172             to it.
173              
174             The return value is the pid of the forked process.
175              
176             =cut
177              
178             sub sync_fhpopen_noshell {
179 5 50   5 1 51 @_ >= 3 or croak 'Usage: sync_fhpopen_noshell fh type cmd...';
180 5         38 my $fh_parent = qualify_to_ref shift, caller;
181 5         49 my ($type, @cmd) = @_;
182 5         13 my ($fh_child, $fh_dup_to, $fh_dup_type, $result);
183              
184 5         39 $fh_child = gensym;
185 5 100       164 if ($type eq 'w') {
    50          
186 2         122 $result = pipe $fh_child, $fh_parent;
187 2         22 $fh_dup_to = \*STDIN;
188 2         8 $fh_dup_type = '<&';
189             }
190             elsif ($type eq 'r') {
191 3         120 $result = pipe $fh_parent, $fh_child;
192 3         15 $fh_dup_to = \*STDOUT;
193 3         18 $fh_dup_type = '>&';
194             }
195             else {
196 0         0 croak "Invalid popen type `$type'";
197             }
198              
199 5 50       29 $result or croak "Can't pipe(): $!";
200             $result = sync_exec sub {
201 2 50 33 2   1292 close $fh_parent
202             and open $fh_dup_to, $fh_dup_type . fileno $fh_child
203             and close $fh_child
204 5         84 }, @cmd;
205 3         376 my $errno = $!;
206 3 50       60 close $fh_child
207             or croak "Error closing parent pipe: $!";
208 3         12 $! = $errno;
209 3         145 return $result;
210             }
211              
212             =item B I I I...
213              
214             This is like B, but you don't have to supply
215             the filehandle.
216              
217             If called in an array context the return value is a list consisting of
218             the filehandle and the PID of the child. In a scalar context only the
219             filehandle is returned.
220              
221             =cut
222              
223             #'
224              
225             sub sync_popen_noshell {
226 0 0   0 1 0 @_ >= 2 or croak 'Usage: sync_popen_noshell type cmd...';
227 0         0 my ($type, @cmd) = @_;
228 0         0 my $fh = gensym;
229 0 0       0 my $pid = sync_fhpopen_noshell $fh, $type, @cmd
230             or return;
231 0 0       0 wantarray ? ($fh, $pid) : $fh;
232             }
233              
234             =item B I [I]
235              
236             This is like a Perl open() except that if a pipe is involved and the
237             implied exec() fails sync_open() fails with $! set appropriately. See
238             L.
239              
240             Like B, B croak()s if there is an unexpected
241             system error (such as a failed pipe()).
242              
243             Also like B, if you use a command which Perl needs to use the
244             shell to interpret you'll only know if the exec of the shell worked.
245             Use B or B to be sure that this doesn't
246             happen.
247              
248             =cut
249              
250             sub sync_open {
251 5 50 33 5 1 18397 @_ == 1 or @_ == 2 or croak 'Usage: sync_open fh [open-spec]';
252 5         129 my $fh = qualify_to_ref shift, caller;
253 5 50       192 my $cmd = @_ ? shift : $fh;
254 5         8 my $type;
255              
256 5         76 $cmd =~ s/^\s+//;
257 5         72 $cmd =~ s/\s+$//;
258 5 100       102 if ($cmd =~ s/^\|//) {
    50          
259 2 50       202 if (substr($cmd, -1) eq '|') {
260 0         0 croak "Can't do bidirectional pipe";
261             }
262 2         14 $type = 'w';
263             }
264             elsif ($cmd =~ s/\|$//) {
265 3         165 $type = 'r';
266             }
267             else {
268             # Not a pipe, just do a regular open.
269 0         0 return open $fh, $cmd;
270             }
271 5         35 return sync_fhpopen_noshell $fh, $type, $cmd;
272             }
273              
274              
275             1
276              
277             __END__