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__ |