File Coverage

blib/lib/Proc/Forking.pm
Criterion Covered Total %
statement 145 409 35.4
branch 43 204 21.0
condition 1 18 5.5
subroutine 20 38 52.6
pod 14 19 73.6
total 223 688 32.4


line stmt bran cond sub pod time code
1             package Proc::Forking;
2              
3             ###########################################################
4             # Fork package
5             # Gnu GPL2 license
6             #
7             # Forking.pm 1.49 2010 09 02 14:52
8              
9             #
10             # Fabrice Dulaunoy
11             ###########################################################
12             # ChangeLog:
13             #
14             ###########################################################
15 4     4   27564 use strict;
  4     4   8  
  4         168  
  4         4044  
  4         8  
  4         164  
16              
17 4     4   3516 use POSIX qw(:signal_h setsid WNOHANG);
  4     4   36132  
  4         32  
  4         16  
  4         8  
  4         44  
18 4     4   10760 use IO::File;
  4     4   52104  
  4         596  
  4         3552  
  4         8  
  4         1452  
19 4     4   40 use Cwd;
  4     4   8  
  4         256  
  4         24  
  4         8  
  4         260  
20 4     4   3508 use Sys::Load qw/getload/;
  4     4   3684  
  4         240  
  4         24  
  4         8  
  4         164  
21 4     4   3200 use Sys::Prctl;
  4     4   21692  
  4         224  
  4         20  
  4         8  
  4         168  
22 4     4   36 use Carp;
  4     4   8  
  4         264  
  4         24  
  4         8  
  4         260  
23              
24 4     4   20 use vars qw( $VERSION);
  4     4   8  
  4         28460  
  4         20  
  4         8  
  4         20400  
