File Coverage

blib/lib/No/Worries/Proc.pm
Criterion Covered Total %
statement 278 327 85.0
branch 115 200 57.5
condition 28 54 51.8
subroutine 33 35 94.2
pod 6 6 100.0
total 460 622 73.9


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