File Coverage

blib/lib/HTTP/Server/Daemon.pm
Criterion Covered Total %
statement 134 184 72.8
branch 26 54 48.1
condition 4 7 57.1
subroutine 18 23 78.2
pod 9 9 100.0
total 191 277 68.9


line stmt bran cond sub pod time code
1             package HTTP::Server::Daemon;
2 7     7   28 use strict;
  7         8  
  7         167  
3 7     7   23 use warnings;
  7         14  
  7         139  
4 7     7   5162 use POSIX qw(strftime setsid WNOHANG);
  7         125184  
  7         74  
5 7     7   8570 use Fcntl ':flock';
  7         21  
  7         1022  
6 7     7   7597 use Socket qw(:all);
  7         30231  
  7         12479  
7 7     7   58 use Carp;
  7         14  
  7         416  
8 7     7   6031 use IO::Select;
  7         11309  
  7         398  
9 7     7   49 use File::Basename qw(dirname basename);
  7         20  
  7         3388  
10 7     7   6092 use Data::Dump qw(dump);
  7         45107  
  7         627  
11 7     7   46 use vars qw(@ISA @EXPORT_OK $pipe_write $pipe_write $pipe_write $pipe_status $pipe_read @idle_children %children $min_children $max_children $port $pidfile $quit $caller_package $caller_filename $caller_line $str $str $str @allow_ips @allow_ips);
  7         8  
  7         13912  
