--- loncom/loncnew	2003/04/24 10:56:55	1.4
+++ loncom/loncnew	2003/06/11 02:04:35	1.8
@@ -2,7 +2,7 @@
 # The LearningOnline Network with CAPA
 # lonc maintains the connections to remote computers
 #
-# $Id: loncnew,v 1.4 2003/04/24 10:56:55 foxr Exp $
+# $Id: loncnew,v 1.8 2003/06/11 02:04:35 foxr Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -34,13 +34,26 @@
 #    - Add timer dispatch.       (done)
 #    - Add ability to accept lonc UNIX domain sockets.  (done)
 #    - Add ability to create/negotiate lond connections (done).
-#    - Add general logic for dispatching requests and timeouts.
-#    - Add support for the lonc/lond requests.
+#    - Add general logic for dispatching requests and timeouts. (done).
+#    - Add support for the lonc/lond requests.          (done).
 #    - Add logging/status monitoring.
 #    - Add Signal handling - HUP restarts. USR1 status report.
-#    - Add Configuration file I/O
-#    - Add Pending request processing on startup.
+#    - Add Configuration file I/O                       (done).
 #    - Add management/status request interface.
+#    - Add deferred request capability.                  (done)
+#
+
+# Change log:
+#    $Log: loncnew,v $
+#    Revision 1.8  2003/06/11 02:04:35  foxr
+#    Support delayed transactions... this is done uniformly by encapsulating
+#    transactions in an object ... a LondTransaction that is implemented by
+#    LondTransaction.pm
+#
+#    Revision 1.7  2003/06/03 01:59:39  foxr
+#    complete coding to support deferred transactions.
+#
+#
 
 use lib "/home/httpd/lib/perl/";
 use lib "/home/foxr/newloncapa/types";
@@ -49,11 +62,13 @@ use POSIX qw(:signal_h);
 use IO::Socket;
 use IO::Socket::INET;
 use IO::Socket::UNIX;
+use IO::Handle;
 use Socket;
 use Crypt::IDEA;
 use LONCAPA::Queue;
 use LONCAPA::Stack;
 use LONCAPA::LondConnection;
+use LONCAPA::LondTransaction;
 use LONCAPA::Configuration;
 use LONCAPA::HashIterator;
 
@@ -85,7 +100,7 @@ my %ChildHash;			# by pid -> host.
 my $MaxConnectionCount = 5;	# Will get from config later.
 my $ClientConnection = 0;	# Uniquifier for client events.
 
-my $DebugLevel = 5;
+my $DebugLevel = 2;
 my $IdleTimeout= 3600;		# Wait an hour before pruning connections.
 
 #
@@ -95,14 +110,20 @@ my $RemoteHost;			# Name of host child i
 my $UnixSocketDir= "/home/httpd/sockets"; 
 my $IdleConnections = Stack->new(); # Set of idle connections
 my %ActiveConnections;		# Connections to the remote lond.
-my %ActiveTransactions;		# Transactions in flight.
+my %ActiveTransactions;		# LondTransactions in flight.
 my %ActiveClients;		# Serial numbers of active clients by socket.
 my $WorkQueue       = Queue->new(); # Queue of pending transactions.
-my $ClientQueue     = Queue->new(); # Queue of clients causing xactinos.
+#  my $ClientQueue     = Queue->new(); # Queue of clients causing xactinos.
 my $ConnectionCount = 0;
 my $IdleSeconds     = 0;	# Number of seconds idle.
 
 #
+#   This disconnected socket makes posible a bit more regular
+#   code when processing delayed requests:
+#
+my $NullSocket = IO::Socket->new();
+
+#
 
 =pod
 
