File Coverage

blib/lib/Net/Server.pm
Criterion Covered Total %
statement 406 688 59.0
branch 188 418 44.9
condition 77 192 40.1
subroutine 66 101 65.3
pod 39 72 54.1
total 776 1471 52.7


line stmt bran cond sub pod time code
1             # -*- perl -*-
2             #
3             # Net::Server
4             # ABSTRACT: Extensible Perl internet server
5             #
6             # Copyright (C) 2001-2017
7             #
8             # Paul Seamons
9             #
10             # Rob Brown
11             #
12             # This package may be distributed under the terms of either the
13             # GNU General Public License
14             # or the
15             # Perl Artistic License
16             #
17             # All rights reserved.
18             #
19             ################################################################
20              
21             package Net::Server;
22              
23 28     28   68829 use strict;
  28         102  
  28         1367  
24 28     28   184 use Socket qw(AF_INET AF_UNIX SOCK_DGRAM SOCK_STREAM);
  28         67  
  28         14746  
25 28     28   250 use IO::Socket ();
  28         84  
  28         615  
26 28     28   1749 use IO::Select ();
  28         5458  
  28         1128  
27 28     28   15006 use POSIX ();
  28         187986  
  28         1085  
28 28     28   2136 use Net::Server::Proto ();
  28         60  
  28         766  
29 28         293575 use Net::Server::Daemonize qw(check_pid_file create_pid_file safe_fork
30 28     28   15347 get_uid get_gid set_uid set_gid);
  28         82  
