File Coverage

blib/lib/No/Worries/Proc.pm
Criterion Covered Total %
statement 290 339 85.5
branch 120 208 57.6
condition 30 60 50.0
subroutine 35 37 94.5
pod 7 7 100.0
total 482 651 74.0


line stmt bran cond sub pod time code
1             #+##############################################################################
2             # #
3             # File: No/Worries/Proc.pm #
4             # #
5             # Description: process handling without worries #
6             # #
7             #-##############################################################################
8              
9             #
10             # module definition
11             #
12              
13             package No::Worries::Proc;
14 15     15   108 use strict;
  15         31  
  15         462  
15 15     15   77 use warnings;
  15         30  
  15         731  
16 15     15   321 use 5.005; # need the four-argument form of substr()
  15         57  
17             our $VERSION = "1.7";
18             our $REVISION = sprintf("%d.%02d", q$Revision: 1.26 $ =~ /(\d+)\.(\d+)/);
19              
20             #
21             # used modules
22             #
23              
24 15     15   118 use Config qw(%Config);
  15         20  
  15         608  
25 15     15   7795 use IO::Select qw();
  15         26013  
  15         447  
26 15     15   91 use No::Worries qw($_IntegerRegexp $_NumberRegexp);
  15         42  
  15         103  
27 15     15   90 use No::Worries::Die qw(dief);
  15         30  
  15         87  
28 15     15   89 use No::Worries::Dir qw(dir_change);
  15         30  
  15         87  
29 15     15   89 use No::Worries::Export qw(export_control);
  15         29  
  15         148  
30 15     15   106 use Params::Validate qw(validate validate_with :types);
  15         30  
  15         2653  
31 15     15   7052 use POSIX qw(:sys_wait_h :errno_h setsid);
  15         86500  
  15         93  
32 15     15   32147 use Time::HiRes qw();
  15         21245  
  15         33808  
