line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Unix::PID; |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
# this works with these uncommented, but we leave them commented out to avoid a little time and memory |
4
|
|
|
|
|
|
|
# use strict; |
5
|
|
|
|
|
|
|
# use warnings; |
6
|
|
|
|
|
|
|
$Unix::PID::VERSION = '0.23'; |
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
sub import { |
9
|
1
|
|
|
1
|
|
9
|
shift; |
10
|
1
|
50
|
33
|
|
|
9
|
my $file = defined $_[0] && $_[0] !~ m{ \A \d+ \. \d+ \. \d+ \z }xms ? shift : ''; |
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
#### handle use Mod '1.2.3'; here? make it play nice with version.pm ?? ## |
13
|
|
|
|
|
|
|
# my $want = shift; |
14
|
|
|
|
|
|
|
# |
15
|
|
|
|
|
|
|
# if(defined $want && $want !~ m{^\d+\.\d+\.\d+$}) { |
16
|
|
|
|
|
|
|
# require Carp; |
17
|
|
|
|
|
|
|
# Carp::croak "Unix::PID is version $VERSION, you requested $want" |
18
|
|
|
|
|
|
|
# if Unix::PID->VERSION < version->new($want)->numify(); |
19
|
|
|
|
|
|
|
# } |
20
|
|
|
|
|
|
|
#### ???? ## |
21
|
|
|
|
|
|
|
|
22
|
1
|
50
|
33
|
|
|
20
|
if ( defined $file && $file ne '' ) { |
23
|
0
|
|
|
|
|
|
require Carp; |
24
|
0
|
0
|
|
|
|
|
Unix::PID->new()->pid_file($file) |
25
|
|
|
|
|
|
|
|| Carp::croak("The PID in $file is still running."); |
26
|
|
|
|
|
|
|
} |
27
|
|
|
|
|
|
|
} |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
sub new { |
30
|
0
|
|
|
0
|
1
|
|
my ( $class, $args_ref ) = @_; |
31
|
0
|
0
|
|
|
|
|
$args_ref = {} if ref($args_ref) ne 'HASH'; |
32
|
0
|
0
|
0
|
|
|
|
my $self = bless( |
|
|
0
|
0
|
|
|
|
|
33
|
|
|
|
|
|
|
{ |
34
|
|
|
|
|
|
|
'ps_path' => '', |
35
|
|
|
|
|
|
|
'errstr' => '', |
36
|
|
|
|
|
|
|
'minimum_pid' => !exists $args_ref->{'minimum_pid'} || $args_ref->{'minimum_pid'} !~ m{\A\d+\z}ms ? 11 : $args_ref->{'minimum_pid'}, |
37
|
|
|
|
|
|
|
'open3' => exists $args_ref->{'use_open3'} && !$args_ref->{'use_open3'} ? 0 : 1, |
38
|
|
|
|
|
|
|
}, |
39
|
|
|
|
|
|
|
$class |
40
|
|
|
|
|
|
|
); |
41
|
0
|
0
|
|
|
|
|
require IPC::Open3 if $self->{'open3'}; |
42
|
|
|
|
|
|
|
|
43
|
0
|
0
|
|
|
|
|
$self->set_ps_path( $args_ref->{'ps_path'} ) if exists $args_ref->{'ps_path'}; |
44
|
|
|
|
|
|
|
|
45
|
0
|
|
|
|
|
|
return $self; |
46
|
|
|
|
|
|
|
} |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
sub get_ps_path { |
49
|
0
|
|
|
0
|
1
|
|
return $_[0]->{'ps_path'}; |
50
|
|
|
|
|
|
|
} |
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
sub get_errstr { |
53
|
0
|
|
|
0
|
1
|
|
return $_[0]->{'errstr'}; |
54
|
|
|
|
|
|
|
} |
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
sub non_blocking_wait { |
57
|
0
|
|
|
0
|
1
|
|
my ($self) = @_; |
58
|
0
|
|
|
|
|
|
while ( ( my $zombie = waitpid( -1, 1 ) ) > 0 ) { } |
59
|
|
|
|
|
|
|
} |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
sub set_ps_path { |
62
|
0
|
|
|
0
|
1
|
|
my ( $self, $path ) = @_; |
63
|
0
|
0
|
|
|
|
|
$path = substr( $path, 0, ( length($path) - 1 ) ) |
64
|
|
|
|
|
|
|
if substr( $path, -1, 1 ) eq '/'; |
65
|
0
|
0
|
0
|
|
|
|
if ( ( -d $path && -x "$path/ps" ) || $path eq '' ) { |
|
|
|
0
|
|
|
|
|
66
|
0
|
|
|
|
|
|
$self->{'ps_path'} = $path; |
67
|
0
|
|
|
|
|
|
return 1; |
68
|
|
|
|
|
|
|
} |
69
|
|
|
|
|
|
|
else { |
70
|
0
|
|
|
|
|
|
return; |
71
|
|
|
|
|
|
|
} |
72
|
|
|
|
|
|
|
} |
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
sub get_pidof { |
75
|
0
|
|
|
0
|
1
|
|
my ( $self, $name, $exact ) = @_; |
76
|
0
|
|
|
|
|
|
my %map; |
77
|
0
|
|
|
|
|
|
for ( $self->_raw_ps( 'axo', 'pid,command' ) ) { |
78
|
0
|
|
|
|
|
|
$_ =~ s{ \A \s* | \s* \z }{}xmsg; |
79
|
0
|
|
|
|
|
|
my ( $pid, $cmd ) = $_ =~ m{ \A (\d+) \s+ (.*) \z }xmsg; |
80
|
0
|
0
|
0
|
|
|
|
$map{$pid} = $cmd if $pid && $pid ne $$ && $cmd; |
|
|
|
0
|
|
|
|
|
81
|
|
|
|
|
|
|
} |
82
|
0
|
|
|
|
|
|
my @pids = |
83
|
|
|
|
|
|
|
$exact |
84
|
0
|
|
|
|
|
|
? grep { $map{$_} =~ m/^\Q$name\E$/ } keys %map |
85
|
0
|
0
|
|
|
|
|
: grep { $map{$_} =~ m/\Q$name\E/ } keys %map; |
86
|
|
|
|
|
|
|
|
87
|
0
|
0
|
|
|
|
|
return wantarray ? @pids : $pids[0]; |
88
|
|
|
|
|
|
|
} |
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
sub kill { |
91
|
0
|
|
|
0
|
1
|
|
my ( $self, $pid, $give_kill_a_chance ) = @_; |
92
|
0
|
|
|
|
|
|
$give_kill_a_chance = int $give_kill_a_chance; |
93
|
0
|
|
|
|
|
|
$pid = int $pid; |
94
|
0
|
|
|
|
|
|
my $min = int $self->{'minimum_pid'}; |
95
|
0
|
0
|
|
|
|
|
if ( $pid < $min ) { |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
# prevent bad args from killing the process group (IE '0') |
98
|
|
|
|
|
|
|
# or general low level ones |
99
|
0
|
|
|
|
|
|
warn "kill() called with integer value less than $min"; |
100
|
0
|
|
|
|
|
|
return; |
101
|
|
|
|
|
|
|
} |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
# CORE::kill 0, $pid : may be false but still running, see `perldoc -f kill` |
104
|
0
|
0
|
|
|
|
|
if ( $self->is_pid_running($pid) ) { |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
# RC from CORE::kill is not a boolean of if the PID was killed or not, only that it was signaled |
107
|
|
|
|
|
|
|
# so it is not an indicator of "success" in killing $pid |
108
|
0
|
|
|
|
|
|
CORE::kill( 15, $pid ); # TERM |
109
|
0
|
|
|
|
|
|
CORE::kill( 2, $pid ); # INT |
110
|
0
|
|
|
|
|
|
CORE::kill( 1, $pid ); # HUP |
111
|
0
|
|
|
|
|
|
CORE::kill( 9, $pid ); # KILL |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
# give kill() some time to take effect? |
114
|
0
|
0
|
|
|
|
|
if ($give_kill_a_chance) { |
115
|
0
|
|
|
|
|
|
sleep($give_kill_a_chance); |
116
|
|
|
|
|
|
|
} |
117
|
0
|
0
|
|
|
|
|
return if $self->is_pid_running($pid); |
118
|
|
|
|
|
|
|
} |
119
|
0
|
|
|
|
|
|
return 1; |
120
|
|
|
|
|
|
|
} |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
sub get_pid_from_pidfile { |
123
|
0
|
|
|
0
|
1
|
|
my ( $self, $pid_file ) = @_; |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
# if this function is ever changed to use $self as a hash object, update pid_file() to not do a class method call |
126
|
0
|
0
|
|
|
|
|
return 0 if !-e $pid_file; |
127
|
|
|
|
|
|
|
|
128
|
0
|
0
|
|
|
|
|
open my $pid_fh, '<', $pid_file or return; |
129
|
0
|
|
|
|
|
|
chomp( my $pid = <$pid_fh> ); |
130
|
0
|
|
|
|
|
|
close $pid_fh; |
131
|
|
|
|
|
|
|
|
132
|
0
|
|
|
|
|
|
return int( abs($pid) ); |
133
|
|
|
|
|
|
|
} |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
sub is_pidfile_running { |
136
|
0
|
|
|
0
|
1
|
|
my ( $self, $pid_file ) = @_; |
137
|
0
|
|
0
|
|
|
|
my $pid = $self->get_pid_from_pidfile($pid_file) || return; |
138
|
0
|
0
|
|
|
|
|
return $pid if $self->is_pid_running($pid); |
139
|
0
|
|
|
|
|
|
return; |
140
|
|
|
|
|
|
|
} |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
sub pid_file { |
143
|
0
|
|
|
0
|
1
|
|
my ( $self, $pid_file, $newpid, $retry_conf ) = @_; |
144
|
0
|
0
|
|
|
|
|
$newpid = $$ if !$newpid; |
145
|
|
|
|
|
|
|
|
146
|
0
|
|
|
|
|
|
my $rc = $self->pid_file_no_unlink( $pid_file, $newpid, $retry_conf ); |
147
|
0
|
0
|
0
|
|
|
|
if ( $rc && $newpid == $$ ) { |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
# prevent forked childrens' END from killing parent's pid files |
150
|
|
|
|
|
|
|
# 'unlink_end_use_current_pid_only' is undocumented as this may change, feedback welcome! |
151
|
|
|
|
|
|
|
# 'carp_unlink_end' undocumented as it is only meant for testing (rt57462, use Test::Carp to test END behavior) |
152
|
0
|
0
|
|
|
|
|
if ( $self->{'unlink_end_use_current_pid_only'} ) { |
153
|
0
|
|
|
|
|
|
eval 'END { unlink $pid_file if $$ eq ' . $$ . '}'; |
154
|
0
|
0
|
|
|
|
|
if ( $self->{'carp_unlink_end'} ) { |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
# eval 'END { require Carp;Carp::carp("[info] $$ !unlink $pid_file (current pid check)") if $$ ne ' . $$ . '}'; |
157
|
0
|
|
|
|
|
|
eval 'END { require Carp;Carp::carp("[info] $$ unlink $pid_file (current pid check)") if $$ eq ' . $$ . '}'; |
158
|
|
|
|
|
|
|
} |
159
|
|
|
|
|
|
|
} |
160
|
|
|
|
|
|
|
else { |
161
|
0
|
|
|
|
|
|
eval 'END { unlink $pid_file if Unix::PID->get_pid_from_pidfile($pid_file) eq $$ }'; |
162
|
0
|
0
|
|
|
|
|
if ( $self->{'carp_unlink_end'} ) { |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
# eval 'END { require Carp;Carp::carp("[info] $$ !unlink $pid_file (pid file check)") if Unix::PID->get_pid_from_pidfile($pid_file) ne $$ }'; |
165
|
0
|
|
|
|
|
|
eval 'END { require Carp;Carp::carp("[info] $$ unlink $pid_file (pid file check)") if Unix::PID->get_pid_from_pidfile($pid_file) eq $$ }'; |
166
|
|
|
|
|
|
|
} |
167
|
|
|
|
|
|
|
} |
168
|
|
|
|
|
|
|
} |
169
|
|
|
|
|
|
|
|
170
|
0
|
0
|
|
|
|
|
return 1 if $rc == 1; |
171
|
0
|
0
|
0
|
|
|
|
return 0 if defined $rc && $rc == 0; |
172
|
0
|
|
|
|
|
|
return; |
173
|
|
|
|
|
|
|
} |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
sub pid_file_no_unlink { |
176
|
0
|
|
|
0
|
1
|
|
my ( $self, $pid_file, $newpid, $retry_conf ) = @_; |
177
|
0
|
0
|
|
|
|
|
$newpid = $$ if !$newpid; |
178
|
|
|
|
|
|
|
|
179
|
0
|
0
|
|
|
|
|
if ( ref($retry_conf) eq 'ARRAY' ) { |
180
|
0
|
|
|
|
|
|
$retry_conf->[0] = int( abs( $retry_conf->[0] ) ); |
181
|
0
|
|
|
|
|
|
for my $idx ( 1 .. scalar( @{$retry_conf} ) - 1 ) { |
|
0
|
|
|
|
|
|
|
182
|
0
|
0
|
|
|
|
|
next if ref $retry_conf->[$idx] eq 'CODE'; |
183
|
0
|
|
|
|
|
|
$retry_conf->[$idx] = int( abs( $retry_conf->[$idx] ) ); |
184
|
|
|
|
|
|
|
} |
185
|
|
|
|
|
|
|
} |
186
|
|
|
|
|
|
|
else { |
187
|
0
|
|
|
|
|
|
$retry_conf = [ 3, 1, 2 ]; |
188
|
|
|
|
|
|
|
} |
189
|
|
|
|
|
|
|
|
190
|
0
|
|
|
|
|
|
my $passes = 0; |
191
|
0
|
|
|
|
|
|
require Fcntl; |
192
|
|
|
|
|
|
|
|
193
|
0
|
|
|
|
|
|
EXISTS: |
194
|
|
|
|
|
|
|
$passes++; |
195
|
0
|
0
|
|
|
|
|
if ( -e $pid_file ) { |
196
|
|
|
|
|
|
|
|
197
|
0
|
|
|
|
|
|
my $curpid = $self->get_pid_from_pidfile($pid_file); |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
# TODO: narrow even more the race condition where $curpid stops running and a new PID is put in |
200
|
|
|
|
|
|
|
# the file between when we pull in $curpid above and check to see if it is running/unlink below |
201
|
|
|
|
|
|
|
|
202
|
0
|
0
|
0
|
|
|
|
return 1 if int $curpid == $$ && $newpid == $$; # already setup |
203
|
0
|
0
|
|
|
|
|
return if int $curpid == $$; # can't change it while $$ is alive |
204
|
0
|
0
|
|
|
|
|
return if $self->is_pid_running( int $curpid ); |
205
|
|
|
|
|
|
|
|
206
|
0
|
|
|
|
|
|
unlink $pid_file; # must be a stale PID file, so try to remove it for sysopen() |
207
|
|
|
|
|
|
|
} |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
# write only if it does not exist: |
210
|
0
|
0
|
|
|
|
|
sysopen( my $pid_fh, $pid_file, Fcntl::O_WRONLY() | Fcntl::O_EXCL() | Fcntl::O_CREAT() ) || do { |
211
|
0
|
0
|
|
|
|
|
return 0 if $passes >= $retry_conf->[0]; |
212
|
0
|
0
|
|
|
|
|
if ( ref( $retry_conf->[$passes] ) eq 'CODE' ) { |
213
|
0
|
|
|
|
|
|
$retry_conf->[$passes]->( $self, $pid_file, $passes ); |
214
|
|
|
|
|
|
|
} |
215
|
|
|
|
|
|
|
else { |
216
|
0
|
0
|
|
|
|
|
sleep( $retry_conf->[$passes] ) if $retry_conf->[$passes]; |
217
|
|
|
|
|
|
|
} |
218
|
0
|
|
|
|
|
|
goto EXISTS; |
219
|
|
|
|
|
|
|
}; |
220
|
|
|
|
|
|
|
|
221
|
0
|
|
|
|
|
|
print {$pid_fh} int( abs($newpid) ); |
|
0
|
|
|
|
|
|
|
222
|
0
|
|
|
|
|
|
close $pid_fh; |
223
|
|
|
|
|
|
|
|
224
|
0
|
|
|
|
|
|
return 1; |
225
|
|
|
|
|
|
|
} |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
sub kill_pid_file { |
228
|
0
|
|
|
0
|
1
|
|
my ( $self, $pidfile ) = @_; |
229
|
0
|
|
|
|
|
|
my $rc = $self->kill_pid_file_no_unlink($pidfile); |
230
|
0
|
0
|
0
|
|
|
|
if ( $rc && -e $pidfile ) { |
231
|
0
|
0
|
|
|
|
|
unlink $pidfile or return -1; |
232
|
|
|
|
|
|
|
} |
233
|
0
|
|
|
|
|
|
return $rc; |
234
|
|
|
|
|
|
|
} |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
sub kill_pid_file_no_unlink { |
237
|
0
|
|
|
0
|
1
|
|
my ( $self, $pidfile ) = @_; |
238
|
0
|
0
|
|
|
|
|
if ( -e $pidfile ) { |
239
|
0
|
|
|
|
|
|
my $pid = $self->get_pid_from_pidfile($pidfile); |
240
|
0
|
0
|
|
|
|
|
$self->kill($pid) or return; |
241
|
0
|
|
|
|
|
|
return $pid; |
242
|
|
|
|
|
|
|
} |
243
|
0
|
|
|
|
|
|
return 1; |
244
|
|
|
|
|
|
|
} |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
sub is_running { |
247
|
0
|
|
|
0
|
1
|
|
my ( $self, $check_this, $exact ) = @_; |
248
|
0
|
0
|
|
|
|
|
return $self->is_pid_running($check_this) if $check_this =~ m{ \A \d+ \z }xms; |
249
|
0
|
|
|
|
|
|
return $self->is_command_running( $check_this, $exact ); |
250
|
|
|
|
|
|
|
} |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
sub pid_info { |
253
|
0
|
|
|
0
|
1
|
|
my ( $self, $pid ) = @_; |
254
|
0
|
|
|
|
|
|
my @outp = $self->_pid_info_raw($pid); |
255
|
0
|
0
|
|
|
|
|
return wantarray ? split( /\s+/, $outp[1], 11 ) : [ split( /\s+/, $outp[1], 11 ) ]; |
256
|
|
|
|
|
|
|
} |
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
sub pid_info_hash { |
259
|
0
|
|
|
0
|
1
|
|
my ( $self, $pid ) = @_; |
260
|
0
|
|
|
|
|
|
my @outp = $self->_pid_info_raw($pid); |
261
|
0
|
|
|
|
|
|
my %info; |
262
|
0
|
|
|
|
|
|
@info{ split( /\s+/, $outp[0], 11 ) } = split( /\s+/, $outp[1], 11 ); |
263
|
0
|
0
|
|
|
|
|
return wantarray ? %info : \%info; |
264
|
|
|
|
|
|
|
} |
265
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
sub _pid_info_raw { |
267
|
0
|
|
|
0
|
|
|
my ( $self, $pid ) = @_; |
268
|
0
|
|
|
|
|
|
my @info = $self->_raw_ps( 'u', '-p', $pid ); |
269
|
0
|
|
|
|
|
|
chomp @info; |
270
|
0
|
0
|
|
|
|
|
return wantarray ? @info : \@info; |
271
|
|
|
|
|
|
|
} |
272
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
sub is_pid_running { |
274
|
0
|
|
|
0
|
1
|
|
my ( $self, $check_pid ) = @_; |
275
|
0
|
|
|
|
|
|
$check_pid = int($check_pid); |
276
|
0
|
0
|
|
|
|
|
return if !$check_pid; |
277
|
|
|
|
|
|
|
|
278
|
0
|
0
|
0
|
|
|
|
return 1 if $> == 0 && CORE::kill( 0, $check_pid ); # if we are superuser we can avoid the the system call. For details see `perldoc -f kill` |
279
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
# If the proc filesystem is available, it's a good test. If not, continue on to system call |
281
|
0
|
0
|
0
|
|
|
|
return 1 if -e "/proc/$$" && -r "/proc/$$" && -r "/proc/$check_pid"; |
|
|
|
0
|
|
|
|
|
282
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
# even if we are superuser, go ahead and call ps just in case CORE::kill 0's false RC was erroneous |
284
|
0
|
|
|
|
|
|
my $info = ( $self->_pid_info_raw($check_pid) )[1]; |
285
|
0
|
0
|
|
|
|
|
return 1 if defined $info; |
286
|
0
|
|
|
|
|
|
return; |
287
|
|
|
|
|
|
|
} |
288
|
|
|
|
|
|
|
|
289
|
|
|
|
|
|
|
sub is_command_running { |
290
|
0
|
|
|
0
|
1
|
|
my ( $self, $check_command, $exact ) = @_; |
291
|
0
|
0
|
|
|
|
|
return scalar $self->get_pidof( $check_command, $exact ) ? 1 : 0; |
292
|
|
|
|
|
|
|
} |
293
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
sub wait_for_pidsof { |
295
|
0
|
|
|
0
|
1
|
|
my ( $self, $wait_ref ) = @_; |
296
|
|
|
|
|
|
|
|
297
|
0
|
0
|
|
|
|
|
$wait_ref->{'get_pidof'} = $self->get_command($$) if !$wait_ref->{'get_pidof'}; |
298
|
0
|
0
|
0
|
|
|
|
$wait_ref->{'max_loops'} = 5 |
299
|
|
|
|
|
|
|
if !defined $wait_ref->{'max_loops'} |
300
|
|
|
|
|
|
|
|| $wait_ref->{'max_loops'} !~ m{ \A \d+ \z }xms; |
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
$wait_ref->{'hit_max_loops'} = sub { |
303
|
0
|
|
|
0
|
|
|
die 'Hit max loops in wait_for_pidsof()'; |
304
|
|
|
|
|
|
|
} |
305
|
0
|
0
|
|
|
|
|
if ref $wait_ref->{'hit_max_loops'} ne 'CODE'; |
306
|
|
|
|
|
|
|
|
307
|
0
|
|
|
|
|
|
my @got_pids; |
308
|
0
|
0
|
|
|
|
|
if ( ref $wait_ref->{'pid_list'} eq 'ARRAY' ) { |
309
|
0
|
0
|
|
|
|
|
@got_pids = grep { defined } map { $self->is_pid_running($_) ? $_ : undef } @{ $wait_ref->{'pid_list'} }; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
} |
311
|
|
|
|
|
|
|
else { |
312
|
0
|
|
|
|
|
|
@got_pids = $self->get_pidof( $wait_ref->{'get_pidof'} ); |
313
|
|
|
|
|
|
|
} |
314
|
|
|
|
|
|
|
|
315
|
0
|
0
|
0
|
|
|
|
if ( $wait_ref->{'use_hires_usleep'} || $wait_ref->{'use_hires_nanosleep'} ) { |
316
|
0
|
|
|
|
|
|
require Time::HiRes; |
317
|
|
|
|
|
|
|
} |
318
|
|
|
|
|
|
|
|
319
|
0
|
|
|
|
|
|
my $lcy = ''; |
320
|
0
|
|
|
|
|
|
my $fib = ''; |
321
|
0
|
0
|
|
|
|
|
if ( ref $wait_ref->{'sleep_for'} ) { |
322
|
0
|
0
|
|
|
|
|
if ( ref $wait_ref->{'sleep_for'} eq 'ARRAY' ) { |
323
|
0
|
|
|
|
|
|
require List::Cycle; |
324
|
0
|
|
|
|
|
|
$lcy = List::Cycle->new( { 'values' => $wait_ref->{'sleep_for'} } ); |
325
|
|
|
|
|
|
|
} |
326
|
0
|
0
|
|
|
|
|
if ( $wait_ref->{'sleep_for'} eq 'HASH' ) { |
327
|
0
|
0
|
|
|
|
|
if ( exists $wait_ref->{'sleep_for'}->{'fibonacci'} ) { |
328
|
0
|
|
|
|
|
|
require Math::Fibonacci::Phi; |
329
|
0
|
|
|
|
|
|
$fib = 1; |
330
|
|
|
|
|
|
|
} |
331
|
|
|
|
|
|
|
} |
332
|
|
|
|
|
|
|
} |
333
|
0
|
0
|
|
|
|
|
$wait_ref->{'sleep_for'} = 60 if !defined $wait_ref->{'sleep_for'}; |
334
|
|
|
|
|
|
|
|
335
|
0
|
|
|
|
|
|
my $loop_cnt = 0; |
336
|
|
|
|
|
|
|
|
337
|
0
|
|
|
|
|
|
while ( scalar @got_pids ) { |
338
|
0
|
|
|
|
|
|
$loop_cnt++; |
339
|
|
|
|
|
|
|
|
340
|
0
|
0
|
|
|
|
|
$wait_ref->{'pre_sleep'}->( $loop_cnt, \@got_pids ) |
341
|
|
|
|
|
|
|
if ref $wait_ref->{'pre_sleep'} eq 'CODE'; |
342
|
|
|
|
|
|
|
|
343
|
0
|
0
|
|
|
|
|
my $period = |
|
|
0
|
|
|
|
|
|
344
|
|
|
|
|
|
|
$lcy ? $lcy->next() |
345
|
|
|
|
|
|
|
: $fib ? Math::Fibonacci::term($loop_cnt) |
346
|
|
|
|
|
|
|
: $wait_ref->{'sleep_for'}; |
347
|
|
|
|
|
|
|
|
348
|
0
|
0
|
|
|
|
|
if ( $wait_ref->{'use_hires_nanosleep'} ) { |
|
|
0
|
|
|
|
|
|
349
|
0
|
|
|
|
|
|
Time::HiRes::nanosleep($period); |
350
|
|
|
|
|
|
|
} |
351
|
|
|
|
|
|
|
elsif ( $wait_ref->{'use_hires_usleep'} ) { |
352
|
0
|
|
|
|
|
|
Time::HiRes::usleep($period); |
353
|
|
|
|
|
|
|
} |
354
|
|
|
|
|
|
|
else { |
355
|
0
|
|
|
|
|
|
sleep $period; |
356
|
|
|
|
|
|
|
} |
357
|
|
|
|
|
|
|
|
358
|
0
|
0
|
|
|
|
|
if ( ref $wait_ref->{'pid_list'} eq 'ARRAY' ) { |
359
|
0
|
0
|
|
|
|
|
@got_pids = grep { defined } map { $self->is_pid_running($_) ? $_ : undef } @{ $wait_ref->{'pid_list'} }; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
} |
361
|
|
|
|
|
|
|
else { |
362
|
0
|
|
|
|
|
|
@got_pids = $self->get_pidof( $wait_ref->{'get_pidof'} ); |
363
|
|
|
|
|
|
|
} |
364
|
|
|
|
|
|
|
|
365
|
0
|
0
|
|
|
|
|
if ( $loop_cnt >= $wait_ref->{'max_loops'} ) { |
366
|
0
|
|
|
|
|
|
$wait_ref->{'hit_max_loops'}->( $loop_cnt, \@got_pids ); |
367
|
0
|
|
|
|
|
|
last; |
368
|
|
|
|
|
|
|
} |
369
|
|
|
|
|
|
|
} |
370
|
|
|
|
|
|
|
} |
371
|
|
|
|
|
|
|
|
372
|
|
|
|
|
|
|
sub _raw_ps { |
373
|
0
|
|
|
0
|
|
|
my ( $self, @ps_args ) = @_; |
374
|
0
|
|
|
|
|
|
my $path = $self->get_ps_path(); |
375
|
0
|
|
|
|
|
|
$self->{'errstr'} = ''; |
376
|
|
|
|
|
|
|
|
377
|
0
|
0
|
|
|
|
|
if ( !$path ) { |
378
|
0
|
|
|
|
|
|
for ( |
379
|
|
|
|
|
|
|
qw( /usr/local/bin /usr/local/sbin |
380
|
|
|
|
|
|
|
/usr/bin /usr/sbin |
381
|
|
|
|
|
|
|
/bin /sbin |
382
|
|
|
|
|
|
|
) |
383
|
|
|
|
|
|
|
) { |
384
|
0
|
0
|
|
|
|
|
if ( -x "$_/ps" ) { |
385
|
0
|
|
|
|
|
|
$self->set_ps_path($_); |
386
|
0
|
|
|
|
|
|
$path = $self->get_ps_path(); |
387
|
0
|
|
|
|
|
|
last; |
388
|
|
|
|
|
|
|
} |
389
|
|
|
|
|
|
|
} |
390
|
|
|
|
|
|
|
} |
391
|
|
|
|
|
|
|
|
392
|
0
|
0
|
|
|
|
|
my $ps = $path ? "$path/ps" : 'ps'; |
393
|
0
|
|
|
|
|
|
my @out; |
394
|
|
|
|
|
|
|
|
395
|
0
|
0
|
|
|
|
|
if ( $self->{'open3'} ) { |
396
|
0
|
|
|
|
|
|
local $SIG{'CHLD'} = 'IGNORE'; |
397
|
|
|
|
|
|
|
|
398
|
|
|
|
|
|
|
# IPC::Open3 says: If CHLD_ERR is false, or the same file descriptor as CHLD_OUT, then STDOUT and STDERR of the child are on the same filehandle (this means that an autovivified lexical cannot be used for the STDERR filehandle, see SYNOPSIS). |
399
|
0
|
|
|
|
|
|
my $err_fh = \*Unix::PID::PS_ERR; |
400
|
0
|
|
|
|
|
|
my $pid = IPC::Open3::open3( my $in_fh, my $out_fh, $err_fh, $ps, @ps_args ); |
401
|
|
|
|
|
|
|
|
402
|
0
|
|
|
|
|
|
@out = <$out_fh>; |
403
|
0
|
|
|
|
|
|
$self->{'errstr'} = join '', <$err_fh>; |
404
|
|
|
|
|
|
|
|
405
|
0
|
|
|
|
|
|
close $in_fh; |
406
|
0
|
|
|
|
|
|
close $out_fh; |
407
|
0
|
|
|
|
|
|
close $err_fh; |
408
|
0
|
|
|
|
|
|
waitpid( $pid, 0 ); |
409
|
|
|
|
|
|
|
} |
410
|
|
|
|
|
|
|
else { |
411
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
# command's STDERR is not captured by backticks so we silence it, if you want finer grained control do not disable open3 |
413
|
0
|
|
|
|
|
|
@out = `$ps @ps_args 2>/dev/null`; # @ps_args will interpolate in these backticks like it does in double quotes |
414
|
|
|
|
|
|
|
} |
415
|
|
|
|
|
|
|
|
416
|
0
|
0
|
|
|
|
|
return wantarray ? @out : join '', @out; |
417
|
|
|
|
|
|
|
} |
418
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
sub AUTOLOAD { |
420
|
0
|
|
|
0
|
|
|
my ( $self, $pid ) = @_; |
421
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
# return if $Unix::PID::AUTOLOAD eq 'Unix::PID::DESTROY'; # don't try to autoload this one ... |
423
|
|
|
|
|
|
|
|
424
|
0
|
|
|
|
|
|
my $subname = $Unix::PID::AUTOLOAD . '='; |
425
|
0
|
|
|
|
|
|
$subname =~ s/.*:://; |
426
|
0
|
|
|
|
|
|
$subname =~ s{\A get\_ }{}xms; |
427
|
|
|
|
|
|
|
|
428
|
0
|
|
|
|
|
|
my $data = $self->_raw_ps( '-p', $pid, '-o', $subname ); |
429
|
0
|
|
|
|
|
|
$data =~ s{ \A \s* | \s* \z }{}xmsg; |
430
|
0
|
|
|
|
|
|
return $data; |
431
|
|
|
|
|
|
|
} |
432
|
|
|
|
|
|
|
|
433
|
0
|
|
|
0
|
|
|
sub DESTROY { } # just to avoid trying to autoload this one ... |
434
|
|
|
|
|
|
|
|
435
|
|
|
|
|
|
|
1; |