File Coverage

mylib/ForkingDaemon.pm
Criterion Covered Total %
statement 140 145 96.5
branch 52 86 60.4
condition 5 14 35.7
subroutine 24 24 100.0
pod n/a
total 221 269 82.1


line stmt bran cond sub pod time code
1             # vim: ts=2 sw=2 expandtab
2 32     32   3866960 use strict; use warnings;
  32     32   41  
  32         932  
  32         93  
  32         38  
  32         1467  
3              
4             # companion to t/90_regression/rt65460-forking.t
5              
6 32     32   13253 use POE::Filter::Reference;
  32         89  
  32         704  
7 32     32   14739 use IO::Handle;
  32         162011  
  32         1521  
8 32     32   13293 use POSIX;
  32         167452  
  32         125  
9 32     32   70016 use Carp;
  32         35  
  32         40732  
10              
11             my $debug = 0;
12              
13             main();
14              
15             # basically ripped off from SimpleDBI::SubProcess
16             sub main {
17             # Autoflush to avoid weirdness
18 32     32   97 $|++;
19              
20             # set binmode, thanks RT #43442
21 32         52 binmode( STDIN );
22 32         40 binmode( STDOUT );
23              
24 32         303 my $filter = POE::Filter::Reference->new();
25              
26             # Okay, now we listen for commands from our parent :)
27 32         480 while ( sysread( STDIN, my $buffer = '', 1024 ) ) {
28             # Feed the line into the filter
29 32         117 my $data = $filter->get( [ $buffer ] );
30              
31             # Process each data structure
32 32         74 foreach my $input ( @$data ) {
33             # should be hashref with data
34 32 50       91 if ( $input->{debug} ) {
35 0         0 $debug = 1;
36             # enable tracing/asserts
37 0         0 eval "sub POE::Kernel::TRACE_DEFAULT () { 1 };sub POE::Kernel::ASSERT_DEFAULT () { 1 };";
38 0 0       0 die $@ if $@;
39             }
40              
41 32         119 do_test( $input->{file}, $input->{timing}, $input->{forked}, $input->{type} );
42 12         0 CORE::exit( 0 );
43             }
44             }
45             }
46              
47             sub do_test {
48 32     32   151 my ($file,$timing,$forked,$type) = @_;
49              
50 32         184 my $oldpid = $$;
51              
52             # hook into warnings/die
53             my $handler = sub {
54 352     352   642 my $l = $_[0];
55 352         15600 $l =~ s/(?:\r|\n)+$//;
56 352 50       12886 open my $fh, '>>', $file or die "Unable to open $file: $!";
57 352         1869 $fh->autoflush( 1 );
58 352         24164 print $fh "$l\n";
59 352         3100 close $fh;
60 352         5529 return;
61 32         126 };
62 32         432 $SIG{'__WARN__'} = $handler;
63 32         67 $SIG{'__DIE__'} = $handler;
64              
65             # Load POE before daemonizing or after?
66 32 100       256 if ( $timing eq 'before' ) {
67 16     22   1071 eval "use POE; use POE::Session;";
  22     22   15385  
  22         119  
  22         125  
  22         110  
  22         25  
  22         49  
68 16 50       54 die $@ if $@;
69             }
70              
71             # Okay, we daemonize before running POE
72 32         82 do_daemonize( $type );
73              
74 12 100       181 if ( $timing eq 'after' ) {
75 6         2690 eval "use POE; use POE::Session;";
76 6 50       23 die $@ if $@;
77             }
78              
79             # Now we inform our test harness the PID
80 12 50       1324 open my $fh, '>>', $file or die "Unable to open $file: $!";
81 12         498 $fh->autoflush( 1 );
82 12         2270 print $fh "OLDPID $oldpid\n";
83 12         469 print $fh "PID $$\n";
84              
85             # start POE and do the test!
86 12 100       345 POE::Kernel->has_forked if $forked eq 'has_fork';
87 12         83 start_poe();
88              
89             # POE finished running, inform our test harness
90 12         816 print $fh "DONE\n";
91 12         157 close $fh;
92 12         151 return;
93             }
94              
95             sub do_daemonize {
96 32     32   45 my $type = shift;
97              
98 32         43 eval {
99 32 100       119 if ( $type eq 'nsd' ) {
    100          
    50          
100 8         22 nsd_daemonize();
101             } elsif ( $type eq 'dd' ) {
102 12         33 dd_daemonize();
103             } elsif ( $type eq 'mxd' ) {
104 12         27 mxd_daemonize();
105             } else {
106 0         0 die "Unknown daemonization method: $type";
107             }
108             };
109 12 50       218 die $@ if $@;
110 12         100 return;
111             }
112              
113             sub start_poe {
114             # start POE with a basic test to see if it handled the daemonization
115             POE::Session->create(
116             inline_states => {
117             _start => sub {
118 12 50   12   39 warn "STARTING TEST" if $debug;
119 12         111 $POE::Kernel::poe_kernel->yield( "do_test" );
120 12         53 return;
121             },
122             do_test => sub {
123 12 50   12   42 warn "STARTING DELAY" if $debug;
124 12         131 $POE::Kernel::poe_kernel->delay( "done" => 1 );
125 12         32 return;
126             },
127             done => sub {
128 12 50   12   159 warn "DONE WITH DELAY" if $debug;
129 12         47 return;
130             },
131             },
132 12     12   835 );
133              
134 12         161 POE::Kernel->run;
135              
136 12         32 return;
137             }
138              
139             # the rest of the code in this file is
140             # ripped off from Net::Server::Daemonize v0.05 as it does single-fork
141             # Removed some unnecessary code like pidfile/uid/gid/chdir stuff
142              
143             ### routine to protect process during fork
144             sub safe_fork () {
145              
146             ### block signal for fork
147 8     8   50 my $sigset = POSIX::SigSet->new(SIGINT);
148 8 50       98 POSIX::sigprocmask(SIG_BLOCK, $sigset)
149             or die "Can't block SIGINT for fork: [$!]\n";
150              
151             ### fork off a child
152 8         16071 my $pid = fork;
153 8 50       914 unless( defined $pid ){
154 0         0 die "Couldn't fork: [$!]\n";
155             }
156              
157             ### make SIGINT kill us as it did before
158 8         586 $SIG{INT} = 'DEFAULT';
159              
160             ### put back to normal
161 8 50       601 POSIX::sigprocmask(SIG_UNBLOCK, $sigset)
162             or die "Can't unblock SIGINT for fork: [$!]\n";
163              
164 8         890 return $pid;
165             }
166              
167             ### routine to completely dissociate from
168             ### terminal process.
169             sub nsd_daemonize {
170 8     8   16 my $pid = safe_fork();
171              
172             ### parent process should do the pid file and exit
173 8 100       226 if( $pid ){
174              
175 4 50       0 $pid && CORE::exit(0);
176              
177              
178             ### child process will continue on
179             }else{
180             ### close all input/output and separate
181             ### from the parent process group
182 4 50       548 open STDIN, '
183 4 50       245 open STDOUT, '>/dev/null' or die "Can't open STDOUT to /dev/null: [$!]\n";
184 4 50       219 open STDERR, '>&STDOUT' or die "Can't open STDERR to STDOUT: [$!]\n";
185              
186             ### Turn process into session leader, and ensure no controlling terminal
187 4         532 POSIX::setsid();
188              
189 4         136 return 1;
190             }
191             }
192              
193             # the rest of the code in this file is
194             # ripped off from Daemon::Daemonize v0.0052 as it does double-fork
195             # Removed some unnecessary code like pidfile/chdir stuff
196              
197             sub _fork_or_die {
198 20     20   34311 my $pid = fork;
199 20 50       1327 confess "Unable to fork" unless defined $pid;
200 20         1196 return $pid;
201             }
202              
203             sub superclose {
204 4   50 4   228 my $from = shift || 0;
205              
206 4         255 my $openmax = POSIX::sysconf( &POSIX::_SC_OPEN_MAX );
207 4 50 33     175 $openmax = 64 if ! defined( $openmax ) || $openmax < 0;
208              
209 4 50       81 return unless $from < $openmax;
210              
211 4         4670 POSIX::close( $_ ) foreach ($from .. $openmax - 1);
212             }
213              
214             sub dd_daemonize {
215 12     12   18 my $close = 1;
216              
217             # Fork once to go into the background
218             {
219 12 100       12 if ( my $pid = _fork_or_die() ) {
  12         24  
220 4         0 CORE::exit 0;
221             }
222             }
223              
224             # Create new session
225 8 50       1304 (POSIX::setsid)
226             || confess "Cannot detach from controlling process";
227              
228             # Fork again to ensure that daemon never reacquires a control terminal
229 8 100       384 _fork_or_die() && CORE::exit 0;
230              
231             # Clear the file creation mask
232 4         204 umask 0;
233              
234 4 50 33     328 if ( $close eq 1 || $close eq '!std' ) {
235             # Close any open file descriptors
236 4 50       386 superclose( $close eq '!std' ? 3 : 0 );
237             }
238              
239 4 50 33     94 if ( $close eq 1 || $close eq 'std' ) {
240             # Re-open STDIN, STDOUT, STDERR to /dev/null
241 4 50       372 open( STDIN, "+>/dev/null" ) or confess "Could not redirect STDIN to /dev/null";
242              
243 4 50       191 open( STDOUT, "+>&STDIN" ) or confess "Could not redirect STDOUT to /dev/null";
244              
245 4 50       139 open( STDERR, "+>&STDIN" ) or confess "Could not redirect STDERR to /dev/null";
246              
247             # Avoid 'stdin reopened for output' warning (taken from MooseX::Daemonize)
248 4         47 local *_NIL;
249 4         554 open( _NIL, '/dev/null' );
250 4         72 <_NIL> if 0;
251             }
252              
253 4         65 return 1;
254             }
255              
256             # the rest of the code in this file is
257             # ripped off from MooseX::Daemonize::Core v0.12 as it does some weird things ;)
258             # Removed some unnecessary code like Moose stuff
259              
260             sub daemon_fork {
261 12 100   12   24504 if (my $pid = fork) {
262 4         0 CORE::exit( 0 );
263             } else {
264             # now in the daemon
265 8         522 return;
266             }
267             }
268              
269             sub daemon_detach {
270 8 50   8   1880 (POSIX::setsid) # set session id
271             || confess "Cannot detach from controlling process";
272             {
273 8         162 $SIG{'HUP'} = 'IGNORE';
  8         746  
274 8 100       6017 fork && CORE::exit;
275             }
276 4         368 umask 0; # clear the file creation mask
277              
278             # get the max numnber of possible file descriptors
279 4         498 my $openmax = POSIX::sysconf( &POSIX::_SC_OPEN_MAX );
280 4 50 33     461 $openmax = 64 if !defined($openmax) || $openmax < 0;
281              
282             # close them all
283 4         5083 POSIX::close($_) foreach (0 .. $openmax);
284              
285             # fixup STDIN ...
286              
287 4 50       485 open(STDIN, "+>/dev/null")
288             or confess "Could not redirect STDOUT to /dev/null";
289              
290             # fixup STDOUT ...
291              
292 4 50       292 open(STDOUT, "+>&STDIN")
293             or confess "Could not redirect STDOUT to /dev/null";
294              
295             # fixup STDERR ...
296              
297 4 50       133 open(STDERR, "+>&STDIN")
298             or confess "Could not redirect STDERR to /dev/null"; ;
299              
300             # do a little house cleaning ...
301              
302             # Avoid 'stdin reopened for output'
303             # warning with newer perls
304 4         194 open( NULLFH, '/dev/null' );
305 4         60 if (0);
306              
307             # return success
308 4         80 return 1;
309             }
310              
311             sub mxd_daemonize {
312 12     12   30 daemon_fork();
313 8         268 daemon_detach();
314             }