33              
34             #
35             # global variables
36             #
37              
38             our(@SigName, $Transient);
39              
40             #
41             # check a command to be executed
42             #
43              
44             sub _chk_cmd (@) {
45 54     54   302 my(@cmd) = @_;
46 54         292 my($path);
47              
48 54 50       792 if ($cmd[0] =~ /\//) {
49 54 50 33     2082 dief("invalid command: %s", $cmd[0]) unless -f $cmd[0] and -x _;
50             } else {
51 0   0     0 $path = $ENV{PATH} || "/usr/bin:/usr/sbin:/bin:/sbin";
52 0         0 foreach my $dir (split(/:/, $path)) {
53 0 0 0     0 next unless length($dir) and -d $dir;
54 0 0 0     0 next unless -f "$dir/$cmd[0]" and -x _;
55 0         0 $cmd[0] = "$dir/$cmd[0]";
56 0         0 last;
57             }
58 0 0       0 dief("command not found: %s", $cmd[0]) unless $cmd[0] =~ /\//;
59             }
60 54         602 return(\@cmd);
61             }
62              
63             #
64             # definition of the process structure
65             #
66              
67             my $nbre = "(\\d+\\.)?\\d+"; # fractional number pattern
68             my $ksre = "([A-Z]+\\/${nbre}\\s+)*[A-Z]+\\/${nbre}"; # kill spec. pattern
69              
70             my %proc_structure = (
71             # public
72             command => { optional => 0, type => ARRAYREF },
73             pid => { optional => 0, type => SCALAR, regex => $_IntegerRegexp },
74             start => { optional => 0, type => SCALAR, regex => $_NumberRegexp },
75             stop => { optional => 1, type => SCALAR, regex => $_NumberRegexp },
76             status => { optional => 1, type => SCALAR, regex => qr/^-?\d+$/ },
77             timeout => { optional => 1, type => SCALAR, regex => $_NumberRegexp },
78             # private
79             kill => { optional => 1, type => SCALAR, regex => qr/^${ksre}$/ },
80             maxtime => { optional => 1, type => SCALAR, regex => $_NumberRegexp },
81             fhin => { optional => 1, type => GLOBREF },
82             fhout => { optional => 1, type => GLOBREF },
83             fherr => { optional => 1, type => GLOBREF },
84             bufin => { optional => 1, type => SCALAR },
85             cbout => { optional => 1, type => CODEREF },
86             cberr => { optional => 1, type => CODEREF },
87             );
88              
89             sub _chk_proc ($) {
90 53     53   192 my($proc) = @_;
91              
92             validate_with(
93             params => $proc,
94             spec => \%proc_structure,
95 0     0   0 on_fail => sub { dief("invalid process structure: %s", $_[0]) },
96 53         7770 );
97 53         8289 return(); # so that validate_with() is called in void context
98             }
99              
100             #
101             # close a file handle used for IPC
102             #
103              
104             sub _close ($$$$) {
105 65     65   352 my($proc, $fh, $what, $ios) = @_;
106              
107 65 100       455 $ios->remove($fh) if $ios;
108 65 50       5476 close($fh) or dief("cannot close(): %s", $!);
109 65         805 delete($proc->{"fh$what"});
110 65         1014 delete($proc->{"cb$what"});
111             }
112              
113             #
114             # try to read from a dead process in case we called _is_alive() on it
115             # before all its output pipes got emptied...
116             #
117              
118             sub _read_zombie ($$$) {
119 44     44   1113 my($proc, $iosr, $iosw) = @_;
120 44         125 my($fh, $buf, $done);
121              
122 44         302 foreach my $what (qw(in)) {
123 44 50 33     891 next unless $proc->{"fh$what"} and $proc->{"cb$what"};
124 0         0 $fh = $proc->{"fh$what"};
125             # no write, simply close
126 0         0 _close($proc, $fh, $what, $iosw);
127             }
128 44         147 foreach my $what (qw(out err)) {
129 88 100 66     711 next unless $proc->{"fh$what"} and $proc->{"cb$what"};
130 20         180 $fh = $proc->{"fh$what"};
131             # read until EOF then close
132 20         43 $done = 1;
133 20         74 while ($done) {
134 27 50 66     287 last if $iosr and not grep($fh eq $_, $iosr->can_read(1));
135 27         1717 $buf = "";
136 27         554 $done = sysread($fh, $buf, 8192);
137 27 50       136 dief("cannot sysread(): %s", $!) unless defined($done);
138 27         112 $proc->{"cb$what"}($proc, $buf);
139             }
140 20         143 _close($proc, $fh, $what, $iosr);
141             }
142             }
143              
144             #
145             # check if a process is alive, record its status if not
146             #
147              
148             sub _is_alive ($$$) {
149 2139     2139   6074 my($proc, $iosr, $iosw) = @_;
150              
151             # check if it recently died
152 2139 100       36270 if (waitpid($proc->{pid}, WNOHANG) == $proc->{pid}) {
153 44         1056 $proc->{status} = $?;
154 44         380 $proc->{stop} = Time::HiRes::time();
155 44         113 delete($proc->{maxtime});
156 44         175 delete($proc->{kill});
157 44         238 _read_zombie($proc, $iosr, $iosw);
158 44         236 return(0); # no
159             }
160             # check if we can kill it
161 2095 50 33     34097 if (kill(0, $proc->{pid}) or $! == EPERM) {
162 2095         13197 return(1); # yes
163             }
164             # ooops
165 0         0 return(); # don't know
166             }
167              
168             #
169             # prepare I/O before creating a process
170             #
171              
172             sub _prepare_stdin ($$) {
173 54     54   255 my($proc, $stdin) = @_;
174 54         124 my($ref, $rdrin, $wrtin);
175              
176 54 100       281 return() unless defined($stdin);
177 11         44 $ref = ref($stdin);
178 11 100       79 if ($ref eq "") {
    50          
179 6 50       24 if ($stdin eq "") {
180 0         0 dief("unexpected stdin: empty string");
181             } else {
182             ## no critic 'InputOutput::RequireBriefOpen'
183 6 50       270 open($rdrin, "<", $stdin)
184             or dief("cannot open(<, %s): %s", $stdin, $!);
185             }
186             } elsif ($ref eq "SCALAR") {
187 5 50       240 pipe($rdrin, $wrtin)
188             or dief("cannot pipe(): %s", $!);
189 5         70 $proc->{fhin} = $wrtin;
190 5         35 $proc->{bufin} = ${ $stdin };
  5         65  
191             } else {
192 0         0 dief("unexpected stdin: ref(%s)", $ref);
193             }
194 11         72 return($rdrin, $wrtin);
195             }
196              
197             sub _prepare_stdout ($$) {
198 54     54   220 my($proc, $stdout) = @_;
199 54         160 my($ref, $rdrout, $wrtout);
200              
201 54 100       183 return() unless defined($stdout);
202 52         149 $ref = ref($stdout);
203 52 100 33     552 if ($ref eq "") {
    50          
204 8 50       184 if ($stdout eq "") {
205 0         0 dief("unexpected stdout: empty string");
206             } else {
207             ## no critic 'InputOutput::RequireBriefOpen'
208 8 50       1256 open($wrtout, ">", $stdout)
209             or dief("cannot open(>, %s): %s", $stdout, $!);
210             }
211             } elsif ($ref eq "CODE" or $ref eq "SCALAR") {
212 44 50       2110 pipe($rdrout, $wrtout)
213             or dief("cannot pipe(): %s", $!);
214 44         246 $proc->{fhout} = $rdrout;
215 44 50       209 if ($ref eq "CODE") {
216 0         0 $proc->{cbout} = $stdout;
217             } else {
218 44         102 ${ $stdout } = "";
  44         121  
219             $proc->{cbout} = sub {
220 66     66   297 my($_proc, $_buf) = @_;
221 66         144 ${ $stdout } .= $_buf;
  66         352  
222 44         406 };
223             }
224             } else {
225 0         0 dief("unexpected stdout: ref(%s)", $ref);
226             }
227 52         233 return($rdrout, $wrtout);
228             }
229              
230             sub _prepare_stderr ($$) {
231 54     54   180 my($proc, $stderr) = @_;
232 54         129 my($ref, $rdrerr, $wrterr, $merge);
233              
234 54 100       309 return() unless defined($stderr);
235 39         102 $ref = ref($stderr);
236 39 100 33     361 if ($ref eq "") {
    50          
237 9 50       63 if ($stderr eq "") {
238             # special case: stderr will be merged with stdout
239 9         27 $merge = 1;
240             } else {
241             ## no critic 'InputOutput::RequireBriefOpen'
242 0 0       0 open($wrterr, ">", $stderr)
243             or dief("cannot open(>, %s): %s", $stderr, $!);
244             }
245             } elsif ($ref eq "CODE" or $ref eq "SCALAR") {
246 30 50       971 pipe($rdrerr, $wrterr)
247             or dief("cannot pipe(): %s", $!);
248 30         139 $proc->{fherr} = $rdrerr;
249 30 50       107 if ($ref eq "CODE") {
250 0         0 $proc->{cberr} = $stderr;
251             } else {
252 30         53 ${ $stderr } = "";
  30         89  
253             $proc->{cberr} = sub {
254 41     41   113 my($_proc, $_buf) = @_;
255 41         79 ${ $stderr } .= $_buf;
  41         335  
256 30         266 };
257             }
258             } else {
259 0         0 dief("unexpected stderr: ref(%s)", $ref);
260             }
261 39         243 return($rdrerr, $wrterr, $merge);
262             }
263              
264             #
265             # redirect I/O after creating a process
266             #
267              
268             sub _redirect_io ($$$$) {
269 9     9   129 my($rdrin, $wrtout, $wrterr, $merge) = @_;
270 9         117 my($fd);
271              
272             # handle stdin
273 9 100       171 if ($rdrin) {
274 2         19 $fd = fileno($rdrin);
275 2 50       35 if (fileno(*STDIN) != $fd) {
276 2 50       334 open(*STDIN, "<&=$fd")
277             or dief("cannot redirect stdin: %s", $!);
278             }
279             }
280             # handle stdout
281 9 100       197 if ($wrtout) {
282 8         90 $fd = fileno($wrtout);
283 8 50       155 if (fileno(*STDOUT) != $fd) {
284 8 50       1023 open(*STDOUT, ">&=$fd")
285             or dief("cannot redirect stdout: %s", $!);
286             }
287             }
288             # handle stderr
289 9 100 100     302 if ($wrterr or $merge) {
290 6 100       154 $fd = $merge ? fileno(*STDOUT) : fileno($wrterr);
291 6 50       32 if (fileno(*STDERR) != $fd) {
292 6 50       319 open(*STDERR, ">&=$fd")
293             or dief("cannot redirect stderr: %s", $!);
294             }
295             }
296             }
297              
298             #
299             # fork a new process, setup its environment and exec() the command
300             #
301              
302             my %proc_create_options = (
303             command => { optional => 0, type => ARRAYREF },
304             cwd => { optional => 1, type => SCALAR },
305             timeout => { optional => 1, type => SCALAR, regex => $_NumberRegexp },
306             kill => { optional => 1, type => SCALAR, regex => qr/^${ksre}$/ },
307             stdin => { optional => 1, type => SCALAR | SCALARREF },
308             stdout => { optional => 1, type => SCALAR | SCALARREF | CODEREF },
309             stderr => { optional => 1, type => SCALAR | SCALARREF | CODEREF },
310             );
311              
312             sub proc_create (@) {
313 54     54 1 414 my(%option, %proc, $merge);
314 54         0 my($rdrin, $wrtin, $rdrout, $wrtout, $rdrerr, $wrterr);
315              
316             #
317             # preparation
318             #
319              
320 54         2648 %option = validate(@_, \%proc_create_options);
321 54         580 $proc{command} = _chk_cmd(@{ $option{command} });
  54         1064  
322             # check the "current working directory" option
323 54 50       275 if (defined($option{cwd})) {
324 0 0       0 dief("invalid directory: %s", $option{cwd}) unless -d $option{cwd};
325             }
326             # prepare I/O
327 54         375 ($rdrin, $wrtin) = _prepare_stdin(\%proc, $option{stdin});
328 54         257 ($rdrout, $wrtout) = _prepare_stdout(\%proc, $option{stdout});
329 54         265 ($rdrerr, $wrterr, $merge) = _prepare_stderr(\%proc, $option{stderr});
330             # fork
331 54         80747 $proc{pid} = fork();
332 54 50       2215 dief("cannot fork(): %s", $!) unless defined($proc{pid});
333              
334             #
335             # handle the child
336             #
337              
338 54 100       589 unless ($proc{pid}) {
339             # we are about to exec() or die()
340 9         526 $Transient = 1;
341             # handle the "current working directory"
342 9 50       363 dir_change($option{cwd}) if defined($option{cwd});
343             # make sure the STD* file handles are "normal"
344 9         285 foreach my $glob (*STDIN, *STDOUT, *STDERR) {
345 27 50       294 next unless tied($glob);
346 15     15   138 no warnings qw(untie); ## no critic 'ProhibitNoWarnings'
  15         31  
  15         38336  
347 0         0 untie($glob);
348             }
349             # handle the pipe ends to close
350 9         119 foreach my $fh ($wrtin, $rdrout, $rdrerr) {
351 27 100       320 next unless $fh;
352 13 50       425 close($fh) or dief("cannot close pipe: %s", $!);
353             }
354             # redirect I/O
355 9         314 _redirect_io($rdrin, $wrtout, $wrterr, $merge);
356             # execute the command
357 9         129 exec({ $proc{command}[0] } @{ $proc{command} })
  9         0  
358 9 0       28 or dief("cannot execute %s: %s", $proc{command}[0], $!);
359 0         0 exit(-1);
360             }
361              
362             #
363             # handle the father
364             #
365              
366             # record the "start" time
367 45         2495 $proc{start} = Time::HiRes::time();
368             # record the maximum running time
369 45 100       530 if (defined($option{timeout})) {
370 6         414 $proc{maxtime} = $proc{start} + $option{timeout};
371             }
372             # record the kill specification
373 45 50       430 $proc{kill} = $option{kill} if $option{kill};
374             # handle the pipe ends to close
375 45         966 foreach my $fh ($rdrin, $wrtout, $wrterr) {
376 135 100       609 next unless $fh;
377 78 50       2366 close($fh) or dief("cannot close pipe: %s", $!);
378             }
379             # so far so good
380 45         6466 return(\%proc);
381             }
382              
383             #
384             # terminate a process
385             #
386              
387             my %proc_terminate_options = (
388             kill => { optional => 1, type => SCALAR, regex => qr/^${ksre}$/ },
389             _iosr => { optional => 1, type => UNDEF|OBJECT },
390             _iosw => { optional => 1, type => UNDEF|OBJECT },
391             );
392              
393             sub proc_terminate ($@) {
394 7     7 1 903471 my($proc, %option, $pid, $sig, $grace, $maxtime);
395              
396             # setup
397 7         246 $proc = shift(@_);
398 7 50       112 if (ref($proc) eq "") {
    50          
399 0 0       0 dief("unexpected pid: %s", $proc) unless $proc =~ /^\d+$/;
400 0         0 $proc = { pid => $proc };
401             } elsif (ref($proc) eq "HASH") {
402 7         72 _chk_proc($proc);
403             } else {
404 0         0 dief("unexpected process: %s", $proc);
405             }
406 7 100       257 %option = validate(@_, \%proc_terminate_options) if @_;
407 7   50     258 $option{kill} ||= $proc->{kill} || "TERM/1 INT/1 QUIT/1";
      33        
408 7         26 $pid = $proc->{pid};
409             # gentle kill
410 7         116 foreach my $spec (split(/\s+/, $option{kill})) {
411 7 50       534 if ($spec =~ /^([A-Z]+)\/(${nbre})$/) {
412 7         574 ($sig, $grace) = ($1, $2);
413             } else {
414 0         0 dief("unexpected kill specification: %s", $spec);
415             }
416 7 50       477 unless (kill($sig, $pid)) {
417 0 0       0 dief("cannot kill(%s, %d): %s", $sig, $pid, $!) unless $! == ESRCH;
418             }
419 7         75 $maxtime = Time::HiRes::time() + $grace;
420 7         54 while (Time::HiRes::time() < $maxtime) {
421 14 100       198 return unless _is_alive($proc, $option{_iosr}, $option{_iosw});
422 7         71376 Time::HiRes::sleep(0.01);
423             }
424 0 0       0 return unless _is_alive($proc, $option{_iosr}, $option{_iosw});
425             }
426             # hard kill
427 0         0 $sig = "KILL";
428 0 0       0 unless (kill($sig, $pid)) {
429 0 0       0 dief("cannot kill(%s, %d): %s", $sig, $pid, $!) unless $! == ESRCH;
430             }
431             }
432              
433             #
434             # setup monitoring
435             #
436              
437             sub _monitor_setup ($) {
438 44     44   248 my($procs) = @_;
439 44         165 my(%process, %map, $iosr, $iosw, $fh);
440              
441             # store the processes to monitor in a hash
442 44         121 foreach my $proc (@{ $procs }) {
  44         477  
443 46         804 _chk_proc($proc);
444 46         610 $process{$proc->{pid}} = $proc;
445             }
446             # record the file handles to monitor
447 44         2101 $iosr = IO::Select->new();
448 44         786 $iosw = IO::Select->new();
449 44         509 foreach my $proc (values(%process)) {
450 46         100 foreach my $what (qw(in out err)) {
451 138         354 $fh = $proc->{"fh$what"};
452 138 100       334 next unless $fh;
453 65 100       395 if ($what eq "in") {
454 4         132 $iosw->add($fh);
455             } else {
456 61         212 $iosr->add($fh);
457             }
458 65         5834 $map{"$fh"} = [ $proc->{pid}, $what ];
459             }
460             }
461 44 100       342 $iosr = undef unless $iosr->count();
462 44 100       527 $iosw = undef unless $iosw->count();
463 44         561 return(\%process, \%map, $iosr, $iosw);
464             }
465              
466             #
467             # monitor I/O
468             #
469              
470             sub _monitor_reading ($$$$$) {
471 1522     1522   5928 my($process, $map, $iosr, $bufsize, $timeout) = @_;
472 1522         2952 my($buf, $done, $proc, $what);
473              
474 1522         6081 foreach my $fh ($iosr->can_read($timeout)) {
475 80         86665 $timeout = 0;
476 80         349 $buf = "";
477 80         1137 $done = sysread($fh, $buf, $bufsize);
478 80 50       345 dief("cannot sysread(): %s", $!) unless defined($done);
479 80         496 $proc = $process->{$map->{"$fh"}[0]};
480 80         256 $what = $map->{"$fh"}[1];
481 80         392 $proc->{"cb$what"}($proc, $buf);
482 80 100       412 unless ($done) {
483 41         347 _close($proc, $fh, $what, $iosr);
484             }
485             }
486 1522         14894740 return($timeout);
487             }
488              
489             sub _monitor_writing ($$$$$) {
490 8     8   36 my($process, $map, $iosw, $bufsize, $timeout) = @_;
491 8         32 my($buf, $done, $proc, $what);
492              
493 8         532 foreach my $fh ($iosw->can_write($timeout)) {
494 8         616 $timeout = 0;
495 8         44 $proc = $process->{$map->{"$fh"}[0]};
496 8         20 $what = $map->{"$fh"}[1];
497 8         24 $buf = $proc->{"buf$what"};
498 8 100       48 if (length($buf)) {
499 4         80 $done = syswrite($fh, $buf, length($buf));
500 4 50       40 dief("cannot syswrite(): %s", $!) unless defined($done);
501 4         28 substr($proc->{"buf$what"}, 0, $done, "");
502             } else {
503 4         96 _close($proc, $fh, $what, $iosw);
504             }
505             }
506 8         84 return($timeout);
507             }
508              
509             #
510             # monitor termination (death and timeout)
511             #
512              
513             sub _monitor_termination ($$$$) {
514 2089     2089   9019 my($process, $iosr, $iosw, $timeout) = @_;
515 2089         4563 my($now);
516              
517             # check if some processes finished
518 2089         3582 foreach my $proc (grep(!defined($_->{status}), values(%{ $process }))) {
  2089         17560  
519 2125 100       7680 next if _is_alive($proc, $iosr, $iosw);
520 37         115 $timeout = 0;
521             }
522             # check if some processes timed out
523 2089         9014 $now = Time::HiRes::time();
524 2089         3658 foreach my $proc (grep($_->{maxtime}, values(%{ $process }))) {
  2089         10567  
525 258 100       1254 next unless $now > $proc->{maxtime};
526 6         162 $timeout = 0;
527 6         72 delete($proc->{maxtime});
528 6         24 $proc->{timeout} = $now;
529 6         90 proc_terminate($proc, _iosr => $iosr, _iosw => $iosw);
530             }
531 2089         5464 return($timeout);
532             }
533              
534             #
535             # monitor one or more processes
536             #
537              
538             my %proc_monitor_options = (
539             timeout => { optional => 1, type => SCALAR, regex => $_NumberRegexp },
540             bufsize => { optional => 1, type => SCALAR, regex => $_IntegerRegexp },
541             deaths => { optional => 1, type => SCALAR, regex => $_IntegerRegexp },
542             );
543              
544             sub proc_monitor ($@) {
545 44     44 1 5839 my($procs, %option, $process, $map, $iosr, $iosw);
546 44         0 my($maxtime, $timeout, $zombies);
547              
548             #
549             # preparation
550             #
551              
552 44         164 $procs = shift(@_);
553 44 100       1015 if (ref($procs) eq "HASH") {
    50          
554 42         259 $procs = [ $procs ];
555             } elsif (ref($procs) ne "ARRAY") {
556 0         0 dief("unexpected processes: %s", $procs);
557             }
558 44 100       331 %option = validate(@_, \%proc_monitor_options) if @_;
559 44   50     1358 $option{bufsize} ||= 8192;
560 44         501 ($process, $map, $iosr, $iosw) = _monitor_setup($procs);
561             # count the number of processes which are already dead
562 44         119 $zombies = grep(defined($_->{status}), values(%{ $process }));
  44         1189  
563              
564             #
565             # work
566             #
567              
568             $maxtime = Time::HiRes::time() + $option{timeout}
569 44 100       319 if defined($option{timeout});
570 44   66     328 while ($iosr or $iosw
      100        
571 610         7620 or grep(!defined($_->{status}), values(%{ $process }))) {
572 2089         6201 $timeout = 0.01;
573             # read what can be read
574             $timeout = _monitor_reading($process, $map, $iosr, $option{bufsize},
575 2089 100       9704 $timeout) if $iosr;
576             # write what can be written
577             $timeout = _monitor_writing($process, $map, $iosw, $option{bufsize},
578 2089 100       10224 $timeout) if $iosw;
579             # check if some processes finished or timed out
580 2089         9241 $timeout = _monitor_termination($process, $iosr, $iosw, $timeout);
581             # or if we timed out
582 2089 50 33     7388 last if $maxtime and Time::HiRes::time() > $maxtime;
583             # or if enough processes died
584             last if $option{deaths}
585 36         163 and grep(defined($_->{status}), values(%{ $process }))
586 2089 100 100     7875 >= $zombies + $option{deaths};
587             # sleep a bit if needed (= if we have not worked before in the loop)
588 2088 100       20729232 Time::HiRes::sleep($timeout) if $timeout;
589             # update the IO::Select objects
590 2088 100 100     39287 $iosr = undef unless $iosr and $iosr->count();
591 2088 100 100     36402 $iosw = undef unless $iosw and $iosw->count();
592             }
593             }
594              
595             #
596             # run the given command
597             #
598              
599             sub proc_run (@) {
600 49     49 1 64682 my(@args) = @_;
601 49         128 my($proc);
602              
603             # create the process
604 49         219 $proc = proc_create(@args);
605             # monitor it until it ends
606 42         896 proc_monitor($proc);
607             # return what is expected
608 42 100       257 return(%{ $proc }) if wantarray();
  6         174  
609 36         1230 return($proc->{status});
610             }
611              
612             #
613             # execute the given command, check its status and return its output
614             #
615              
616             sub proc_output (@) {
617 4     4 1 10232 my(@command) = @_;
618 4         12 my($output, $status);
619              
620 4         24 $output = "";
621 4         16 $status = proc_run(command => \@command, stdout => \$output);
622 3 50       75 dief("%s failed: %d", $command[0], $status) if $status;
623 3         48 return($output);
624             }
625              
626             #
627             # detach ourself and go in the background
628             #
629              
630             my %proc_detach_options = (
631             callback => { optional => 1, type => CODEREF },
632             );
633              
634             sub proc_detach (@) {
635 0     0 1 0 my(%option, $pid, $sid);
636              
637 0 0       0 %option = validate(@_, \%proc_detach_options) if @_;
638             # change directory to a known place
639 0         0 dir_change("/");
640             # fork and let dad die
641 0         0 $pid = fork();
642 0 0       0 dief("cannot fork(): %s", $!) unless defined($pid);
643 0 0       0 if ($pid) {
644             # we are about to exit()
645 0         0 $Transient = 1;
646 0 0       0 $option{callback}->($pid) if $option{callback};
647 0         0 exit(0);
648             }
649             # create a new session
650 0         0 $sid = setsid();
651 0 0       0 dief("cannot setsid(): %s", $!) if $sid == -1;
652             # detach std* from anything but plain files (i.e. allow: cmd --detach > log)
653 0 0       0 unless (-f STDIN) {
654 0 0       0 open(STDIN, "<", "/dev/null")
655             or dief("cannot re-open stdin: %s", $!);
656             }
657 0 0       0 unless (-f STDOUT) {
658 0 0       0 open(STDOUT, ">", "/dev/null")
659             or dief("cannot re-open stdout: %s", $!);
660             }
661 0 0       0 unless (-f STDERR) {
662 0 0       0 open(STDERR, ">", "/dev/null")
663             or dief("cannot re-open stderr: %s", $!);
664             }
665             }
666              
667             #
668             # return a string representation of the process status
669             #
670              
671             sub proc_status ($) {
672 6     6 1 4797 my($status) = @_;
673 6         27 my($signum, @list);
674              
675 6 100       54 return("ok") unless $status;
676 3         24 $signum = $status & 127;
677 3         36 push(@list, sprintf("code=%d", $status >> 8));
678 3 50 0     15 push(@list, sprintf("signal=%s", $SigName[$signum] || $signum))
679             if $signum;
680 3 50       24 push(@list, "(core dumped)")
681             if $status & 128;
682 3         45 return(join(" ", @list));
683             }
684              
685             #
686             # module initialization
687             #
688              
689             @SigName[split(/\s+/, $Config{sig_num})] = split(/\s+/, $Config{sig_name});
690              
691             #
692             # export control
693             #
694              
695             sub import : method {
696 26     26   144 my($pkg, %exported);
697              
698 26         66 $pkg = shift(@_);
699 26         262 grep($exported{$_}++,
700             map("proc_$_", qw(create detach monitor output run status terminate)));
701 26         159 export_control(scalar(caller()), $pkg, \%exported, @_);
702             }
703              
704             1;
705              
706             __DATA__