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