31              
32             our $VERSION = '2.010';
33              
34             sub new {
35 71   50 71 1 1255 my $class = shift || die "Missing class";
36 71 100       1379 my $args = @_ == 1 ? shift : {@_};
37 71         719 return bless {server => {%$args}}, $class;
38             }
39              
40 62     62 0 123 sub net_server_type { __PACKAGE__ }
41 0     0 0 0 sub get_property { $_[0]->{'server'}->{$_[1]} }
42 0     0 0 0 sub set_property { $_[0]->{'server'}->{$_[1]} = $_[2] }
43              
44             sub run {
45 70 100   70 1 37735 my $self = ref($_[0]) ? shift() : shift->new; # pass package or object
46 70 100       1450 $self->{'server'}->{'_run_args'} = [@_ == 1 ? %{$_[0]} : @_];
  3         12  
47 70         689 $self->_initialize; # configure all parameters
48              
49 70         445 $self->post_configure; # verification of passed parameters
50 70         302 $self->post_configure_hook; # user customizable hook
51              
52 70         415 $self->pre_bind; # finalize ports to be bound
53 70         574 $self->bind; # connect to port(s), setup selection handle for multi port
54 70         1431 $self->post_bind_hook; # user customizable hook
55 70         302 $self->post_bind; # allow for chrooting, becoming a different user and group
56              
57 70         383 $self->pre_loop_hook; # user customizable hook
58 70         358 $self->loop; # repeat accept/process cycle
59              
60 59         223 $self->server_close; # close the server and release the port
61             }
62              
63             sub run_client_connection {
64 13     13 1 28 my $self = shift;
65 13         116 my $c = $self->{'server'}->{'client'};
66              
67 13         174 $self->post_accept($c); # prepare client for processing
68 13         135 $self->get_client_info($c); # determines information about peer and local
69 13         157 $self->post_accept_hook($c); # user customizable hook
70              
71 13   33     94 my $ok = $self->allow_deny($c) && $self->allow_deny_hook($c); # do allow/deny check on client info
72 13 50       125 if ($ok) {
73 13         84 $self->process_request($c); # This is where the core functionality of a Net::Server should be.
74             } else {
75 0         0 $self->request_denied_hook($c); # user customizable hook
76             }
77              
78 6         255 $self->post_process_request_hook($ok); # user customizable hook
79 6         54 $self->post_process_request; # clean up client connection, etc
80 6         307 $self->post_client_connection_hook; # one last hook
81             }
82              
83             ###----------------------------------------------------------------###
84              
85             sub _initialize {
86 73     73   254 my $self = shift;
87 73   50     532 my $prop = $self->{'server'} ||= {};
88              
89 73 100       194 $self->commandline($self->_get_commandline) if ! eval { local $SIG{__DIE__}; $self->commandline }; # save for a HUP
  73         833  
  73         648  
90 73         612 $self->configure_hook; # user customizable hook
91 73         521 $self->configure; # allow for reading of commandline, program, and configuration file parameters
92              
93 73 50       326 my @defaults = %{ $self->default_values || {} }; # allow yet another way to pass defaults
  73         177  
94 73 100       384 $self->process_args(\@defaults) if @defaults;
95             }
96              
97             sub commandline {
98 143     143 0 302 my $self = shift;
99 143 50       894 $self->{'server'}->{'commandline'} = ref($_[0]) ? shift : \@_ if @_;
    100          
100 143   100     1753 return $self->{'server'}->{'commandline'} || die "commandline was not set during initialization";
101             }
102              
103             sub _get_commandline {
104 70     70   236 my $self = shift;
105 70         375 my $script = $0;
106 70 50 33     1607 $script = $ENV{'PWD'} .'/'. $script if $script =~ m|^[^/]+/| && $ENV{'PWD'}; # add absolute to relative - avoid Cwd
107 70         420 $script =~ /^(.+)$/; # untaint for later use in hup
108 70         940 return [$1, @ARGV]
109             }
110              
111       73 1   sub configure_hook {}
112              
113             sub configure {
114 100     100 1 246 my $self = shift;
115 100         278 my $prop = $self->{'server'};
116 100 100 66     459 my $template = ($_[0] && ref($_[0])) ? shift : undef;
117              
118 100 100       489 $self->process_args(\@ARGV, $template) if @ARGV; # command line
119 100 50       879 $self->process_args($prop->{'_run_args'}, $template) if $prop->{'_run_args'}; # passed to run
120              
121 100 100       300 if ($prop->{'conf_file'}) {
122 8         25 $self->process_args($self->_read_conf($prop->{'conf_file'}), $template);
123             } else {
124 92   50     659 my $def = $self->default_values || {};
125 92 100       379 $self->process_args($self->_read_conf($def->{'conf_file'}), $template) if $def->{'conf_file'};
126             }
127             }
128              
129 133     133 1 427 sub default_values { {} }
130              
131             sub post_configure {
132 70     70 1 137 my $self = shift;
133 70         145 my $prop = $self->{'server'};
134              
135 70 50 33     264 $prop->{'log_level'} = 2 if ! defined($prop->{'log_level'}) || $prop->{'log_level'} !~ /^\d+$/;
136 70 50       206 $prop->{'log_level'} = 4 if $prop->{'log_level'} > 4;
137 70         468 $self->initialize_logging;
138              
139 70 50       193 if ($prop->{'pid_file'}) { # see if a daemon is already running
140 0 0       0 if (! eval{ check_pid_file($prop->{'pid_file'}) }) {
  0         0  
141 0 0       0 warn $@ if !$ENV{'BOUND_SOCKETS'};
142 0         0 $self->fatal(my $e = $@);
143             }
144             }
145              
146 70 100       181 if (! $prop->{'_is_inet'}) { # completetly daemonize by closing STDIN, STDOUT (should be done before fork)
147 69 50 33     505 if ($prop->{'setsid'} || length($prop->{'log_file'})) {
148 0 0       0 open(STDIN, '<', '/dev/null') || die "Cannot read /dev/null [$!]";
149 0 0       0 open(STDOUT, '>', '/dev/null') || die "Cannot write /dev/null [$!]";
150             }
151             }
152              
153 70 50       292 if (!$ENV{'BOUND_SOCKETS'}) { # don't need to redo this if hup'ing
154 70 50 33     409 if ($prop->{'setsid'} || $prop->{'background'}) {
155 0         0 my $pid = eval { safe_fork() };
  0         0  
156 0 0       0 $self->fatal(my $e = $@) if ! defined $pid;
157 0 0       0 exit(0) if $pid;
158 0         0 $self->log(2, "Process Backgrounded");
159             }
160              
161 70 50       186 POSIX::setsid() if $prop->{'setsid'}; # completely remove myself from parent process
162             }
163              
164 70 50 33     345 if (length($prop->{'log_file'})
    50          
165             && !$prop->{'log_function'}) {
166 0         0 open STDERR, '>&_SERVER_LOG' || die "Cannot open STDERR to _SERVER_LOG [$!]";
167             } elsif ($prop->{'setsid'}) { # completely daemonize by closing STDERR (should be done after fork)
168 0         0 open STDERR, '>&STDOUT' || die "Cannot open STDERR to STDOUT [$!]";
169             }
170              
171             # allow for a pid file (must be done after backgrounding and chrooting)
172             # Remove of this pid may fail after a chroot to another location... however it doesn't interfere either.
173 70 50       232 if ($prop->{'pid_file'}) {
174 0 0       0 if (eval { create_pid_file($prop->{'pid_file'}) }) {
  0         0  
175 0         0 $prop->{'pid_file_unlink'} = 1;
176             } else {
177 0         0 $self->fatal(my $e = $@);
178             }
179             }
180              
181             # make sure that allow and deny look like array refs
182 70         308 $prop->{$_} = [] for grep {! ref $prop->{$_}} qw(allow deny cidr_allow cidr_deny);
  280         776  
183             }
184              
185             sub initialize_logging {
186 70     70 0 139 my $self = shift;
187 70         287 my $prop = $self->{'server'};
188 70 50       194 if (! defined($prop->{'log_file'})) {
189 70         171 $prop->{'log_file'} = ''; # log to STDERR
190 70         164 return;
191             }
192              
193             # pluggable logging
194 0 0       0 if ($prop->{'log_file'} =~ /^([a-zA-Z]\w*(?:::[a-zA-Z]\w*)*)$/) {
195 0         0 my $pkg = "Net::Server::Log::$prop->{'log_file'}";
196 0         0 (my $file = "$pkg.pm") =~ s|::|/|g;
197 0 0 0     0 if (eval { require $file }) {
  0 0       0  
198 0         0 $prop->{'log_function'} = $pkg->initialize($self);
199 0         0 $prop->{'log_class'} = $pkg;
200 0         0 return;
201 0         0 } elsif ($file =~ /::/ || grep {-e "$_/$file"} @INC) {
202 0         0 $self->fatal("Unable to load log module $pkg from file $file: $@");
203             }
204             }
205              
206             # regular file based logging
207 0 0       0 die "Unsecure filename \"$prop->{'log_file'}\"" if $prop->{'log_file'} !~ m|^([\:\w\.\-/\\]+)$|;
208 0         0 $prop->{'log_file'} = $1; # open a logging file
209 0 0       0 open(_SERVER_LOG, ">>", $prop->{'log_file'})
210             || die "Couldn't open log file \"$prop->{'log_file'}\" [$!].";
211 0         0 _SERVER_LOG->autoflush(1);
212 0         0 push @{ $prop->{'chown_files'} }, $prop->{'log_file'};
  0         0  
213             }
214              
215       70 1   sub post_configure_hook {}
216              
217 69     69   353 sub _server_type { ref($_[0]) }
218              
219             sub pre_bind { # make sure we have good port parameters
220 69     69 1 129 my $self = shift;
221 69         133 my $prop = $self->{'server'};
222              
223 69         292 my $super = $self->net_server_type;
224 69         261 my $type = $self->_server_type;
225 69 100       620 if ($self->isa('Net::Server::MultiType')) {
226 3   33     62 my $base = delete($prop->{'_recursive_multitype'}) || Net::Server::MultiType->net_server_type;
227 3         15 $super = "$super -> MultiType -> $base";
228             }
229 69 50       321 $type .= " (type $super)" if $type ne $super;
230 69         617 $self->log(2, $self->log_time ." $type starting! pid($$)");
231              
232 69         203 $prop->{'sock'} = [grep {$_} map { $self->proto_object($_) } @{ $self->prepared_ports }];
  100         318  
  100         448  
  69         356  
233 69 50       123 $self->fatal("No valid socket parameters found") if ! @{ $prop->{'sock'} };
  69         239  
234             }
235              
236             sub prepared_ports {
237 69     69 0 139 my $self = shift;
238 69         132 my $prop = $self->{'server'};
239              
240 69         288 my ($ports, $hosts, $protos, $ipvs) = @$prop{qw(port host proto ipv)};
241 69   33     201 $ports ||= $prop->{'ports'};
242 69 100 66     609 if (!defined($ports) || (ref($ports) && !@$ports)) {
      66        
243 7         60 $ports = $self->default_port;
244 7 50 33     40 if (!defined($ports) || (ref($ports) && !@$ports)) {
      33        
245 0         0 $ports = default_port();
246 0         0 $self->log(2, "Port Not Defined. Defaulting to '$ports'");
247             }
248             }
249              
250 69         127 my %bound;
251 69         273 my $bind = $prop->{'_bind'} = [];
252 69 100       337 for my $_port (ref($ports) ? @$ports : $ports) {
253 100 100       365 my $_host = ref($hosts) ? $hosts->[ @$bind >= @$hosts ? -1 : $#$bind + 1] : $hosts; # if ports are greater than hosts - augment with the last host
    50          
254 100 100       336 my $_proto = ref($protos) ? $protos->[@$bind >= @$protos ? -1 : $#$bind + 1] : $protos;
    50          
255 100 100       342 my $_ipv = ref($ipvs) ? $ipvs->[ @$bind >= @$ipvs ? -1 : $#$bind + 1] : $ipvs;
    50          
256 100         508 foreach my $info ($self->port_info($_port, $_host, $_proto, $_ipv)) {
257 100         331 my ($port, $host, $proto, $ipv) = @$info{qw(port host proto ipv)}; # use cleaned values
258 100 50 33     763 if ($port ne "0" && $bound{"$host\e$port\e$proto\e$ipv"}++) {
259 0         0 $self->log(2, "Duplicate configuration (\U$proto\E) on [$host]:$port with IPv$ipv) - skipping");
260 0         0 next;
261             }
262 100         317 push @$bind, $info;
263             }
264             }
265              
266 69         257 return $bind;
267             }
268              
269             sub port_info {
270 100     100 0 295 my ($self, $port, $host, $proto, $ipv) = @_;
271 100         981 return Net::Server::Proto->parse_info($port, $host, $proto, $ipv, $self);
272             }
273              
274             sub proto_object {
275 100     100 0 234 my ($self, $info) = @_;
276 100         533 return Net::Server::Proto->object($info, $self);
277             }
278              
279             sub bind { # bind to the port (This should serve all but INET)
280 12     12 1 35 my $self = shift;
281 12         54 my $prop = $self->{'server'};
282              
283 12 50       81 if (exists $ENV{'BOUND_SOCKETS'}) {
284 0         0 $self->restart_open_hook;
285 0         0 $self->log(2, "Binding open file descriptors");
286 0         0 my %map;
287 0         0 foreach my $info (split /\s*;\s*/, $ENV{'BOUND_SOCKETS'}) {
288 0         0 my ($fd, $host, $port, $proto, $ipv, $orig) = split /\|/, $info;
289 0 0       0 $orig = $port if ! defined $orig; # allow for things like service ports or port 0
290 0 0       0 $fd = ($fd =~ /^(\d+)$/) ? $1 : $self->fatal("Bad file descriptor");
291 0         0 $map{"$host|$orig|$proto|$ipv"}->{$fd} = $port;
292             }
293 0         0 foreach my $sock (@{ $prop->{'sock'} }) {
  0         0  
294 0         0 $sock->log_connect($self);
295 0 0       0 if (my $ref = $map{$sock->hup_string}) {
296 0         0 my ($fd, $port) = each %$ref;
297 0         0 $sock->reconnect($fd, $self, $port);
298 0         0 delete $ref->{$fd};
299 0 0       0 delete $map{$sock->hup_string} if ! keys %$ref;
300             } else {
301 0         0 $self->log(2, "Added new port configuration");
302 0         0 $sock->connect($self);
303             }
304             }
305 0         0 foreach my $str (keys %map) {
306 0         0 foreach my $fd (keys %{ $map{$str} }) {
  0         0  
307 0         0 $self->log(2, "Closing un-mapped port ($str) on fd $fd");
308 0         0 POSIX::close($fd);
309             }
310             }
311 0         0 delete $ENV{'BOUND_SOCKETS'};
312 0         0 $self->{'hup_waitpid'} = 1;
313              
314             } else { # connect to fresh ports
315 12         31 foreach my $sock (@{ $prop->{'sock'} }) {
  12         118  
316 18         115 $sock->log_connect($self);
317 18         92 $sock->connect($self);
318             }
319             }
320              
321 12 100 100     38 if (@{ $prop->{'sock'} } > 1 || $prop->{'multi_port'}) {
  12         181  
322 7         33 $prop->{'multi_port'} = 1;
323 7         172 $prop->{'select'} = IO::Select->new; # if more than one socket we'll need to select on it
324 7         132 $prop->{'select'}->add($_) for @{ $prop->{'sock'} };
  7         54  
325             } else {
326 5         23 $prop->{'multi_port'} = undef;
327 5         31 $prop->{'select'} = undef;
328             }
329             }
330              
331       70 1   sub post_bind_hook {}
332              
333              
334             sub post_bind { # secure the process and background it
335 13     13 1 53 my $self = shift;
336 13         119 my $prop = $self->{'server'};
337              
338 13 50       79 if (! defined $prop->{'group'}) {
    0          
339 13         475 $self->log(1, "Group Not Defined. Defaulting to EGID '$)'");
340 13         131 $prop->{'group'} = $);
341             } elsif ($prop->{'group'} =~ /^([\w-]+(?: [\w-]+)*)$/) {
342 0         0 $prop->{'group'} = eval { get_gid($1) };
  0         0  
343 0 0       0 $self->fatal(my $e = $@) if $@;
344             } else {
345 0         0 $self->fatal("Invalid group \"$prop->{'group'}\"");
346             }
347              
348 13 50       89 if (! defined $prop->{'user'}) {
    0          
349 13         252 $self->log(1, "User Not Defined. Defaulting to EUID '$>'");
350 13         121 $prop->{'user'} = $>;
351             } elsif ($prop->{'user'} =~ /^([\w-]+)$/) {
352 0         0 $prop->{'user'} = eval { get_uid($1) };
  0         0  
353 0 0       0 $self->fatal(my $e = $@) if $@;
354             } else {
355 0         0 $self->fatal("Invalid user \"$prop->{'user'}\"");
356             }
357              
358             # chown any files or sockets that we need to
359 13 50 33     391 if ($prop->{'group'} ne $) || $prop->{'user'} ne $>) {
360 0         0 my @chown_files;
361 0         0 push @chown_files, map {$_->NS_port} grep {$_->NS_proto =~ /^UNIX/} @{ $prop->{'sock'} };
  0         0  
  0         0  
  0         0  
362 0 0       0 push @chown_files, $prop->{'pid_file'} if $prop->{'pid_file_unlink'};
363 0 0       0 push @chown_files, $prop->{'lock_file'} if $prop->{'lock_file_unlink'};
364 0 0       0 push @chown_files, @{ $prop->{'chown_files'} || [] };
  0         0  
365 0         0 my $uid = $prop->{'user'};
366 0         0 my $gid = (split /\ /, $prop->{'group'})[0];
367 0         0 foreach my $file (@chown_files){
368 0 0       0 chown($uid, $gid, $file) || $self->fatal("Couldn't chown \"$file\" [$!]");
369             }
370             }
371              
372 13 50       83 if ($prop->{'chroot'}) {
373 0 0       0 $self->fatal("Specified chroot \"$prop->{'chroot'}\" doesn't exist.") if ! -d $prop->{'chroot'};
374 0         0 $self->log(2, "Chrooting to $prop->{'chroot'}");
375 0 0       0 chroot($prop->{'chroot'}) || $self->fatal("Couldn't chroot to \"$prop->{'chroot'}\": $!");
376             }
377              
378             # drop privileges
379 13         39 eval {
380 13 50       139 if ($prop->{'group'} ne $)) {
381 0         0 $self->log(2, "Setting gid to \"$prop->{'group'}\"");
382 0         0 set_gid($prop->{'group'} );
383             }
384 13 50       168 if ($prop->{'user'} ne $>) {
385 0         0 $self->log(2, "Setting uid to \"$prop->{'user'}\"");
386 0         0 set_uid($prop->{'user'});
387             }
388             };
389 13 50       63 if ($@) {
390 0 0       0 if ($> == 0) {
    0          
391 0         0 $self->fatal(my $e = $@);
392             } elsif ($< == 0) {
393 0         0 $self->log(2, "NOTICE: Effective UID changed, but Real UID is 0: $@");
394             } else {
395 0         0 $self->log(2, my $e = $@);
396             }
397             }
398              
399 13         131 $prop->{'requests'} = 0; # record number of request
400              
401 13     0   812 $SIG{'INT'} = $SIG{'TERM'} = $SIG{'QUIT'} = sub { $self->server_close; };
  0         0  
402 13         324 $SIG{'PIPE'} = 'IGNORE'; # most cases, a closed pipe will take care of itself
403 13         194 $SIG{'CHLD'} = \&sig_chld; # catch children (mainly for Fork and PreFork but works for any chld)
404 13     0   318 $SIG{'HUP'} = sub { $self->sig_hup };
  0         0  
405             }
406              
407             sub sig_chld {
408 0     0 0 0 1 while waitpid(-1, POSIX::WNOHANG()) > 0;
409 0         0 $SIG{'CHLD'} = \&sig_chld;
410             }
411              
412       70 1   sub pre_loop_hook {}
413              
414             sub loop {
415 9     9 1 25 my $self = shift;
416 9         181 while ($self->accept) {
417 12         240 $self->run_client_connection;
418 5 100       67 last if $self->done;
419             }
420             }
421              
422             sub accept {
423 13     13 0 1325 my $self = shift;
424 13         39 my $prop = $self->{'server'};
425              
426 13         33 my $sock = undef;
427 13         29 my $retries = 30;
428 13         61 while ($retries--) {
429 13 100       48 if ($prop->{'multi_port'}) { # with more than one port, use select to get the next one
430 8 50       44 return 0 if $prop->{'_HUP'};
431 8   50     83 $sock = $self->accept_multi_port || next; # keep trying for the rest of retries
432 7 50       66 return 0 if $prop->{'_HUP'};
433 7 50       70 if ($self->can_read_hook($sock)) {
434 0         0 $retries++;
435 0         0 next;
436             }
437             } else {
438 5         14 $sock = $prop->{'sock'}->[0]; # single port is bound - just accept
439             }
440 12 50       48 $self->fatal("Received a bad sock!") if ! defined $sock;
441              
442 12 100       292 if (SOCK_DGRAM == $sock->getsockopt(Socket::SOL_SOCKET(), Socket::SO_TYPE())) { # receive a udp packet
443 1         46 $prop->{'client'} = $sock;
444 1         3 $prop->{'udp_true'} = 1;
445 1         8 $prop->{'udp_peer'} = $sock->recv($prop->{'udp_data'}, $sock->NS_recv_len, $sock->NS_recv_flags);
446              
447             } else { # blocking accept per proto
448 11         447 delete $prop->{'udp_true'};
449 11         79 $prop->{'client'} = $sock->accept();
450             }
451              
452 12 50       169 return 0 if $prop->{'_HUP'};
453 12 50       103 return 1 if $prop->{'client'};
454              
455 0         0 $self->log(2,"Accept failed with $retries tries left: $!");
456 0         0 sleep(1);
457             }
458              
459 0         0 $self->log(1,"Ran out of accept retries!");
460 0         0 return undef;
461             }
462              
463              
464             sub accept_multi_port {
465 8     8 0 151 my @waiting = shift->{'server'}->{'select'}->can_read();
466 7 50       5621 return undef if ! @waiting;
467 7         248 return $waiting[rand @waiting];
468             }
469              
470       7 1   sub can_read_hook {}
471              
472             sub post_accept {
473 12     12 1 31 my $self = shift;
474 12         44 my $prop = $self->{'server'};
475 12   33     54 my $client = shift || $prop->{'client'};
476              
477 12         46 $prop->{'requests'}++;
478 12 100       72 return if $prop->{'udp_true'}; # no need to do STDIN/STDOUT in UDP
479              
480 11 50       39 if (!$client) {
481 0         0 $self->log(1,"Client socket information could not be determined!");
482 0         0 return;
483             }
484              
485 11 100       302 $client->post_accept() if $client->can("post_accept");
486 11 100       5199 if (! $prop->{'no_client_stdout'}) {
487 10         130 close STDIN; # duplicate some handles and flush them
488 10         181 close STDOUT;
489 10 100 66     295 if ($prop->{'tie_client_stdout'} || ($client->can('tie_stdout') && $client->tie_stdout)) {
    50 100        
490 4 50       163 open STDIN, '<', '/dev/null' or die "Couldn't open STDIN to the client socket: $!";
491 4 50       121 open STDOUT, '>', '/dev/null' or die "Couldn't open STDOUT to the client socket: $!";
492 4 50       63 tie *STDOUT, 'Net::Server::TiedHandle', $client, $prop->{'tied_stdout_callback'} or die "Couldn't tie STDOUT: $!";
493 4 50       19 tie *STDIN, 'Net::Server::TiedHandle', $client, $prop->{'tied_stdin_callback'} or die "Couldn't tie STDIN: $!";
494             } elsif (defined(my $fileno = fileno $prop->{'client'})) {
495 6 50       248 open STDIN, '<&', $fileno or die "Couldn't open STDIN to the client socket: $!";
496 6 50       139 open STDOUT, '>&', $fileno or die "Couldn't open STDOUT to the client socket: $!";
497             } else {
498 0         0 *STDIN = \*{ $client };
  0         0  
499 0         0 *STDOUT = \*{ $client };
  0         0  
500             }
501 10         94 STDIN->autoflush(1);
502 10         494 STDOUT->autoflush(1);
503 10         333 select STDOUT;
504             }
505             }
506              
507             sub get_client_info {
508 12     12 1 36 my $self = shift;
509 12         42 my $prop = $self->{'server'};
510 12   33     51 my $client = shift || $prop->{'client'};
511              
512 12 50       55 if ($client->NS_proto =~ /^UNIX/) {
513 0         0 delete @$prop{qw(sockaddr sockport peeraddr peerport peerhost)};
514 0 0 0     0 $self->log(3, $self->log_time." CONNECT ".$client->NS_proto." Socket: \"".$client->NS_port."\"") if $prop->{'log_level'} && 3 <= $prop->{'log_level'};
515 0         0 return;
516             }
517              
518 12 50       193 if (my $sockname = $client->sockname) {
519 12         524 $prop->{'sockaddr'} = $client->sockhost;
520 12         870 $prop->{'sockport'} = $client->sockport;
521             } else {
522 0   0     0 @{ $prop }{qw(sockaddr sockhost sockport)} = ($ENV{'REMOTE_HOST'} || '0.0.0.0', 'inet.test', 0); # commandline
  0         0  
523             }
524              
525 12         347 my $addr;
526 12 100       204 if ($prop->{'udp_true'}) {
    50          
527 1 50       16 if ($client->sockdomain == AF_INET) {
528 1         20 ($prop->{'peerport'}, $addr) = Socket::sockaddr_in($prop->{'udp_peer'});
529 1         23 $prop->{'peeraddr'} = Socket::inet_ntoa($addr);
530             } else {
531 0         0 ($prop->{'peerport'}, $addr) = Socket6::sockaddr_in6($prop->{'udp_peer'});
532 0 0       0 $prop->{'peeraddr'} = Socket6->can('inet_ntop')
533             ? Socket6::inet_ntop($client->sockdomain, $addr)
534             : Socket::inet_ntoa($addr);
535             }
536             } elsif ($prop->{'peername'} = $client->peername) {
537 11         438 $addr = $client->peeraddr;
538 11         442 $prop->{'peeraddr'} = $client->peerhost;
539 11         442 $prop->{'peerport'} = $client->peerport;
540             } else {
541 0         0 @{ $prop }{qw(peeraddr peerhost peerport)} = ('0.0.0.0', 'inet.test', 0); # commandline
  0         0  
542             }
543              
544 12 50 33     427 if ($addr && defined $prop->{'reverse_lookups'}) {
545 0 0 0     0 if ($INC{'Socket6.pm'} && Socket6->can('getnameinfo')) {
546 0         0 my @res = Socket6::getnameinfo($addr, 0);
547 0 0       0 $prop->{'peerhost'} = $res[0] if @res > 1;
548             }else{
549 0         0 $prop->{'peerhost'} = gethostbyaddr($addr, AF_INET);
550             }
551             }
552              
553             $self->log(3, $self->log_time
554             ." CONNECT ".$client->NS_proto
555             ." Peer: \"[$prop->{'peeraddr'}]:$prop->{'peerport'}\""
556 12 50 33     190 ." Local: \"[$prop->{'sockaddr'}]:$prop->{'sockport'}\"") if $prop->{'log_level'} && 3 <= $prop->{'log_level'};
557             }
558              
559       13 1   sub post_accept_hook {}
560              
561             sub allow_deny {
562 13     13 1 41 my $self = shift;
563 13         36 my $prop = $self->{'server'};
564 13   66     65 my $sock = shift || $prop->{'client'};
565              
566             # unix sockets are immune to this check
567 13 50 33     142 return 1 if $sock && $sock->NS_proto =~ /^UNIX/;
568              
569             # if no allow or deny parameters are set, allow all
570 13         104 return 1 if ! @{ $prop->{'allow'} }
571 13         101 && ! @{ $prop->{'deny'} }
572 13         125 && ! @{ $prop->{'cidr_allow'} }
573 13 50 33     27 && ! @{ $prop->{'cidr_deny'} };
  13   33     149  
      33        
574              
575             # work around Net::CIDR::cidrlookup() croaking,
576             # if first parameter is an IPv4 address in IPv6 notation.
577 0 0       0 my $peeraddr = ($prop->{'peeraddr'} =~ /^\s*::ffff:([0-9.]+\s*)$/) ? $1 : $prop->{'peeraddr'};
578              
579             # if the addr or host matches a deny, reject it immediately
580 0         0 foreach (@{ $prop->{'deny'} }) {
  0         0  
581             return 0 if $prop->{'reverse_lookups'}
582 0 0 0     0 && defined($prop->{'peerhost'}) && $prop->{'peerhost'} =~ /^$_$/;
      0        
583 0 0       0 return 0 if $peeraddr =~ /^$_$/;
584             }
585 0 0       0 if (@{ $prop->{'cidr_deny'} }) {
  0         0  
586 0         0 require Net::CIDR;
587 0 0       0 return 0 if Net::CIDR::cidrlookup($peeraddr, @{ $prop->{'cidr_deny'} });
  0         0  
588             }
589              
590             # if the addr or host isn't blocked yet, allow it if it is allowed
591 0         0 foreach (@{ $prop->{'allow'} }) {
  0         0  
592             return 1 if $prop->{'reverse_lookups'}
593 0 0 0     0 && defined($prop->{'peerhost'}) && $prop->{'peerhost'} =~ /^$_$/;
      0        
594 0 0       0 return 1 if $peeraddr =~ /^$_$/;
595             }
596 0 0       0 if (@{ $prop->{'cidr_allow'} }) {
  0         0  
597 0         0 require Net::CIDR;
598 0 0       0 return 1 if Net::CIDR::cidrlookup($peeraddr, @{ $prop->{'cidr_allow'} });
  0         0  
599             }
600              
601 0         0 return 0;
602             }
603              
604 13     13 1 71 sub allow_deny_hook { 1 } # false to deny request
605              
606       0 1   sub request_denied_hook {}
607              
608             sub process_request { # sample echo server - override for full functionality
609 10     10 1 36 my $self = shift;
610 10         37 my $prop = $self->{'server'};
611              
612 10 100       49 if ($prop->{'udp_true'}) { # udp echo server
613 1   33     5 my $client = shift || $prop->{'client'};
614 1 50       13 if ($prop->{'udp_data'} =~ /dump/) {
615 0         0 require Data::Dumper;
616 0         0 return $client->send(Data::Dumper::Dumper($self), 0);
617             }
618 1         21 return $client->send("You said \"$prop->{'udp_data'}\"", 0);
619             }
620              
621 9         745 print "Welcome to \"".ref($self)."\" ($$)\015\012";
622 9         2359 my $previous_alarm = alarm 30;
623 9         38 eval {
624 9     0   346 local $SIG{'ALRM'} = sub { die "Timed Out!\n" };
  0         0  
625 9         1540 while () {
626 9         38518 s/[\r\n]+$//;
627 9         422 print ref($self),":$$: You said \"$_\"\015\012";
628 9         428 $self->log(5, $_); # very verbose log
629 9 50       148 if (/get\s+(\w+)/) { print "$1: $self->{'server'}->{$1}\015\012" }
  0 50       0  
    100          
    50          
630 0         0 elsif (/dump/) { require Data::Dumper; print Data::Dumper::Dumper($self) }
  0         0  
631 3         8 elsif (/quit/) { last }
632 6         179 elsif (/exit/) { $self->server_close }
633 0         0 alarm 30; # another 30
634             }
635 3         81 alarm($previous_alarm);
636             };
637 3         22 alarm 0;
638 3 50       31 print "Timed Out.\015\012" if $@ eq "Timed Out!\n";
639             }
640              
641       6 1   sub post_process_request_hook {}
642              
643       6 1   sub post_client_connection_hook {}
644              
645             sub post_process_request {
646 6     6 1 19 my $self = shift;
647 6         89 $self->close_client_stdout;
648             }
649              
650             sub close_client_stdout {
651 6     6 0 34 my $self = shift;
652 6         18 my $prop = $self->{'server'};
653 6 100       28 return if $prop->{'udp_true'};
654              
655 5 100       333 if (! $prop->{'no_client_stdout'}) {
656 4 100       13 my $t = tied *STDOUT; if ($t) { undef $t; untie *STDOUT };
  4         13  
  2         5  
  2         59  
657 4 100       14 $t = tied *STDIN; if ($t) { undef $t; untie *STDIN };
  4         25  
  2         4  
  2         6  
658 4 50       335 open(STDIN, '<', '/dev/null') || die "Cannot read /dev/null [$!]";
659 4 50       170 open(STDOUT, '>', '/dev/null') || die "Cannot write /dev/null [$!]";
660             }
661 5         109 $prop->{'client'}->close;
662             }
663              
664             sub done {
665 3     3 0 10 my $self = shift;
666 3 50       14 $self->{'server'}->{'done'} = shift if @_;
667 3         25 return $self->{'server'}->{'done'};
668             }
669              
670       5 1   sub pre_fork_hook {}
671       1 1   sub child_init_hook {}
672       1 1   sub child_finish_hook {}
673              
674             sub run_dequeue { # fork off a child process to handle dequeuing
675 0     0 0 0 my $self = shift;
676 0         0 $self->pre_fork_hook('dequeue');
677 0         0 my $pid = fork;
678 0 0       0 $self->fatal("Bad fork [$!]") if ! defined $pid;
679 0 0       0 if (!$pid) { # child
680             $SIG{'INT'} = $SIG{'TERM'} = $SIG{'QUIT'} = $SIG{'HUP'} = sub {
681 0     0   0 $self->child_finish_hook('dequeue');
682 0         0 exit;
683 0         0 };
684 0         0 $SIG{'PIPE'} = $SIG{'TTIN'} = $SIG{'TTOU'} = 'DEFAULT';
685 0         0 $self->child_init_hook('dequeue');
686 0         0 $self->dequeue();
687 0         0 $self->child_finish_hook('dequeue');
688 0         0 exit;
689             }
690 0         0 $self->log(4, "Running dequeue child $pid");
691              
692             $self->{'server'}->{'children'}->{$pid}->{'status'} = 'dequeue'
693 0 0       0 if $self->{'server'}->{'children'};
694             }
695              
696 11     11 0 60822 sub default_port { 20203 }
697              
698       0 0   sub dequeue {}
699              
700       12 1   sub pre_server_close_hook {}
701              
702             sub server_close {
703 12     12 1 85 my ($self, $exit_val) = @_;
704 12         46 my $prop = $self->{'server'};
705              
706 12         279 $SIG{'INT'} = 'DEFAULT';
707              
708             ### if this is a child process, signal the parent and close
709             ### normally the child shouldn't, but if they do...
710             ### otherwise the parent continues with the shutdown
711             ### this is safe for non standard forked child processes
712             ### as they will not have server_close as a handler
713 12 50 66     211 if (defined($prop->{'ppid'})
      33        
714             && $prop->{'ppid'} != $$
715             && ! defined($prop->{'no_close_by_child'})) {
716 0         0 $self->close_parent;
717 0         0 exit;
718             }
719              
720 12         379 $self->pre_server_close_hook;
721              
722 12         136 $self->log(2, $self->log_time . " Server closing!");
723              
724 12 0 33     77 if ($prop->{'kind_quit'} && $prop->{'children'}) {
725 0         0 $self->log(3, "Attempting a slow shutdown");
726 0         0 $prop->{$_} = 0 for qw(min_servers max_servers);
727 0         0 $self->hup_children; # send children signal to finish up
728 0         0 while (1) {
729 0         0 Net::Server::SIG::check_sigs();
730 0 0       0 $self->coordinate_children if $self->can('coordinate_children');
731 0 0       0 last if !keys %{$self->{'server'}->{'children'}};
  0         0  
732 0         0 sleep 1;
733             }
734             }
735              
736 12 50 33     90 if ($prop->{'_HUP'} && $prop->{'leave_children_open_on_hup'}) {
737 0         0 $self->hup_children;
738              
739             } else {
740 12 100       140 $self->close_children() if $prop->{'children'};
741 12         491 $self->post_child_cleanup_hook;
742             }
743              
744 12 50 66     165 if (defined($prop->{'lock_file'})
      66        
745             && -e $prop->{'lock_file'}
746             && defined($prop->{'lock_file_unlink'})) {
747 2 50       120 unlink($prop->{'lock_file'}) || $self->log(1, "Couldn't unlink \"$prop->{'lock_file'}\" [$!]");
748             }
749 12 0 33     109 if (defined($prop->{'pid_file'})
      33        
      0        
750             && -e $prop->{'pid_file'}
751             && !$prop->{'_HUP'}
752             && defined($prop->{'pid_file_unlink'})) {
753 0 0       0 unlink($prop->{'pid_file'}) || $self->log(1, "Couldn't unlink \"$prop->{'pid_file'}\" [$!]");
754             }
755              
756 12 50       58 if ($prop->{'_HUP'}) {
757 0         0 $self->restart_close_hook();
758 0         0 $self->hup_server; # execs at the end
759             }
760              
761 12         261 $self->shutdown_sockets;
762 12 50       49 return $self if $prop->{'no_exit_on_close'};
763 12         117 $self->server_exit($exit_val);
764             }
765              
766             sub server_exit {
767 12     12 1 72 my ($self, $exit_val) = @_;
768 12   50     4319 exit($exit_val || 0);
769             }
770              
771             sub shutdown_sockets {
772 12     12 1 43 my $self = shift;
773 12         35 my $prop = $self->{'server'};
774              
775 12         26 foreach my $sock (@{ $prop->{'sock'} }) { # unlink remaining socket files (if any)
  12         104  
776 16         581 $sock->shutdown(2);
777 16 50       759 unlink $sock->NS_port if $sock->NS_proto =~ /^UNIX/;
778             }
779              
780 12         119 $prop->{'sock'} = []; # delete the sock objects
781 12         93 return 1;
782             }
783              
784             ### Allow children to send INT signal to parent (or use another method)
785             ### This method is only used by forking servers
786             sub close_parent {
787 0     0 0 0 my $self = shift;
788 0         0 my $prop = $self->{'server'};
789 0 0       0 die "Missing parent pid (ppid)" if ! $prop->{'ppid'};
790 0         0 kill 2, $prop->{'ppid'};
791             }
792              
793             ### SIG INT the children
794             ### This method is only used by forking servers (ie Fork, PreFork)
795             sub close_children {
796 3     3 0 10 my $self = shift;
797 3         10 my $prop = $self->{'server'};
798 3 50 50     45 return unless $prop->{'children'} && scalar keys %{ $prop->{'children'} };
  3         36  
799              
800 3         15 foreach my $pid (keys %{ $prop->{'children'} }) {
  3         26  
801 4         29 $self->log(4, "Kill TERM pid $pid");
802 4 50 33     331 if (kill(15, $pid) || ! kill(0, $pid)) { # if it is killable, kill it
803 4         57 $self->delete_child($pid);
804             }
805             }
806              
807 3         50 1 while waitpid(-1, POSIX::WNOHANG()) > 0;
808             }
809              
810              
811 0     0 0 0 sub is_prefork { 0 }
812              
813             sub hup_children {
814 0     0 0 0 my $self = shift;
815 0         0 my $prop = $self->{'server'};
816 0 0 0     0 return unless defined $prop->{'children'} && scalar keys %{ $prop->{'children'} };
  0         0  
817 0 0       0 return if ! $self->is_prefork;
818 0         0 $self->log(2, "Sending children hup signal");
819              
820 0         0 for my $pid (keys %{ $prop->{'children'} }) {
  0         0  
821 0         0 $self->log(4, "Kill HUP pid $pid");
822 0 0       0 kill(1, $pid) or $self->log(2, "Failed to kill pid $pid: $!");
823             }
824             }
825              
826       12 1   sub post_child_cleanup_hook {}
827              
828             ### handle sig hup
829             ### this will prepare the server for a restart via exec
830             sub sig_hup {
831 0     0 0 0 my $self = shift;
832 0         0 my $prop = $self->{'server'};
833              
834 0         0 $self->log(2, "Received a SIG HUP");
835              
836 0         0 my $i = 0;
837 0         0 my @fd;
838 0         0 $prop->{'_HUP'} = [];
839 0         0 foreach my $sock (@{ $prop->{'sock'} }) {
  0         0  
840 0   0     0 my $fd = POSIX::dup($sock->fileno) || $self->fatal("Cannot duplicate the socket [$!]");
841              
842             # hold on to the socket copy until exec;
843             # just temporary: any socket domain will do,
844             # forked process will decide to use IO::Socket::INET6 if necessary
845 0         0 $prop->{'_HUP'}->[$i] = IO::Socket::INET->new;
846 0 0       0 $prop->{'_HUP'}->[$i]->fdopen($fd, 'w') || $self->fatal("Cannot open to file descriptor [$!]");
847              
848             # turn off the FD_CLOEXEC bit to allow reuse on exec
849 0         0 require Fcntl;
850 0         0 $prop->{'_HUP'}->[$i]->fcntl(Fcntl::F_SETFD(), my $flags = "");
851              
852 0         0 push @fd, $fd .'|'. $sock->hup_string; # save file-descriptor and host|port|proto|ipv
853              
854 0         0 $sock->close();
855 0         0 $i++;
856             }
857 0         0 delete $prop->{'select'}; # remove any blocking obstacle
858 0         0 $ENV{'BOUND_SOCKETS'} = join "; ", @fd;
859              
860 0 0 0     0 if ($prop->{'leave_children_open_on_hup'} && scalar keys %{ $prop->{'children'} }) {
  0         0  
861 0         0 $ENV{'HUP_CHILDREN'} = join "\n", map {"$_\t$prop->{'children'}->{$_}->{'status'}"} sort keys %{ $prop->{'children'} };
  0         0  
  0         0  
862             }
863             }
864              
865              
866             sub hup_server {
867 0     0 0 0 my $self = shift;
868 0         0 $self->log(0, $self->log_time()." Re-exec server during HUP");
869 0         0 delete @ENV{$self->hup_delete_env_keys};
870 0         0 exec @{ $self->commandline };
  0         0  
871             }
872              
873 0     0 0 0 sub hup_delete_env_keys { return qw(PATH) }
874              
875       0 1   sub restart_open_hook {} # this hook occurs if a server has been HUP'ed it occurs just before opening to the fileno's
876              
877       0 1   sub restart_close_hook {} # this hook occurs if a server has been HUP'ed it occurs just before exec'ing the server
878              
879             ###----------------------------------------------------------###
880              
881             sub fatal {
882 0     0 0 0 my ($self, $error) = @_;
883 0         0 my ($package, $file, $line) = caller;
884 0         0 $self->fatal_hook($error, $package, $file, $line);
885 0         0 $self->log(0, $self->log_time ." $error\n at line $line in file $file");
886 0         0 $self->server_close(1);
887             }
888              
889       0 1   sub fatal_hook {}
890              
891             ###----------------------------------------------------------###
892              
893             sub log {
894 93     93 1 359 my ($self, $level, $msg, @therest) = @_;
895 93         202 my $prop = $self->{'server'};
896 93 50       293 return if ! $prop->{'log_level'};
897 93 100 66     1514 return if $level =~ /^\d+$/ && $level > $prop->{'log_level'};
898 68 50       258 $msg = sprintf($msg, @therest) if @therest; # if multiple arguments are passed, assume that the first is a format string
899              
900 68 50       212 if ($prop->{'log_function'}) {
901 0 0       0 return if eval { $prop->{'log_function'}->($level, $msg); 1 };
  0         0  
  0         0  
902 0         0 my $err = $@;
903 0 0 0     0 if ($prop->{'log_class'} && $prop->{'log_class'}->can('handle_error')) {
904 0         0 $prop->{'log_class'}->handle_log_error($self, $err, [$level, $msg]);
905             } else {
906 0         0 $self->handle_log_error($err, [$level, $msg]);
907             }
908             }
909              
910 68 50       805 return if $level !~ /^\d+$/;
911 68         370 $self->write_to_log_hook($level, $msg);
912             }
913              
914              
915 0     0 0 0 sub handle_log_error { my ($self, $error) = @_; die $error }
  0         0  
