line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Proc::Fork::Control; |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
# |
4
|
|
|
|
|
|
|
# Copyright (C) 2014 Colin Faber |
5
|
|
|
|
|
|
|
# |
6
|
|
|
|
|
|
|
# This program is free software: you can redistribute it and/or modify |
7
|
|
|
|
|
|
|
# it under the terms of the GNU General Public License as published by |
8
|
|
|
|
|
|
|
# the Free Software Foundation version 2 of the License. |
9
|
|
|
|
|
|
|
# |
10
|
|
|
|
|
|
|
# This program is distributed in the hope that it will be useful, |
11
|
|
|
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
12
|
|
|
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13
|
|
|
|
|
|
|
# GNU General Public License for more details. |
14
|
|
|
|
|
|
|
# |
15
|
|
|
|
|
|
|
# You should have received a copy of the GNU General Public License |
16
|
|
|
|
|
|
|
# along with this program. If not, see . |
17
|
|
|
|
|
|
|
# |
18
|
|
|
|
|
|
|
# |
19
|
|
|
|
|
|
|
# Original author: Colin Faber |
20
|
|
|
|
|
|
|
# Original creation date: 10/02/2014 |
21
|
|
|
|
|
|
|
# Version: $Id: Control.pm,v 1.6 2015/12/14 16:38:54 cfaber Exp $ |
22
|
|
|
|
|
|
|
# |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
# Version number - cvs automagically updated. |
25
|
|
|
|
|
|
|
our $VERSION = $1 if('$Revision: 1.6 $' =~ /: ([\d\.]+) \$/); |
26
|
|
|
|
|
|
|
|
27
|
1
|
|
|
1
|
|
5802
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
27
|
|
28
|
1
|
|
|
1
|
|
846
|
use POSIX ('WNOHANG','setsid'); |
|
1
|
|
|
|
|
7748
|
|
|
1
|
|
|
|
|
13
|
|
29
|
1
|
|
|
1
|
|
2174
|
use Time::HiRes 'usleep'; |
|
1
|
|
|
|
|
1481
|
|
|
1
|
|
|
|
|
5
|
|
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
require Exporter; |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
# Exported routines |
34
|
|
|
|
|
|
|
our @ISA = ('Exporter'); |
35
|
|
|
|
|
|
|
our @EXPORT = qw( |
36
|
|
|
|
|
|
|
cfork |
37
|
|
|
|
|
|
|
cfork_wait |
38
|
|
|
|
|
|
|
cfork_wait_pid |
39
|
|
|
|
|
|
|
cfork_init |
40
|
|
|
|
|
|
|
cfork_exit |
41
|
|
|
|
|
|
|
cfork_exit_code |
42
|
|
|
|
|
|
|
cfork_maxchildren |
43
|
|
|
|
|
|
|
cfork_errstr |
44
|
|
|
|
|
|
|
cfork_is_child |
45
|
|
|
|
|
|
|
cfork_has_children |
46
|
|
|
|
|
|
|
cfork_nonblocking |
47
|
|
|
|
|
|
|
cfork_daemonize |
48
|
|
|
|
|
|
|
cfork_sleep |
49
|
|
|
|
|
|
|
cfork_usleep |
50
|
|
|
|
|
|
|
cfork_ssleep |
51
|
|
|
|
|
|
|
cfork_active_children |
52
|
|
|
|
|
|
|
cfork_kill_children |
53
|
|
|
|
|
|
|
cfork_list_children |
54
|
|
|
|
|
|
|
cfork_child_dob |
55
|
|
|
|
|
|
|
); |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
# Defaults |
58
|
|
|
|
|
|
|
&cfork_init; |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
require 5.008; |
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
$Proc::Fork::Control::VERSION = '$Revision: 1.6 $'; |
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
=head1 NAME |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
Proc::Fork::Control |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
=head1 DESCRIPTION |
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
Proc::Fork::Control is a simple to use library which functions much the same way |
71
|
|
|
|
|
|
|
as Proc::Fork. That said, Proc::Fork is not used, as fork() is accessed directly. |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
Proc::Fork::Control allows you to manage forks, control number of children |
74
|
|
|
|
|
|
|
allowed, monitor children, control blocking and nonblocking states, etc. |
75
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
=head1 SYNOPSIS |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
#!/usr/bin/perl |
79
|
|
|
|
|
|
|
use Proc::Fork::Control; |
80
|
|
|
|
|
|
|
use Fcntl ':flock'; |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
# Initialize the system allowing 25 forks per cfork() level |
83
|
|
|
|
|
|
|
cfork_init(25); |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
for(my $i = 0; $i < 50; $i++){ |
86
|
|
|
|
|
|
|
# Fork this if possible, if all avaliable fork slots are full |
87
|
|
|
|
|
|
|
# block until one becomes avaliable. |
88
|
|
|
|
|
|
|
cfork(sub { |
89
|
|
|
|
|
|
|
# Initialize for children |
90
|
|
|
|
|
|
|
cfork_init(2); |
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
for('A' .. 'Z'){ |
93
|
|
|
|
|
|
|
cfork(sub { |
94
|
|
|
|
|
|
|
# Lock STDOUT for writing. |
95
|
|
|
|
|
|
|
flock(STDOUT, &LOCK_EX); |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
# Print out a string. |
98
|
|
|
|
|
|
|
print STDOUT "Fork: $i: $_\n"; |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
# Unlock STDOUT. |
101
|
|
|
|
|
|
|
flock(STDOUT, &LOCK_UN); |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
cfork_exit(); |
104
|
|
|
|
|
|
|
}); |
105
|
|
|
|
|
|
|
} |
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
# Wait for sub children to exit |
108
|
|
|
|
|
|
|
cfork_wait() |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
}); |
111
|
|
|
|
|
|
|
} |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
# Wait until all forks have finished. |
114
|
|
|
|
|
|
|
cfork_wait(); |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
=head1 METHODS |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
Note - because of the nature of forking within perl. I've against objectifying this library. Rather it uses direct function calls which are exported to the global namespace Below is a list of these calls and how to access them. |
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
=head2 cfork(code, code, code) |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
Provide managed forking functions. |
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
Returns nothing on error and sets the cfork_errstr error handler. |
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
if cfork() is called with in an cfork()ed process the calling cfork() process will block until all children with in it die off. |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
=cut |
129
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
sub cfork { |
131
|
0
|
|
|
0
|
1
|
0
|
_errstr(); |
132
|
0
|
0
|
|
|
|
0
|
if(!$Proc::Fork::Control::HEAP->{max_children}){ |
133
|
0
|
|
|
|
|
0
|
return _errstr("cfork_init() not set"); |
134
|
|
|
|
|
|
|
} |
135
|
|
|
|
|
|
|
|
136
|
0
|
0
|
|
|
|
0
|
if(!defined $Proc::Fork::Control::HEAP->{children}){ |
137
|
0
|
|
|
|
|
0
|
$Proc::Fork::Control::HEAP->{children} = 0; |
138
|
|
|
|
|
|
|
} |
139
|
|
|
|
|
|
|
|
140
|
0
|
0
|
|
|
|
0
|
if(!defined $Proc::Fork::Control::HEAP->{max_children}){ |
141
|
0
|
|
|
|
|
0
|
$Proc::Fork::Control::HEAP->{max_children} = 0; |
142
|
|
|
|
|
|
|
} |
143
|
|
|
|
|
|
|
|
144
|
0
|
|
|
|
|
0
|
my ($i, $delay); |
145
|
0
|
|
|
|
|
0
|
while(1){ |
146
|
0
|
|
|
|
|
0
|
my $cl =_cleanup(); |
147
|
|
|
|
|
|
|
|
148
|
0
|
0
|
|
|
|
0
|
if($Proc::Fork::Control::HEAP->{children} < $Proc::Fork::Control::HEAP->{max_children}){ |
149
|
0
|
|
|
|
|
0
|
last; |
150
|
|
|
|
|
|
|
} |
151
|
|
|
|
|
|
|
|
152
|
0
|
0
|
|
|
|
0
|
if($Proc::Fork::Control::HEAP->{children}){ |
153
|
0
|
0
|
|
|
|
0
|
if($cl){ |
154
|
|
|
|
|
|
|
# There are still children alive, and we've cleaned up at least 1 |
155
|
|
|
|
|
|
|
# child on the last iteration so don't delay at all. |
156
|
0
|
|
|
|
|
0
|
$i = 0; |
157
|
0
|
|
|
|
|
0
|
$delay = 0; |
158
|
|
|
|
|
|
|
} else { |
159
|
|
|
|
|
|
|
# There are still children alive, delay based on the number of interations |
160
|
|
|
|
|
|
|
# Minimum delay time (in micro seconds) |
161
|
0
|
|
0
|
|
|
0
|
$delay ||= 5; |
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
# iterator for delay multiplication. |
164
|
0
|
|
|
|
|
0
|
$i++; |
165
|
|
|
|
|
|
|
|
166
|
0
|
|
|
|
|
0
|
$delay = $delay * $i; |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
# Maximum delay value |
169
|
0
|
0
|
|
|
|
0
|
$delay = ($delay > 5000 ? 5000 : $delay); |
170
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
# sleep for a while.. |
172
|
0
|
|
|
|
|
0
|
cfork_usleep($delay); |
173
|
|
|
|
|
|
|
} |
174
|
|
|
|
|
|
|
} |
175
|
|
|
|
|
|
|
} |
176
|
|
|
|
|
|
|
|
177
|
0
|
0
|
|
|
|
0
|
if($Proc::Fork::Control::HEAP->{is_child}){ |
178
|
0
|
|
|
|
|
0
|
$Proc::Fork::Control::HEAP->{has_children} = 1; |
179
|
|
|
|
|
|
|
} |
180
|
|
|
|
|
|
|
|
181
|
0
|
|
|
|
|
0
|
my $pid = fork; |
182
|
0
|
0
|
|
|
|
0
|
if($pid < 0){ |
|
|
0
|
|
|
|
|
|
183
|
0
|
|
|
|
|
0
|
return _errstr('fork failed: ' . $!); |
184
|
|
|
|
|
|
|
} elsif($pid){ |
185
|
|
|
|
|
|
|
# This probably should use CLOCK_MONOTONIC time here, but it's not a big deal. |
186
|
0
|
|
|
|
|
0
|
$Proc::Fork::Control::HEAP->{cidlist}->{$pid} = time(); |
187
|
0
|
|
|
|
|
0
|
$Proc::Fork::Control::HEAP->{children}++; |
188
|
|
|
|
|
|
|
} else { |
189
|
0
|
|
|
|
|
0
|
cfork_init(); |
190
|
0
|
|
|
|
|
0
|
$Proc::Fork::Control::HEAP->{is_child} = 1; |
191
|
0
|
|
|
|
|
0
|
$SIG{PIPE} = 'IGNORE'; |
192
|
0
|
|
|
|
|
0
|
for my $code (@_){ |
193
|
0
|
0
|
|
|
|
0
|
if(ref($code) eq 'CODE'){ |
194
|
0
|
|
|
|
|
0
|
&{ $code }; |
|
0
|
|
|
|
|
0
|
|
195
|
|
|
|
|
|
|
} |
196
|
|
|
|
|
|
|
} |
197
|
|
|
|
|
|
|
|
198
|
0
|
|
|
|
|
0
|
cfork_exit(2); |
199
|
|
|
|
|
|
|
} |
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
# Wait for children to finish (if nonblocking |
202
|
0
|
|
|
|
|
0
|
cfork_wait(); |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
# Return our PID for further use. |
205
|
0
|
|
|
|
|
0
|
return $pid; |
206
|
|
|
|
|
|
|
} |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
=head2 cfork_nonblocking(BOOL) |
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
Set the cfork() behavior to nonblocking mode if is true, This will result in the fork returning right away rather than waiting for any possible children to die. |
211
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
Also, cfork_nonblocking() should always be turned off after the bit of code you want to run, runs. |
213
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
=item EXAMPLE |
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
cfork_nonblocking(0); |
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
cfork(sub { |
219
|
|
|
|
|
|
|
do some work; |
220
|
|
|
|
|
|
|
}); |
221
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
cfork_nonblocking(1); |
223
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
=cut |
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
sub cfork_nonblocking { |
227
|
0
|
|
|
0
|
1
|
0
|
$Proc::Fork::Control::HEAP->{nonblocking} = $_[0]; |
228
|
|
|
|
|
|
|
} |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
=head2 cfork_is_child() |
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
Return true if called with in a forked enviroment, otherwise return false. |
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
=cut |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
sub cfork_is_child { |
237
|
0
|
|
|
0
|
1
|
0
|
return $Proc::Fork::Control::HEAP->{is_child}; |
238
|
|
|
|
|
|
|
} |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
=head2 cfork_has_children() |
241
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
Return true if children exist with in a forked enviroment. |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
=cut |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
sub cfork_has_children { |
247
|
0
|
|
|
0
|
1
|
0
|
return $Proc::Fork::Control::HEAP->{has_children}; |
248
|
|
|
|
|
|
|
} |
249
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
=head2 cfork_errstr() |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
Return the last error message. |
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
=cut |
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
sub cfork_errstr { |
257
|
0
|
|
|
0
|
1
|
0
|
my ($err) = @_; |
258
|
0
|
0
|
|
|
|
0
|
$Proc::Fork::Control::errstr = $err if $err; |
259
|
0
|
|
|
|
|
0
|
return $Proc::Fork::Control::errstr; |
260
|
|
|
|
|
|
|
} |
261
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
sub _errstr { |
263
|
0
|
|
|
0
|
|
0
|
my ($err) = @_; |
264
|
0
|
|
|
|
|
0
|
cfork_errstr($err); |
265
|
0
|
|
|
|
|
0
|
return; |
266
|
|
|
|
|
|
|
} |
267
|
|
|
|
|
|
|
|
268
|
|
|
|
|
|
|
=head2 cfork_init(children) |
269
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
Initialize the CHLD reaper with a maximum number of |
271
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
This should be called prior to any cfork() calls |
273
|
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
=cut |
275
|
|
|
|
|
|
|
|
276
|
|
|
|
|
|
|
sub cfork_init { |
277
|
1
|
|
|
1
|
1
|
5
|
my $ic = $Proc::Fork::Control::HEAP->{is_child}; |
278
|
|
|
|
|
|
|
|
279
|
1
|
|
|
|
|
2
|
$Proc::Fork::Control::HEAP = {}; |
280
|
1
|
|
|
|
|
3
|
$Proc::Fork::Control::HEAP->{children} = 0; |
281
|
1
|
|
|
|
|
2
|
$Proc::Fork::Control::HEAP->{cidlist} = {}; |
282
|
1
|
50
|
|
|
|
3
|
$Proc::Fork::Control::HEAP->{is_child} = ($ic ? 1 : 0); |
283
|
|
|
|
|
|
|
|
284
|
1
|
50
|
|
|
|
20
|
$SIG{CHLD} = \&Proc::Fork::Control::_sigchld if !$ic; |
285
|
|
|
|
|
|
|
|
286
|
1
|
50
|
|
|
|
6
|
if($_[0]){ |
287
|
0
|
|
|
|
|
|
$Proc::Fork::Control::HEAP->{max_children} = $_[0]; |
288
|
|
|
|
|
|
|
} |
289
|
|
|
|
|
|
|
} |
290
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
=head2 cfork_exit(int) |
292
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
Exit a process cleanly and set an exit code. |
294
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
Normally this can be easily handled with $? however, in some cases $? is not reliably delivered. |
296
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
Once called, drop to END {} block and terminate. |
298
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
=cut |
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
sub cfork_exit { |
302
|
0
|
|
|
0
|
1
|
|
my ($exit) = @_; |
303
|
0
|
|
|
|
|
|
$Proc::Fork::Control::HEAP->{exit} = $exit; |
304
|
0
|
|
|
|
|
|
exit($exit); |
305
|
|
|
|
|
|
|
} |
306
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
=head2 cfork_exit_code() |
308
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
Returns the last known cfork_exit() code. |
310
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
=cut |
312
|
|
|
|
|
|
|
|
313
|
|
|
|
|
|
|
sub cfork_exit_code { |
314
|
0
|
|
|
0
|
1
|
|
return $Proc::Fork::Control::HEAP->{exit}; |
315
|
|
|
|
|
|
|
} |
316
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
=head2 cfork_maxchildren(int) |
318
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
Set/Reset the maximum number of children allowed. |
320
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
=cut |
322
|
|
|
|
|
|
|
|
323
|
|
|
|
|
|
|
sub cfork_maxchildren { |
324
|
0
|
0
|
|
0
|
1
|
|
$Proc::Fork::Control::HEAP->{max_children} = $_[0] if $_[0]; |
325
|
|
|
|
|
|
|
} |
326
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
=head2 cfork_wait() |
328
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
Block until all cfork() children have died off unless cfork_nonblocking() is enabled. |
330
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
=cut |
332
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
sub cfork_wait { |
334
|
0
|
|
|
0
|
1
|
|
my ($to) = @_; |
335
|
0
|
0
|
|
|
|
|
return 1 if $Proc::Fork::Control::HEAP->{nonblocking}; |
336
|
|
|
|
|
|
|
|
337
|
0
|
0
|
|
|
|
|
$to = time + $to if $to; |
338
|
|
|
|
|
|
|
|
339
|
0
|
|
|
|
|
|
my ($i, $delay); |
340
|
0
|
|
|
|
|
|
while(1){ |
341
|
0
|
|
|
|
|
|
my $cl =_cleanup(); |
342
|
|
|
|
|
|
|
|
343
|
0
|
0
|
0
|
|
|
|
if(!$Proc::Fork::Control::HEAP->{children}){ |
|
|
0
|
|
|
|
|
|
344
|
0
|
|
|
|
|
|
last; |
345
|
|
|
|
|
|
|
} elsif($to && time >= $to){ |
346
|
0
|
|
|
|
|
|
last; |
347
|
|
|
|
|
|
|
} |
348
|
|
|
|
|
|
|
|
349
|
0
|
0
|
|
|
|
|
if($Proc::Fork::Control::HEAP->{children}){ |
350
|
0
|
0
|
|
|
|
|
if($cl){ |
351
|
|
|
|
|
|
|
# There are still children alive, and we've cleaned up at least 1 |
352
|
|
|
|
|
|
|
# child on the last iteration so don't delay at all. |
353
|
0
|
|
|
|
|
|
$i = 0; |
354
|
0
|
|
|
|
|
|
$delay = 0; |
355
|
|
|
|
|
|
|
} else { |
356
|
|
|
|
|
|
|
# There are still children alive, delay based on the number of interations |
357
|
|
|
|
|
|
|
# Minimum delay time (in micro seconds) |
358
|
0
|
|
0
|
|
|
|
$delay ||= 5; |
359
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
# iterator for delay multiplication. |
361
|
0
|
|
|
|
|
|
$i++; |
362
|
|
|
|
|
|
|
|
363
|
0
|
|
|
|
|
|
$delay = $delay * $i; |
364
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
# Maximum delay value |
366
|
0
|
0
|
|
|
|
|
$delay = ($delay > 5000 ? 5000 : $delay); |
367
|
|
|
|
|
|
|
|
368
|
|
|
|
|
|
|
# sleep for a while.. |
369
|
0
|
|
|
|
|
|
cfork_usleep($delay); |
370
|
|
|
|
|
|
|
} |
371
|
|
|
|
|
|
|
} |
372
|
|
|
|
|
|
|
} |
373
|
|
|
|
|
|
|
|
374
|
0
|
|
|
|
|
|
return 1; |
375
|
|
|
|
|
|
|
} |
376
|
|
|
|
|
|
|
|
377
|
|
|
|
|
|
|
=head2 cfork_wait_pid(PID, PID, PID, ..) |
378
|
|
|
|
|
|
|
|
379
|
|
|
|
|
|
|
cfork_wait_pid() functions much like cfork_wait() with the exception that it expects a list of PID's and blocks until those PID's have died off. Like cfork_wait(), cfork_wait_pid() will NOT block if cfork_nonblocking() mode is enabled. |
380
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
=cut |
382
|
|
|
|
|
|
|
|
383
|
|
|
|
|
|
|
sub cfork_wait_pid { |
384
|
0
|
|
|
0
|
1
|
|
my (@PID) = @_; |
385
|
|
|
|
|
|
|
|
386
|
0
|
0
|
|
|
|
|
return 1 if $Proc::Fork::Control::HEAP->{nonblocking}; |
387
|
|
|
|
|
|
|
|
388
|
0
|
|
|
|
|
|
my $block; |
389
|
0
|
|
|
|
|
|
while(1){ |
390
|
0
|
|
|
|
|
|
my @TPID; |
391
|
0
|
|
|
|
|
|
for(my $i = 0; $i < @PID; $i++){ |
392
|
0
|
0
|
|
|
|
|
if(kill(undef, $PID[$i])){ |
393
|
0
|
|
|
|
|
|
push @TPID, $PID[$i]; |
394
|
|
|
|
|
|
|
} |
395
|
|
|
|
|
|
|
} |
396
|
|
|
|
|
|
|
|
397
|
0
|
|
|
|
|
|
@PID = (@TPID); |
398
|
|
|
|
|
|
|
|
399
|
0
|
0
|
|
|
|
|
last if !@PID; |
400
|
|
|
|
|
|
|
|
401
|
0
|
|
|
|
|
|
cfork_usleep(5000); |
402
|
|
|
|
|
|
|
} |
403
|
|
|
|
|
|
|
|
404
|
0
|
|
|
|
|
|
return 1; |
405
|
|
|
|
|
|
|
} |
406
|
|
|
|
|
|
|
|
407
|
|
|
|
|
|
|
|
408
|
|
|
|
|
|
|
=head2 cfork_active_children() |
409
|
|
|
|
|
|
|
|
410
|
|
|
|
|
|
|
Return the total number of active children. |
411
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
=cut |
413
|
|
|
|
|
|
|
|
414
|
|
|
|
|
|
|
sub cfork_active_children { |
415
|
0
|
|
|
0
|
1
|
|
_cleanup(); |
416
|
0
|
0
|
|
|
|
|
return ($Proc::Fork::Control::HEAP->{children} ? $Proc::Fork::Control::HEAP->{children} : 0); |
417
|
|
|
|
|
|
|
} |
418
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
=head2 cfork_daemonize(BOOL) |
420
|
|
|
|
|
|
|
|
421
|
|
|
|
|
|
|
Daemonize the the calling script. |
422
|
|
|
|
|
|
|
|
423
|
|
|
|
|
|
|
If is true write _ALL_ output to /dev/null. |
424
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
If you have termination handling, i.e. %SIG and END {} block control, cfork_daemonize triggers exit signal 2. So... $? == 4 |
426
|
|
|
|
|
|
|
|
427
|
|
|
|
|
|
|
=cut |
428
|
|
|
|
|
|
|
|
429
|
|
|
|
|
|
|
sub cfork_daemonize { |
430
|
0
|
|
|
0
|
1
|
|
my $q = $_[0]; |
431
|
0
|
0
|
|
|
|
|
chdir('/') || die "Can't chdir to /: $!\n"; |
432
|
0
|
0
|
|
|
|
|
if(!$q){ |
433
|
0
|
|
|
|
|
|
open STDIN, '/dev/null' || die "Can't read /dev/null: $!\n"; |
434
|
0
|
|
|
|
|
|
open STDOUT, '>/dev/null' || die "Can't write to /dev/null: $!\n"; |
435
|
0
|
|
|
|
|
|
open STDERR, '>&STDOUT' || die "Can't dup stdout: $!"; |
436
|
|
|
|
|
|
|
} |
437
|
|
|
|
|
|
|
|
438
|
0
|
0
|
|
|
|
|
defined(my $pid = fork) || die "Can't fork: $!\n"; |
439
|
0
|
0
|
|
|
|
|
cfork_exit(4) if $pid; |
440
|
0
|
0
|
|
|
|
|
setsid || die "Can't start a new session: $!\n"; |
441
|
|
|
|
|
|
|
} |
442
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
=head2 cfork_sleep(int) |
444
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
Provides an alarm safe sleep() wrapper. Beacuse we sleep() with in this, ALRM will be issued with in the fork once the sleep cycle has completed. This function wraps sleep with in a while() block and tests to make sure that the seconds requested for the sleep were slept. |
446
|
|
|
|
|
|
|
|
447
|
|
|
|
|
|
|
=cut |
448
|
|
|
|
|
|
|
|
449
|
|
|
|
|
|
|
sub cfork_sleep { |
450
|
0
|
|
|
0
|
1
|
|
my $sleep = $_[0]; |
451
|
0
|
0
|
|
|
|
|
return if $sleep !~ /^\d+$/; |
452
|
|
|
|
|
|
|
|
453
|
0
|
|
|
|
|
|
my $sleeper = 0; |
454
|
0
|
|
|
|
|
|
my $slept = 0; |
455
|
|
|
|
|
|
|
|
456
|
0
|
|
|
|
|
|
while(1){ |
457
|
0
|
0
|
0
|
|
|
|
if($sleeper < 0 || $sleep <= 0){ |
|
|
0
|
|
|
|
|
|
458
|
0
|
|
|
|
|
|
last; |
459
|
|
|
|
|
|
|
} elsif(!$sleeper) { |
460
|
0
|
|
|
|
|
|
$sleeper = $sleep; |
461
|
|
|
|
|
|
|
} |
462
|
|
|
|
|
|
|
|
463
|
0
|
|
|
|
|
|
my $remain = sleep( abs($sleeper) ); |
464
|
|
|
|
|
|
|
|
465
|
0
|
0
|
0
|
|
|
|
if($remain ne $sleeper && $remain < $sleep){ |
466
|
0
|
|
|
|
|
|
$slept += $remain; |
467
|
0
|
|
|
|
|
|
$sleeper = $sleeper - $remain; |
468
|
|
|
|
|
|
|
|
469
|
0
|
|
|
|
|
|
next; |
470
|
|
|
|
|
|
|
} else { |
471
|
0
|
|
|
|
|
|
last; |
472
|
|
|
|
|
|
|
} |
473
|
|
|
|
|
|
|
} |
474
|
|
|
|
|
|
|
|
475
|
0
|
|
|
|
|
|
return $slept; |
476
|
|
|
|
|
|
|
} |
477
|
|
|
|
|
|
|
|
478
|
|
|
|
|
|
|
=head2 cfork_usleep(int) |
479
|
|
|
|
|
|
|
|
480
|
|
|
|
|
|
|
Provides an alarm safe Time::HiRes usleep() wrapper. Beacuse we sleep() with in this, ALRM will be issued with in the fork once the sleep cycle has completed. This function wraps sleep with in a while() block and tests to make sure that the seconds requested for the sleep were slept. |
481
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
This function is only avaliable if Time::HiRes is avaliable otherwise it will simply return nothing at all. |
483
|
|
|
|
|
|
|
|
484
|
|
|
|
|
|
|
=cut |
485
|
|
|
|
|
|
|
|
486
|
|
|
|
|
|
|
sub cfork_usleep { |
487
|
0
|
|
|
0
|
1
|
|
my $sleep = $_[0]; |
488
|
|
|
|
|
|
|
|
489
|
0
|
|
|
|
|
|
my $sleeper = 0; |
490
|
0
|
|
|
|
|
|
my $slept = 0; |
491
|
|
|
|
|
|
|
|
492
|
0
|
|
|
|
|
|
while(1){ |
493
|
0
|
0
|
0
|
|
|
|
if($sleeper < 0 || $sleep <= 0){ |
|
|
0
|
|
|
|
|
|
494
|
0
|
|
|
|
|
|
last; |
495
|
|
|
|
|
|
|
} elsif(!$sleeper) { |
496
|
0
|
|
|
|
|
|
$sleeper = $sleep; |
497
|
|
|
|
|
|
|
} |
498
|
|
|
|
|
|
|
|
499
|
0
|
|
|
|
|
|
my $remain = usleep( abs($sleeper) ); |
500
|
|
|
|
|
|
|
|
501
|
0
|
0
|
0
|
|
|
|
if($remain ne $sleeper && $remain < $sleep){ |
502
|
0
|
|
|
|
|
|
$slept += $remain; |
503
|
0
|
|
|
|
|
|
$sleeper = $sleeper - $remain; |
504
|
|
|
|
|
|
|
|
505
|
0
|
|
|
|
|
|
next; |
506
|
|
|
|
|
|
|
} else { |
507
|
0
|
|
|
|
|
|
last; |
508
|
|
|
|
|
|
|
} |
509
|
|
|
|
|
|
|
} |
510
|
|
|
|
|
|
|
|
511
|
0
|
|
|
|
|
|
return $slept; |
512
|
|
|
|
|
|
|
} |
513
|
|
|
|
|
|
|
|
514
|
|
|
|
|
|
|
=head2 cfork_ssleep(int) |
515
|
|
|
|
|
|
|
|
516
|
|
|
|
|
|
|
Preform an cfork_sleep() except rather than using standard sleep() (with interruption handling) use a select() call to sleep. This can be useful in environments where sleep() does not behave correctly, and a select() will block for the desired number of seconds properly. |
517
|
|
|
|
|
|
|
|
518
|
|
|
|
|
|
|
=cut |
519
|
|
|
|
|
|
|
|
520
|
|
|
|
|
|
|
sub cfork_ssleep { |
521
|
0
|
|
|
0
|
1
|
|
$Proc::Fork::Control::HEAP->{select_sleep} = 1; |
522
|
0
|
|
|
|
|
|
my $r = cfork_sleep(@_); |
523
|
0
|
|
|
|
|
|
$Proc::Fork::Control::HEAP->{select_sleep} = 0; |
524
|
0
|
|
|
|
|
|
return $r; |
525
|
|
|
|
|
|
|
} |
526
|
|
|
|
|
|
|
|
527
|
|
|
|
|
|
|
=head2 cfork_kill_children(SIGNAL) |
528
|
|
|
|
|
|
|
|
529
|
|
|
|
|
|
|
Send all children (if any) this . |
530
|
|
|
|
|
|
|
|
531
|
|
|
|
|
|
|
If the argument is omitted kill TERM will be used. |
532
|
|
|
|
|
|
|
|
533
|
|
|
|
|
|
|
=cut |
534
|
|
|
|
|
|
|
|
535
|
|
|
|
|
|
|
sub cfork_kill_children { |
536
|
0
|
|
|
0
|
1
|
|
my $sig = $_[0]; |
537
|
0
|
|
|
|
|
|
_cleanup(); |
538
|
0
|
0
|
|
|
|
|
if(!$sig){ |
539
|
0
|
|
|
|
|
|
$sig = 'TERM'; |
540
|
|
|
|
|
|
|
} |
541
|
|
|
|
|
|
|
|
542
|
0
|
0
|
|
|
|
|
if($Proc::Fork::Control::HEAP->{cidlist}){ |
543
|
0
|
|
|
|
|
|
kill($sig, keys %{ $Proc::Fork::Control::HEAP->{cidlist} }); |
|
0
|
|
|
|
|
|
|
544
|
|
|
|
|
|
|
} |
545
|
|
|
|
|
|
|
} |
546
|
|
|
|
|
|
|
|
547
|
|
|
|
|
|
|
=head2 cfork_list_children(BOOL) |
548
|
|
|
|
|
|
|
|
549
|
|
|
|
|
|
|
Return a list of PID's currently running under this fork. |
550
|
|
|
|
|
|
|
|
551
|
|
|
|
|
|
|
If BOOL is true a hash will be returned rather than a list. |
552
|
|
|
|
|
|
|
|
553
|
|
|
|
|
|
|
=cut |
554
|
|
|
|
|
|
|
|
555
|
|
|
|
|
|
|
sub cfork_list_children { |
556
|
0
|
|
|
0
|
1
|
|
my ($use_hash) = @_; |
557
|
0
|
|
|
|
|
|
_cleanup(); |
558
|
|
|
|
|
|
|
|
559
|
0
|
0
|
|
|
|
|
if(!$Proc::Fork::Control::HEAP->{cidlist}){ |
560
|
0
|
|
|
|
|
|
return; |
561
|
|
|
|
|
|
|
} |
562
|
|
|
|
|
|
|
|
563
|
0
|
0
|
|
|
|
|
if($use_hash){ |
564
|
0
|
|
|
|
|
|
return (%{ $Proc::Fork::Control::HEAP->{cidlist} }); |
|
0
|
|
|
|
|
|
|
565
|
|
|
|
|
|
|
} else { |
566
|
0
|
|
|
|
|
|
return keys %{ $Proc::Fork::Control::HEAP->{cidlist} }; |
|
0
|
|
|
|
|
|
|
567
|
|
|
|
|
|
|
} |
568
|
|
|
|
|
|
|
} |
569
|
|
|
|
|
|
|
|
570
|
|
|
|
|
|
|
=head2 cfork_child_dob(PID) |
571
|
|
|
|
|
|
|
|
572
|
|
|
|
|
|
|
Return the EPOCH Date of Birth for this childs |
573
|
|
|
|
|
|
|
|
574
|
|
|
|
|
|
|
Returns 0 if no child exists under that PID for this fork. |
575
|
|
|
|
|
|
|
|
576
|
|
|
|
|
|
|
=cut |
577
|
|
|
|
|
|
|
|
578
|
|
|
|
|
|
|
sub cfork_child_dob { |
579
|
0
|
|
|
0
|
1
|
|
my $pid = $_[0]; |
580
|
0
|
|
|
|
|
|
_cleanup(); |
581
|
0
|
0
|
|
|
|
|
if($Proc::Fork::Control::HEAP->{cidlist}->{$pid}){ |
582
|
0
|
|
|
|
|
|
return $Proc::Fork::Control::HEAP->{cidlist}->{$pid}; |
583
|
|
|
|
|
|
|
} else { |
584
|
0
|
|
|
|
|
|
return; |
585
|
|
|
|
|
|
|
} |
586
|
|
|
|
|
|
|
} |
587
|
|
|
|
|
|
|
|
588
|
|
|
|
|
|
|
# Child handler |
589
|
|
|
|
|
|
|
sub _sigchld { |
590
|
0
|
|
|
0
|
|
|
my $our; |
591
|
0
|
|
|
|
|
|
while((my $p = waitpid(-1, WNOHANG)) > 0){ |
592
|
|
|
|
|
|
|
# Mark the process is done ONLY if it's one of our processes. |
593
|
0
|
0
|
|
|
|
|
if($Proc::Fork::Control::HEAP->{cidlist}->{$p}){ |
594
|
0
|
|
|
|
|
|
$Proc::Fork::Control::HEAP->{cidlist}->{$p} = 0; |
595
|
0
|
|
|
|
|
|
$our = 1; |
596
|
|
|
|
|
|
|
} |
597
|
|
|
|
|
|
|
} |
598
|
|
|
|
|
|
|
|
599
|
|
|
|
|
|
|
# self reference only if it's one of our processes. |
600
|
0
|
0
|
|
|
|
|
if($our){ |
601
|
0
|
|
|
|
|
|
$SIG{CHLD} = \&Proc::Fork::Control::_sigchld; |
602
|
|
|
|
|
|
|
} |
603
|
|
|
|
|
|
|
} |
604
|
|
|
|
|
|
|
|
605
|
|
|
|
|
|
|
|
606
|
|
|
|
|
|
|
# clean up lists - thanks to gmargo@perlmonks for this idea. |
607
|
|
|
|
|
|
|
sub _cleanup { |
608
|
0
|
|
|
0
|
|
|
my $i = 0; |
609
|
0
|
|
|
|
|
|
my @dpid = grep { $Proc::Fork::Control::HEAP->{cidlist}->{$_} == 0 } keys %{ $Proc::Fork::Control::HEAP->{cidlist} }; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
610
|
0
|
0
|
|
|
|
|
if(@dpid){ |
611
|
0
|
|
|
|
|
|
for(@dpid){ |
612
|
0
|
0
|
|
|
|
|
if(exists $Proc::Fork::Control::HEAP->{cidlist}->{$_}){ |
613
|
0
|
|
|
|
|
|
delete $Proc::Fork::Control::HEAP->{cidlist}->{$_}; |
614
|
0
|
|
|
|
|
|
$i++; |
615
|
|
|
|
|
|
|
|
616
|
0
|
0
|
|
|
|
|
$Proc::Fork::Control::HEAP->{children} -- if $Proc::Fork::Control::HEAP->{children}; |
617
|
|
|
|
|
|
|
} |
618
|
|
|
|
|
|
|
} |
619
|
|
|
|
|
|
|
} |
620
|
|
|
|
|
|
|
|
621
|
|
|
|
|
|
|
# Do some additional checks to see if these children are really alive. |
622
|
0
|
|
|
|
|
|
for(keys %{ $Proc::Fork::Control::HEAP->{cidlist} }){ |
|
0
|
|
|
|
|
|
|
623
|
0
|
0
|
|
|
|
|
if(!kill(0, $_)){ |
624
|
0
|
0
|
|
|
|
|
if(exists $Proc::Fork::Control::HEAP->{cidlist}->{$_}){ |
625
|
0
|
|
|
|
|
|
delete $Proc::Fork::Control::HEAP->{cidlist}->{$_}; |
626
|
0
|
|
|
|
|
|
$i++; |
627
|
|
|
|
|
|
|
|
628
|
0
|
0
|
|
|
|
|
$Proc::Fork::Control::HEAP->{children} -- if $Proc::Fork::Control::HEAP->{children}; |
629
|
|
|
|
|
|
|
} |
630
|
|
|
|
|
|
|
} |
631
|
|
|
|
|
|
|
} |
632
|
|
|
|
|
|
|
|
633
|
0
|
|
|
|
|
|
return $i; |
634
|
|
|
|
|
|
|
} |
635
|
|
|
|
|
|
|
|
636
|
|
|
|
|
|
|
1; |