@@ -155,15 +176,28 @@ sub SocketDump {
 
 =pod
 
+=head2 ShowStatus
+
+ Place some text as our pid status.
+
+=cut
+sub ShowStatus {
+    my $status = shift;
+    $0 =  "lonc: ".$status;
+}
+
+=pod
+
 =head2 Tick
 
 Invoked  each timer tick.
 
 =cut
 
+
 sub Tick {
     my $client;
-    $0 = 'lonc: '.GetServerHost()." Connection count: ".$ConnectionCount;
+    ShowStatus(GetServerHost()." Connection count: ".$ConnectionCount);
     Debug(6, "Tick");
     Debug(6, "    Current connection count: ".$ConnectionCount);
     foreach $client (keys %ActiveClients) {
@@ -177,11 +211,26 @@ sub Tick {
 	$IdleSeconds++;
 	if($IdleSeconds > $IdleTimeout) { # Prune a connection...
 	    $Socket = $IdleConnections->pop();
-	    KillSocket($Socket, 0);
+	    KillSocket($Socket);
 	}
     } else {
 	$IdleSeconds = 0;	# Reset idle count if not idle.
     }
+
+    # Do we have work in the queue, but no connections to service them?
+    # If so, try to make some new connections to get things going again.
+    #
+    
+    my $Requests = $WorkQueue->Count();
+    if (($ConnectionCount == 0)  && ($Requests > 0)) {
+	my $Connections = ($Requests <= $MaxConnectionCount) ?
+	                           $Requests : $MaxConnectionCount;
+	Debug(1,"Work but no connections, starting ".$Connections." of them");
+	for ($i =0; $i < $Connections; $i++) {
+	    MakeLondConnection();
+	}
+       
+    }
 }
 
 =pod
@@ -222,24 +271,21 @@ long enough, it will be shut down and re
 
 sub ServerToIdle {
     my $Socket   = shift;	# Get the socket.
+    delete($ActiveTransactions{$Socket}); # Server has no transaction
 
     &Debug(6, "Server to idle");
 
     #  If there's work to do, start the transaction:
 
-    $reqdata = $WorkQueue->dequeue();
-    Debug(9, "Queue gave request data: ".$reqdata);
+    $reqdata = $WorkQueue->dequeue(); # This is a LondTransaction
     unless($reqdata eq undef)  {
-	my $unixSocket = $ClientQueue->dequeue();
-	&Debug(6, "Starting new work request");
-	&Debug(7, "Request: ".$reqdata);
-	
-	&StartRequest($Socket, $unixSocket, $reqdata);
+	Debug(9, "Queue gave request data: ".$reqdata->getRequest());
+	&StartRequest($Socket,  $reqdata);
+
     } else {
 	
     #  There's no work waiting, so push the server to idle list.
 	&Debug(8, "No new work requests, server connection going idle");
-	delete($ActiveTransactions{$Socket});
 	$IdleConnections->push($Socket);
     }
 }
@@ -274,51 +320,55 @@ sub ClientWritable {
     &Debug(6, "ClientWritable writing".$Data);
     &Debug(9, "Socket is: ".$Socket);
 
-    my $result = $Socket->send($Data, 0);
-
-    # $result undefined: the write failed.
-    # otherwise $result is the number of bytes written.
-    # Remove that preceding string from the data.
-    # If the resulting data is empty, destroy the watcher
-    # and set up a read event handler to accept the next
-    # request.
-
-    &Debug(9,"Send result is ".$result." Defined: ".defined($result));
-    if(defined($result)) {
-	&Debug(9, "send result was defined");
-	if($result == length($Data)) { # Entire string sent.
-	    &Debug(9, "ClientWritable data all written");
-	    $Watcher->cancel();
-	    #
-	    #  Set up to read next request from socket:
-	    
-	    my $descr     = sprintf("Connection to lonc client %d",
-				    $ActiveClients{$Socket});
-	    Event->io(cb    => \&ClientRequest,
-		      poll  => 'r',
-		      desc  => $descr,
-		      data  => "",
-		      fd    => $Socket);
-
-	} else {		# Partial string sent.
-	    $Watcher->data(substr($Data, $result));
-	}
+    if($Socket->connected) {
+	my $result = $Socket->send($Data, 0);
 	
-    } else {			# Error of some sort...
-
-	# Some errnos are possible:
-	my $errno = $!;
-	if($errno == POSIX::EWOULDBLOCK   ||
-	   $errno == POSIX::EAGAIN        ||
-	   $errno == POSIX::EINTR) {
-	    # No action taken?
-	} else {		# Unanticipated errno.
-	    &Debug(5,"ClientWritable error or peer shutdown".$RemoteHost);
-	    $Watcher->cancel;	# Stop the watcher.
-	    $Socket->shutdown(2); # Kill connection
-	    $Socket->close();	# Close the socket.
-	}
+	# $result undefined: the write failed.
+	# otherwise $result is the number of bytes written.
+	# Remove that preceding string from the data.
+	# If the resulting data is empty, destroy the watcher
+	# and set up a read event handler to accept the next
+	# request.
 	
+	&Debug(9,"Send result is ".$result." Defined: ".defined($result));
+	if(defined($result)) {
+	    &Debug(9, "send result was defined");
+	    if($result == length($Data)) { # Entire string sent.
+		&Debug(9, "ClientWritable data all written");
+		$Watcher->cancel();
+		#
+		#  Set up to read next request from socket:
+		
+		my $descr     = sprintf("Connection to lonc client %d",
+					$ActiveClients{$Socket});
+		Event->io(cb    => \&ClientRequest,
+			  poll  => 'r',
+			  desc  => $descr,
+			  data  => "",
+			  fd    => $Socket);
+		
+	    } else {		# Partial string sent.
+		$Watcher->data(substr($Data, $result));
+	    }
+	    
+	} else {			# Error of some sort...
+	    
+	    # Some errnos are possible:
+	    my $errno = $!;
+	    if($errno == POSIX::EWOULDBLOCK   ||
+	       $errno == POSIX::EAGAIN        ||
+	       $errno == POSIX::EINTR) {
+		# No action taken?
+	    } else {		# Unanticipated errno.
+		&Debug(5,"ClientWritable error or peer shutdown".$RemoteHost);
+		$Watcher->cancel;	# Stop the watcher.
+		$Socket->shutdown(2); # Kill connection
+		$Socket->close();	# Close the socket.
+	    }
+	    
+	}
+    } else {
+	$Watcher->cancel();	# A delayed request...just cancel.
     }
 }
 
@@ -339,22 +389,53 @@ Parameters:
 Socket on which the lond transaction occured.  This is a
 LondConnection. The data received is in the TransactionReply member.
 
-=item Client
+=item Transaction
 
-Unix domain socket open on the ultimate client.
+The transaction that is being completed.
 
 =cut
 
 sub CompleteTransaction {
     &Debug(6,"Complete transaction");
     my $Socket = shift;
-    my $Client = shift;
+    my $Transaction = shift;
+
+    if (!$Transaction->isDeferred()) { # Normal transaction
+	my $data   = $Socket->GetReply(); # Data to send.
+	StartClientReply($Transaction, $data);
+    } else {			# Delete deferred transaction file.
+	&Debug(4, "Deferred transaction complete: ".$Transaction->getFile().
+	       " request: ".$Transaction->getRequest().
+	       " answer: ".$Socket->GetReply());
+	unlink $Transaction->getFile();
+    }
+}
+=pod
+=head1 StartClientReply
+
+   Initiates a reply to a client where the reply data is a parameter.
+
+=head2  parameters:
+
+=item Transaction
+
+    The transaction for which we are responding to the client.
 
-    my $data   = $Socket->GetReply(); # Data to send.
+=item data
+
+    The data to send to apached client.
+
+=cut
+sub StartClientReply {
+    my $Transaction   = shift;
+    my $data     = shift;
+
+    my $Client   = $Transaction->getClient();
 
     &Debug(8," Reply was: ".$data);
     my $Serial         = $ActiveClients{$Client};
     my $desc           = sprintf("Connection to lonc client %d",
+
 				 $Serial);
     Event->io(fd       => $Client,
 	      poll     => "w",
@@ -366,30 +447,47 @@ sub CompleteTransaction {
 =head2 FailTransaction
 
   Finishes a transaction with failure because the associated lond socket
-  disconnected.  It is up to our client to retry if desired.  
+  disconnected.  There are two possibilities:
+  - The transaction is deferred: in which case we just quietly
+    delete the transaction since there is no client connection.
+  - The transaction is 'live' in which case we initiate the sending
+    of "con_lost" to the client.
+
+Deleting the transaction means killing it from the 
+%ActiveTransactions hash.
 
 Parameters:
 
 =item client  
  
-   The UNIX domain socket open on our client.
-
+   The LondTransaction we are failing.
+ 
 =cut
 
 sub FailTransaction {
-    my $client = shift;
+    my $transaction = shift;
+    my $Lond        = $transaction->getServer();
+    if (!$client->isDeferred()) { # If the transaction is deferred we'll get to it.
+	my $client  = $transcation->getClient();
+	StartClientReply($client, "con_lost");
+    }
+# not needed, done elsewhere if active.
+#    delete $ActiveTransactions{$Lond};
 
-    &Debug(8, "Failing transaction due to disconnect");
-    my $Serial = $ActiveClients{$client};
-    my $desc   = sprintf("Connection to lonc client %d", $Serial);
-    my $data   = "error: Connection to lond lost\n";
-
-    Event->io(fd     => $client,
-	      poll   => "w",
-	      desc   => $desc,
-	      cb     => \&ClientWritable,
-	      data   => $data);
+}
+
+=pod
+=head1  EmptyQueue
 
+  Fails all items in the work queue with con_lost.
+  Note that each item in the work queue is a transaction.
+
+=cut
+sub EmptyQueue {
+    while($WorkQueue->Count()) {
+	my $request = $Workqueue->dequeue(); # This is a transaction
+	FailTransaction($request);
+    }
 }
 
 =pod
@@ -416,10 +514,12 @@ nonzero if we are allowed to create a ne
 =cut
 sub KillSocket {
     my $Socket = shift;
-    my $Restart= shift;
 
-    #  If the socket came from the active connection set, delete it.
-    # otherwise it came from the idle set and has already been destroyed:
+    #  If the socket came from the active connection set,
+    #  delete its transaction... note that FailTransaction should
+    #  already have been called!!!
+    #  otherwise it came from the idle set.
+    #  
     
     if(exists($ActiveTransactions{$Socket})) {
 	delete ($ActiveTransactions{$Socket});
@@ -428,10 +528,13 @@ sub KillSocket {
 	delete($ActiveConnections{$Socket});
     }
     $ConnectionCount--;
-    if( ($ConnectionCount = 0) && ($Restart)) {
-	MakeLondConnection();
-    }
 
+    #  If the connection count has gone to zero and there is work in the
+    #  work queue, the work all gets failed with con_lost.
+    #
+    if($ConnectionCount == 0) {
+	EmptyQueue;
+    }
 }
 
 =pod
@@ -493,15 +596,17 @@ transaction is in progress, the socket a
 =cut
 
 sub LondReadable {
+
     my $Event      = shift;
     my $Watcher    = $Event->w;
     my $Socket     = $Watcher->data;
     my $client     = undef;
 
+    &Debug(6,"LondReadable called state = ".$State);
+
 
     my $State = $Socket->GetState(); # All action depends on the state.
 
-    &Debug(6,"LondReadable called state = ".$State);
     SocketDump(6, $Socket);
 
     if($Socket->Readable() != 0) {
@@ -513,7 +618,7 @@ sub LondReadable {
 	    FailTransaction($ActiveTransactions{$Socket});
 	}
 	$Watcher->cancel();
-	KillSocket($Socket, 1);
+	KillSocket($Socket);
 	return;
     }
     SocketDump(6,$Socket);
@@ -529,8 +634,8 @@ sub LondReadable {
 	# in the connection takes care of setting that up.  Just
 	# need to transition to writable:
 
-	$Watcher->poll("w");
 	$Watcher->cb(\&LondWritable);
+	$Watcher->poll("w");
 
     } elsif ($State eq "ChallengeReplied") {
 
@@ -539,22 +644,22 @@ sub LondReadable {
 	#  The ok was received.  Now we need to request the key
 	#  That requires us to be writable:
 
-	$Watcher->poll("w");
 	$Watcher->cb(\&LondWritable);
+	$Watcher->poll("w");
 
     } elsif ($State eq "ReceivingKey") {
 
     } elsif ($State eq "Idle") {
 	# If necessary, complete a transaction and then go into the
 	# idle queue.
+	$Watcher->cancel();
 	if(exists($ActiveTransactions{$Socket})) {
 	    Debug(8,"Completing transaction!!");
 	    CompleteTransaction($Socket, 
 				$ActiveTransactions{$Socket});
 	}
-	$Watcher->cancel();
 	ServerToIdle($Socket);	# Next work unit or idle.
-
+	
     } elsif ($State eq "SendingRequest") {
 	#  We need to be writable for this and probably don't belong
 	#  here inthe first place.
@@ -636,15 +741,15 @@ is the socket on which to return a reply
 sub LondWritable {
     my $Event   = shift;
     my $Watcher = $Event->w;
-    my @data    = $Watcher->data;
-    Debug(6,"LondWritable State = ".$State." data has ".@data." elts.\n");
+    my $Socket  = $Watcher->data;
+    my $State   = $Socket->GetState();
 
-    my $Socket  = $data[0];	# I know there's at least a socket.
+    Debug(6,"LondWritable State = ".$State."\n");
 
+ 
     #  Figure out what to do depending on the state of the socket:
     
 
-    my $State   = $Socket->GetState();
 
 
     SocketDump(6,$Socket);
@@ -654,12 +759,9 @@ sub LondWritable {
 	if ($Socket->Writable() != 0) {
 	    #  The write resulted in an error.
 	    # We'll treat this as if the socket got disconnected:
-	    if(exists($ActiveTransactions{$Socket})) {
-		Debug(3, "Lond connection lost, failing transactions");
-		FailTransaction($ActiveTransactions{$Socket});
-	    }
+
 	    $Watcher->cancel();
-	    KillSocket($Socket, 1);
+	    KillSocket($Socket);
 	    return;
 	}
 	#  "init" is being sent...
@@ -670,8 +772,8 @@ sub LondWritable {
 	# Now that init was sent, we switch 
 	# to watching for readability:
 
-	$Watcher->poll("r");
 	$Watcher->cb(\&LondReadable);
+	$Watcher->poll("r");
 
     } elsif ($State eq "ChallengeReceived") {
 	# We received the challenge, now we 
@@ -679,15 +781,18 @@ sub LondWritable {
 	# we're waiting for the state to change
 	
 	if($Socket->Writable() != 0) {
-	    # Write of the next chunk resulted in an error.
+
+	    $Watcher->cancel();
+	    KillSocket($Socket);
+	    return;
 	}
 	
     } elsif ($State eq "ChallengeReplied")  {
 	# The echo was sent back, so we switch
 	# to watching readability.
 
-	$Watcher->poll("r");
 	$Watcher->cb(\&LondReadable);
+	$Watcher->poll("r");
 
     } elsif ($State eq "RequestingKey")     {
 	# At this time we're requesting the key.
@@ -697,30 +802,41 @@ sub LondWritable {
 
 	if($Socket->Writable() != 0) {
 	    # Write resulted in an error.
-	}
 
+	    $Watcher->cancel();
+	    KillSocket($Socket);
+	    return;
+
+	}
     } elsif ($State eq "ReceivingKey")      {
 	# Now we need to wait for the key
 	# to come back from the peer:
 
-	$Watcher->poll("r");
 	$Watcher->cb(\&LondReadable);
+	$Watcher->poll("r");
 
     } elsif ($State eq "SendingRequest")    {
 	# At this time we are sending a request to the
 	# peer... write the next chunk:
 
 	if($Socket->Writable() != 0) {
-	    # Write resulted in an error.
 
+	    if(exists($ActiveTransactions{$Socket})) {
+		Debug(3, "Lond connection lost, failing transactions");
+		FailTransaction($ActiveTransactions{$Socket});
+	    }
+	    $Watcher->cancel();
+	    KillSocket($Socket);
+	    return;
+	    
 	}
 
     } elsif ($State eq "ReceivingReply")    {
 	# The send has completed.  Wait for the
 	# data to come in for a reply.
 	Debug(8,"Writable sent request/receiving reply");
-	$Watcher->poll("r");
 	$Watcher->cb(\&LondReadable);
+	$Watcher->poll("r");
 
     } else {
 	#  Control only passes here on an error: 
@@ -732,6 +848,35 @@ sub LondWritable {
     }
     
 }
+=pod
+    
+=cut
+sub QueueDelayed {
+    Debug(3,"QueueDelayed called");
+
+    my $path = "$perlvar{'lonSockDir'}/delayed";
+
+    Debug(4, "Delayed path: ".$path);
+    opendir(DIRHANDLE, $path);
+    
+    @alldelayed = grep /\.$RemoteHost$/, readdir DIRHANDLE;
+    Debug(4, "Got ".$alldelayed." delayed files");
+    closedir(DIRHANDLE);
+    my $dfname;
+    my $reqfile;
+    foreach $dfname (sort  @alldelayed) {
+	$reqfile = "$path/$dfname";
+	Debug(4, "queueing ".$reqfile);
+	my $Handle = IO::File->new($reqfile);
+	my $cmd    = <$Handle>;
+	chomp $cmd;		# There may or may not be a newline...
+	$cmd = $cmd."\ny";	# now for sure there's exactly one newline.
+	my $Transaction = LondTransaction->new($cmd);
+	$Transaction->SetDeferred($reqfile);
+	QueueTransaction($Transaction);
+    }
+    
+}
 
 =pod
 
@@ -753,31 +898,34 @@ sub MakeLondConnection {
 					 &GetServerPort());
 
     if($Connection == undef) {	# Needs to be more robust later.
-	die "Failed to make a connection!!".$!."\n";
+	Debug(0,"Failed to make a connection with lond.");
+    }  else {
+	# The connection needs to have writability 
+	# monitored in order to send the init sequence
+	# that starts the whole authentication/key
+	# exchange underway.
+	#
+	my $Socket = $Connection->GetSocket();
+	if($Socket == undef) {
+	    die "did not get a socket from the connection";
+	} else {
+	    &Debug(9,"MakeLondConnection got socket: ".$Socket);
+	}
 	
-    } 
-    # The connection needs to have writability 
-    # monitored in order to send the init sequence
-    # that starts the whole authentication/key
-    # exchange underway.
-    #
-    my $Socket = $Connection->GetSocket();
-    if($Socket == undef) {
-	die "did not get a socket from the connection";
-    } else {
-	&Debug(9,"MakeLondConnection got socket: ".$Socket);
+	
+	$event = Event->io(fd       => $Socket,
+			   poll     => 'w',
+			   cb       => \&LondWritable,
+			   data     => $Connection,
+			   desc => 'Connection to lond server');
+	$ActiveConnections{$Connection} = $event;
+	
+	$ConnectionCount++;
+	Debug(4, "Connection count = ".$ConnectionCount);
+	if($ConnectionCount == 1) { # First Connection:
+	    QueueDelayed;
+	}
     }
-
-    
-    $event = Event->io(fd       => $Socket,
-		       poll     => 'w',
-		       cb       => \&LondWritable,
-		       data     => ($Connection, undef),
-		       desc => 'Connection to lond server');
-    $ActiveConnections{$Connection} = $event;
-
-    $ConnectionCount++;
-   
     
 }
 
@@ -808,17 +956,17 @@ The text of the request to send.
 
 sub StartRequest {
     my $Lond     = shift;
-    my $Client   = shift;
-    my $Request  = shift;
+    my $Request  = shift;	# This is a LondTransaction.
     
-    Debug(6, "StartRequest: ".$Request);
+    Debug(6, "StartRequest: ".$Request->getRequest());
 
     my $Socket = $Lond->GetSocket();
     
-    $ActiveTransactions{$Lond} = $Client; # Socket to relay to client.
+    $Request->Activate($Lond);
+    $ActiveTransactions{$Lond} = $Request;
 
-    $Lond->InitiateTransaction($Request);
-    $event = Event->io(fd      => $Lond->GetSocket(),
+    $Lond->InitiateTransaction($Request->getRequest());
+    $event = Event->io(fd      => $Socket,
 		       poll    => "w",
 		       cb      => \&LondWritable,
 		       data    => $Lond,
@@ -849,15 +997,15 @@ data to send to the lond.
 =cut
 
 sub QueueTransaction {
-    my $requestSocket = shift;
-    my $requestData   = shift;
 
-    Debug(6,"QueueTransaction: ".$requestData);
+    my $requestData   = shift;	# This is a LondTransaction.
+    my $cmd           = $requestData->getRequest();
+
+    Debug(6,"QueueTransaction: ".$cmd);
 
     my $LondSocket    = $IdleConnections->pop();
     if(!defined $LondSocket) {	# Need to queue request.
 	Debug(8,"Must queue...");
-	$ClientQueue->enqueue($requestSocket);
 	$WorkQueue->enqueue($requestData);
 	if($ConnectionCount < $MaxConnectionCount) {
 	    Debug(4,"Starting additional lond connection");
@@ -865,7 +1013,7 @@ sub QueueTransaction {
 	}
     } else {			# Can start the request:
 	Debug(8,"Can start...");
-	StartRequest($LondSocket, $requestSocket, $requestData);
+	StartRequest($LondSocket,  $requestData);
     }
 }
 
@@ -905,7 +1053,9 @@ sub ClientRequest {
     $watcher->data($data);
     if($data =~ /(.*\n)/) {	# Request entirely read.
 	Debug(8, "Complete transaction received: ".$data);
-	QueueTransaction($socket, $data);
+	my $Transaction = LondTransaction->new($data);
+	$Transaction->SetClient($socket);
+	QueueTransaction($Transaction);
 	$watcher->cancel();	# Done looking for input data.
     }
 
@@ -967,7 +1117,7 @@ Returns the host whose lond we talk with
 
 =cut
 
-sub GetServerHost {		# Stub - get this from config.
+sub GetServerHost {
     return $RemoteHost;		# Setup by the fork.
 }
 
@@ -979,7 +1129,7 @@ Returns the lond port number.
 
 =cut
 
-sub GetServerPort {		# Stub - get this from config.
+sub GetServerPort {
     return $perlvar{londPort};
 }
 
@@ -1000,7 +1150,7 @@ sub SetupLoncListener {
     my $socket;
     my $SocketName = GetLoncSocketPath();
     unlink($SocketName);
-    unless ($socket = IO::Socket::UNIX->new(Local  => $SocketName,
+    unless ($socket =IO::Socket::UNIX->new(Local  => $SocketName,
 					    Listen => 10, 
 					    Type   => SOCK_STREAM)) {
 	die "Failed to create a lonc listner socket";
@@ -1043,7 +1193,11 @@ sub ChildProcess {
 # Setup the initial server connection:
     
     &MakeLondConnection();
-    
+
+    if($ConnectionCount == 0) {
+	Debug(1,"Could not make initial connection..\n");
+	Debug(1,"Will retry when there's work to do\n");
+    }
     Debug(9,"Entering event loop");
     my $ret = Event::loop();		#  Start the main event loop.
     
@@ -1062,6 +1216,7 @@ sub CreateChild {
     if($pid) {			# Parent
 	$ChildHash{$pid} = $RemoteHost;
     } else {			# child.
+	ShowStatus("Connected to ".$RemoteHost);
 	ChildProcess;
     }
 
@@ -1076,6 +1231,39 @@ sub CreateChild {
 #  Each exit gets logged and the child gets restarted.
 #
 
+#
+#   Fork and start in new session so hang-up isn't going to 
+#   happen without intent.
+#
+
+
+
+
+
+
+ShowStatus("Forming new session");
+my $childpid = fork;
+if ($childpid != 0) {
+    sleep 4;			# Give child a chacne to break to
+    exit 0;			# a new sesion.
+}
+#
+#   Write my pid into the pid file so I can be located
+#
+
+ShowStatus("Parent writing pid file:");
+$execdir = $perlvar{'lonDaemons'};
+open (PIDSAVE, ">$execdir/logs/lonc.pid");
+print PIDSAVE "$$\n";
+close(PIDSAVE);
+
+if (POSIX::setsid() < 0) {
+    print "Could not create new session\n";
+    exit -1;
+}
+
+ShowStatus("Forking node servers");
+
 my $HostIterator = LondConnection::GetHostIterator;
 while (! $HostIterator->end()) {
 
@@ -1086,6 +1274,8 @@ while (! $HostIterator->end()) {
 
 # Maintain the population:
 
+ShowStatus("Parent keeping the flock");
+
 while(1) {
     $deadchild = wait();
     if(exists $ChildHash{$deadchild}) {	# need to restart.