#
# Copyright (C) 2006-2008 Georgia Public Library Service
+# Copyright (C) 2013 Equinox Software, Inc.
#
# Author: David J. Fiander
+# Author: Mike Rylander
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of version 2 of the GNU General Public
use warnings;
use Exporter;
use Sys::Syslog qw(syslog);
+use Net::Server::Multiplex;
use Net::Server::PreFork;
use Net::Server::Proto;
use IO::Socket::INET;
"syslog_facility=" . LOG_SIP;
#
-# Server Management: set parameters for the Net::Server::PreFork
-# module. The module silently ignores parameters that it doesn't
+# Server Management: set parameters for the Net::Server personality
+# chosen, defaulting to PreFork.
+#
+# The PreFork module silently ignores parameters that it doesn't
# recognize, and complains about invalid values for parameters
# that it does.
#
+# The Fork module only cares about max_servers, for our purposes, which
+# defaults to 256.
+#
+# The Multiplex module ignores all runtime params, and triggers an
+# alternate implementation of the processing loop. See the Net::Server
+# personality documentation for details.
+#
if (defined($config->{'server-params'})) {
while (my ($key, $val) = each %{$config->{'server-params'}}) {
push @parms, $key . '=' . $val;
+ @ISA = ('Net::Server::'.$val) if ($key eq 'personality');
}
}
#
# process_request is the callback used by Net::Server to handle
-# an incoming connection request.
+# an incoming connection request when the peronsality is either
+# Fork or PreFork.
sub process_request {
my $self = shift;
syslog("LOG_INFO", '%s: shutting down', $transport);
}
+# mux_input is the callback used by Net::Server to handle
+# an incoming connection request when the peronsality is
+# Multiplex.
+
+my %kid_hash;
+my $kid_count;
+
+sub REAPER {
+ for (keys(%kid_hash)) {
+ if ( my $reaped = waitpid($_, &WNOHANG) > 0 ) {
+ # Mourning... done.
+ $kid_count--;
+ delete $kid_hash{$_};
+ }
+ }
+ $SIG{CHLD} = &REAPER();
+}
+
+my %active_connections;
+
+sub mux_connection {
+ my $self = shift;
+ my $mux = shift;
+ my $fh = shift;
+
+ my $service;
+ my $sockname;
+ my ($sockaddr, $port, $proto);
+ my $transport;
+
+ $self->{config} = $config;
+
+ $sockaddr = $self->{net_server}{server}->{sockaddr};
+ $port = $self->{net_server}{server}->{sockport};
+ $proto = $self->{net_server}{server}->{client}->NS_proto();
+
+ syslog('LOG_INFO', "Inbound connection from $sockaddr on port $port and proto $proto");
+
+ $self->{service} = $config->find_service( $sockaddr, $port, $proto );
+
+ if (! defined($self->{service})) {
+ syslog( "LOG_ERR", "process_request: Unrecognized server connection: %s:%s/%s",
+ $sockaddr, $port, $proto );
+ die "process_request: Bad server connection";
+ }
+
+ $transport = $transports{ $self->{service}->{transport} };
+
+ $active_connections{fileno($fh)} = { complete => 0, transport => $transport, net_server => bless({%$self}, ref($self)) };
+
+ if ( !defined($transport) ) {
+ syslog("LOG_WARNING", "Unknown transport '%s', dropping", $service->{transport});
+ return;
+ }
+
+}
+
+
+sub mux_input {
+ my $mself = shift;
+ my $mux = shift;
+ my $fh = shift;
+
+ # check for kids that went away
+ REAPER();
+
+ my $connection = $active_connections{fileno($fh)};
+ my $self = $connection->{net_server};
+
+ if (!$connection->{complete}) { # log them in
+ my $transport = $connection->{transport};
+
+ &$transport($self);
+
+ $self->{ils}->disconnect() if (UNIVERSAL::can($self->{ils},'disconnect'));
+ $connection->{ils} = ref($self->{ils});
+ delete $$self{ils};
+
+ $connection->{complete} = 1;
+
+ return;
+ }
+
+ my $pid = fork();
+ die "Cannot fork: $!" unless (defined($pid) && $pid > -1);
+ if ($pid == 0) { # in kid
+
+ # build the connection we deleted after logging in
+ $self->{ils} = $connection->{ils}->new($self->{institution}, $self->{account});
+
+ my $input = Sip::read_SIP_packet($fh);
+ $input =~ s/[\r\n]+$//sm; # Strip off any trailing line ends
+
+ my $status = Sip::MsgType::handle($input, $self, '');
+
+ if (!$status) {
+ syslog("LOG_ERR", "raw_transport: failed to handle %s", substr($input,0,2));
+ die "sip_protocol_loop: failed Sip::MsgType::handle('$input', $self, '')";
+ }
+
+ exit(0);
+
+ } else { # in parent
+ $kid_count++;
+ $kid_hash{$pid} = 1;
+ }
+
+}
+
#
# Transports
#