25              
26             $VERSION = '1.50';
27              
28             my $DAEMON_PID;
29             $SIG{ CHLD } = \&garbage_child;
30              
31             my %PID;
32             my %NAME;
33              
34             my @CODE;
35             $CODE[0] = [ 0, " success" ];
36             $CODE[1] = [ 1, " Can't fork a new process" ];
37             $CODE[2] = [ 2, " Can't open PID file" ];
38             $CODE[3] = [ 3, " Process already running with same PID" ];
39             $CODE[4] = [ 4, " maximun LOAD reached" ];
40             $CODE[5] = [ 5, " maximun number of processes reached" ];
41             $CODE[6] = [ 6, " error in parameters" ];
42             $CODE[7] = [ 7, " No function provided" ];
43             $CODE[8] = [ 8, " Can't fork" ];
44             $CODE[9] = [ 9, " PID already present in list of PID processes" ];
45             $CODE[10] = [ 10, " NAME already present in list of NAME processes" ];
46             $CODE[11] = [ 11, " Can't chdir" ];
47             $CODE[12] = [ 12, " Can't chroot" ];
48             $CODE[13] = [ 13, " Can't become DAEMON" ];
49             $CODE[14] = [ 14, " Can't unlink PID file" ];
50             $CODE[15] = [ 15, " maximun MEM used reached" ];
51             $CODE[16] = [ 16, " Expiration TIMEOUT reached" ];
52             $CODE[17] = [ 17, " NO expiration parameter" ];
53             $CODE[18] = [ 18, " Don't fork, NAME already present (STRICT mode enabled)" ];
54             $CODE[19] = [ 19, " Don't fork, PID_FILE already present (STRICT mode enabled)" ];
55              
56             sub daemonize
57             {
58              
59              
60 0     0 1 0 my @param = @_;
61 0         0 my $self = shift @param;
62            
63             $SIG{ INT } = $SIG{ KILL } = $SIG{ TERM } = sub {
64 0     0   0 $self->killall_childs;
65 0         0 unlink $DAEMON_PID;
66 0         0 exit 0 ;
67 0         0 };
68            
69 0 0       0 if ( @param % 2 )
70             {
71 0         0 return ( $CODE[6][0], 0, $CODE[6][1] );
72             }
73 0         0 my %param = @param;
74 0 0       0 my $uid = exists( $param{ uid } ) ? $param{ uid } : '';
75 0 0       0 my $gid = exists( $param{ gid } ) ? $param{ gid } : '';
76 0 0       0 my $home = exists( $param{ home } ) ? $param{ home } : '';
77 0 0       0 my $pid_file = $param{ pid_file } if exists( $param{ pid_file } );
78 0 0       0 my $name = $param{ name } if exists( $param{ name } );
79 0 0       0 if ( defined( $name ) )
80             {
81 0         0 my $exp_name = $name;
82 0         0 $exp_name =~ s/##/$$/g;
83 0         0 $0 = $exp_name;
84 0         0 my $main_process = new Sys::Prctl();
85 0         0 $main_process->name( $0 );
86             }
87              
88 0         0 my $child = fork;
89 0 0       0 if ( !defined $child )
90             {
91 0         0 return ( $CODE[13][0], 0, $CODE[13][1] );
92             }
93 0 0       0 exit 0 if $child; # parent dies;
94              
95 0 0       0 if ( exists( $param{ pid_file } ) )
96             {
97 0         0 $pid_file =~ s/##/$$/g;
98 0         0 $DAEMON_PID = $pid_file;
99 0         0 my @ret = create_pid_file( $pid_file, $$ );
100 0 0       0 if ( $ret[0] )
101             {
102             # die "Another process is RUNNING\n";
103 0         0 carp "Another process is RUNNING\n";
104 0         0 return ( $CODE[3][0], 0, $CODE[3][1] ) ;
105             }
106             }
107              
108 0         0 my $luid = -1;
109 0         0 my $lgid = -1;
110 0 0       0 if ( $uid ne '' )
111             {
112 0         0 $luid = $uid;
113             }
114 0 0       0 if ( $gid ne '' )
115             {
116 0         0 $lgid = $gid;
117             }
118 0         0 chown $luid, $lgid, $pid_file;
119 0 0       0 if ( $home ne '' )
120             {
121 0         0 local ( $>, $< ) = ( $<, $> );
122 0         0 my $cwd = $home;
123 0 0       0 chdir( $cwd ) || return ( $CODE[11][0], 0, $CODE[11][1] );
124 0 0       0 chroot( $cwd ) || return ( $CODE[12][0], 0, $CODE[12][1] );
125 0         0 $< = $>;
126             }
127              
128 0 0       0 if ( $gid ne '' )
129             {
130 0         0 $) = "$gid $gid";
131             }
132              
133 0 0       0 if ( $uid ne '' )
134             {
135 0         0 $> = $uid;
136             }
137 0         0 POSIX::setsid();
138 0         0 open( STDIN, "
139 0         0 open( STDOUT, ">/dev/null" );
140 0         0 open( STDERR, ">&STDOUT" );
141 0         0 chdir '/';
142 0         0 umask( 0 );
143 0         0 $ENV{ PATH } = '/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin';
144 0         0 delete @ENV{ 'IFS', 'CDPATH', 'ENV', 'BASH_ENV' };
145 0         0 $SIG{ CHLD } = \&garbage_child;
146             }
147              
148             sub new
149             {
150 4     4 1 800 my ( $class ) = @_;
151 4         96 bless {
152             _function => $_[1],
153             _args => $_[2],
154             _name => $_[3],
155             _pid => $_[4],
156             _pid_file => $_[5],
157             _home => $_[6],
158             _uid => $_[7],
159             _gid => $_[8],
160             _max_child => $_[9],
161             _max_load => $_[10],
162             _pids => $_[11],
163             _names => $_[12],
164             _max_mem => $_[13],
165             _expiration => $_[14],
166             _expiration_auto => $_[15],
167             _start_time => $_[16],
168             _eagain_sleep => $_[17],
169             # _strict => $_[17],
170             }, $class;
171              
172             }
173              
174             sub fork_child
175             {
176 12     12 1 522 my @param = @_;
177 12         42 my $self = shift @param;
178 12         64 my $start_time = time;
179 12 50       67 if ( @param % 2 )
180             {
181 0         0 return ( $CODE[6][0], 0, $CODE[6][1] );
182             }
183 12         75 my %param = @param;
184 12 50       58 if ( !exists( $param{ function } ) )
185             {
186 0         0 return ( $CODE[7][0], 0, $CODE[7][1] );
187             }
188 12         53 $self->{ _function } = $param{ function };
189 12 50       137 $self->{ _args } = $param{ args } if exists( $param{ args } );
190 12 100       73 $self->{ _name } = $param{ name } if exists( $param{ name } );
191 12 100       130 $self->{ _home } = exists( $param{ home } ) ? $param{ home } : '';
192 12 50       82 $self->{ _uid } = exists( $param{ uid } ) ? $param{ uid } : '';
193 12 50       79 $self->{ _gid } = exists( $param{ gid } ) ? $param{ gid } : '';
194 12 50       72 $self->{ _eagain_sleep } = exists( $param{ eagain_sleep } ) ? $param{ eagain_sleep }: 5; ;
195            
196            
197 12         40 $self->{ _strict } = 0;
198 12 50       51 if ( exists( $param{ strict } ) )
199             {
200 0         0 $self->{ _strict } = $param{ strict };
201 0 0       0 if ( exists( $self->{ _names }{ $param{ name } }{ pid } ) )
202             {
203 0         0 return ( $CODE[18][0], $self->{ _pid }, ( $param{ name } . $CODE[18][1] ) );
204             }
205 0 0       0 if ( exists( $param{ pid_file } ) )
206             {
207 0 0       0 if ( -e $param{ pid_file } )
208             {
209             # pid file already exists
210 0         0 my $fh = IO::File->new( $param{ pid_file } );
211 0         0 my $pid_num = <$fh>;
212 0         0 close $fh;
213 0 0       0 if ( kill 0 => $pid_num )
214             {
215 0         0 return ( $CODE[19][0], $pid_num, $CODE[19][1] );
216             }
217             }
218             }
219             }
220              
221 12 50       90 $self->{ _pid_file } = exists( $param{ pid_file } ) ? $param{ pid_file } : '';
222              
223 12 50       52 if ( exists( $param{ max_load } ) )
224             {
225 0         0 $self->{ _max_load } = $param{ max_load };
226 0 0       0 if ( $self->{ _max_load } <= ( getload() )[0] )
227             {
228 0         0 return ( $CODE[4][0], 0, $CODE[4][1] );
229             }
230             }
231              
232 12 50       45 if ( exists( $param{ max_child } ) )
233             {
234 0         0 $self->{ _max_child } = $param{ max_child };
235 0 0       0 if ( $self->{ _max_child } <= ( keys %{ $self->{ _pids } } ) )
  0         0  
236             {
237 0         0 return ( $CODE[5][0], 0, $CODE[5][1] );
238             }
239             }
240              
241 12 50       49 if ( exists( $param{ max_mem } ) )
242             {
243 0         0 $self->{ _max_mem } = $param{ max_mem };
244 0 0       0 if ( $self->{ _max_mem } >= getmemfree() )
245             {
246 0         0 return ( $CODE[15][0], 0, $CODE[15][1] );
247             }
248             }
249              
250 12 50       40 if ( exists( $param{ expiration } ) )
251             {
252 0         0 $self->{ _expiration } = $param{ expiration } + $start_time;
253 0 0       0 if ( exists( $param{ expiration_auto } ) )
254             {
255 0         0 $self->{ _expiration_auto } = $param{ expiration_auto };
256             }
257             # else
258             # {
259             # $self->{ _expiration_auto } = 0;
260             # }
261             }
262             else
263             {
264 12         39 $self->{ _expiration } = 0;
265             }
266              
267             {
268 12         35 my $pid;
  12         18  
269             my $ret;
270              
271 12 100       25805 if ( $pid = fork() )
    50          
    0          
272             {
273             ## in parent
274 9         184 $self->{ _pid } = $pid;
275 9         117 my $pid_file;
276             my $exp_name;
277 9         290 $self->{ _start_time } = $^T;
278 9 100       159 if ( defined( $self->{ _name } ) )
279             {
280 3         116 $exp_name = $self->{ _name };
281 3         44 $exp_name =~ s/##/$pid/g;
282             }
283 9 50       71 if ( defined( $self->{ _pid_file } ) )
284             {
285 9         140 $pid_file = $self->{ _pid_file };
286 9         44 $pid_file =~ s/##/$pid/g;
287             }
288 9 50       228 if ( !defined( $self->{ _pids }{ $pid } ) )
289             {
290 9         406 $self->{ _pids }{ $pid }{ name } = $exp_name;
291 9         92 $self->{ _pids }{ $pid }{ start_time } = $start_time;
292 9 50       52 if ( defined( $self->{ _expiration } ) )
293             {
294 9         75 $self->{ _pids }{ $pid }{ expiration } = $self->{ _expiration };
295             }
296 9 50       48 if ( defined( $self->{ _expiration_auto } ) )
297             {
298 0         0 $self->{ _pids }{ $pid }{ expiration_auto } = $self->{ _expiration_auto };
299             }
300 9         107 $PID{ $pid }{ name } = $exp_name;
301 9 50       50 if ( defined( $self->{ _pid_file } ) )
302             {
303 9         106 $self->{ _pids }{ $pid }{ pid_file } = $pid_file;
304 9         40 $PID{ $pid }{ pid_file } = $pid_file;
305             }
306 9 50       104 if ( defined( $self->{ _home } ) )
307             {
308 9         144 $self->{ _pids }{ $pid }{ home } = $self->{ _home };
309 9         30 $PID{ $pid }{ home } = $self->{ _home };
310             }
311             }
312             else
313             {
314 0         0 return ( $CODE[9][0], $self->{ _pid }, $CODE[9][1] );
315             }
316 9 100       171 if ( !defined( $self->{ _names }{ $exp_name }{ pid } ) )
317             {
318 5         63 $self->{ _names }{ $exp_name }{ pid } = $pid;
319 5         15 $self->{ _names }{ $exp_name }{ start_time } = $start_time;
320 5 50       36 if ( defined( $self->{ _expiration } ) )
321             {
322 5         15 $self->{ _names }{ $exp_name }{ expiration } = $self->{ _expiration };
323             }
324 5 50       36 if ( defined( $self->{ _expiration_auto } ) )
325             {
326 0         0 $self->{ _names }{ $exp_name }{ expiration_auto } = $self->{ _expiration_auto };
327             }
328 5         62 $NAME{ $exp_name }{ pid } = $pid;
329 5 50       33 if ( defined( $self->{ _pid_file } ) )
330             {
331 5         13 $self->{ _names }{ $exp_name }{ pid_file } = $pid_file;
332 5         10 $NAME{ $exp_name }{ pid_file } = $pid_file;
333             }
334 5 50       16 if ( defined( $self->{ _home } ) )
335             {
336 5         34 $self->{ _names }{ $exp_name }{ home } = $self->{ _home };
337 5         13 $NAME{ $exp_name }{ home } = $self->{ _home };
338             }
339             }
340             else
341             {
342 4         645 return ( $CODE[10][0], $self->{ _pid }, ( $self->{ _name } . $CODE[10][1] ) );
343             }
344              
345 5         3890 return ( $CODE[0][0], $self->{ _pid }, $CODE[0][1] );
346             }
347             elsif ( defined $pid )
348             {
349             ## in child
350 3         699 $SIG{ INT } = $SIG{ CHLD } = $SIG{ TERM } = 'DEFAULT';
351 3 100       101 if ( defined( $self->{ _name } ) )
352             {
353 2         38 my $exp_name = $self->{ _name };
354 2         69 $exp_name =~ s/##/$$/g;
355 2         210 $0 = $exp_name;
356 2         321 my $main_process = new Sys::Prctl();
357 2         110 $main_process->name( $0 );
358             }
359 3         368 $self->{ _start_time } = $start_time;
360              
361 3         22 $self->{ _pid } = $pid;
362 3 50       43 if ( $self->{ _home } ne '' )
363             {
364 0         0 local ( $>, $< ) = ( $<, $> );
365 0         0 my $cwd = $self->{ _home };
366 0 0       0 chdir( $cwd ) || return ( $CODE[11][0], 0, $CODE[11][1] );
367 0 0       0 chroot( $cwd ) || return ( $CODE[12][0], 0, $CODE[12][1] );
368 0         0 $< = $>;
369             }
370              
371 3 50       35 if ( $self->{ _gid } ne '' )
372             {
373 0         0 my $gid = $self->{ _gid };
374 0         0 $) = "$gid $gid";
375             }
376 3 50       19 if ( $self->{ _uid } ne '' )
377             {
378 0         0 $> = $self->{ _uid };
379             }
380 3 50       93 if ( $self->{ _pid_file } ne '' )
381             {
382 0         0 my $pid_file = $self->{ _pid_file };
383              
384 0         0 $pid_file =~ s/##/$$/g;
385              
386 0 0       0 if ( defined $self->{ _pid_folder } )
387             {
388 0         0 $pid_file = $self->{ _pid_folder } . $pid_file;
389             }
390 0         0 $ret = create_pid_file( $pid_file, $$ );
391             }
392 3 50 33     97 if ( ( exists( $self->{ _expiration } ) && ( exists( $self->{ _expiration_auto } ) ) ) )
393             {
394 3         32 my $sta;
395 3         166 eval {
396             local $SIG{ ALRM } = sub {
397 0 0   0   0 if ( defined $self->{ _pid_file } )
398             {
399 0         0 my $pid_file = $self->{ _pid_file };
400 0         0 $pid_file =~ s/##/$$/g;
401              
402 0 0       0 if ( -e $pid_file )
403             {
404 0         0 delete_pid_file( $pid_file );
405             }
406             }
407 0         0 return ( $CODE[16][0], 16, $CODE[16][1] );
408             # die "TIMEOUT";
409 3         292 };
410 3         212 alarm( $self->{ _expiration } - $self->{ _start_time } );
411 3         11 eval { $self->{ _function }( $self->{ _args } ); };
  3         99  
412 3         2181408 alarm 0;
413 3         109 return ( $CODE[16][0], 16, $CODE[16][1] );
414             };
415 3         17 alarm 0;
416             # if ($@ && $@ =~ /TIMEOUT/)
417 3 50       192 if ( $! =~ /Interrupted system call/ )
418             {
419 0         0 return ( $CODE[16][0], 16, $CODE[16][1] );
420             }
421             }
422             else
423             {
424 0         0 $self->{ _function }( $self->{ _args } );
425             }
426              
427 3 50       21 if ( defined $self->{ _pid_file } )
428             {
429 3         29 my $pid_file = $self->{ _pid_file };
430 3         18 $pid_file =~ s/##/$$/g;
431              
432 3 50       87 if ( -e $pid_file )
433             {
434 0         0 delete_pid_file( $pid_file );
435             }
436             }
437 3         1050 exit 0;
438             }
439             elsif ( $! == &POSIX::EAGAIN )
440             {
441 0         0 my $o0 = $0;
442 0         0 $0 = "$o0: waiting to fork";
443             # sleep 5;
444 0         0 sleep $self->{ _eagain_sleep };
445 0         0 $0 = $o0;
446 0         0 redo;
447             }
448             else
449             {
450 0         0 return ( $CODE[8][0], 0, $CODE[8][1] );
451             }
452             }
453              
454             }
455              
456             sub kill_child
457             {
458 0     0 1 0 my $self = shift;
459 0         0 my $pid = shift;
460 0   0     0 my $signal = shift || 15;
461 0         0 kill $signal => $pid;
462 0         0 my ($dp , $dn) = $self->clean_childs();
463 0 0       0 return wantarray ? ( scalar( @{$dp} ), $dp, $dn ) : scalar( @{$dp} );
  0         0  
  0         0  
464             }
465              
466             sub killall_childs
467             {
468 0     0 1 0 my $self = shift;
469 0   0     0 my $signal = shift || 15;
470 0         0 my $pids = $self->{ _pids };
471 0         0 my %pids = %{ $pids };
  0         0  
472              
473 0         0 foreach ( keys %pids )
474             {
475 0         0 kill $signal => $_;
476             }
477 0         0 my ($dp , $dn) = $self->clean_childs();
478 0 0       0 return wantarray ? ( scalar( @{$dp} ), $dp, $dn ) : scalar( @{$dp} );
  0         0  
  0         0  
479             }
480              
481             sub expirate
482             {
483 0     0 1 0 my $self = shift;
484 0   0     0 my $signal = shift || 15;
485 0         0 my $pids = $self->{ _pids };
486 0         0 my %pids = %{ $pids };
  0         0  
487 0         0 my $now = time;
488              
489 0         0 foreach my $pid ( keys %pids )
490             {
491 0 0       0 if ( $self->{ _pids }{ $pid }{ expiration } < $now )
492             {
493 0         0 kill $signal => $pid;
494             }
495             }
496 0         0 my ($dp , $dn) = $self->clean_childs();
497 0 0       0 return wantarray ? ( scalar( @{$dp} ), $dp, $dn ) : scalar( @{$dp} );
  0         0  
  0         0  
498             }
499              
500             sub get_expiration
501             {
502 0     0 0 0 my $self = shift;
503 0         0 my $pid = shift;
504 0 0       0 if ( exists( $self->{ _pids }{ $pid }{ expiration } ) )
505             {
506 0         0 return ( $self->{ _pids }{ $pid }{ expiration } );
507             }
508             else
509             {
510 0         0 return ( $CODE[17][0], 17, $CODE[17][1] );
511             }
512             }
513              
514             sub set_expiration
515             {
516 0     0 0 0 my $self = shift;
517 0         0 my $pid = shift;
518 0         0 my $new_expiration = shift;
519 0         0 $new_expiration += time;
520              
521 0 0       0 if ( exists( $self->{ _pids }{ $pid }{ expiration } ) )
522             {
523 0         0 $self->{ _pids }{ $pid }{ expiration } = $new_expiration;
524 0         0 my $name = $self->{ _pids }{ $pid }{ name };
525 0         0 $self->{ _names }{ $name }{ expiration } = $new_expiration;
526 0         0 return ( $self->{ _pids }{ $pid }{ expiration } );
527             }
528             else
529             {
530 0         0 return ( $CODE[17][0], 17, $CODE[17][1] );
531             }
532             }
533              
534             sub list_pids
535             {
536 0     0 1 0 my $self = shift;
537             # $self->clean_childs();
538 0         0 return $self->{ _pids };
539             }
540              
541             sub list_names
542             {
543 0     0 1 0 my $self = shift;
544             # $self->clean_childs();
545 0         0 return $self->{ _names };
546             }
547              
548             sub pid_nbr
549             {
550 3     3 1 375 my $self = shift;
551             # $self->clean_childs();
552 3         9 return ( scalar( keys %{ $self->{ _pids } } ) );
  3         27  
553             }
554              
555             sub clean_childs
556             {
557 0     0 1 0 my $self = shift;
558 0         0 my @pid_remove_list;
559             my @name_remove_list;
560 0         0 foreach my $child ( keys %{ $self->{ _pids } } )
  0         0  
561             {
562 0         0 my $state = kill 0 => $child;
563 0 0       0 if ( !$state )
564             {
565 0         0 my $name = $self->{ _pids }{ $child }{ name };
566 0 0       0 if ( defined $self->{ _pids }{ $child }{ pid_file } )
567             {
568 0         0 my $pid_file = $self->{ _pids }{ $child }{ pid_file };
569 0 0       0 if ( defined $self->{ _pids }{ $child }{ home } )
570             {
571 0         0 $pid_file = $self->{ _pids }{ $child }{ home } . $pid_file;
572             }
573              
574 0 0       0 if ( -e $pid_file )
575             {
576 0         0 delete_pid_file( $pid_file );
577             }
578 0         0 delete $self->{ _pids }{ $child }{ pid_file };
579 0         0 delete $self->{ _names }{ $name }{ pid_file };
580             }
581 0         0 delete $self->{ _pids }{ $child }{ start_time };
582 0         0 delete $self->{ _pids }{ $child }{ name };
583 0         0 delete $self->{ _pids }{ $child };
584 0         0 delete $self->{ _names }{ $name }{ start_time };
585 0         0 delete $self->{ _names }{ $name }{ pid };
586 0         0 delete $self->{ _names }{ $name };
587              
588 0         0 delete $NAME{ $name }{ pid };
589 0         0 delete $NAME{ $name };
590              
591 0         0 push @pid_remove_list, $child;
592 0         0 push @name_remove_list, $name;
593             }
594             }
595              
596 0         0 return \@pid_remove_list, \@name_remove_list;
597             }
598              
599             sub test_pid
600             {
601 0     0 1 0 my $self = shift;
602             # $self->clean_childs();
603 0         0 my $child = shift;
604 0         0 my $state;
605 0 0       0 if ( exists $self->{ _pids }{ $child } )
606             {
607 0         0 $state = kill 0 => $child;
608 0 0       0 return wantarray ? ( $state, ( $self->{ _pids }{ $child }{ name } ) ) : $state;
609             }
610 0 0       0 return wantarray ? ( 0, ( $self->{ _pids }{ $child }{ name } ) ) : $state;
611             }
612              
613             sub test_name
614             {
615 0     0 1 0 my $self = shift;
616             # $self->clean_childs();
617 0         0 my $name = shift;
618 0         0 my $state;
619 0 0       0 if ( defined( $self->{ _names }{ $name } ) )
620             {
621 0         0 $state = kill 0 => ( $self->{ _names }{ $name }{ pid } );
622 0 0       0 return wantarray ? ( $state, ( $self->{ _names }{ $name }{ pid } ) ) : $state;
623             }
624 0 0       0 return wantarray ? ( 0, ( $self->{ _names }{ $name }{ pid } ) ) : $state;
625             }
626              
627             sub version
628             {
629 0     0 1 0 my $self = shift;
630 0         0 return $VERSION;
631             }
632              
633             sub create_pid_file
634             {
635 0     0 0 0 my $file = shift;
636 0         0 my $pid_num = shift;
637 0 0       0 if ( -z $file )
638             {
639 0 0 0     0 if ( !( -w $file && unlink $file ) )
640             {
641 0         0 return ( $CODE[14][0], $pid_num, $CODE[14][1] );
642             }
643             }
644 0 0       0 if ( -e $file )
645             {
646              
647             # pid file already exists
648 0         0 my $fh = IO::File->new( $file );
649 0         0 my $pid_num = <$fh>;
650 0         0 close $fh;
651 0 0       0 if ( kill 0 => $pid_num )
652             {
653 0         0 return ( $CODE[3][0], $pid_num, $CODE[3][1] );
654             }
655 0 0 0     0 if ( !( -w $file && unlink $file ) )
656             {
657 0         0 return ( $CODE[14][0], $pid_num, $CODE[14][1] );
658             }
659             }
660 0         0 my $fh = IO::File->new( $file, O_WRONLY | O_CREAT | O_EXCL, 0644 );
661 0 0       0 if ( !$fh ) { return ( $CODE[2][0], $pid_num, $CODE[2][1] ); }
  0         0  
662 0         0 print $fh $pid_num."\n";
663 0         0 close $fh;
664 0         0 return ( $CODE[0][0], $pid_num, $CODE[0][1] );
665             }
666              
667             sub delete_pid_file
668             {
669 0     0 0 0 my $file = shift;
670 0 0       0 if ( -e $file )
671             {
672 0 0 0     0 if ( !( -w $file && unlink $file ) )
673             {
674 0         0 Carp::carp "Can't unlink PID file $file";
675             }
676             }
677             }
678              
679             sub garbage_child
680             {
681 9     9 0 11937022 while ( ( my $child = waitpid( -1, WNOHANG ) ) > 0 )
682             {
683 0         0 my $name = $PID{ $child }{ name };
684 0 0       0 if ( defined $PID{ $child }{ pid_file } )
685             {
686 0         0 my $pid_file = $PID{ $child }{ pid_file };
687 0         0 $pid_file =~ s/##/$child/g;
688              
689 0 0       0 if ( defined $PID{ $child }{ home } )
690             {
691 0         0 $pid_file = $PID{ $child }{ home } . $pid_file;
692             }
693              
694 0 0       0 if ( -e $pid_file )
695             {
696 0         0 delete_pid_file( $pid_file );
697             }
698 0         0 delete $PID{ $child }{ pid_file };
699 0         0 delete $NAME{ $name }{ pid_file };
700             }
701              
702 0         0 delete $PID{ $child }{ name };
703 0         0 delete $PID{ $child };
704 0 0       0 if ( exists $NAME{ $name } )
705             {
706 0         0 delete $NAME{ $name }{ pid };
707 0         0 delete $NAME{ $name };
708             }
709             }
710 9         350 $SIG{ CHLD } = \&garbage_child;
711             }
712              
713             sub DESTROY
714             {
715 0     0     my $self = shift;
716 0           unlink $self->{ _pid_file };
717             }
718              
719             sub getmemfree
720             {
721 0     0 1   undef $/;
722 0           open MEM, "/proc/meminfo";
723 0           my $temp = ;
724 0           close MEM;
725 0           $temp =~ /MemFree:\s+(\d+) (\w+)\s/;
726 0           my $mem = $1;
727 0           my $unit = $2;
728 0 0         if ( $unit =~ /kb/i )
    0          
729             {
730 0           $mem *= 1024;
731             }
732             elsif ( $unit =~ /mb/i )
733             {
734 0           $mem *= 1048576;
735             }
736 0           $temp =~ /SwapFree:\s+(\d+) (\w+)\s/;
737 0           my $swap = $1;
738 0           $unit = $2;
739 0 0         if ( $unit =~ /kb/i )
    0          
740             {
741 0           $swap *= 1024;
742             }
743             elsif ( $unit =~ /mb/i )
744             {
745 0           $swap *= 1048576;
746             }
747 0           my $tot = $mem + $swap;
748 0 0         return wantarray ? ( $tot, $mem, $swap ) : $tot;
749             }
750              
751             1;
752              
753             =head1 ABSTRACT
754              
755             The B module provides a set of tool to fork and daemonize.
756             The module fork a function code
757              
758             =head1 SYNOPSIS
759              
760             =over 3
761              
762             #!/usr/bin/perl
763              
764             use strict;
765             use Proc::Forking;
766             use Data::Dumper;
767             use Time::HiRes qw(usleep); # to allow micro sleep
768              
769             my $f = Proc::Forking->new();
770             $SIG{ KILL } = $SIG{ TERM } = $SIG{ INT } = sub { $f->killall_childs;sleep 1; exit },
771             $f->daemonize(
772             ## uid => 1000,
773             ## gid => 1000,
774             ## home => "/tmp",
775             pid_file => "/tmp/master.pid"
776             );
777              
778             open( STDOUT, ">>/tmp/master.log" );
779             my $nbr = 0;
780             my $timemout;
781              
782             while ( 1 )
783             {
784             if ( $nbr < 20 )
785             {
786             my $extra = "other parameter";
787             my ( $status, $pid, $error ) = $f->fork_child(
788             function => \&func,
789             name => "new_name.##",
790             args => [ "hello SOMEONE", 3, $extra ],
791             pid_file => "/tmp/fork.##.pid",
792             uid => 1000,
793             gid => 1000,
794             home => "/tmp",
795             max_load => 5,
796             max_mem => 185000000,
797             expiration => 10,
798             # expiration_auto => 1,
799             );
800             if ( $status == 4 ) # if the load become to high
801             {
802             print "Max load reached, do a little nap\n";
803             usleep( 100000 );
804             next;
805             }
806             elsif ( $status ) # if another kind of error
807            
808             {
809             print "PID=$pid\t error=$error\n";
810             print Dumper( $f->list_names() );
811             print Dumper( $f->list_pids() );
812             }
813             }
814             $nbr = $f->pid_nbr;
815             my ( $n, @dp, @dn ) = $f->expirate;
816             if ( $n )
817             {
818             print Dumper( @dp );
819             }
820             print "free=<" . scalar( $f->getmemfree ) . ">\n";
821             usleep( 100000 ); # always a good idea to put a small sleep to allow task swapper to gain some free resources
822             }
823            
824             sub func
825             {
826             my $ref = shift;
827             my @args = @$ref;
828             my ( $data, $time_out, $sockC ) = @args;
829             $SIG{ USR1 } = sub { open my $log, ">>/tmp/log.s"; print $log "signal USR1 received\n"; close $log; };
830             if ( !$time_out )
831             {
832             $time_out = 3;
833             }
834             open my $FF, ">>/tmp/loglist";
835             print $FF $$, " start time =", $^T;
836             close $FF;
837            
838             for ( 1 .. 4 )
839             {
840             open my $fh, ">>/tmp/log";
841             if ( defined $fh )
842             {
843             print $fh "TMOUT = $time_out " . time . " PID=$$ cwd=" . Cwd::cwd() . " name =$0\n";
844             $fh->close;
845             }
846             sleep $time_out + rand( 5 );
847             }
848             }
849              
850              
851             =back
852              
853             =head1 REQUIREMENT
854              
855             The B module need the following modules
856              
857             POSIX
858             IO::File
859             Cwd
860             Sys::Load
861              
862             =head1 METHODS
863              
864             =over 1
865              
866             The Fork module is object oriented and provide the following method
867              
868              
869             =back
870              
871             =head2 new
872              
873             To create of a new pool of child:
874              
875             my $f = Proc::Forking->new();
876              
877              
878             =head2 fork_child
879              
880             To fork a process
881              
882             my ( $status, $pid, $error ) = $f->fork_child(
883             function => \&func,
884             name => "new_name.$_",
885             args => [ "\thello SOMEONE",3, $other param],
886             pid_file => "/tmp/fork.$_.pid",
887             uid => 1000,
888             gid => 1000,
889             home => "/tmp",q
890             max_load => 5,
891             max_child => 5,
892             max_mem => 1850000000,
893             expiration => 20,
894             expiration_auto => 1,
895             strict => 1,
896             eagain_sleep => 2,
897             );
898            
899             The only mandatory parameter is the reference to the function to fork (function => \&func)
900             The normal return value is an array with: 3 elements (see B)
901              
902             =over 2
903              
904             =back
905              
906             =head3 function
907              
908             =over 3
909              
910             I is the reference to the function to use as code for the child. It is the only mandatory parameter.
911              
912             =back
913              
914             =head3 name
915              
916             =over 3
917              
918             I is the name for the newly created process (affect new_name to $0 in the child).
919             A ## (double sharp) into the name is replaced with the PID of the process created.
920              
921             =back
922              
923             =head3 home
924              
925             =over 3
926              
927             the I provided will become the working directory of the child with a chroot.
928             Be carefull for the files created into the process forked, authorizasions and paths are relative to this chroot
929              
930             =back
931              
932             =head3 uid
933              
934             =over 3
935              
936             the child get this new I (numerical value)
937             Be carefull for the files created into the process forked, authorizations and paths are relative to this chroot
938              
939             =back
940              
941             =head3 gid
942              
943             =over 3
944              
945             the child get this new I (numerical value)
946             Be carefull for the files created into the process forked, authorizations and paths are relative to this chroot
947              
948             =back
949              
950             =head3 pid_file
951              
952             =over 3
953              
954             I give the file containing the pid of the child (be care of uid, gid and chroot because the pid_file is created by the child)
955             A ## (double sharp ) into the name is expanded with the PID of the process created
956              
957             =back
958              
959             =head3 max_load
960              
961             =over 3
962              
963             if the "1 minute" load is greater than I, the process is not forked
964             and the function will return [ 4, 0, "maximun LOAD reached" ]
965              
966             =back
967              
968             =head3 max_child
969              
970             =over 3
971              
972             if the number of running child is greater than max_child, the process is not forked
973             and the function return [ 5, 0, "maximun number of processes reached" ]
974              
975             =back
976              
977             =head3 max_mem
978              
979             =over 3
980              
981             if the total free memory is lower than this value, the process is not forked
982             and the function will return [ 15, 0, "maximun MEM used reached" ]
983              
984             =back
985              
986             =head3 expiration
987              
988             =over 3
989              
990             it is a value linked with each forked process to allow the function expirate()
991             to kill the process if it is still running after that expiration time
992             The expiration value write in list_pids and list_names are this value (in sec ) + the start_time
993             (to allow set_expiration to modify the value)
994              
995             =back
996              
997              
998             =head3 expiration_auto
999              
1000             =over 3
1001              
1002             if defined, the child kill themselve after the defined expiration time (!!! the set_expiration function is not able to modify this expiration time)
1003              
1004             =back
1005              
1006              
1007             =head3 strict
1008              
1009             =over 3
1010              
1011             if defined, the process is not forked if the NAME is already in process table, or if the PID_FILE id present and a corresponding process is still running
1012              
1013             BECARE, because the test is done before the fork, the NAME and the PID_FILE is not expanded with the child PID
1014              
1015              
1016             =back
1017              
1018              
1019             =head3 eagain_sleep
1020              
1021             =over 3
1022              
1023             timeout between a new try of forking if POSIX::EAGAIN error occor ( default 5 second);
1024              
1025              
1026             =back
1027              
1028             =head2 kill_child
1029              
1030             $f->kill_child(PID[,SIGNAL]);
1031            
1032             This function kill with a signal 15 (by default) the process with the provided PID.
1033             An optional signal could be provided.
1034             This function return the number of childs killed, a ref to a list of PID killed, a ref to a list of names killed.
1035              
1036              
1037             =head2 killall_childs
1038              
1039             $f->killall_childs([SIGNAL]);
1040              
1041             This function kills all processes with a signal 15 (by default).
1042             An optional signal could be provided.
1043             This function return the number of childs killed, a ref to a list of PID killed, a ref to a list of names killed.
1044            
1045             =head2 list_pids
1046              
1047             my $pid = $f->list_pids;
1048              
1049             This function return a reference to a HASH like
1050              
1051             {
1052             '1458' => {
1053             'pid_file' => '/tmp/fork.3.pid',
1054             'name' => 'new_name.3',
1055             'home' => '/tmp',
1056             'expiration' => '1105369235',
1057             'start_time' => 1104998945
1058             },
1059             '1454' => {
1060             'pid_file' => '/tmp/fork.1.pid',
1061             'name' => 'new_name.1',
1062             'home' => '/tmp'
1063             },
1064             '1456' => {
1065             'pid_file' => '/tmp/fork.2.pid',
1066             'name' => 'new_name.2',
1067             'home' => '/tmp'
1068             }
1069             };
1070              
1071              
1072             The I element in the HASH is only present if we provide the corresponding tag in the constructor B
1073             Same for I element
1074              
1075             =head2 list_names
1076              
1077             my $name = $f->list_names;
1078              
1079             This function return a reference to a HASH like
1080            
1081             {
1082             'new_name.2' => {
1083             'pid_file' => '/tmp/fork.2.pid',
1084             'pid' => 1456,
1085             'home' => '/tmp'
1086             'expiration' => '1104999045',
1087             'start_time' => 1104998945
1088             },
1089             'new_name.3' => {
1090             'pid_file' => '/tmp/fork.3.pid',
1091             'pid' => 1458,
1092             'home' => '/tmp'
1093             },
1094             'new_name.1' => {
1095             'pid_file' => '/tmp/fork.1.pid',
1096             'pid' => 1454,
1097             'home' => '/tmp'
1098             }
1099             };
1100              
1101             The I element in the HASH is only present if we provide the corresponding tag in the constructor B
1102             Same for I element
1103            
1104             =head2 expirate
1105              
1106             my ($n, $dp, n ) =$f->expirate([signal])
1107              
1108             This function test if child reach the expiration time and kill if necessary with the optional signal (default 15).
1109             In scalar context, this function return the number of childs killed.
1110             In array context, this function return the number of childs killed, a ref to a list of PID killed, a ref to a list of names killed.
1111              
1112             =head2 get_expirate
1113              
1114             $f->get_expirate(PID)
1115              
1116             This function return the expiration time for the PID process provided
1117             Be care!!! If called from a child, you could only receive the value of child forked before the child from where you call that function
1118              
1119             =head2 set_expirate
1120              
1121             $f->set_expirate(PID, EXP)
1122              
1123             This function set the expiration time for the PID process provided.
1124             The new expiration time is the value + the present time.
1125             This function is only useable fron main program (not childs)
1126              
1127              
1128             =head2 getmemfree
1129              
1130             $f->getmemfree
1131              
1132             In scalar context, this function return the total free memory (real + swap).
1133             In array context, this function return ( total_memory, real_memory, swap_memory).
1134              
1135            
1136             =head2 pid_nbr
1137              
1138             $f->pid_nbr
1139              
1140             This function return the number of process
1141              
1142             =head2 clean_childs
1143              
1144             my (@pid_removed , @name_removed) =$f->clean_childs
1145            
1146             This function return a ref to a list list of pid(s) and a ref to a list of name(s) removed because no more responding
1147              
1148             =head2 test_pid
1149              
1150             my @state = $f->test_pid(PID);
1151            
1152             In ARRAY context, this function return a ARRAY with
1153             the first element is the status (1 = running and 0 = not running)
1154             the second element is the NAME of process if the process with the PID is present in pid list and running
1155             In SCALAR contect, this function return the status (1 = running and 0 = not running)
1156              
1157             =head2 test_name
1158              
1159             my @state = $f->test_pid(NAME);
1160            
1161             In ARRAY context, this function return a ARRAY with
1162             the first element is the status (1 = running and 0 = not running)
1163             the second element is the PID of the process if the process with the NAME is present in name list and running.
1164             In SCALAR contect, this function return the status (1 = running and 0 = not running)
1165            
1166             =head2 version
1167              
1168             $f->version;
1169              
1170             Return the version number
1171              
1172             =head2 daemonize
1173              
1174             $f->daemonize(
1175             uid=>1000,
1176             gid => 1000,
1177             home => "/tmp",
1178             pid_file => "/tmp/master.pid"
1179             name => "DAEMON"
1180             );
1181            
1182             This function put the main process in daemon mode and detaches it from console
1183             All parameter are optional
1184             The I is always created in absolute path, before any chroot either if I is provided.
1185             After it's creation, the file is chmod according to the provided uid and gig
1186             When process is kill, the pid_file is deleted
1187              
1188             =head3 uid
1189              
1190             =over 3
1191              
1192             the process get this new uid (numerical value)
1193              
1194             =back
1195              
1196             =head3 gid
1197              
1198             =over 3
1199              
1200             the process get this new gid (numerical value)
1201              
1202             =back
1203              
1204             =head3 home
1205              
1206             =over 3
1207              
1208             the path provided become the working directory of the child with a chroot
1209              
1210             =back
1211              
1212             =head3 pid_file
1213              
1214             I specified the path to the pid_file for the child
1215             Be carefull of uid, gid and chroot because the pid_file is created by the child)
1216              
1217             =head3 name
1218              
1219             =over 3
1220              
1221             I is the name for the newly created process (affect new_name to $0 in the child).
1222             A ## (double sharp ) into the name is replaced with the PID of the process created.
1223              
1224             =back
1225              
1226             =head1 RETURN VALUE
1227              
1228             I constructor returns an array of 3 elements:
1229            
1230             1) the numerical value of the status
1231             2) th epid if the fork succeed
1232             3) the text of the status
1233            
1234             the different possible values are:
1235              
1236             [ 0, PID, "success" ];
1237             [ 1, 0, "Can't fork a new process" ];
1238             [ 2, PID, "Can't open PID file" ];
1239             [ 3, PID, "Process already running with same PID" ];
1240             [ 4, 0, "maximun LOAD reached" ];
1241             [ 5, 0, "maximun number of processes reached" ];
1242             [ 6, 0, "error in parameters" ];
1243             [ 7, 0, "No function provided" ];
1244             [ 8, 0 "Can't fork" ];
1245             [ 9, PID, "PID already present in list of PID processes" ];
1246             [ 10, PID, "NAME already present in list of NAME processes" ];
1247             [ 11, 0, "Can't chdir" ];
1248             [ 12, 0 "Can't chroot" ];
1249             [ 13, 0, "Can't become DAEMON" ];
1250             [ 14, PID, "Can't unlink PID file" ];
1251             [ 15, 0, "maximun MEM used reached" ];
1252             [ 16, 16, "Expiration TIMEOUT reached" ];
1253             [ 17, 16, "NO expiration parameter" ];
1254             [ 18, " Don't fork, NAME already present (STRICT mode enbled)" ];
1255             [ 19, " Don't fork, PID_FILE already present (STRICT mode enbled)" ];
1256              
1257             =head1 EXAMPLES
1258              
1259             #!/usr/bin/perl
1260            
1261             use strict;
1262             use Proc::Forking;
1263             use Data::Dumper;
1264             use Cache::FastMmap;
1265            
1266             my $Cache = Cache::FastMmap->new( raw_values => 1 );
1267             my $f = Proc::Forking->new();
1268            
1269             my $nbr = 0;
1270             my $timemout;
1271             my $flag = 1;
1272             $SIG{ INT } = $SIG{ TERM } = sub { $flag = 0; };
1273            
1274             while ( $flag )
1275             {
1276             if ( $nbr < 5 )
1277             {
1278             my $extra = "other parameter";
1279             my ( $status, $pid, $error ) = $f->fork_child(
1280             function => \&func,
1281             name => "new_name.##",
1282             args => [ "hello SOMEONE", ( 300 + rand( 100 ) ), $extra ],
1283             pid_file => "/tmp/fork.##.pid",
1284             # uid => 1000,
1285             # gid => 1000,
1286             # home => "/tmp",
1287             # max_load => 5,
1288             # max_mem => 1850000000,
1289             # expiration_auto => 0,
1290             expiration => 10 + rand( 10 ),
1291             );
1292             if ( $status == 4 ) # if the load become to high
1293             {
1294             print "Max load reached, do a little nap\n";
1295             usleep( 100000 );
1296             next;
1297             }
1298             elsif ( $status ) # if another kind of error
1299             {
1300             print "PID=$pid\t error=$error\n";
1301             }
1302             }
1303             $nbr = $f->pid_nbr;
1304             print "nbr=$nbr\n";
1305            
1306             foreach ( keys %list )
1307             {
1308             my $val = $Cache->get( $_ );
1309             if ( $val )
1310             {
1311             $Cache->remove( $_ );
1312             $f->set_expiration( $_, $val );
1313             print "*********PID=$_ val=$val\n";
1314             }
1315             }
1316             sleep 1;
1317            
1318             my ($n,@dp,@dn)=$f->expirate;
1319             if($n)
1320             {
1321             print Dumper(@dp);
1322             }
1323             }
1324            
1325            
1326            
1327             sub func
1328             {
1329             my $ref = shift;
1330             my @args = @$ref;
1331             my ( $data, $time_out, $sockC ) = @args;
1332             $SIG{ USR1 } = sub { open my $log, ">>/tmp/log.s"; print $log "signal USR1 received\n"; close $log; };
1333             $SIG{ USR2 } = sub { open my $log, ">>/tmp/log.s"; print $log "signal USR2 received for process $$ \n"; close $log; $Cache->set( $$, 123 ); };
1334             if ( !$time_out )
1335             {
1336             $time_out = 3;
1337             }
1338            
1339             open my $FF, ">>/tmp/loglist";
1340             print $FF "$$ free=<" . scalar( $f->getmemfree ) . ">\n";
1341             close $FF;
1342            
1343             while ( 1 )
1344             {
1345             open my $fh, ">>/tmp/log";
1346             if ( defined $fh )
1347             {
1348             print $fh "$$ expiration=<" . $f->get_expiration . ">\n";
1349             print $fh "TMOUT = $time_out " . time . " PID=$$ cwd=" . Cwd::cwd() . " name =$0\n";
1350             $fh->close;
1351             }
1352             sleep $time_out + rand( 5 );
1353             }
1354             }
1355            
1356              
1357              
1358            
1359             =head1 TODO
1360              
1361             =over
1362              
1363             =item *
1364              
1365             May be a kind of IPC
1366              
1367             =item *
1368              
1369             A log, debug and/or syslog part
1370              
1371             =item *
1372              
1373             A good test.pl for the install
1374              
1375             =back
1376              
1377             =head1 AUTHOR
1378              
1379             Fabrice Dulaunoy
1380              
1381             15 July 2009
1382              
1383             =head1 LICENSE
1384              
1385             Under the GNU GPL2
1386              
1387            
1388             This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public
1389             License as published by the Free Software Foundation; either version 2 of the License,
1390             or (at your option) any later version.
1391              
1392             This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
1393             without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1394             See the GNU General Public License for more details.
1395              
1396             You should have received a copy of the GNU General Public License along with this program;
1397             if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
1398              
1399             Proc::Forking Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 DULAUNOY Fabrice Proc::Forking comes with ABSOLUTELY NO WARRANTY;
1400             for details See: L
1401             This is free software, and you are welcome to redistribute it under certain conditions;
1402