File Coverage

blib/lib/Net/Server.pm
Criterion Covered Total %
statement 405 687 58.9
branch 188 418 44.9
condition 77 192 40.1
subroutine 66 101 65.3
pod 39 72 54.1
total 775 1470 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   41197 use strict;
  28         93  
  28         941  
24 28     28   141 use Socket qw(AF_INET AF_UNIX SOCK_DGRAM SOCK_STREAM);
  28         82  
  28         8118  
25 28     28   167 use IO::Socket ();
  28         57  
  28         396  
26 28     28   914 use IO::Select ();
  28         3798  
  28         419  
27 28     28   7152 use POSIX ();
  28         129169  
  28         777  
28 28     28   1310 use Net::Server::Proto ();
  28         59  
  28         705  
29 28         205779 use Net::Server::Daemonize qw(check_pid_file create_pid_file safe_fork
30 28     28   9150 get_uid get_gid set_uid set_gid);
  28         77  
31              
32             our $VERSION = '2.009';
33              
34             sub new {
35 71   50 71 1 1429 my $class = shift || die "Missing class";
36 71 100       429 my $args = @_ == 1 ? shift : {@_};
37 71         881 return bless {server => {%$args}}, $class;
38             }
39              
40 62     62 0 118 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 23603 my $self = ref($_[0]) ? shift() : shift->new; # pass package or object
46 70 100       1252 $self->{'server'}->{'_run_args'} = [@_ == 1 ? %{$_[0]} : @_];
  3         9  