12              
13             require Exporter;
14             @ISA = qw(Exporter);
15             @EXPORT_OK = qw(server_perfork_dynamic check_pid become_daemon become_netserver get_msg send_msg peer_info net_filter);
16              
17             our $VERSION = '0.04';
18              
19             =head1 NAME
20              
21             Daemon - Start an Application as a Daemon
22              
23             =head1 SYNOPSIS
24              
25             use HTTP::Server::Daemon qw(check_pid become_daemon);
26              
27             my $child_num = 0;
28             my $quit = 1;
29             my $pidfile = become_daemon(__FILE__);
30             $SIG{CHLD} = sub { while(waitpid(-1,WNOHANG)>0){ $child_num--; } };
31             $SIG{TERM} = $SIG{INT} = sub { unlink $pidfile; $quit--; };
32             while ($quit){
33             #do your things;
34             }
35              
36             =head1 DESCRIPTION
37              
38             Help running an application as a daemon.
39              
40             =head1 METHODS
41              
42             =head2 server_perfork_dynamic($child_func, $port, $min_children, $max_children)
43              
44             Prefork a net server listen on the given port.
45              
46             =cut
47              
48             sub server_perfork_dynamic
49             {
50 4     4 1 48 my $child_func = shift;
51 4         76 my $port = shift;
52 4   50     36 my $min_children = shift || 10;
53 4   50     32 my $max_children = shift || 20;
54              
55 4 50       76 croak "avg1 must be a function.\n" unless ref $child_func eq 'CODE';
56 4 50       200 croak "port must be a munber.\n" unless $port =~ /\d+/;
57 4 50       96 croak "min_children must be a munber.\n" unless $min_children =~ /\d+/;
58 4 50       28 croak "max_children must be a munber.\n" unless $max_children =~ /\d+/;
59              
60 4         8 my $quit = 0;
61 4         16 my %children = ();
62 4         48 my $server = become_netserver($port);
63              
64 4         12 my $pipe_read;
65             my $pipe_write;
66 4         112 pipe($pipe_read, $pipe_write);
67 4         216 my $pipe_status = IO::Select->new($pipe_read);
68              
69 4     0   780 $SIG{CHLD} = sub { while(waitpid(-1,WNOHANG)>0){} };
  0         0  
70 4     1   56 $SIG{HUP} = sub { kill HUP => keys %children; $quit++; };
  1         26  
  1         33  
71              
72 4         24 perfork_child_pipe($server, $pipe_write, $child_func);
73              
74 3         117 while (!$quit)
75             {
76 16 100       419 if ($pipe_status->can_read)
77             {
78 15         2209972 my $pipe_msg;
79 15 50       1752 next unless sysread($pipe_read, $pipe_msg, 4096);
80 15         145 my @pipe_msg = split "\n", $pipe_msg;
81 15         745 foreach (@pipe_msg)
82             {
83 15 50       459 next unless my ($pid, $sta) = /^(\d+)\s*(\w+)$/;
84 15 50       972 if ($sta eq 'exit')
85             {
86 0         0 delete $children{$pid};
87             }
88             else
89             {
90 15         225 $children{$pid} = $sta;
91             }
92             }
93             }
94              
95 16         109 my @idle_children = sort {$a <=> $b} grep {$children{$_} eq 'idle'} keys %children;
  21         118  
  38         1636  
96              
97 16 100       1206 if (@idle_children < $min_children)
    50          
98             {
99 5         125 perfork_child_pipe($server, $pipe_write, $child_func);
100             }
101             elsif(@idle_children > $max_children)
102             {
103 0         0 my @kill_pids = @idle_children[0..@idle_children - $max_children - 1];
104 0         0 my $kill_pid = kill HUP => @kill_pids;
105             }
106             }
107             }
108              
109             =head2 perfork_child_pipe($server_sock, $pipe_write, $child_func_ref, $dead_after_requests_num)
110              
111             Fork a child listen the port.
112             (Internal methods).
113              
114             =cut
115            
116             sub perfork_child_pipe
117             {
118 9     9 1 23 my $server = shift;
119 9         22 my $pipe_write = shift;
120 9         20 my $child_func = shift;
121 9         18 my $max_request = shift;
122 9 50       355 $max_request = int(rand 99) + 9 unless $max_request;
123              
124 9 50       604 croak "function perfork_child_pipe() avg3 must be a function.\n" unless ref $child_func eq 'CODE';
125              
126 9         42259 my $child = fork;
127 9 100       3114 if ($child == 0)
128             {
129 3         157 undef $pipe_status;
130 3         207 undef $pipe_read;
131 3         232 undef @idle_children;
132 3         115 undef %children;
133 3         10 undef $min_children;
134 3         47 undef $max_children;
135 3         57 undef $port;
136 3         156 undef $quit;
137              
138 3         49 my $quit = 0;
139 3         221 my $caller = $0;
140 3     0   506 local $SIG{HUP} = sub {$0 = "$caller busy hup"; $quit++; exit 0;};
  0         0  
  0         0  
  0         0  
141 3   66     174 while(!$quit and $max_request--)
142             {
143 7         50 my $sock;
144 7         5832 syswrite $pipe_write, "$$ idle\n";
145 7         561 $0 = "$caller life=$max_request idle";
146              
147             next unless eval
148 7 100       70 {
149 7     3   168 local $SIG{HUP} = sub {$0 = "$caller idle hup"; $quit++; die;};
  3         58  
  3         32  
  3         195  
150 7         2376426 accept($sock, $server);
151             };
152              
153 4         2981 syswrite $pipe_write, "$$ busy\n";
154 4         73 $0 = "$caller life=$max_request busy";
155 4         132 &$child_func($sock);
156              
157 4         272 close $sock;
158             }
159 3         84 close $server;
160 3         58 syswrite $pipe_write, "$$ exit\n";
161 3         20 close $pipe_write;
162 3         1144 exit 0;
163             }
164             }
165              
166             =head2 become_netserver($port)
167              
168             Let the proccess listen on given port using protocol 'TCP'.
169              
170             =cut
171              
172             sub become_netserver
173             {
174 4     4 1 12 my $port = shift;
175 4         140 my $address = sockaddr_in($port, INADDR_ANY);
176 4         96 my $server;
177 4 50       236 socket($server, AF_INET, SOCK_STREAM, IPPROTO_TCP) || die "socket create: $!\n";
178 4 50       76 setsockopt($server, SOL_SOCKET, SO_REUSEADDR, 1) || die "socket reuse: $!\n";
179 4 50       104 bind($server, $address) || die "socket bind: $!\n";
180 4 50       112 listen($server, SOMAXCONN) || die "socket listen: $!\n";
181 4         28 return $server;
182             }
183              
184             =head2 send_msg($sock)
185              
186             Send msg to sock using protocol 'PON'(Perl Object Notation).
187              
188             =cut
189              
190             sub send_msg
191             {
192 0     0 1 0 my $sock = shift;
193 0         0 my $script = shift;
194 0         0 my $data = shift;
195 0         0 my %str;
196 0         0 $str{'script'} = $script;
197 0         0 $str{'data'} = \%{$data};
  0         0  
198 0         0 $str = dump(%str);
199 0         0 my $str_length = length($str);
200             #print $str;
201 0         0 my $binstr = pack('N', $str_length) . $str;
202 0         0 syswrite($sock, $binstr);
203 0         0 return $str_length;
204             }
205              
206             =head2 get_msg($sock)
207              
208             Receive msg from sock using protocol 'PON'(Perl Object Notation).
209              
210             =cut
211              
212             sub get_msg
213             {
214 0     0 1 0 my $sock = shift;
215 0         0 my $buf = '';
216 0         0 sysread($sock, $buf, 4);
217 0         0 my $msg_length = unpack('N',$buf);
218 0         0 sysread($sock, $buf, $msg_length);
219 0         0 return eval($buf);
220             }
221              
222             =head2 peer_info($sock)
223              
224             Return ($peer_port, $peer_ip).
225              
226             =cut
227              
228             sub peer_info
229             {
230 4     4 1 16 my $sock = shift;
231 4         64 my $hersockaddr = getpeername $sock;
232 4         88 my ($peer_port, $heraddr) = sockaddr_in($hersockaddr);
233 4         144 my $peer_ip = inet_ntoa($heraddr);
234 4         71 return ($peer_port, $peer_ip);
235             }
236              
237             =head2 net_filter($sock)
238              
239             Enable white list net filter.
240             Allow only ip list in 'conf/allowip.conf' access, return 0.
241             Others return 'deny'.
242              
243             =cut
244              
245             sub net_filter
246             {
247 0     0 1 0 my $sock = shift;
248 0         0 my ($peer_port, $peer_ip) = peer_info($sock);
249 0 0       0 return 'deny' if $peer_ip eq '255.255.255.255';
250              
251 0         0 my $ip_conf = `cat conf/allow_ip.conf conf/captain_ip.conf`;
252 0         0 @allow_ips = split "\n",$ip_conf;
253 0         0 foreach (@allow_ips)
254             {
255 0 0       0 next if $_ =~ /^#/;
256 0 0       0 next if $_ =~ /^\s+$/;
257 0 0       0 return 0 if $_ eq $peer_ip;
258             }
259 0         0 return 'deny';
260             }
261              
262             =head2 check_pid($invoker_name)
263              
264             Deal with pid file things. Can be used independently.
265              
266             =cut
267              
268             sub check_pid
269             {
270 4     4 1 48 my $invoker = shift;
271 4         224 my $pidfile = basename($invoker) . ".pid";
272 4 50       172 if (-e $pidfile)
273             {
274 0         0 open my $pidfh,"<",$pidfile;
275 0         0 my $pid = <$pidfh>;
276 0         0 close $pidfh;
277 0 0       0 if (kill 0 => $pid)
278             {
279 0         0 print "$invoker is serving in company, programme exit.\n";
280 0         0 exit;
281             }
282             else
283             {
284 0         0 print "pid file exist, try to unlink it.\n";
285 0 0       0 if (unlink $pidfile)
286             {
287 0         0 print "pid file unlinked.\n";
288 0         0 print "$invoker on posion.\n";
289 0         0 open my $pidfh,">",$pidfile;
290 0         0 print $pidfh $$;
291 0         0 close $pidfh;
292             }
293             else
294             {
295 0         0 print "pid file unlink failed.\n";
296 0         0 exit 0;
297             }
298             }
299             }
300             else
301             {
302 4         1476 print "$invoker on posion.\n";
303 4         1156 open my $pidfh,">",$pidfile;
304 4         140 print $pidfh $$;
305 4         356 close $pidfh;
306             }
307 4         100 return $pidfile;
308             }
309              
310             =head2 become_daemon($invoker_name)
311              
312             Let the proccess become a daemon. Can be used independently.
313              
314             =cut
315              
316             sub become_daemon
317             {
318 5     5 1 20 my $invoker = shift;
319 5 50       35423 defined (my $child = fork) or die "Can`t fork: $!";
320 5 100       2253 exit 0 if $child;
321 4         1760 setsid();
322              
323 4         29620 my $rootdir = `pwd`;
324 4         172 chomp $rootdir;
325 4         1312 my $caller_filename = basename($invoker);
326 4         496 $0 = $rootdir."/".$caller_filename;
327              
328             #open(STDOUT, ">/dev/null");
329             #open(STDERR, ">/dev/null");
330 4         344 open(STDIN, "
331 4         128 chdir($rootdir);
332 4         100 umask(0);
333 4         468 $ENV{PATH} = "/sbin:/bin:/usr/sbin:/usr/bin";
334 4         92 return check_pid($invoker);
335             }
336              
337             1;
338              
339             =head1 AUTHOR
340              
341             Written by ChenGang, yikuyiku.com@gmail.com
342              
343             L
344              
345              
346             =head1 COPYRIGHT
347              
348             Copyright (c) 2011 ChenGang.
349             This library is free software; you can redistribute it and/or
350             modify it under the same terms as Perl itself.
351              
352              
353             =head1 SEE ALSO
354              
355             L, L
356              
357             =cut
358