916 0     0 1 0 sub handle_syslog_error { &handle_log_error }
917              
918             sub write_to_log_hook {
919 68     68 1 184 my ($self, $level, $msg) = @_;
920 68         130 my $prop = $self->{'server'};
921 68         155 chomp $msg;
922 68         218 $msg =~ s/([^\n\ -\~])/sprintf("%%%02X",ord($1))/eg;
  0         0  
923              
924 68 50       276 if ($prop->{'log_file'}) {
    50          
925 0         0 print _SERVER_LOG $msg, "\n";
926             } elsif ($prop->{'setsid'}) {
927             # do nothing ?
928             } else {
929 68         268 my $old = select STDERR;
930 68         227 print $msg. "\n";
931 68         417 select $old;
932             }
933             }
934              
935              
936             sub log_time {
937 81     81 0 4358 my ($sec,$min,$hour,$day,$mon,$year) = localtime;
938 81         1874 return sprintf "%04d/%02d/%02d-%02d:%02d:%02d", $year + 1900, $mon + 1, $day, $hour, $min, $sec;
939             }
940              
941             ###----------------------------------------------------------###
942              
943             sub options {
944 106     106 0 429 my $self = shift;
945 106   50     299 my $ref = shift || {};
946 106         193 my $prop = $self->{'server'};
947              
948 106         361 foreach (qw(port host proto ipv allow deny cidr_allow cidr_deny)) {
949 848 100       1594 if (! defined $prop->{$_}) {
    100          
950 552         1523 $prop->{$_} = [];
951             } elsif (! ref $prop->{$_}) {
952 8         18 $prop->{$_} = [$prop->{$_}]; # nicely turn us into an arrayref if we aren't one already
953             }
954 848         1628 $ref->{$_} = $prop->{$_};
955             }
956              
957 106         251 foreach (qw(conf_file
958             user group chroot log_level
959             log_file pid_file background setsid
960             listen reverse_lookups
961             no_close_by_child
962             no_client_stdout tie_client_stdout tied_stdout_callback tied_stdin_callback
963             leave_children_open_on_hup
964             )) {
965 1802         4673 $ref->{$_} = \$prop->{$_};
966             }
967 106         204 return $ref;
968             }
969              
970              
971             ### routine for parsing commandline, module, and conf file
972             ### method has the benefit of leaving unused arguments in @ARGV
973             sub process_args {
974 133     133 0 368 my ($self, $args, $template) = @_;
975 133 100 66     1095 $self->options($template = {}) if ! $template || ! ref $template;
976 133 0 66     999 if (!$_[2] && !scalar(keys %$template) && !$self->{'server'}->{'_no_options'}++) {
      33        
977 0         0 warn "Configuration options were empty - skipping any commandline, config file, or run argument parsing.\n";
978             }
979              
980             # we want subsequent calls to not overwrite or add to previously set values so that command line arguments win
981 133         729 my %previously_set;
982 133         607 foreach (my $i = 0; $i < @$args; $i++) {
983 332 100 100     2778 if ($args->[$i] =~ /^(?:--)?(\w+)(?:[=\ ](\S+))?$/
984             && exists $template->{$1}) {
985 314         1001 my ($key, $val) = ($1, $2);
986 314         565 splice @$args, $i, 1;
987 314 100       676 if (! defined $val) {
988 311 50 66     1541 if ($i > $#$args
      33        
989             || ($args->[$i] && $args->[$i] =~ /^--\w+/)) {
990 0         0 $val = 1; # allow for options such as --setsid
991             } else {
992 311         685 $val = splice @$args, $i, 1;
993 311 50 100     814 $val = $val->[0] if ref($val) eq 'ARRAY' && @$val == 1 && ref($template->{$key}) ne 'ARRAY';
      66        
994             }
995             }
996 314         403 $i--;
997 314 100       1023 $val =~ s/%([A-F0-9])/chr(hex $1)/eig if ! ref $val;
  0         0  
998              
999 314 100       771 if (ref $template->{$key} eq 'ARRAY') {
1000 184 100       393 if (! defined $previously_set{$key}) {
1001 142         193 $previously_set{$key} = scalar @{ $template->{$key} };
  142         356  
1002             }
1003 184 100       421 next if $previously_set{$key};
1004 172 100       230 push @{ $template->{$key} }, ref($val) eq 'ARRAY' ? @$val : $val;
  172         941  
1005             } else {
1006 130 100       277 if (! defined $previously_set{$key}) {
1007 112 100       308 $previously_set{$key} = defined(${ $template->{$key} }) ? 1 : 0;
  112         355  
1008             }
1009 130 100       321 next if $previously_set{$key};
1010 105 50       219 die "Found multiple values on the configuration item \"$key\" which expects only one value" if ref($val) eq 'ARRAY';
1011 105         143 ${ $template->{$key} } = $val;
  105         473  
1012             }
1013             }
1014             }
1015             }
1016              
1017             sub _read_conf {
1018 9     9   20 my ($self, $file) = @_;
1019 9         14 my @args;
1020 9 50       40 $file = ($file =~ m|^([\w\.\-\/\\\:]+)$|) ? $1 : $self->fatal("Unsecure filename \"$file\"");
1021 9 50       371 open my $fh, '<', $file or do {
1022 0 0       0 $self->fatal("Couldn't open conf \"$file\" [$!]") if $ENV{'BOUND_SOCKETS'};
1023 0         0 warn "Couldn't open conf \"$file\" [$!]\n";
1024             };
1025 9         226 while (defined(my $line = <$fh>)) {
1026 189 100       947 push @args, $1, $2 if $line =~ m/^\s* ((?:--)?\w+) (?:\s*[=:]\s*|\s+) (\S+)/x;
1027             }
1028 9         101 close $fh;
1029 9         77 return \@args;
1030             }
1031              
1032             ###----------------------------------------------------------------###
1033              
1034       0 0   sub other_child_died_hook {}
1035              
1036       4 0   sub delete_child_hook {}
1037              
1038             sub delete_child {
1039 4     4 0 19 my ($self, $pid) = @_;
1040 4         13 my $prop = $self->{'server'};
1041              
1042 4 50       24 return $self->other_child_died_hook($pid) if ! exists $prop->{'children'}->{$pid};
1043              
1044             # prefork server check to clear child communication
1045 4 100       25 if ($prop->{'child_communication'}) {
1046 1 50       5 if ($prop->{'children'}->{$pid}->{'sock'}) {
1047 1         8 $prop->{'child_select'}->remove($prop->{'children'}->{$pid}->{'sock'});
1048 1         120 $prop->{'children'}->{$pid}->{'sock'}->close;
1049             }
1050             }
1051            
1052 4         300 $self->delete_child_hook($pid); # user customizable hook
1053              
1054 4         32 delete $prop->{'children'}->{$pid};
1055             }
1056              
1057             # send signal to all children - used by forking servers
1058             sub sig_pass {
1059 0     0 0 0 my ($self, $sig) = @_;
1060 0         0 foreach my $chld (keys %{ $self->{'server'}->{'children'} }) {
  0         0  
1061 0         0 $self->log(4, "signaling $chld with $sig" );
1062 0 0       0 kill($sig, $chld) || $self->log(1, "child $chld not signaled with $sig");
1063             }
1064             }
1065              
1066             # register sigs to allow passthrough to children
1067             sub register_sig_pass {
1068 3     3 0 15 my $self = shift;
1069 3   50     53 my $ref = $self->{'server'}->{'sig_passthrough'} || [];
1070 3 50       15 $ref = [$ref] if ! ref $ref;
1071 3 50       14 $self->fatal('invalid sig_passthrough') if ref $ref ne 'ARRAY';
1072 3 50       23 return if ! @$ref;
1073 0         0 $self->log(4, "sig_passthrough option found");
1074 0         0 require Net::Server::SIG;
1075 0         0 foreach my $sig (map {split /\s*,\s*/, $_} @$ref) {
  0         0  
1076 0         0 my $code = Net::Server::SIG::sig_is_registered($sig);
1077 0 0       0 if ($code) {
1078 0         0 $self->log(2, "Installing passthrough for $sig even though it is already registered.");
1079             } else {
1080 0 0       0 $code = ref($SIG{$sig}) eq 'CODE' ? $SIG{$sig} : undef;
1081             }
1082 0 0   0   0 Net::Server::SIG::register_sig($sig => sub { $self->sig_pass($sig); $code->($sig) if $code; });
  0         0  
  0         0  
1083 0         0 $self->log(2, "Installed passthrough for $sig");
1084             }
1085             }
1086              
1087             ###----------------------------------------------------------------###
1088              
1089             package Net::Server::TiedHandle;
1090 8     8   19 sub TIEHANDLE { my $pkg = shift; return bless [@_], $pkg }
  8         101  