47 70         442 $self->_initialize; # configure all parameters
48              
49 70         276 $self->post_configure; # verification of passed parameters
50 70         248 $self->post_configure_hook; # user customizable hook
51              
52 70         391 $self->pre_bind; # finalize ports to be bound
53 70         387 $self->bind; # connect to port(s), setup selection handle for multi port
54 70         1127 $self->post_bind_hook; # user customizable hook
55 70         212 $self->post_bind; # allow for chrooting, becoming a different user and group
56              
57 70         313 $self->pre_loop_hook; # user customizable hook
58 70         326 $self->loop; # repeat accept/process cycle
59              
60 59         205 $self->server_close; # close the server and release the port
61             }
62              
63             sub run_client_connection {
64 13     13 1 39 my $self = shift;
65 13         113 my $c = $self->{'server'}->{'client'};
66              
67 13         132 $self->post_accept($c); # prepare client for processing
68 13         104 $self->get_client_info($c); # determines information about peer and local
69 13         108 $self->post_accept_hook($c); # user customizable hook
70              
71 13   33     75 my $ok = $self->allow_deny($c) && $self->allow_deny_hook($c); # do allow/deny check on client info
72 13 50       41 if ($ok) {
73 13         77 $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         155 $self->post_process_request_hook($ok); # user customizable hook
79 6         42 $self->post_process_request; # clean up client connection, etc
80 6         185 $self->post_client_connection_hook; # one last hook
81             }
82              
83             ###----------------------------------------------------------------###
84              
85             sub _initialize {
86 73     73   173 my $self = shift;
87 73   50     308 my $prop = $self->{'server'} ||= {};
88              
89 73 100       156 $self->commandline($self->_get_commandline) if ! eval { $self->commandline }; # save for a HUP
  73         514  
90 73         318 $self->configure_hook; # user customizable hook
91 73         407 $self->configure; # allow for reading of commandline, program, and configuration file parameters
92              
93 73 50       113 my @defaults = %{ $self->default_values || {} }; # allow yet another way to pass defaults
  73         154  
94 73 100       326 $self->process_args(\@defaults) if @defaults;
95             }
96              
97             sub commandline {
98 143     143 0 228 my $self = shift;
99 143 50       602 $self->{'server'}->{'commandline'} = ref($_[0]) ? shift : \@_ if @_;
    100          
100 143   100     1267 return $self->{'server'}->{'commandline'} || die "commandline was not set during initialization";
101             }
102              
103             sub _get_commandline {
104 70     70   188 my $self = shift;
105 70         311 my $script = $0;
106 70 50 33     1224 $script = $ENV{'PWD'} .'/'. $script if $script =~ m|^[^/]+/| && $ENV{'PWD'}; # add absolute to relative - avoid Cwd
107 70         287 $script =~ /^(.+)$/; # untaint for later use in hup
108 70         601 return [$1, @ARGV]
109             }
110              
111       73 1   sub configure_hook {}
112              
113             sub configure {
114 100     100 1 175 my $self = shift;
115 100         300 my $prop = $self->{'server'};
116 100 100 66     361 my $template = ($_[0] && ref($_[0])) ? shift : undef;
117              
118 100 100       230 $self->process_args(\@ARGV, $template) if @ARGV; # command line
119 100 50       564 $self->process_args($prop->{'_run_args'}, $template) if $prop->{'_run_args'}; # passed to run
120              
121 100 100       841 if ($prop->{'conf_file'}) {
122 8         22 $self->process_args($self->_read_conf($prop->{'conf_file'}), $template);
123             } else {
124 92   50     363 my $def = $self->default_values || {};
125 92 100       487 $self->process_args($self->_read_conf($def->{'conf_file'}), $template) if $def->{'conf_file'};
126             }
127             }
128              
129 133     133 1 482 sub default_values { {} }
130              
131             sub post_configure {
132 70     70 1 108 my $self = shift;
133 70         128 my $prop = $self->{'server'};
134              
135 70 50 33     215 $prop->{'log_level'} = 2 if ! defined($prop->{'log_level'}) || $prop->{'log_level'} !~ /^\d+$/;
136 70 50       208 $prop->{'log_level'} = 4 if $prop->{'log_level'} > 4;
137 70         340 $self->initialize_logging;
138              
139 70 50       141 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       156 if (! $prop->{'_is_inet'}) { # completetly daemonize by closing STDIN, STDOUT (should be done before fork)
147 69 50 33     332 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       199 if (!$ENV{'BOUND_SOCKETS'}) { # don't need to redo this if hup'ing
154 70 50 33     325 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       156 POSIX::setsid() if $prop->{'setsid'}; # completely remove myself from parent process
162             }
163              
164 70 50 33     391 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       199 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         202 $prop->{$_} = [] for grep {! ref $prop->{$_}} qw(allow deny cidr_allow cidr_deny);
  280         837  
183             }
184              
185             sub initialize_logging {
186 70     70 0 101 my $self = shift;
187 70         111 my $prop = $self->{'server'};
188 70 50       170 if (! defined($prop->{'log_file'})) {
189 70         133 $prop->{'log_file'} = ''; # log to STDERR
190 70         121 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   137 sub _server_type { ref($_[0]) }
218              
219             sub pre_bind { # make sure we have good port parameters
220 69     69 1 158 my $self = shift;
221 69         113 my $prop = $self->{'server'};
222              
223 69         256 my $super = $self->net_server_type;
224 69         198 my $type = $self->_server_type;
225 69 100       467 if ($self->isa('Net::Server::MultiType')) {
226 3   33     37 my $base = delete($prop->{'_recursive_multitype'}) || Net::Server::MultiType->net_server_type;
227 3         13 $super = "$super -> MultiType -> $base";
228             }
229 69 50       286 $type .= " (type $super)" if $type ne $super;
230 69         246 $self->log(2, $self->log_time ." $type starting! pid($$)");
231              
232 69         168 $prop->{'sock'} = [grep {$_} map { $self->proto_object($_) } @{ $self->prepared_ports }];
  100         257  
  100         323  
  69         239  
233 69 50       108 $self->fatal("No valid socket parameters found") if ! @{ $prop->{'sock'} };
  69         216  
234             }
235              
236             sub prepared_ports {
237 69     69 0 116 my $self = shift;
238 69         123 my $prop = $self->{'server'};
239              
240 69         185 my ($ports, $hosts, $protos, $ipvs) = @$prop{qw(port host proto ipv)};
241 69   33     161 $ports ||= $prop->{'ports'};
242 69 100 66     460 if (!defined($ports) || (ref($ports) && !@$ports)) {
      66        
243 7         16 $ports = $self->default_port;
244 7 50 33     28 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         195 my %bound;
251 69         204 my $bind = $prop->{'_bind'} = [];
252 69 100       233 for my $_port (ref($ports) ? @$ports : $ports) {
253 100 100       321 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       250 my $_proto = ref($protos) ? $protos->[@$bind >= @$protos ? -1 : $#$bind + 1] : $protos;
    50          
255 100 100       261 my $_ipv = ref($ipvs) ? $ipvs->[ @$bind >= @$ipvs ? -1 : $#$bind + 1] : $ipvs;
    50          
256 100         348 foreach my $info ($self->port_info($_port, $_host, $_proto, $_ipv)) {
257 100         277 my ($port, $host, $proto, $ipv) = @$info{qw(port host proto ipv)}; # use cleaned values
258 100 50 33     575 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         274 push @$bind, $info;
263             }
264             }
265              
266 69         195 return $bind;
267             }
268              
269             sub port_info {
270 100     100 0 205 my ($self, $port, $host, $proto, $ipv) = @_;
271 100         661 return Net::Server::Proto->parse_info($port, $host, $proto, $ipv, $self);
272             }
273              
274             sub proto_object {
275 100     100 0 189 my ($self, $info) = @_;
276 100         376 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 37 my $self = shift;
281 12         44 my $prop = $self->{'server'};
282              
283 12 50       55 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         27 foreach my $sock (@{ $prop->{'sock'} }) {
  12         94  
316 18         89 $sock->log_connect($self);
317 18         110 $sock->connect($self);
318             }
319             }
320              
321 12 100 100     41 if (@{ $prop->{'sock'} } > 1 || $prop->{'multi_port'}) {
  12         132  
322 7         52 $prop->{'multi_port'} = 1;
323 7         124 $prop->{'select'} = IO::Select->new; # if more than one socket we'll need to select on it
324 7         161 $prop->{'select'}->add($_) for @{ $prop->{'sock'} };
  7         60  
325             } else {
326 5         14 $prop->{'multi_port'} = undef;
327 5         38 $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 38 my $self = shift;
336 13         43 my $prop = $self->{'server'};
337              
338 13 50       65 if (! defined $prop->{'group'}) {
    0          
339 13         343 $self->log(1, "Group Not Defined. Defaulting to EGID '$)'");
340 13         78 $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       87 if (! defined $prop->{'user'}) {
    0          
349 13         178 $self->log(1, "User Not Defined. Defaulting to EUID '$>'");
350 13         69 $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     244 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       63 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         42 eval {
380 13 50       80 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       80 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       83 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         98 $prop->{'requests'} = 0; # record number of request
400              
401 13     0   546 $SIG{'INT'} = $SIG{'TERM'} = $SIG{'QUIT'} = sub { $self->server_close; };
  0         0  
402 13         130 $SIG{'PIPE'} = 'IGNORE'; # most cases, a closed pipe will take care of itself
403 13         78 $SIG{'CHLD'} = \&sig_chld; # catch children (mainly for Fork and PreFork but works for any chld)
404 13     0   135 $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 24 my $self = shift;
416 9         101 while ($self->accept) {
417 12         119 $self->run_client_connection;
418 5 100       55 last if $self->done;
419             }
420             }
421              
422             sub accept {
423 12     12 0 1023 my $self = shift;
424 12         39 my $prop = $self->{'server'};
425              
426 12         36 my $sock = undef;
427 12         31 my $retries = 30;
428 12         86 while ($retries--) {
429 12 100       148 if ($prop->{'multi_port'}) { # with more than one port, use select to get the next one
430 7 50       82 return 0 if $prop->{'_HUP'};
431 7   50     81 $sock = $self->accept_multi_port || next; # keep trying for the rest of retries
432 7 50       32 return 0 if $prop->{'_HUP'};
433 7 50       50 if ($self->can_read_hook($sock)) {
434 0         0 $retries++;
435 0         0 next;
436             }
437             } else {
438 5         12 $sock = $prop->{'sock'}->[0]; # single port is bound - just accept
439             }
440 12 50       64 $self->fatal("Received a bad sock!") if ! defined $sock;
441              
442 12 100       166 if (SOCK_DGRAM == $sock->getsockopt(Socket::SOL_SOCKET(), Socket::SO_TYPE())) { # receive a udp packet
443 1         23 $prop->{'client'} = $sock;
444 1         7 $prop->{'udp_true'} = 1;
445 1         4 $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         621 delete $prop->{'udp_true'};
449 11         80 $prop->{'client'} = $sock->accept();
450             }
451              
452 12 50       100 return 0 if $prop->{'_HUP'};
453 12 50       74 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 7     7 0 88 my @waiting = shift->{'server'}->{'select'}->can_read();
466 7 50       2835 return undef if ! @waiting;
467 7         202 return $waiting[rand @waiting];
468             }
469              
470       7 1   sub can_read_hook {}
471              
472             sub post_accept {
473 12     12 1 28 my $self = shift;
474 12         64 my $prop = $self->{'server'};
475 12   33     56 my $client = shift || $prop->{'client'};
476              
477 12         31 $prop->{'requests'}++;
478 12 100       39 return if $prop->{'udp_true'}; # no need to do STDIN/STDOUT in UDP
479              
480 11 50       35 if (!$client) {
481 0         0 $self->log(1,"Client socket information could not be determined!");
482 0         0 return;
483             }
484              
485 11 100       166 $client->post_accept() if $client->can("post_accept");
486 11 100       2853 if (! $prop->{'no_client_stdout'}) {
487 10         70 close STDIN; # duplicate some handles and flush them
488 10         87 close STDOUT;
489 10 100 66     187 if ($prop->{'tie_client_stdout'} || ($client->can('tie_stdout') && $client->tie_stdout)) {
    50 100        
490 4 50       113 open STDIN, '<', '/dev/null' or die "Couldn't open STDIN to the client socket: $!";
491 4 50       129 open STDOUT, '>', '/dev/null' or die "Couldn't open STDOUT to the client socket: $!";
492 4 50       53 tie *STDOUT, 'Net::Server::TiedHandle', $client, $prop->{'tied_stdout_callback'} or die "Couldn't tie STDOUT: $!";
493 4 50       23 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       101 open STDIN, '<&', $fileno or die "Couldn't open STDIN to the client socket: $!";
496 6 50       47 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         69 STDIN->autoflush(1);
502 10         532 STDOUT->autoflush(1);
503 10         370 select STDOUT;
504             }
505             }
506              
507             sub get_client_info {
508 12     12 1 36 my $self = shift;
509 12         32 my $prop = $self->{'server'};
510 12   33     73 my $client = shift || $prop->{'client'};
511              
512 12 50       56 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       135 if (my $sockname = $client->sockname) {
519 12         287 $prop->{'sockaddr'} = $client->sockhost;
520 12         755 $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         362 my $addr;
526 12 100       152 if ($prop->{'udp_true'}) {
    50          
527 1 50       9 if ($client->sockdomain == AF_INET) {
528 1         14 ($prop->{'peerport'}, $addr) = Socket::sockaddr_in($prop->{'udp_peer'});
529 1         16 $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         468 $addr = $client->peeraddr;
538 11         450 $prop->{'peeraddr'} = $client->peerhost;
539 11         451 $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     646 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     118 ." 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 34 my $self = shift;
563 13         94 my $prop = $self->{'server'};
564 13   66     56 my $sock = shift || $prop->{'client'};
565              
566             # unix sockets are immune to this check
567 13 50 33     137 return 1 if $sock && $sock->NS_proto =~ /^UNIX/;
568              
569             # if no allow or deny parameters are set, allow all
570 13         102 return 1 if ! @{ $prop->{'allow'} }
571 13         70 && ! @{ $prop->{'deny'} }
572 13         83 && ! @{ $prop->{'cidr_allow'} }
573 13 50 33     27 && ! @{ $prop->{'cidr_deny'} };
  13   33     164  
      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 46 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 32 my $self = shift;
610 10         24 my $prop = $self->{'server'};
611              
612 10 100       40 if ($prop->{'udp_true'}) { # udp echo server
613 1   33     2 my $client = shift || $prop->{'client'};
614 1 50       8 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         7 return $client->send("You said \"$prop->{'udp_data'}\"", 0);
619             }
620              
621 9         503 print "Welcome to \"".ref($self)."\" ($$)\015\012";
622 9         2338 my $previous_alarm = alarm 30;
623 9         27 eval {
624 9     0   205 local $SIG{'ALRM'} = sub { die "Timed Out!\n" };
  0         0  
625 9         818 while () {
626 9         35540 s/[\r\n]+$//;
627 9         326 print ref($self),":$$: You said \"$_\"\015\012";
628 9         436 $self->log(5, $_); # very verbose log
629 9 50       141 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         10 elsif (/quit/) { last }
632 6         102 elsif (/exit/) { $self->server_close }
633 0         0 alarm 30; # another 30
634             }
635 3         53 alarm($previous_alarm);
636             };
637 3         13 alarm 0;
638 3 50       21 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 17 my $self = shift;
647 6         84 $self->close_client_stdout;
648             }
649              
650             sub close_client_stdout {
651 6     6 0 15 my $self = shift;
652 6         17 my $prop = $self->{'server'};
653 6 100       40 return if $prop->{'udp_true'};
654              
655 5 100       103 if (! $prop->{'no_client_stdout'}) {
656 4 100       23 my $t = tied *STDOUT; if ($t) { undef $t; untie *STDOUT };
  4         17  
  2         6  
  2         43  
657 4 100       13 $t = tied *STDIN; if ($t) { undef $t; untie *STDIN };
  4         17  
  2         8  
  2         9  
658 4 50       185 open(STDIN, '<', '/dev/null') || die "Cannot read /dev/null [$!]";
659 4 50       87 open(STDOUT, '>', '/dev/null') || die "Cannot write /dev/null [$!]";
660             }
661 5         87 $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 43447 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 74 my ($self, $exit_val) = @_;
704 12         39 my $prop = $self->{'server'};
705              
706 12         178 $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     135 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         378 $self->pre_server_close_hook;
721              
722 12         81 $self->log(2, $self->log_time . " Server closing!");
723              
724 12 0 33     76 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     81 if ($prop->{'_HUP'} && $prop->{'leave_children_open_on_hup'}) {
737 0         0 $self->hup_children;
738              
739             } else {
740 12 100       129 $self->close_children() if $prop->{'children'};
741 12         186 $self->post_child_cleanup_hook;
742             }
743              
744 12 50 66     132 if (defined($prop->{'lock_file'})
      66        
745             && -e $prop->{'lock_file'}
746             && defined($prop->{'lock_file_unlink'})) {
747 2 50       84 unlink($prop->{'lock_file'}) || $self->log(1, "Couldn't unlink \"$prop->{'lock_file'}\" [$!]");
748             }
749 12 0 33     66 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       48 if ($prop->{'_HUP'}) {
757 0         0 $self->restart_close_hook();
758 0         0 $self->hup_server; # execs at the end
759             }
760              
761 12         105 $self->shutdown_sockets;
762 12 50       50 return $self if $prop->{'no_exit_on_close'};
763 12         81 $self->server_exit($exit_val);
764             }
765              
766             sub server_exit {
767 12     12 1 56 my ($self, $exit_val) = @_;
768 12   50     1511 exit($exit_val || 0);
769             }
770              
771             sub shutdown_sockets {
772 12     12 1 33 my $self = shift;
773 12         32 my $prop = $self->{'server'};
774              
775 12         30 foreach my $sock (@{ $prop->{'sock'} }) { # unlink remaining socket files (if any)
  12         75  
776 16         247 $sock->shutdown(2);
777 16 50       431 unlink $sock->NS_port if $sock->NS_proto =~ /^UNIX/;
778             }
779              
780 12         103 $prop->{'sock'} = []; # delete the sock objects
781 12         89 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 8 my $self = shift;
797 3         9 my $prop = $self->{'server'};
798 3 50 50     30 return unless $prop->{'children'} && scalar keys %{ $prop->{'children'} };
  3         19  
799              
800 3         8 foreach my $pid (keys %{ $prop->{'children'} }) {
  3         14  
801 4         28 $self->log(4, "Kill TERM pid $pid");
802 4 50 33     103 if (kill(15, $pid) || ! kill(0, $pid)) { # if it is killable, kill it
803 4         66 $self->delete_child($pid);
804             }
805             }
806              
807 3         33 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 336 my ($self, $level, $msg, @therest) = @_;
895 93         221 my $prop = $self->{'server'};
896 93 50       277 return if ! $prop->{'log_level'};
897 93 100 66     1078 return if $level =~ /^\d+$/ && $level > $prop->{'log_level'};
898 68 50       222 $msg = sprintf($msg, @therest) if @therest; # if multiple arguments are passed, assume that the first is a format string
899              
900 68 50       227 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       313 return if $level !~ /^\d+$/;
911 68         305 $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 294 my ($self, $level, $msg) = @_;
920 68         139 my $prop = $self->{'server'};
921 68         153 chomp $msg;
922 68         239 $msg =~ s/([^\n\ -\~])/sprintf("%%%02X",ord($1))/eg;
  0         0  
923              
924 68 50       261 if ($prop->{'log_file'}) {
    50          
925 0         0 print _SERVER_LOG $msg, "\n";
926             } elsif ($prop->{'setsid'}) {
927             # do nothing ?
928             } else {
929 68         274 my $old = select STDERR;
930 68         313 print $msg. "\n";
931 68         404 select $old;
932             }
933             }
934              
935              
936             sub log_time {
937 81     81 0 2612 my ($sec,$min,$hour,$day,$mon,$year) = localtime;
938 81         1256 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 338 my $self = shift;
945 106   50     247 my $ref = shift || {};
946 106         156 my $prop = $self->{'server'};
947              
948 106         263 foreach (qw(port host proto ipv allow deny cidr_allow cidr_deny)) {
949 848 100       1420 if (! defined $prop->{$_}) {
    100          
950 552         1093 $prop->{$_} = [];
951             } elsif (! ref $prop->{$_}) {
952 8         14 $prop->{$_} = [$prop->{$_}]; # nicely turn us into an arrayref if we aren't one already
953             }
954 848         1371 $ref->{$_} = $prop->{$_};
955             }
956              
957 106         178 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         3506 $ref->{$_} = \$prop->{$_};
966             }
967 106         205 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 266 my ($self, $args, $template) = @_;
975 133 100 66     867 $self->options($template = {}) if ! $template || ! ref $template;
976 133 0 66     788 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         196 my %previously_set;
982 133         345 foreach (my $i = 0; $i < @$args; $i++) {
983 332 100 100     2172 if ($args->[$i] =~ /^(?:--)?(\w+)(?:[=\ ](\S+))?$/
984             && exists $template->{$1}) {
985 314         901 my ($key, $val) = ($1, $2);
986 314         497 splice @$args, $i, 1;
987 314 100       605 if (! defined $val) {
988 311 50 66     1341 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         543 $val = splice @$args, $i, 1;
993 311 50 100     733 $val = $val->[0] if ref($val) eq 'ARRAY' && @$val == 1 && ref($template->{$key}) ne 'ARRAY';
      66        
994             }
995             }
996 314         419 $i--;
997 314 100       607 $val =~ s/%([A-F0-9])/chr(hex $1)/eig if ! ref $val;
  0         0  
998              
999 314 100       681 if (ref $template->{$key} eq 'ARRAY') {
1000 184 100       353 if (! defined $previously_set{$key}) {
1001 142         192 $previously_set{$key} = scalar @{ $template->{$key} };
  142         329  
1002             }
1003 184 100       361 next if $previously_set{$key};
1004 172 100       223 push @{ $template->{$key} }, ref($val) eq 'ARRAY' ? @$val : $val;
  172         850  
1005             } else {
1006 130 100       296 if (! defined $previously_set{$key}) {
1007 112 100       151 $previously_set{$key} = defined(${ $template->{$key} }) ? 1 : 0;
  112         287  
1008             }
1009 130 100       339 next if $previously_set{$key};
1010 105 50       214 die "Found multiple values on the configuration item \"$key\" which expects only one value" if ref($val) eq 'ARRAY';
1011 105         165 ${ $template->{$key} } = $val;
  105         512  
1012             }
1013             }
1014             }
1015             }
1016              
1017             sub _read_conf {
1018 9     9   16 my ($self, $file) = @_;
1019 9         15 my @args;
1020 9 50       36 $file = ($file =~ m|^([\w\.\-\/\\\:]+)$|) ? $1 : $self->fatal("Unsecure filename \"$file\"");
1021 9 50       205 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         121 while (defined(my $line = <$fh>)) {
1026 189 100       748 push @args, $1, $2 if $line =~ m/^\s* ((?:--)?\w+) (?:\s*[=:]\s*|\s+) (\S+)/x;
1027             }
1028 9         47 close $fh;
1029 9         41 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 15 my ($self, $pid) = @_;
1040 4         10 my $prop = $self->{'server'};
1041              
1042 4 50       18 return $self->other_child_died_hook($pid) if ! exists $prop->{'children'}->{$pid};
1043              
1044             # prefork server check to clear child communication
1045 4 100       14 if ($prop->{'child_communication'}) {
1046 1 50       5 if ($prop->{'children'}->{$pid}->{'sock'}) {
1047 1         6 $prop->{'child_select'}->remove($prop->{'children'}->{$pid}->{'sock'});
1048 1         78 $prop->{'children'}->{$pid}->{'sock'}->close;
1049             }
1050             }
1051            
1052 4         71 $self->delete_child_hook($pid); # user customizable hook
1053              
1054 4         20 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 10 my $self = shift;
1069 3   50     28 my $ref = $self->{'server'}->{'sig_passthrough'} || [];
1070 3 50       96 $ref = [$ref] if ! ref $ref;
1071 3 50       51 $self->fatal('invalid sig_passthrough') if ref $ref ne 'ARRAY';
1072 3 50       21 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   28 sub TIEHANDLE { my $pkg = shift; return bless [@_], $pkg }
  8         163  
1091 2 50   2   5 sub READLINE { my $s = shift; $s->[1] ? $s->[1]->($s->[0], 'getline', @_) : $s->[0]->getline }
  2         43  
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   186 sub PRINT { my $s = shift; $s->[1] ? $s->[1]->($s->[0], 'print', @_) : $s->[0]->print(@_) }
  7         63  
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