Add support for the "Multiplex" personality
authorMike Rylander <mrylander@gmail.com>
Wed, 11 Sep 2013 19:52:00 +0000 (15:52 -0400)
committerMike Rylander <mrylander@gmail.com>
Wed, 11 Sep 2013 19:52:00 +0000 (15:52 -0400)
Signed-off-by: Mike Rylander <mrylander@gmail.com>
SIPServer.pm
SIPconfig.xml

index ff80f38..7bdc5b1 100644 (file)
@@ -1,7 +1,9 @@
 #
 # 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
@@ -23,6 +25,7 @@ use strict;
 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;
@@ -82,14 +85,24 @@ push @parms,
     "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');
     }
 }
 
@@ -104,7 +117,8 @@ SIPServer->run(@parms);
 #
 
 # 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;
@@ -142,6 +156,115 @@ sub process_request {
     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
 #
index e5feefd..2989be8 100644 (file)
@@ -1,8 +1,10 @@
 <!--
 #
 # 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
 
   <error-detect enabled="true" />
 
-  <!-- Set Net::Server::PreFork runtime parameters -->
+  <!-- Set Net::Server runtime parameters.  "personality" may -->
+  <!-- be either PreFork or Mulitplex. -->
   <server-params
+           personality='PreFork'
            min_servers='1'
            min_spare_servers='0' />