Optimistic login using fork-and-check over File::Queue
authorMike Rylander <mrylander@gmail.com>
Fri, 25 Jul 2014 18:30:11 +0000 (14:30 -0400)
committerBill Erickson <berick@esilibrary.com>
Fri, 8 Aug 2014 14:08:36 +0000 (10:08 -0400)
Signed-off-by: Mike Rylander <mrylander@gmail.com>
SIPServer.pm

index 669f6fd..0538f7e 100644 (file)
@@ -41,6 +41,12 @@ use Sip::Configuration;
 use Sip::Checksum qw(checksum verify_cksum);
 use Sip::MsgType;
 
+use File::Queue;
+use Storable qw(freeze thaw);
+
+my $mp_fifo = new File::Queue ( "/tmp/SIPServer.mulitplex-fifo.$$");
+END { $mp_fifo->delete };
+
 use constant LOG_SIP => "local6"; # Local alias for the logging facility
 
 our $VERSION = 0.02;
@@ -184,6 +190,17 @@ sub REAPER {
 }
 
 my %active_connections;
+sub PERMAFROST {
+    while (my $login = $mp_fifo->deq) {
+        $login = thaw($login);
+        if ($$login{success}) {
+            $active_connections{$$login{id}} = $login;
+        } else {
+            delete $active_connections{$$login{id}};
+        }
+    }
+}
+
 sub mux_input {
     my $mself = shift;
     my $mux = shift;
@@ -201,6 +218,12 @@ sub mux_input {
     # check for kids that went away
     REAPER();
 
+    # and process any pending logins
+    PERMAFROST();
+
+    my $c = scalar(keys %active_connections);
+    syslog("LOG_DEBUG", "multi: new active connection; $c total");
+
     if ($kid_count >= $max_concurrent) {
         # XXX should we say something to the client? maybe wait and try again?
         syslog('LOG_ERR', "Unwilling to fork new child process, at least $max_concurrent already ongoing");
@@ -237,33 +260,45 @@ sub mux_input {
             return;
         }
 
-        eval { &$transport($self, $str_fh) };
-        if ($@) {
-            syslog('LOG_ERR', "ILS login error: $@");
+        # We stick this here, assuming success. Cleanup comes later via PERMAFROST().
+        $active_connections{$conn_id} = { id => $conn_id, transport => $transport };
+        my $pid = fork();
+        if (!defined($pid) or $pid < 0) {
+            syslog('LOG_ERR', "Unable to fork new child process $!");
             return;
         }
 
-        $active_connections{$conn_id} =
-            { id         => $conn_id,
-              transport  => $transport,
-              net_server => bless({%$self}, ref($self))
-            };
-    
-        # Evergreen, at least, needs a chance to clean up before forking for other requests
-        $self->{ils}->disconnect() if (UNIVERSAL::can($self->{ils},'disconnect'));
-
-        # Stash the ILS module somewhere handy for later
-        $active_connections{$conn_id}->{ils} = ref($self->{ils});
-
-        # And, if the ILS module wants to save some state, allow that
-        $active_connections{$conn_id}->{state} = $self->{ils}->state() if (UNIVERSAL::can($self->{ils},'state'));;
+        if ($pid == 0) { # in kid
 
-        delete $$self{ils};
+            eval { &$transport($self, $str_fh) };
 
-        my $c = scalar(keys %active_connections);
-        syslog("LOG_DEBUG", "multi: new active connection; $c total");
+            my $success = 1;
+            if ($@) {
+                syslog('LOG_ERR', "ILS login error: $@");
+                $success = 0;
+                exit(0);
+            }
+    
+            # Evergreen, at least, needs a chance to clean up before forking for other requests
+            $self->{ils}->disconnect() if (UNIVERSAL::can($self->{ils},'disconnect'));
+
+            # Stash the ILS module somewhere handy for later
+            $self->{ils} = ref($self->{ils});
+
+            $mp_fifo->enq(
+                freeze({
+                    id => $conn_id,
+                    success => $success,
+                    transport  => $transport,
+                    net_server => bless({%$self}, ref($self))
+                })
+            );
+    
+            exit(0);
+        }
 
-        return;
+        return; # NEXT CUSTOMER PLEASE STEP UP
     }
 
     $self = $active_connections{$conn_id}->{net_server};
@@ -351,6 +386,7 @@ sub raw_transport {
                 die 'raw_transport: sending SC status before login not enabled, exiting';
             }
             Sip::MsgType::handle($input, $self, SC_STATUS);
+            $strikes++; # it's allowed, don't charge for it
             next;
         }
         last if Sip::MsgType::handle($input, $self, LOGIN);