1091 2 50   2   7 sub READLINE { my $s = shift; $s->[1] ? $s->[1]->($s->[0], 'getline', @_) : $s->[0]->getline }
  2         51  
1092 0 0   0   0 sub SAY { my $s = shift; $s->[1] ? $s->[1]->($s->[0], 'say', @_) : $s->[0]->say(@_) }
  0         0  
1093 7 100   7   138 sub PRINT { my $s = shift; $s->[1] ? $s->[1]->($s->[0], 'print', @_) : $s->[0]->print(@_) }
  7         71  
1094 0 0   0     sub PRINTF { my $s = shift; $s->[1] ? $s->[1]->($s->[0], 'printf', @_) : $s->[0]->printf(@_) }
  0            
1095 0 0   0     sub READ { my $s = shift; $s->[1] ? $s->[1]->($s->[0], 'read', @_) : $s->[0]->read(@_) }
  0            
1096 0 0   0     sub WRITE { my $s = shift; $s->[1] ? $s->[1]->($s->[0], 'write', @_) : $s->[0]->write(@_) }
  0            
1097 0 0   0     sub SYSREAD { my $s = shift; $s->[1] ? $s->[1]->($s->[0], 'sysread', @_) : $s->[0]->sysread(@_) }
  0            
1098 0 0   0     sub SYSWRITE { my $s = shift; $s->[1] ? $s->[1]->($s->[0], 'syswrite', @_) : $s->[0]->syswrite(@_) }
  0            
1099 0 0   0     sub SEEK { my $s = shift; $s->[1] ? $s->[1]->($s->[0], 'seek', @_) : $s->[0]->seek(@_) }
  0            
1100       0     sub BINMODE {}
1101       0     sub FILENO {}
1102 0 0   0     sub CLOSE { my $s = shift; $s->[1] ? $s->[1]->($s->[0], 'close', @_) : $s->[0]->close(@_) }
  0            
1103              
1104              
1105             1;
1106              
1107             ### The documentation is in Net/Server.pod