Annotation of loncom/loncnew, revision 1.69

1.1       foxr        1: #!/usr/bin/perl
1.2       albertel    2: # The LearningOnline Network with CAPA
                      3: # lonc maintains the connections to remote computers
                      4: #
1.69    ! matthew     5: # $Id: loncnew,v 1.68 2005/03/15 01:12:20 albertel Exp $
1.2       albertel    6: #
                      7: # Copyright Michigan State University Board of Trustees
                      8: #
                      9: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
1.17      foxr       10: ## LON-CAPA is free software; you can redistribute it and/or modify
1.2       albertel   11: # it under the terms of the GNU General Public License as published by
                     12: # the Free Software Foundation; either version 2 of the License, or
                     13: # (at your option) any later version.
                     14: #
                     15: # LON-CAPA is distributed in the hope that it will be useful,
                     16: # but WITHOUT ANY WARRANTY; without even the implied warranty of
                     17: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                     18: # GNU General Public License for more details.
                     19: #
                     20: # You should have received a copy of the GNU General Public License
                     21: # along with LON-CAPA; if not, write to the Free Software
                     22: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
                     23: #
                     24: # /home/httpd/html/adm/gpl.txt
                     25: #
                     26: # http://www.lon-capa.org/
                     27: #
1.1       foxr       28: #
1.15      foxr       29: # new lonc handles n request out bver m connections to londs.
1.1       foxr       30: # This module is based on the Event class.
                     31: #   Development iterations:
                     32: #    - Setup basic event loop.   (done)
                     33: #    - Add timer dispatch.       (done)
                     34: #    - Add ability to accept lonc UNIX domain sockets.  (done)
                     35: #    - Add ability to create/negotiate lond connections (done).
1.7       foxr       36: #    - Add general logic for dispatching requests and timeouts. (done).
                     37: #    - Add support for the lonc/lond requests.          (done).
1.38      foxr       38: #    - Add logging/status monitoring.                    (done)
                     39: #    - Add Signal handling - HUP restarts. USR1 status report. (done)
1.7       foxr       40: #    - Add Configuration file I/O                       (done).
1.38      foxr       41: #    - Add management/status request interface.         (done)
1.8       foxr       42: #    - Add deferred request capability.                  (done)
1.38      foxr       43: #    - Detect transmission timeouts.                     (done)
1.7       foxr       44: #
                     45: 
1.23      foxr       46: use strict;
1.1       foxr       47: use lib "/home/httpd/lib/perl/";
                     48: use Event qw(:DEFAULT );
                     49: use POSIX qw(:signal_h);
1.12      foxr       50: use POSIX;
1.1       foxr       51: use IO::Socket;
                     52: use IO::Socket::INET;
                     53: use IO::Socket::UNIX;
1.9       foxr       54: use IO::File;
1.6       foxr       55: use IO::Handle;
1.1       foxr       56: use Socket;
                     57: use Crypt::IDEA;
                     58: use LONCAPA::Queue;
                     59: use LONCAPA::Stack;
                     60: use LONCAPA::LondConnection;
1.7       foxr       61: use LONCAPA::LondTransaction;
1.1       foxr       62: use LONCAPA::Configuration;
                     63: use LONCAPA::HashIterator;
1.67      albertel   64: use Fcntl qw(:flock);
1.1       foxr       65: 
                     66: 
                     67: # Read the httpd configuration file to get perl variables
                     68: # normally set in apache modules:
                     69: 
                     70: my $perlvarref = LONCAPA::Configuration::read_conf('loncapa.conf');
                     71: my %perlvar    = %{$perlvarref};
                     72: 
                     73: #
                     74: #  parent and shared variables.
                     75: 
                     76: my %ChildHash;			# by pid -> host.
1.26      foxr       77: my %HostToPid;			# By host -> pid.
                     78: my %HostHash;			# by loncapaname -> IP.
1.62      foxr       79: my %listening_to;		# Socket->host table for who the parent
                     80:                                 # is listening to.
                     81: my %parent_dispatchers;         # host-> listener watcher events. 
1.1       foxr       82: 
1.65      foxr       83: my %parent_handlers;		# Parent signal handlers...
                     84: 
1.9       foxr       85: my $MaxConnectionCount = 10;	# Will get from config later.
1.1       foxr       86: my $ClientConnection = 0;	# Uniquifier for client events.
                     87: 
1.9       foxr       88: my $DebugLevel = 0;
1.29      foxr       89: my $NextDebugLevel= 2;		# So Sigint can toggle this.
1.50      albertel   90: my $IdleTimeout= 600;		# Wait 10 minutes before pruning connections.
1.1       foxr       91: 
1.39      foxr       92: my $LogTransactions = 0;	# When True, all transactions/replies get logged.
1.65      foxr       93: my $executable      = $0;	# Get the full path to me.
1.39      foxr       94: 
1.1       foxr       95: #
                     96: #  The variables below are only used by the child processes.
                     97: #
                     98: my $RemoteHost;			# Name of host child is talking to.
1.20      albertel   99: my $UnixSocketDir= $perlvar{'lonSockDir'};
1.1       foxr      100: my $IdleConnections = Stack->new(); # Set of idle connections
                    101: my %ActiveConnections;		# Connections to the remote lond.
1.7       foxr      102: my %ActiveTransactions;		# LondTransactions in flight.
1.1       foxr      103: my %ActiveClients;		# Serial numbers of active clients by socket.
                    104: my $WorkQueue       = Queue->new(); # Queue of pending transactions.
                    105: my $ConnectionCount = 0;
1.4       foxr      106: my $IdleSeconds     = 0;	# Number of seconds idle.
1.9       foxr      107: my $Status          = "";	# Current status string.
1.14      foxr      108: my $RecentLogEntry  = "";
1.30      foxr      109: my $ConnectionRetries=2;	# Number of connection retries allowed.
                    110: my $ConnectionRetriesLeft=2;	# Number of connection retries remaining.
1.40      foxr      111: my $LondVersion     = "unknown"; # Version of lond we talk with.
1.49      foxr      112: my $KeyMode         = "";       # e.g. ssl, local, insecure from last connect.
1.54      foxr      113: my $LondConnecting  = 0;       # True when a connection is being built.
1.1       foxr      114: 
1.60      foxr      115: 
                    116: 
1.65      foxr      117: my $DieWhenIdle     = 1;	# When true children die when trimmed -> 0.
1.62      foxr      118: my $I_am_child      = 0;	# True if this is the child process.
1.57      foxr      119: 
1.1       foxr      120: #
1.9       foxr      121: #   The hash below gives the HTML format for log messages
                    122: #   given a severity.
                    123: #    
                    124: my %LogFormats;
                    125: 
1.45      albertel  126: $LogFormats{"CRITICAL"} = "<font color='red'>CRITICAL: %s</font>";
                    127: $LogFormats{"SUCCESS"}  = "<font color='green'>SUCCESS: %s</font>";
                    128: $LogFormats{"INFO"}     = "<font color='yellow'>INFO: %s</font>";
                    129: $LogFormats{"WARNING"}  = "<font color='blue'>WARNING: %s</font>";
1.9       foxr      130: $LogFormats{"DEFAULT"}  = " %s ";
                    131: 
1.10      foxr      132: 
1.57      foxr      133: #  UpdateStatus;
                    134: #    Update the idle status display to show how many connections
                    135: #    are left, retries and other stuff.
                    136: #
                    137: sub UpdateStatus {
                    138:     if ($ConnectionRetriesLeft > 0) {
                    139: 	ShowStatus(GetServerHost()." Connection count: ".$ConnectionCount
                    140: 		   ." Retries remaining: ".$ConnectionRetriesLeft
                    141: 		   ." ($KeyMode)");
                    142:     } else {
                    143: 	ShowStatus(GetServerHost()." >> DEAD <<");
                    144:     }
                    145: }
                    146: 
1.10      foxr      147: 
                    148: =pod
                    149: 
                    150: =head2 LogPerm
                    151: 
                    152: Makes an entry into the permanent log file.
                    153: 
                    154: =cut
1.69    ! matthew   155: 
1.10      foxr      156: sub LogPerm {
                    157:     my $message=shift;
                    158:     my $execdir=$perlvar{'lonDaemons'};
                    159:     my $now=time;
                    160:     my $local=localtime($now);
                    161:     my $fh=IO::File->new(">>$execdir/logs/lonnet.perm.log");
                    162:     print $fh "$now:$message:$local\n";
                    163: }
1.9       foxr      164: 
                    165: =pod
                    166: 
                    167: =head2 Log
                    168: 
                    169: Logs a message to the log file.
                    170: Parameters:
                    171: 
                    172: =item severity
                    173: 
                    174: One of CRITICAL, WARNING, INFO, SUCCESS used to select the
                    175: format string used to format the message.  if the severity is
                    176: not a defined severity the Default format string is used.
                    177: 
                    178: =item message
                    179: 
                    180: The base message.  In addtion to the format string, the message
                    181: will be appended to a string containing the name of our remote
                    182: host and the time will be formatted into the message.
                    183: 
                    184: =cut
                    185: 
                    186: sub Log {
1.47      foxr      187: 
                    188:     my ($severity, $message) = @_;
                    189: 
1.9       foxr      190:     if(!$LogFormats{$severity}) {
                    191: 	$severity = "DEFAULT";
                    192:     }
                    193: 
                    194:     my $format = $LogFormats{$severity};
                    195:     
                    196:     #  Put the window dressing in in front of the message format:
                    197: 
                    198:     my $now   = time;
                    199:     my $local = localtime($now);
                    200:     my $finalformat = "$local ($$) [$RemoteHost] [$Status] ";
                    201:     my $finalformat = $finalformat.$format."\n";
                    202: 
                    203:     # open the file and put the result.
                    204: 
                    205:     my $execdir = $perlvar{'lonDaemons'};
                    206:     my $fh      = IO::File->new(">>$execdir/logs/lonc.log");
                    207:     my $msg = sprintf($finalformat, $message);
1.14      foxr      208:     $RecentLogEntry = $msg;
1.9       foxr      209:     print $fh $msg;
                    210:     
1.10      foxr      211:     
1.9       foxr      212: }
1.6       foxr      213: 
1.3       albertel  214: 
1.1       foxr      215: =pod
1.3       albertel  216: 
                    217: =head2 GetPeerName
                    218: 
                    219: Returns the name of the host that a socket object is connected to.
                    220: 
1.1       foxr      221: =cut
                    222: 
                    223: sub GetPeername {
1.47      foxr      224: 
                    225: 
                    226:     my ($connection, $AdrFamily) = @_;
                    227: 
1.1       foxr      228:     my $peer       = $connection->peername();
                    229:     my $peerport;
                    230:     my $peerip;
                    231:     if($AdrFamily == AF_INET) {
                    232: 	($peerport, $peerip) = sockaddr_in($peer);
1.23      foxr      233: 	my $peername    = gethostbyaddr($peerip, $AdrFamily);
1.1       foxr      234: 	return $peername;
                    235:     } elsif ($AdrFamily == AF_UNIX) {
                    236: 	my $peerfile;
                    237: 	($peerfile) = sockaddr_un($peer);
                    238: 	return $peerfile;
                    239:     }
                    240: }
                    241: =pod
1.3       albertel  242: 
1.1       foxr      243: =head2 Debug
1.3       albertel  244: 
                    245: Invoked to issue a debug message.
                    246: 
1.1       foxr      247: =cut
1.3       albertel  248: 
1.1       foxr      249: sub Debug {
1.47      foxr      250: 
                    251:     my ($level, $message) = @_;
                    252: 
1.1       foxr      253:     if ($level <= $DebugLevel) {
1.23      foxr      254: 	Log("INFO", "-Debug- $message host = $RemoteHost");
1.1       foxr      255:     }
                    256: }
                    257: 
                    258: sub SocketDump {
1.47      foxr      259: 
                    260:     my ($level, $socket) = @_;
                    261: 
1.1       foxr      262:     if($level <= $DebugLevel) {
1.48      foxr      263: 	$socket->Dump(-1);	# Ensure it will get dumped.
1.1       foxr      264:     }
                    265: }
1.3       albertel  266: 
1.1       foxr      267: =pod
1.3       albertel  268: 
1.5       foxr      269: =head2 ShowStatus
                    270: 
                    271:  Place some text as our pid status.
1.10      foxr      272:  and as what we return in a SIGUSR1
1.5       foxr      273: 
                    274: =cut
1.69    ! matthew   275: 
1.5       foxr      276: sub ShowStatus {
1.10      foxr      277:     my $state = shift;
                    278:     my $now = time;
                    279:     my $local = localtime($now);
                    280:     $Status   = $local.": ".$state;
                    281:     $0='lonc: '.$state.' '.$local;
1.5       foxr      282: }
                    283: 
                    284: =pod
                    285: 
1.69    ! matthew   286: =head2 SocketTimeout
1.15      foxr      287: 
                    288:     Called when an action on the socket times out.  The socket is 
                    289:    destroyed and any active transaction is failed.
                    290: 
                    291: 
                    292: =cut
1.69    ! matthew   293: 
1.15      foxr      294: sub SocketTimeout {
                    295:     my $Socket = shift;
1.38      foxr      296:     Log("WARNING", "A socket timeout was detected");
1.52      foxr      297:     Debug(5, " SocketTimeout called: ");
1.48      foxr      298:     $Socket->Dump(0);
1.42      foxr      299:     if(exists($ActiveTransactions{$Socket})) {
1.43      albertel  300: 	FailTransaction($ActiveTransactions{$Socket});
1.42      foxr      301:     }
1.22      foxr      302:     KillSocket($Socket);	# A transaction timeout also counts as
                    303:                                 # a connection failure:
                    304:     $ConnectionRetriesLeft--;
1.42      foxr      305:     if($ConnectionRetriesLeft <= 0) {
1.52      foxr      306: 	Log("CRITICAL", "Host marked DEAD: ".GetServerHost());
1.56      foxr      307: 	$LondConnecting = 0;
1.42      foxr      308:     }
                    309: 
1.15      foxr      310: }
1.64      foxr      311: #
                    312: #   This function should be called by the child in all cases where it must
                    313: #   exit.  If the child process is running with the DieWhenIdle turned on
                    314: #   it must create a lock file for the AF_UNIX socket in order to prevent
                    315: #   connection requests from lonnet in the time between process exit
                    316: #   and the parent picking up the listen again.
                    317: # Parameters:
                    318: #     exit_code           - Exit status value, however see the next parameter.
                    319: #     message             - If this optional parameter is supplied, the exit
                    320: #                           is via a die with this message.
                    321: #
                    322: sub child_exit {
                    323:     my ($exit_code, $message) = @_;
                    324: 
                    325:     # Regardless of how we exit, we may need to do the lock thing:
                    326: 
                    327:     if($DieWhenIdle) {
                    328: 	#
                    329: 	#  Create a lock file since there will be a time window
                    330: 	#  between our exit and the parent's picking up the listen
                    331: 	#  during which no listens will be done on the
                    332: 	#  lonnet client socket.
                    333: 	#
                    334: 	my $lock_file = GetLoncSocketPath().".lock";
                    335: 	open(LOCK,">$lock_file");
                    336: 	print LOCK "Contents not important";
                    337: 	close(LOCK);
                    338: 	
                    339: 	exit(0);
                    340:     }
                    341:     #  Now figure out how we exit:
                    342: 
                    343:     if($message) {
                    344: 	die $message;
                    345:     } else {
                    346: 	exit($exit_code);
                    347:     }
                    348: }
1.35      foxr      349: #----------------------------- Timer management ------------------------
1.15      foxr      350: 
                    351: =pod
                    352: 
1.1       foxr      353: =head2 Tick
1.3       albertel  354: 
                    355: Invoked  each timer tick.
                    356: 
1.1       foxr      357: =cut
                    358: 
1.5       foxr      359: 
1.1       foxr      360: sub Tick {
1.52      foxr      361:     my ($Event)       = @_;
                    362:     my $clock_watcher = $Event->w;
                    363: 
1.1       foxr      364:     my $client;
1.57      foxr      365:     UpdateStatus();
                    366: 
1.4       foxr      367:     # Is it time to prune connection count:
                    368: 
                    369: 
                    370:     if($IdleConnections->Count()  && 
                    371:        ($WorkQueue->Count() == 0)) { # Idle connections and nothing to do?
1.52      foxr      372: 	$IdleSeconds++;
1.4       foxr      373: 	if($IdleSeconds > $IdleTimeout) { # Prune a connection...
1.23      foxr      374: 	    my $Socket = $IdleConnections->pop();
1.6       foxr      375: 	    KillSocket($Socket);
1.54      foxr      376: 	    $IdleSeconds = 0;	# Otherwise all connections get trimmed to fast.
1.57      foxr      377: 	    UpdateStatus();
                    378: 	    if(($ConnectionCount == 0) && $DieWhenIdle) {
1.64      foxr      379: 		&child_exit(0);
                    380: 
1.57      foxr      381: 	    }
1.4       foxr      382: 	}
                    383:     } else {
                    384: 	$IdleSeconds = 0;	# Reset idle count if not idle.
                    385:     }
1.15      foxr      386:     #
                    387:     #  For each inflight transaction, tick down its timeout counter.
                    388:     #
1.35      foxr      389: 
1.34      albertel  390:     foreach my $item (keys %ActiveConnections) {
                    391: 	my $State = $ActiveConnections{$item}->data->GetState();
1.35      foxr      392: 	if ($State ne 'Idle') {
1.34      albertel  393: 	    Debug(5,"Ticking Socket $State $item");
                    394: 	    $ActiveConnections{$item}->data->Tick();
                    395: 	}
1.15      foxr      396:     }
1.5       foxr      397:     # Do we have work in the queue, but no connections to service them?
                    398:     # If so, try to make some new connections to get things going again.
                    399:     #
1.57      foxr      400:     #   Note this code is dead now...
                    401:     #
1.5       foxr      402:     my $Requests = $WorkQueue->Count();
1.56      foxr      403:     if (($ConnectionCount == 0)  && ($Requests > 0) && (!$LondConnecting)) { 
1.10      foxr      404: 	if ($ConnectionRetriesLeft > 0) {
1.56      foxr      405: 	    Debug(5,"Work but no connections, Make a new one");
                    406: 	    my $success;
                    407: 	    $success    = &MakeLondConnection;
                    408: 	    if($success == 0) { # All connections failed:
1.29      foxr      409: 		Debug(5,"Work in queue failed to make any connectiouns\n");
1.22      foxr      410: 		EmptyQueue();	# Fail pending transactions with con_lost.
1.42      foxr      411: 		CloseAllLondConnections(); # Should all be closed but....
1.10      foxr      412: 	    }
                    413: 	} else {
1.56      foxr      414: 	    $LondConnecting = 0;
1.22      foxr      415: 	    ShowStatus(GetServerHost()." >>> DEAD!!! <<<");
1.29      foxr      416: 	    Debug(5,"Work in queue, but gave up on connections..flushing\n");
1.10      foxr      417: 	    EmptyQueue();	# Connections can't be established.
1.42      foxr      418: 	    CloseAllLondConnections(); # Should all already be closed but...
1.5       foxr      419: 	}
                    420:        
                    421:     }
1.49      foxr      422:     if ($ConnectionCount == 0) {
                    423: 	$KeyMode = ""; 
1.52      foxr      424: 	$clock_watcher->cancel();
1.49      foxr      425:     }
1.66      albertel  426:     &UpdateStatus();
1.1       foxr      427: }
                    428: 
                    429: =pod
1.3       albertel  430: 
1.1       foxr      431: =head2 SetupTimer
                    432: 
1.3       albertel  433: Sets up a 1 per sec recurring timer event.  The event handler is used to:
1.1       foxr      434: 
1.3       albertel  435: =item
                    436: 
                    437: Trigger timeouts on communications along active sockets.
                    438: 
                    439: =item
                    440: 
                    441: Trigger disconnections of idle sockets.
1.1       foxr      442: 
                    443: =cut
                    444: 
                    445: sub SetupTimer {
1.52      foxr      446:     Debug(6, "SetupTimer");
                    447:     Event->timer(interval => 1, cb => \&Tick );
1.1       foxr      448: }
1.3       albertel  449: 
1.1       foxr      450: =pod
1.3       albertel  451: 
1.1       foxr      452: =head2 ServerToIdle
1.3       albertel  453: 
                    454: This function is called when a connection to the server is
                    455: ready for more work.
                    456: 
                    457: If there is work in the Work queue the top element is dequeued
1.1       foxr      458: and the connection will start to work on it.  If the work queue is
                    459: empty, the connection is pushed on the idle connection stack where
                    460: it will either get another work unit, or alternatively, if it sits there
                    461: long enough, it will be shut down and released.
                    462: 
1.3       albertel  463: =cut
1.1       foxr      464: 
                    465: sub ServerToIdle {
                    466:     my $Socket   = shift;	# Get the socket.
1.49      foxr      467:     $KeyMode = $Socket->{AuthenticationMode};
1.7       foxr      468:     delete($ActiveTransactions{$Socket}); # Server has no transaction
1.1       foxr      469: 
1.29      foxr      470:     &Debug(5, "Server to idle");
1.1       foxr      471: 
                    472:     #  If there's work to do, start the transaction:
                    473: 
1.23      foxr      474:     my $reqdata = $WorkQueue->dequeue(); # This is a LondTransaction
1.29      foxr      475:     if ($reqdata ne undef)  {
                    476: 	Debug(5, "Queue gave request data: ".$reqdata->getRequest());
1.7       foxr      477: 	&StartRequest($Socket,  $reqdata);
1.8       foxr      478: 
1.1       foxr      479:     } else {
                    480: 	
                    481:     #  There's no work waiting, so push the server to idle list.
1.29      foxr      482: 	&Debug(5, "No new work requests, server connection going idle");
1.1       foxr      483: 	$IdleConnections->push($Socket);
                    484:     }
                    485: }
1.3       albertel  486: 
1.1       foxr      487: =pod
1.3       albertel  488: 
1.1       foxr      489: =head2 ClientWritable
1.3       albertel  490: 
                    491: Event callback for when a client socket is writable.
                    492: 
                    493: This callback is established when a transaction reponse is
                    494: avaiable from lond.  The response is forwarded to the unix socket
                    495: as it becomes writable in this sub.
                    496: 
1.1       foxr      497: Parameters:
                    498: 
1.3       albertel  499: =item Event
                    500: 
                    501: The event that has been triggered. Event->w->data is
                    502: the data and Event->w->fd is the socket to write.
1.1       foxr      503: 
                    504: =cut
1.3       albertel  505: 
1.1       foxr      506: sub ClientWritable {
                    507:     my $Event    = shift;
                    508:     my $Watcher  = $Event->w;
                    509:     my $Data     = $Watcher->data;
                    510:     my $Socket   = $Watcher->fd;
                    511: 
                    512:     # Try to send the data:
                    513: 
                    514:     &Debug(6, "ClientWritable writing".$Data);
                    515:     &Debug(9, "Socket is: ".$Socket);
                    516: 
1.6       foxr      517:     if($Socket->connected) {
                    518: 	my $result = $Socket->send($Data, 0);
                    519: 	
                    520: 	# $result undefined: the write failed.
                    521: 	# otherwise $result is the number of bytes written.
                    522: 	# Remove that preceding string from the data.
                    523: 	# If the resulting data is empty, destroy the watcher
                    524: 	# and set up a read event handler to accept the next
                    525: 	# request.
                    526: 	
                    527: 	&Debug(9,"Send result is ".$result." Defined: ".defined($result));
1.29      foxr      528: 	if($result ne undef) {
1.6       foxr      529: 	    &Debug(9, "send result was defined");
                    530: 	    if($result == length($Data)) { # Entire string sent.
                    531: 		&Debug(9, "ClientWritable data all written");
                    532: 		$Watcher->cancel();
                    533: 		#
                    534: 		#  Set up to read next request from socket:
                    535: 		
                    536: 		my $descr     = sprintf("Connection to lonc client %d",
                    537: 					$ActiveClients{$Socket});
                    538: 		Event->io(cb    => \&ClientRequest,
                    539: 			  poll  => 'r',
                    540: 			  desc  => $descr,
                    541: 			  data  => "",
                    542: 			  fd    => $Socket);
                    543: 		
                    544: 	    } else {		# Partial string sent.
                    545: 		$Watcher->data(substr($Data, $result));
1.15      foxr      546: 		if($result == 0) {    # client hung up on us!!
1.52      foxr      547: 		    # Log("INFO", "lonc pipe client hung up on us!");
1.15      foxr      548: 		    $Watcher->cancel;
                    549: 		    $Socket->shutdown(2);
                    550: 		    $Socket->close();
                    551: 		}
1.6       foxr      552: 	    }
                    553: 	    
                    554: 	} else {			# Error of some sort...
                    555: 	    
                    556: 	    # Some errnos are possible:
                    557: 	    my $errno = $!;
                    558: 	    if($errno == POSIX::EWOULDBLOCK   ||
                    559: 	       $errno == POSIX::EAGAIN        ||
                    560: 	       $errno == POSIX::EINTR) {
                    561: 		# No action taken?
                    562: 	    } else {		# Unanticipated errno.
                    563: 		&Debug(5,"ClientWritable error or peer shutdown".$RemoteHost);
                    564: 		$Watcher->cancel;	# Stop the watcher.
                    565: 		$Socket->shutdown(2); # Kill connection
                    566: 		$Socket->close();	# Close the socket.
                    567: 	    }
1.1       foxr      568: 	    
                    569: 	}
1.6       foxr      570:     } else {
                    571: 	$Watcher->cancel();	# A delayed request...just cancel.
1.1       foxr      572:     }
                    573: }
                    574: 
                    575: =pod
1.3       albertel  576: 
1.1       foxr      577: =head2 CompleteTransaction
1.3       albertel  578: 
                    579: Called when the reply data has been received for a lond 
1.1       foxr      580: transaction.   The reply data must now be sent to the
                    581: ultimate client on the other end of the Unix socket.  This is
                    582: done by setting up a writable event for the socket with the
                    583: data the reply data.
1.3       albertel  584: 
1.1       foxr      585: Parameters:
1.3       albertel  586: 
                    587: =item Socket
                    588: 
                    589: Socket on which the lond transaction occured.  This is a
                    590: LondConnection. The data received is in the TransactionReply member.
                    591: 
1.7       foxr      592: =item Transaction
1.3       albertel  593: 
1.7       foxr      594: The transaction that is being completed.
1.1       foxr      595: 
                    596: =cut
1.3       albertel  597: 
1.1       foxr      598: sub CompleteTransaction {
1.29      foxr      599:     &Debug(5,"Complete transaction");
1.47      foxr      600: 
                    601:     my ($Socket, $Transaction) = @_;
1.1       foxr      602: 
1.7       foxr      603:     if (!$Transaction->isDeferred()) { # Normal transaction
                    604: 	my $data   = $Socket->GetReply(); # Data to send.
1.39      foxr      605: 	if($LogTransactions) {
                    606: 	    Log("SUCCESS", "Reply from lond: '$data'");
                    607: 	}
1.7       foxr      608: 	StartClientReply($Transaction, $data);
                    609:     } else {			# Delete deferred transaction file.
1.9       foxr      610: 	Log("SUCCESS", "A delayed transaction was completed");
1.23      foxr      611: 	LogPerm("S:$Transaction->getClient() :".$Transaction->getRequest());
1.7       foxr      612: 	unlink $Transaction->getFile();
                    613:     }
1.6       foxr      614: }
1.42      foxr      615: 
1.6       foxr      616: =pod
1.42      foxr      617: 
1.6       foxr      618: =head1 StartClientReply
                    619: 
                    620:    Initiates a reply to a client where the reply data is a parameter.
                    621: 
1.7       foxr      622: =head2  parameters:
                    623: 
                    624: =item Transaction
                    625: 
                    626:     The transaction for which we are responding to the client.
                    627: 
                    628: =item data
                    629: 
                    630:     The data to send to apached client.
                    631: 
1.6       foxr      632: =cut
1.42      foxr      633: 
1.6       foxr      634: sub StartClientReply {
1.1       foxr      635: 
1.47      foxr      636:     my ($Transaction, $data) = @_;
1.12      foxr      637: 
1.7       foxr      638:     my $Client   = $Transaction->getClient();
                    639: 
1.1       foxr      640:     &Debug(8," Reply was: ".$data);
                    641:     my $Serial         = $ActiveClients{$Client};
                    642:     my $desc           = sprintf("Connection to lonc client %d",
                    643: 				 $Serial);
                    644:     Event->io(fd       => $Client,
                    645: 	      poll     => "w",
                    646: 	      desc     => $desc,
                    647: 	      cb       => \&ClientWritable,
                    648: 	      data     => $data);
                    649: }
1.42      foxr      650: 
1.4       foxr      651: =pod
1.42      foxr      652: 
1.4       foxr      653: =head2 FailTransaction
                    654: 
                    655:   Finishes a transaction with failure because the associated lond socket
1.7       foxr      656:   disconnected.  There are two possibilities:
                    657:   - The transaction is deferred: in which case we just quietly
                    658:     delete the transaction since there is no client connection.
                    659:   - The transaction is 'live' in which case we initiate the sending
                    660:     of "con_lost" to the client.
                    661: 
1.42      foxr      662: Deleting the transaction means killing it from the %ActiveTransactions hash.
1.4       foxr      663: 
                    664: Parameters:
                    665: 
                    666: =item client  
                    667:  
1.7       foxr      668:    The LondTransaction we are failing.
                    669:  
1.42      foxr      670: 
1.4       foxr      671: =cut
                    672: 
                    673: sub FailTransaction {
1.7       foxr      674:     my $transaction = shift;
1.52      foxr      675:     
                    676:     #  If the socket is dead, that's already logged.
                    677: 
                    678:     if ($ConnectionRetriesLeft > 0) {
                    679: 	Log("WARNING", "Failing transaction "
                    680: 	    .$transaction->getRequest());
                    681:     }
1.30      foxr      682:     Debug(1, "Failing transaction: ".$transaction->getRequest());
1.10      foxr      683:     if (!$transaction->isDeferred()) { # If the transaction is deferred we'll get to it.
1.11      foxr      684: 	my $client  = $transaction->getClient();
1.30      foxr      685: 	Debug(1," Replying con_lost to ".$transaction->getRequest());
1.11      foxr      686: 	StartClientReply($transaction, "con_lost\n");
1.7       foxr      687:     }
1.4       foxr      688: 
                    689: }
                    690: 
                    691: =pod
1.69    ! matthew   692: 
1.6       foxr      693: =head1  EmptyQueue
1.7       foxr      694: 
1.6       foxr      695:   Fails all items in the work queue with con_lost.
1.7       foxr      696:   Note that each item in the work queue is a transaction.
                    697: 
1.6       foxr      698: =cut
1.69    ! matthew   699: 
1.6       foxr      700: sub EmptyQueue {
1.22      foxr      701:     $ConnectionRetriesLeft--;	# Counts as connection failure too.
1.6       foxr      702:     while($WorkQueue->Count()) {
1.10      foxr      703: 	my $request = $WorkQueue->dequeue(); # This is a transaction
1.7       foxr      704: 	FailTransaction($request);
1.6       foxr      705:     }
                    706: }
                    707: 
                    708: =pod
1.4       foxr      709: 
1.9       foxr      710: =head2 CloseAllLondConnections
                    711: 
                    712: Close all connections open on lond prior to exit e.g.
                    713: 
                    714: =cut
1.69    ! matthew   715: 
1.9       foxr      716: sub CloseAllLondConnections {
1.23      foxr      717:     foreach my $Socket (keys %ActiveConnections) {
1.42      foxr      718:       if(exists($ActiveTransactions{$Socket})) {
                    719: 	FailTransaction($ActiveTransactions{$Socket});
                    720:       }
                    721:       KillSocket($Socket);
1.9       foxr      722:     }
                    723: }
                    724: 
                    725: =pod
                    726: 
1.4       foxr      727: =head2 KillSocket
                    728:  
                    729: Destroys a socket.  This function can be called either when a socket
                    730: has died of 'natural' causes or because a socket needs to be pruned due to
                    731: idleness.  If the socket has died naturally, if there are no longer any 
                    732: live connections a new connection is created (in case there are transactions
                    733: in the queue).  If the socket has been pruned, it is never re-created.
                    734: 
                    735: Parameters:
1.1       foxr      736: 
1.4       foxr      737: =item Socket
                    738:  
                    739:   The socket to kill off.
                    740: 
                    741: =item Restart
                    742: 
                    743: nonzero if we are allowed to create a new connection.
                    744: 
1.69    ! matthew   745: =cut
1.4       foxr      746: 
                    747: sub KillSocket {
                    748:     my $Socket = shift;
                    749: 
1.17      foxr      750:     Log("WARNING", "Shutting down a socket");
1.9       foxr      751:     $Socket->Shutdown();
                    752: 
1.7       foxr      753:     #  If the socket came from the active connection set,
                    754:     #  delete its transaction... note that FailTransaction should
                    755:     #  already have been called!!!
                    756:     #  otherwise it came from the idle set.
                    757:     #  
1.4       foxr      758:     
                    759:     if(exists($ActiveTransactions{$Socket})) {
                    760: 	delete ($ActiveTransactions{$Socket});
                    761:     }
                    762:     if(exists($ActiveConnections{$Socket})) {
                    763: 	delete($ActiveConnections{$Socket});
1.37      albertel  764: 	$ConnectionCount--;
                    765: 	if ($ConnectionCount < 0) { $ConnectionCount = 0; }
1.4       foxr      766:     }
1.6       foxr      767:     #  If the connection count has gone to zero and there is work in the
                    768:     #  work queue, the work all gets failed with con_lost.
                    769:     #
                    770:     if($ConnectionCount == 0) {
1.22      foxr      771: 	EmptyQueue();
1.42      foxr      772: 	CloseAllLondConnections; # Should all already be closed but...
1.4       foxr      773:     }
                    774: }
1.1       foxr      775: 
                    776: =pod
1.3       albertel  777: 
1.1       foxr      778: =head2 LondReadable
1.3       albertel  779: 
1.1       foxr      780: This function is called whenever a lond connection
                    781: is readable.  The action is state dependent:
                    782: 
1.3       albertel  783: =head3 State=Initialized
                    784: 
                    785: We''re waiting for the challenge, this is a no-op until the
1.1       foxr      786: state changes.
1.3       albertel  787: 
1.1       foxr      788: =head3 State=Challenged 
1.3       albertel  789: 
                    790: The challenge has arrived we need to transition to Writable.
1.1       foxr      791: The connection must echo the challenge back.
1.3       albertel  792: 
1.1       foxr      793: =head3 State=ChallengeReplied
1.3       albertel  794: 
                    795: The challenge has been replied to.  The we are receiveing the 
1.1       foxr      796: 'ok' from the partner.
1.3       albertel  797: 
1.40      foxr      798: =head3  State=ReadingVersionString
                    799: 
                    800: We have requested the lond version and are reading the
                    801: version back.  Upon completion, we'll store the version away
                    802: for future use(?).
                    803: 
                    804: =head3 State=HostSet
                    805: 
                    806: We have selected the domain name of our peer (multhomed hosts)
                    807: and are getting the reply (presumably ok) back.
                    808: 
1.1       foxr      809: =head3 State=RequestingKey
1.3       albertel  810: 
                    811: The ok has been received and we need to send the request for
1.1       foxr      812: an encryption key.  Transition to writable for that.
1.3       albertel  813: 
1.1       foxr      814: =head3 State=ReceivingKey
1.3       albertel  815: 
                    816: The the key has been requested, now we are reading the new key.
                    817: 
1.1       foxr      818: =head3 State=Idle 
1.3       albertel  819: 
                    820: The encryption key has been negotiated or we have finished 
1.1       foxr      821: reading data from the a transaction.   If the callback data has
                    822: a client as well as the socket iformation, then we are 
                    823: doing a transaction and the data received is relayed to the client
                    824: before the socket is put on the idle list.
1.3       albertel  825: 
1.1       foxr      826: =head3 State=SendingRequest
1.3       albertel  827: 
                    828: I do not think this state can be received here, but if it is,
1.1       foxr      829: the appropriate thing to do is to transition to writable, and send
                    830: the request.
1.3       albertel  831: 
1.1       foxr      832: =head3 State=ReceivingReply
1.3       albertel  833: 
                    834: We finished sending the request to the server and now transition
1.1       foxr      835: to readable to receive the reply. 
                    836: 
                    837: The parameter to this function are:
1.3       albertel  838: 
1.1       foxr      839: The event. Implicit in this is the watcher and its data.  The data 
                    840: contains at least the lond connection object and, if a 
                    841: transaction is in progress, the socket attached to the local client.
                    842: 
1.3       albertel  843: =cut
1.1       foxr      844: 
                    845: sub LondReadable {
1.8       foxr      846: 
1.41      albertel  847:     my $Event      = shift;
                    848:     my $Watcher    = $Event->w;
                    849:     my $Socket     = $Watcher->data;
                    850:     my $client     = undef;
1.40      foxr      851: 
1.41      albertel  852:     &Debug(6,"LondReadable called state = ".$Socket->GetState());
1.40      foxr      853: 
                    854: 
1.41      albertel  855:     my $State = $Socket->GetState(); # All action depends on the state.
1.40      foxr      856: 
1.41      albertel  857:     SocketDump(6, $Socket);
                    858:     my $status = $Socket->Readable();
1.40      foxr      859: 
1.41      albertel  860:     &Debug(2, "Socket->Readable returned: $status");
1.40      foxr      861: 
1.41      albertel  862:     if($status != 0) {
                    863: 	# bad return from socket read. Currently this means that
                    864: 	# The socket has become disconnected. We fail the transaction.
1.40      foxr      865: 
1.41      albertel  866: 	Log("WARNING",
                    867: 	    "Lond connection lost.");
                    868: 	if(exists($ActiveTransactions{$Socket})) {
                    869: 	    FailTransaction($ActiveTransactions{$Socket});
1.56      foxr      870: 	} else {
                    871: 	    #  Socket is connecting and failed... need to mark
                    872: 	    #  no longer connecting.
                    873: 	   
                    874: 	    $LondConnecting = 0;
1.41      albertel  875: 	}
                    876: 	$Watcher->cancel();
                    877: 	KillSocket($Socket);
                    878: 	$ConnectionRetriesLeft--;       # Counts as connection failure
                    879: 	return;
                    880:     }
                    881:     SocketDump(6,$Socket);
1.17      foxr      882: 
1.41      albertel  883:     $State = $Socket->GetState(); # Update in case of transition.
                    884:     &Debug(6, "After read, state is ".$State);
1.1       foxr      885: 
1.41      albertel  886:     if($State eq "Initialized") {
1.1       foxr      887: 
                    888: 
1.41      albertel  889:     } elsif ($State eq "ChallengeReceived") {
1.1       foxr      890: 	#  The challenge must be echoed back;  The state machine
                    891: 	# in the connection takes care of setting that up.  Just
                    892: 	# need to transition to writable:
1.41      albertel  893: 	
                    894: 	$Watcher->cb(\&LondWritable);
                    895: 	$Watcher->poll("w");
1.1       foxr      896: 
1.41      albertel  897:     } elsif ($State eq "ChallengeReplied") {
1.1       foxr      898: 
1.41      albertel  899:     } elsif ($State eq "RequestingVersion") {
                    900: 	# Need to ask for the version... that is writiability:
1.1       foxr      901: 
1.41      albertel  902: 	$Watcher->cb(\&LondWritable);
                    903: 	$Watcher->poll("w");
                    904: 
                    905:     } elsif ($State eq "ReadingVersionString") {
                    906: 	# Read the rest of the version string... 
                    907:     } elsif ($State eq "SetHost") {
                    908: 	# Need to request the actual domain get set...
                    909: 
                    910: 	$Watcher->cb(\&LondWritable);
                    911: 	$Watcher->poll("w");
                    912:     } elsif ($State eq "HostSet") {
                    913: 	# Reading the 'ok' from the peer.
                    914: 
                    915:     } elsif ($State eq "RequestingKey") {
1.1       foxr      916: 	#  The ok was received.  Now we need to request the key
                    917: 	#  That requires us to be writable:
                    918: 
1.41      albertel  919: 	$Watcher->cb(\&LondWritable);
                    920: 	$Watcher->poll("w");
1.1       foxr      921: 
1.41      albertel  922:     } elsif ($State eq "ReceivingKey") {
1.1       foxr      923: 
1.41      albertel  924:     } elsif ($State eq "Idle") {
1.40      foxr      925:    
1.41      albertel  926: 	# This is as good a spot as any to get the peer version
                    927: 	# string:
1.40      foxr      928:    
1.41      albertel  929: 	if($LondVersion eq "unknown") {
                    930: 	    $LondVersion = $Socket->PeerVersion();
                    931: 	    Log("INFO", "Connected to lond version: $LondVersion");
                    932: 	}
1.1       foxr      933: 	# If necessary, complete a transaction and then go into the
                    934: 	# idle queue.
1.22      foxr      935: 	#  Note that a trasition to idle indicates a live lond
                    936: 	# on the other end so reset the connection retries.
                    937: 	#
1.41      albertel  938: 	$ConnectionRetriesLeft = $ConnectionRetries; # success resets the count
                    939: 	$Watcher->cancel();
                    940: 	if(exists($ActiveTransactions{$Socket})) {
                    941: 	    Debug(5,"Completing transaction!!");
                    942: 	    CompleteTransaction($Socket, 
                    943: 				$ActiveTransactions{$Socket});
                    944: 	} else {
                    945: 	    Log("SUCCESS", "Connection ".$ConnectionCount." to "
                    946: 		.$RemoteHost." now ready for action");
                    947: 	}
                    948: 	ServerToIdle($Socket);	# Next work unit or idle.
1.54      foxr      949: 
                    950: 	#
                    951: 	$LondConnecting = 0;	# Best spot I can think of for this.
                    952: 	# 
1.6       foxr      953: 	
1.41      albertel  954:     } elsif ($State eq "SendingRequest") {
1.1       foxr      955: 	#  We need to be writable for this and probably don't belong
                    956: 	#  here inthe first place.
                    957: 
1.41      albertel  958: 	Deubg(6, "SendingRequest state encountered in readable");
                    959: 	$Watcher->poll("w");
                    960: 	$Watcher->cb(\&LondWritable);
1.1       foxr      961: 
1.41      albertel  962:     } elsif ($State eq "ReceivingReply") {
1.1       foxr      963: 
                    964: 
1.41      albertel  965:     } else {
                    966: 	# Invalid state.
                    967: 	Debug(4, "Invalid state in LondReadable");
                    968:     }
1.1       foxr      969: }
1.3       albertel  970: 
1.1       foxr      971: =pod
1.3       albertel  972: 
1.1       foxr      973: =head2 LondWritable
1.3       albertel  974: 
1.1       foxr      975: This function is called whenever a lond connection
                    976: becomes writable while there is a writeable monitoring
                    977: event.  The action taken is very state dependent:
1.3       albertel  978: 
1.1       foxr      979: =head3 State = Connected 
1.3       albertel  980: 
                    981: The connection is in the process of sending the 'init' hailing to the
                    982: lond on the remote end.  The connection object''s Writable member is
                    983: called.  On error, ConnectionError is called to destroy the connection
                    984: and remove it from the ActiveConnections hash
                    985: 
1.1       foxr      986: =head3 Initialized
1.3       albertel  987: 
                    988: 'init' has been sent, writability monitoring is removed and
                    989: readability monitoring is started with LondReadable as the callback.
                    990: 
1.1       foxr      991: =head3 ChallengeReceived
1.3       albertel  992: 
                    993: The connection has received the who are you challenge from the remote
                    994: system, and is in the process of sending the challenge
                    995: response. Writable is called.
                    996: 
1.1       foxr      997: =head3 ChallengeReplied
1.3       albertel  998: 
                    999: The connection has replied to the initial challenge The we switch to
                   1000: monitoring readability looking for the server to reply with 'ok'.
                   1001: 
1.1       foxr     1002: =head3 RequestingKey
1.3       albertel 1003: 
                   1004: The connection is in the process of requesting its encryption key.
                   1005: Writable is called.
                   1006: 
1.1       foxr     1007: =head3 ReceivingKey
1.3       albertel 1008: 
                   1009: The connection has sent the request for a key.  Switch to readability
                   1010: monitoring to accept the key
                   1011: 
1.1       foxr     1012: =head3 SendingRequest
1.3       albertel 1013: 
                   1014: The connection is in the process of sending a request to the server.
                   1015: This request is part of a client transaction.  All the states until
                   1016: now represent the client setup protocol. Writable is called.
                   1017: 
1.1       foxr     1018: =head3 ReceivingReply
                   1019: 
1.3       albertel 1020: The connection has sent a request.  Now it must receive a reply.
                   1021: Readability monitoring is requested.
                   1022: 
                   1023: This function is an event handler and therefore receives as
1.1       foxr     1024: a parameter the event that has fired.  The data for the watcher
                   1025: of this event is a reference to a list of one or two elements,
                   1026: depending on state. The first (and possibly only) element is the
                   1027: socket.  The second (present only if a request is in progress)
                   1028: is the socket on which to return a reply to the caller.
                   1029: 
                   1030: =cut
1.3       albertel 1031: 
1.1       foxr     1032: sub LondWritable {
                   1033:     my $Event   = shift;
                   1034:     my $Watcher = $Event->w;
1.8       foxr     1035:     my $Socket  = $Watcher->data;
                   1036:     my $State   = $Socket->GetState();
1.1       foxr     1037: 
1.8       foxr     1038:     Debug(6,"LondWritable State = ".$State."\n");
1.1       foxr     1039: 
1.8       foxr     1040:  
1.1       foxr     1041:     #  Figure out what to do depending on the state of the socket:
                   1042:     
                   1043: 
                   1044: 
                   1045: 
                   1046:     SocketDump(6,$Socket);
                   1047: 
1.42      foxr     1048:     #  If the socket is writable, we must always write.
                   1049:     # Only by writing will we undergo state transitions.
                   1050:     # Old logic wrote in state specific code below, however
                   1051:     # That forces us at least through another invocation of
                   1052:     # this function after writability is possible again.
                   1053:     # This logic also factors out common code for handling
                   1054:     # write failures... in all cases, write failures 
                   1055:     # Kill the socket.
                   1056:     #  This logic makes the branches of the >big< if below
                   1057:     # so that the writing states are actually NO-OPs.
                   1058: 
                   1059:     if ($Socket->Writable() != 0) {
1.43      albertel 1060: 	#  The write resulted in an error.
                   1061: 	# We'll treat this as if the socket got disconnected:
                   1062: 	Log("WARNING", "Connection to ".$RemoteHost.
                   1063: 	    " has been disconnected");
                   1064: 	if(exists($ActiveTransactions{$Socket})) {
                   1065: 	    FailTransaction($ActiveTransactions{$Socket});
1.56      foxr     1066: 	} else {
                   1067: 	    #  In the process of conneting, so need to turn that off.
                   1068: 	    
                   1069: 	    $LondConnecting = 0;
1.43      albertel 1070: 	}
                   1071: 	$Watcher->cancel();
                   1072: 	KillSocket($Socket);
                   1073: 	return;
1.42      foxr     1074:     }
                   1075: 
                   1076: 
                   1077: 
1.41      albertel 1078:     if      ($State eq "Connected")         {
1.1       foxr     1079: 
1.41      albertel 1080: 	#  "init" is being sent...
1.42      foxr     1081:  
1.41      albertel 1082:     } elsif ($State eq "Initialized")       {
1.4       foxr     1083: 
1.41      albertel 1084: 	# Now that init was sent, we switch 
                   1085: 	# to watching for readability:
1.1       foxr     1086: 
1.41      albertel 1087: 	$Watcher->cb(\&LondReadable);
                   1088: 	$Watcher->poll("r");
                   1089: 	
                   1090:     } elsif ($State eq "ChallengeReceived") {
                   1091: 	# We received the challenge, now we 
                   1092: 	# are echoing it back. This is a no-op,
                   1093: 	# we're waiting for the state to change
1.1       foxr     1094: 	
1.41      albertel 1095:     } elsif ($State eq "ChallengeReplied")  {
                   1096: 	# The echo was sent back, so we switch
                   1097: 	# to watching readability.
                   1098: 
                   1099: 	$Watcher->cb(\&LondReadable);
                   1100: 	$Watcher->poll("r");
                   1101:     } elsif ($State eq "RequestingVersion") {
                   1102: 	# Sending the peer a version request...
1.42      foxr     1103: 
1.41      albertel 1104:     } elsif ($State eq "ReadingVersionString") {
                   1105: 	# Transition to read since we have sent the
                   1106: 	# version command and now just need to read the
                   1107: 	# version string from the peer:
1.40      foxr     1108:       
1.41      albertel 1109: 	$Watcher->cb(\&LondReadable);
                   1110: 	$Watcher->poll("r");
1.40      foxr     1111:       
1.41      albertel 1112:     } elsif ($State eq "SetHost") {
                   1113: 	#  Setting the remote domain...
1.42      foxr     1114: 
1.41      albertel 1115:     } elsif ($State eq "HostSet") {
                   1116: 	# Back to readable to get the ok.
1.40      foxr     1117:       
1.41      albertel 1118: 	$Watcher->cb(\&LondReadable);
                   1119: 	$Watcher->poll("r");
1.40      foxr     1120:       
                   1121: 
1.41      albertel 1122:     } elsif ($State eq "RequestingKey")     {
                   1123: 	# At this time we're requesting the key.
                   1124: 	# again, this is essentially a no-op.
                   1125: 
                   1126:     } elsif ($State eq "ReceivingKey")      {
                   1127: 	# Now we need to wait for the key
                   1128: 	# to come back from the peer:
                   1129: 
                   1130: 	$Watcher->cb(\&LondReadable);
                   1131: 	$Watcher->poll("r");
                   1132: 
                   1133:     } elsif ($State eq "SendingRequest")    {
1.40      foxr     1134:  
1.41      albertel 1135: 	# At this time we are sending a request to the
1.1       foxr     1136: 	# peer... write the next chunk:
                   1137: 
1.41      albertel 1138: 
                   1139:     } elsif ($State eq "ReceivingReply")    {
                   1140: 	# The send has completed.  Wait for the
                   1141: 	# data to come in for a reply.
                   1142: 	Debug(8,"Writable sent request/receiving reply");
                   1143: 	$Watcher->cb(\&LondReadable);
                   1144: 	$Watcher->poll("r");
1.1       foxr     1145: 
1.41      albertel 1146:     } else {
                   1147: 	#  Control only passes here on an error: 
                   1148: 	#  the socket state does not match any
                   1149: 	#  of the known states... so an error
                   1150: 	#  must be logged.
1.1       foxr     1151: 
1.41      albertel 1152: 	&Debug(4, "Invalid socket state ".$State."\n");
                   1153:     }
1.1       foxr     1154:     
                   1155: }
1.6       foxr     1156: =pod
                   1157:     
                   1158: =cut
1.69    ! matthew  1159: 
1.6       foxr     1160: sub QueueDelayed {
1.8       foxr     1161:     Debug(3,"QueueDelayed called");
                   1162: 
1.6       foxr     1163:     my $path = "$perlvar{'lonSockDir'}/delayed";
1.8       foxr     1164: 
                   1165:     Debug(4, "Delayed path: ".$path);
1.6       foxr     1166:     opendir(DIRHANDLE, $path);
1.8       foxr     1167:     
1.23      foxr     1168:     my @alldelayed = grep /\.$RemoteHost$/, readdir DIRHANDLE;
1.6       foxr     1169:     closedir(DIRHANDLE);
                   1170:     my $dfname;
1.8       foxr     1171:     my $reqfile;
                   1172:     foreach $dfname (sort  @alldelayed) {
                   1173: 	$reqfile = "$path/$dfname";
                   1174: 	Debug(4, "queueing ".$reqfile);
1.6       foxr     1175: 	my $Handle = IO::File->new($reqfile);
                   1176: 	my $cmd    = <$Handle>;
1.8       foxr     1177: 	chomp $cmd;		# There may or may not be a newline...
1.12      foxr     1178: 	$cmd = $cmd."\n";	# now for sure there's exactly one newline.
1.7       foxr     1179: 	my $Transaction = LondTransaction->new($cmd);
                   1180: 	$Transaction->SetDeferred($reqfile);
                   1181: 	QueueTransaction($Transaction);
1.6       foxr     1182:     }
                   1183:     
                   1184: }
1.1       foxr     1185: 
                   1186: =pod
1.3       albertel 1187: 
1.1       foxr     1188: =head2 MakeLondConnection
1.3       albertel 1189: 
                   1190: Create a new lond connection object, and start it towards its initial
                   1191: idleness.  Once idle, it becomes elligible to receive transactions
                   1192: from the work queue.  If the work queue is not empty when the
                   1193: connection is completed and becomes idle, it will dequeue an entry and
                   1194: start off on it.
                   1195: 
1.1       foxr     1196: =cut
1.3       albertel 1197: 
1.1       foxr     1198: sub MakeLondConnection {     
                   1199:     Debug(4,"MakeLondConnection to ".GetServerHost()." on port "
                   1200: 	  .GetServerPort());
                   1201: 
                   1202:     my $Connection = LondConnection->new(&GetServerHost(),
                   1203: 					 &GetServerPort());
                   1204: 
1.30      foxr     1205:     if($Connection eq undef) {	# Needs to be more robust later.
1.9       foxr     1206: 	Log("CRITICAL","Failed to make a connection with lond.");
1.10      foxr     1207: 	$ConnectionRetriesLeft--;
                   1208: 	return 0;		# Failure.
1.5       foxr     1209:     }  else {
1.22      foxr     1210: 
1.5       foxr     1211: 	# The connection needs to have writability 
                   1212: 	# monitored in order to send the init sequence
                   1213: 	# that starts the whole authentication/key
                   1214: 	# exchange underway.
                   1215: 	#
                   1216: 	my $Socket = $Connection->GetSocket();
1.30      foxr     1217: 	if($Socket eq undef) {
1.64      foxr     1218: 	    &child_exit(-1, "did not get a socket from the connection");
1.5       foxr     1219: 	} else {
                   1220: 	    &Debug(9,"MakeLondConnection got socket: ".$Socket);
                   1221: 	}
1.1       foxr     1222: 	
1.21      foxr     1223: 	$Connection->SetTimeoutCallback(\&SocketTimeout);
                   1224: 
1.23      foxr     1225: 	my $event = Event->io(fd       => $Socket,
1.5       foxr     1226: 			   poll     => 'w',
                   1227: 			   cb       => \&LondWritable,
1.8       foxr     1228: 			   data     => $Connection,
1.5       foxr     1229: 			   desc => 'Connection to lond server');
                   1230: 	$ActiveConnections{$Connection} = $event;
1.52      foxr     1231: 	if ($ConnectionCount == 0) {
                   1232: 	    &SetupTimer;	# Need to handle timeouts with connections...
                   1233: 	}
1.5       foxr     1234: 	$ConnectionCount++;
1.8       foxr     1235: 	Debug(4, "Connection count = ".$ConnectionCount);
1.6       foxr     1236: 	if($ConnectionCount == 1) { # First Connection:
                   1237: 	    QueueDelayed;
                   1238: 	}
1.9       foxr     1239: 	Log("SUCESS", "Created connection ".$ConnectionCount
                   1240: 	    ." to host ".GetServerHost());
1.54      foxr     1241: 	$LondConnecting = 1;	# Connection in progress.
1.10      foxr     1242: 	return 1;		# Return success.
1.1       foxr     1243:     }
                   1244:     
                   1245: }
1.3       albertel 1246: 
1.1       foxr     1247: =pod
1.3       albertel 1248: 
1.1       foxr     1249: =head2 StartRequest
1.3       albertel 1250: 
                   1251: Starts a lond request going on a specified lond connection.
                   1252: parameters are:
                   1253: 
                   1254: =item $Lond
                   1255: 
                   1256: Connection to the lond that will send the transaction and receive the
                   1257: reply.
                   1258: 
                   1259: =item $Client
                   1260: 
                   1261: Connection to the client that is making this request We got the
                   1262: request from this socket, and when the request has been relayed to
                   1263: lond and we get a reply back from lond it will get sent to this
                   1264: socket.
                   1265: 
                   1266: =item $Request
                   1267: 
                   1268: The text of the request to send.
                   1269: 
1.1       foxr     1270: =cut
                   1271: 
                   1272: sub StartRequest {
1.47      foxr     1273: 
                   1274:     my ($Lond, $Request) = @_;
1.1       foxr     1275:     
1.7       foxr     1276:     Debug(6, "StartRequest: ".$Request->getRequest());
1.1       foxr     1277: 
                   1278:     my $Socket = $Lond->GetSocket();
                   1279:     
1.7       foxr     1280:     $Request->Activate($Lond);
                   1281:     $ActiveTransactions{$Lond} = $Request;
1.1       foxr     1282: 
1.7       foxr     1283:     $Lond->InitiateTransaction($Request->getRequest());
1.23      foxr     1284:     my $event = Event->io(fd      => $Socket,
1.1       foxr     1285: 		       poll    => "w",
                   1286: 		       cb      => \&LondWritable,
                   1287: 		       data    => $Lond,
                   1288: 		       desc    => "lond transaction connection");
                   1289:     $ActiveConnections{$Lond} = $event;
                   1290:     Debug(8," Start Request made watcher data with ".$event->data."\n");
                   1291: }
                   1292: 
                   1293: =pod
1.3       albertel 1294: 
1.1       foxr     1295: =head2 QueueTransaction
1.3       albertel 1296: 
                   1297: If there is an idle lond connection, it is put to work doing this
                   1298: transaction.  Otherwise, the transaction is placed in the work queue.
                   1299: If placed in the work queue and the maximum number of connections has
                   1300: not yet been created, a new connection will be started.  Our goal is
                   1301: to eventually have a sufficient number of connections that the work
                   1302: queue will typically be empty.  parameters are:
                   1303: 
                   1304: =item Socket
                   1305: 
                   1306: open on the lonc client.
                   1307: 
                   1308: =item Request
                   1309: 
                   1310: data to send to the lond.
1.1       foxr     1311: 
                   1312: =cut
1.3       albertel 1313: 
1.1       foxr     1314: sub QueueTransaction {
                   1315: 
1.7       foxr     1316:     my $requestData   = shift;	# This is a LondTransaction.
                   1317:     my $cmd           = $requestData->getRequest();
                   1318: 
                   1319:     Debug(6,"QueueTransaction: ".$cmd);
1.1       foxr     1320: 
                   1321:     my $LondSocket    = $IdleConnections->pop();
                   1322:     if(!defined $LondSocket) {	# Need to queue request.
1.29      foxr     1323: 	Debug(5,"Must queue...");
1.1       foxr     1324: 	$WorkQueue->enqueue($requestData);
1.56      foxr     1325: 	Debug(5, "Queue Transaction startnew $ConnectionCount $LondConnecting");
                   1326: 	if(($ConnectionCount < $MaxConnectionCount)   && (! $LondConnecting)) {
                   1327: 
1.22      foxr     1328: 	    if($ConnectionRetriesLeft > 0) {
1.29      foxr     1329: 		Debug(5,"Starting additional lond connection");
1.56      foxr     1330: 		if(&MakeLondConnection() == 0) {
1.22      foxr     1331: 		    EmptyQueue();	# Fail transactions, can't make connection.
1.42      foxr     1332: 		    CloseAllLondConnections; # Should all be closed but...
1.22      foxr     1333: 		}
                   1334: 	    } else {
                   1335: 		ShowStatus(GetServerHost()." >>> DEAD !!!! <<<");
1.56      foxr     1336: 		$LondConnecting = 0;
1.22      foxr     1337: 		EmptyQueue();	# It's worse than that ... he's dead Jim.
1.42      foxr     1338: 		CloseAllLondConnections; # Should all be closed but..
1.17      foxr     1339: 	    }
1.1       foxr     1340: 	}
                   1341:     } else {			# Can start the request:
                   1342: 	Debug(8,"Can start...");
1.7       foxr     1343: 	StartRequest($LondSocket,  $requestData);
1.1       foxr     1344:     }
                   1345: }
                   1346: 
                   1347: #-------------------------- Lonc UNIX socket handling ---------------------
1.3       albertel 1348: 
1.1       foxr     1349: =pod
1.3       albertel 1350: 
1.1       foxr     1351: =head2 ClientRequest
1.3       albertel 1352: Callback that is called when data can be read from the UNIX domain
                   1353: socket connecting us with an apache server process.
1.1       foxr     1354: 
                   1355: =cut
                   1356: 
                   1357: sub ClientRequest {
                   1358:     Debug(6, "ClientRequest");
                   1359:     my $event   = shift;
                   1360:     my $watcher = $event->w;
                   1361:     my $socket  = $watcher->fd;
                   1362:     my $data    = $watcher->data;
                   1363:     my $thisread;
                   1364: 
                   1365:     Debug(9, "  Watcher named: ".$watcher->desc);
                   1366: 
                   1367:     my $rv = $socket->recv($thisread, POSIX::BUFSIZ, 0);
                   1368:     Debug(8, "rcv:  data length = ".length($thisread)
                   1369: 	  ." read =".$thisread);
1.29      foxr     1370:     unless (defined $rv  && length($thisread)) {
1.1       foxr     1371: 	 # Likely eof on socket.
                   1372: 	Debug(5,"Client Socket closed on lonc for ".$RemoteHost);
                   1373: 	close($socket);
                   1374: 	$watcher->cancel();
                   1375: 	delete($ActiveClients{$socket});
1.10      foxr     1376: 	return;
1.1       foxr     1377:     }
                   1378:     Debug(8,"Data: ".$data." this read: ".$thisread);
                   1379:     $data = $data.$thisread;	# Append new data.
                   1380:     $watcher->data($data);
1.44      albertel 1381:     if($data =~ /\n$/) {	# Request entirely read.
1.10      foxr     1382: 	if($data eq "close_connection_exit\n") {
1.9       foxr     1383: 	    Log("CRITICAL",
                   1384: 		"Request Close Connection ... exiting");
                   1385: 	    CloseAllLondConnections();
                   1386: 	    exit;
                   1387: 	}
1.1       foxr     1388: 	Debug(8, "Complete transaction received: ".$data);
1.39      foxr     1389: 	if($LogTransactions) {
                   1390: 	    Log("SUCCESS", "Transaction: '$data'"); # Transaction has \n.
                   1391: 	}
1.8       foxr     1392: 	my $Transaction = LondTransaction->new($data);
1.7       foxr     1393: 	$Transaction->SetClient($socket);
                   1394: 	QueueTransaction($Transaction);
1.1       foxr     1395: 	$watcher->cancel();	# Done looking for input data.
                   1396:     }
                   1397: 
                   1398: }
                   1399: 
1.62      foxr     1400: #
                   1401: #     Accept a connection request for a client (lonc child) and
                   1402: #    start up an event watcher to keep an eye on input from that 
                   1403: #    Event.  This can be called both from NewClient and from
                   1404: #    ChildProcess if we are started in DieWhenIdle mode.
                   1405: # Parameters:
                   1406: #    $socket       - The listener socket.
                   1407: # Returns:
                   1408: #   NONE
                   1409: # Side Effects:
                   1410: #    An event is made to watch the accepted connection.
                   1411: #    Active clients hash is updated to reflect the new connection.
                   1412: #    The client connection count is incremented.
                   1413: #
                   1414: sub accept_client {
                   1415:     my ($socket) = @_;
                   1416: 
                   1417:     Debug(8, "Entering accept for lonc UNIX socket\n");
                   1418:     my $connection = $socket->accept();	# Accept the client connection.
                   1419:     Debug(8,"Connection request accepted from "
                   1420: 	  .GetPeername($connection, AF_UNIX));
                   1421: 
                   1422: 
                   1423:     my $description = sprintf("Connection to lonc client %d",
                   1424: 			      $ClientConnection);
                   1425:     Debug(9, "Creating event named: ".$description);
                   1426:     Event->io(cb      => \&ClientRequest,
                   1427: 	      poll    => 'r',
                   1428: 	      desc    => $description,
                   1429: 	      data    => "",
                   1430: 	      fd      => $connection);
                   1431:     $ActiveClients{$connection} = $ClientConnection;
                   1432:     $ClientConnection++;
                   1433: }
1.1       foxr     1434: 
                   1435: =pod
1.3       albertel 1436: 
1.1       foxr     1437: =head2  NewClient
1.3       albertel 1438: 
                   1439: Callback that is called when a connection is received on the unix
                   1440: socket for a new client of lonc.  The callback is parameterized by the
                   1441: event.. which is a-priori assumed to be an io event, and therefore has
                   1442: an fd member that is the Listener socket.  We Accept the connection
                   1443: and register a new event on the readability of that socket:
                   1444: 
1.1       foxr     1445: =cut
1.3       albertel 1446: 
1.1       foxr     1447: sub NewClient {
                   1448:     Debug(6, "NewClient");
                   1449:     my $event      = shift;		# Get the event parameters.
                   1450:     my $watcher    = $event->w; 
                   1451:     my $socket     = $watcher->fd;	# Get the event' socket.
                   1452: 
1.62      foxr     1453:     &accept_client($socket);
1.1       foxr     1454: }
1.3       albertel 1455: 
                   1456: =pod
                   1457: 
                   1458: =head2 GetLoncSocketPath
                   1459: 
                   1460: Returns the name of the UNIX socket on which to listen for client
                   1461: connections.
1.1       foxr     1462: 
1.58      foxr     1463: =head2 Parameters:
                   1464: 
                   1465:     host (optional)  - Name of the host socket to return.. defaults to
                   1466:                        the return from GetServerHost().
                   1467: 
1.1       foxr     1468: =cut
1.3       albertel 1469: 
1.1       foxr     1470: sub GetLoncSocketPath {
1.58      foxr     1471: 
                   1472:     my $host = GetServerHost();	# Default host.
                   1473:     if (@_) {
                   1474: 	($host)  = @_;		# Override if supplied.
                   1475:     }
                   1476:     return $UnixSocketDir."/".$host;
1.1       foxr     1477: }
                   1478: 
1.3       albertel 1479: =pod
                   1480: 
                   1481: =head2 GetServerHost
                   1482: 
                   1483: Returns the host whose lond we talk with.
                   1484: 
1.1       foxr     1485: =cut
1.3       albertel 1486: 
1.7       foxr     1487: sub GetServerHost {
1.1       foxr     1488:     return $RemoteHost;		# Setup by the fork.
                   1489: }
1.3       albertel 1490: 
                   1491: =pod
                   1492: 
                   1493: =head2 GetServerPort
                   1494: 
                   1495: Returns the lond port number.
                   1496: 
1.1       foxr     1497: =cut
1.3       albertel 1498: 
1.7       foxr     1499: sub GetServerPort {
1.1       foxr     1500:     return $perlvar{londPort};
                   1501: }
1.3       albertel 1502: 
                   1503: =pod
                   1504: 
                   1505: =head2 SetupLoncListener
                   1506: 
                   1507: Setup a lonc listener event.  The event is called when the socket
                   1508: becomes readable.. that corresponds to the receipt of a new
                   1509: connection.  The event handler established will accept the connection
                   1510: (creating a communcations channel), that int turn will establish
                   1511: another event handler to subess requests.
1.1       foxr     1512: 
1.58      foxr     1513: =head2  Parameters:
                   1514: 
                   1515:    host (optional)   Name of the host to set up a unix socket to.
                   1516: 
1.1       foxr     1517: =cut
1.3       albertel 1518: 
1.1       foxr     1519: sub SetupLoncListener {
                   1520: 
1.58      foxr     1521:     my $host       = GetServerHost(); # Default host.
                   1522:     if (@_) {
                   1523: 	($host)    = @_		# Override host with parameter.
                   1524:     }
                   1525: 
1.1       foxr     1526:     my $socket;
1.58      foxr     1527:     my $SocketName = GetLoncSocketPath($host);
1.1       foxr     1528:     unlink($SocketName);
1.7       foxr     1529:     unless ($socket =IO::Socket::UNIX->new(Local  => $SocketName,
1.55      albertel 1530: 					    Listen => 250, 
1.1       foxr     1531: 					    Type   => SOCK_STREAM)) {
1.64      foxr     1532: 	if($I_am_child) {
                   1533: 	    &child_exit(-1, "Failed to create a lonc listener socket");
                   1534: 	} else {
                   1535: 	    die "Failed to create a lonc listner socket";
                   1536: 	}
1.1       foxr     1537:     }
1.59      foxr     1538:     return $socket;
1.1       foxr     1539: }
                   1540: 
1.39      foxr     1541: #
                   1542: #   Toggle transaction logging.
                   1543: #  Implicit inputs:  
                   1544: #     LogTransactions
                   1545: #  Implicit Outputs:
                   1546: #     LogTransactions
                   1547: sub ToggleTransactionLogging {
                   1548:     print STDERR "Toggle transaction logging...\n";
                   1549:     if(!$LogTransactions) {
                   1550: 	$LogTransactions = 1;
                   1551:     } else {
                   1552: 	$LogTransactions = 0;
                   1553:     }
                   1554: 
                   1555: 
                   1556:     Log("SUCCESS", "Toggled transaction logging: $LogTransactions \n");
                   1557: }
                   1558: 
1.14      foxr     1559: =pod 
                   1560: 
                   1561: =head2 ChildStatus
                   1562:  
                   1563: Child USR1 signal handler to report the most recent status
                   1564: into the status file.
                   1565: 
1.22      foxr     1566: We also use this to reset the retries count in order to allow the
                   1567: client to retry connections with a previously dead server.
1.69    ! matthew  1568: 
1.14      foxr     1569: =cut
1.46      albertel 1570: 
1.14      foxr     1571: sub ChildStatus {
                   1572:     my $event = shift;
                   1573:     my $watcher = $event->w;
                   1574: 
                   1575:     Debug(2, "Reporting child status because : ".$watcher->data);
                   1576:     my $docdir = $perlvar{'lonDocRoot'};
1.67      albertel 1577:     
                   1578:     open(LOG,">>$docdir/lon-status/loncstatus.txt");
                   1579:     flock(LOG,LOCK_EX);
                   1580:     print LOG $$."\t".$RemoteHost."\t".$Status."\t".
1.14      foxr     1581: 	$RecentLogEntry."\n";
1.38      foxr     1582:     #
                   1583:     #  Write out information about each of the connections:
                   1584:     #
1.46      albertel 1585:     if ($DebugLevel > 2) {
1.67      albertel 1586: 	print LOG "Active connection statuses: \n";
1.46      albertel 1587: 	my $i = 1;
                   1588: 	print STDERR  "================================= Socket Status Dump:\n";
                   1589: 	foreach my $item (keys %ActiveConnections) {
                   1590: 	    my $Socket = $ActiveConnections{$item}->data;
                   1591: 	    my $state  = $Socket->GetState();
1.67      albertel 1592: 	    print LOG "Connection $i State: $state\n";
1.46      albertel 1593: 	    print STDERR "---------------------- Connection $i \n";
1.48      foxr     1594: 	    $Socket->Dump(-1);	# Ensure it gets dumped..
1.46      albertel 1595: 	    $i++;	
                   1596: 	}
1.38      foxr     1597:     }
1.67      albertel 1598:     flock(LOG,LOCK_UN);
                   1599:     close(LOG);
1.22      foxr     1600:     $ConnectionRetriesLeft = $ConnectionRetries;
1.14      foxr     1601: }
                   1602: 
1.1       foxr     1603: =pod
1.3       albertel 1604: 
1.10      foxr     1605: =head2 SignalledToDeath
                   1606: 
                   1607: Called in response to a signal that causes a chid process to die.
                   1608: 
                   1609: =cut
                   1610: 
                   1611: 
                   1612: sub SignalledToDeath {
1.14      foxr     1613:     my $event  = shift;
                   1614:     my $watcher= $event->w;
                   1615: 
                   1616:     Debug(2,"Signalled to death! via ".$watcher->data);
1.17      foxr     1617:     my ($signal) = $watcher->data;
1.10      foxr     1618:     chomp($signal);
                   1619:     Log("CRITICAL", "Abnormal exit.  Child $$ for $RemoteHost "
                   1620: 	."died through "."\"$signal\"");
1.68      albertel 1621:     #LogPerm("F:lonc: $$ on $RemoteHost signalled to death: "
                   1622: #	    ."\"$signal\"");
1.12      foxr     1623:     exit 0;
1.10      foxr     1624: 
                   1625: }
1.16      foxr     1626: 
1.69    ! matthew  1627: =pod
        !          1628: 
1.16      foxr     1629: =head2 ToggleDebug
                   1630: 
                   1631: This sub toggles trace debugging on and off.
                   1632: 
                   1633: =cut
                   1634: 
                   1635: sub ToggleDebug {
                   1636:     my $Current    = $DebugLevel;
                   1637:        $DebugLevel = $NextDebugLevel;
                   1638:        $NextDebugLevel = $Current;
                   1639: 
                   1640:     Log("SUCCESS", "New debugging level for $RemoteHost now $DebugLevel");
                   1641: 
                   1642: }
                   1643: 
1.69    ! matthew  1644: =pod
        !          1645: 
1.1       foxr     1646: =head2 ChildProcess
                   1647: 
                   1648: This sub implements a child process for a single lonc daemon.
1.61      foxr     1649: Optional parameter:
                   1650:    $socket  - if provided, this is a socket already open for listen
                   1651:               on the client socket. Otherwise, a new listen is set up.
1.1       foxr     1652: 
                   1653: =cut
                   1654: 
                   1655: sub ChildProcess {
1.62      foxr     1656:     #  If we are in DieWhenIdle mode, we've inherited all the
                   1657:     #  events of our parent and those have to be cancelled or else
                   1658:     #  all holy bloody chaos will result.. trust me, I already made
                   1659:     #  >that< mistake.
                   1660: 
                   1661:     my $host = GetServerHost();
                   1662:     foreach my $listener (keys %parent_dispatchers) {
                   1663: 	my $watcher = $parent_dispatchers{$listener};
                   1664: 	my $s       = $watcher->fd;
                   1665: 	if ($listener ne $host) { # Close everyone but me.
                   1666: 	    Debug(5, "Closing listen socket for $listener");
                   1667: 	    $s->close();
                   1668: 	}
                   1669: 	Debug(5, "Killing watcher for $listener");
                   1670: 
                   1671: 	$watcher->cancel();
1.65      foxr     1672: 	delete($parent_dispatchers{$listener});
1.62      foxr     1673: 
                   1674:     }
1.65      foxr     1675: 
                   1676:     #  kill off the parent's signal handlers too!  
                   1677:     #
                   1678: 
                   1679:     for my $handler (keys %parent_handlers) {
                   1680: 	my $watcher = $parent_handlers{$handler};
                   1681: 	$watcher->cancel();
                   1682: 	delete($parent_handlers{$handler});
                   1683:     }
                   1684: 
1.64      foxr     1685:     $I_am_child    = 1;		# Seems like in spite of it all I may still getting
                   1686:                                 # parent event dispatches.. flag I'm a child.
1.1       foxr     1687: 
                   1688: 
1.14      foxr     1689:     #
                   1690:     #  Signals must be handled by the Event framework...
1.61      foxr     1691:     #
1.14      foxr     1692: 
                   1693:     Event->signal(signal   => "QUIT",
                   1694: 		  cb       => \&SignalledToDeath,
                   1695: 		  data     => "QUIT");
                   1696:     Event->signal(signal   => "HUP",
                   1697: 		  cb       => \&ChildStatus,
                   1698: 		  data     => "HUP");
                   1699:     Event->signal(signal   => "USR1",
                   1700: 		  cb       => \&ChildStatus,
                   1701: 		  data     => "USR1");
1.39      foxr     1702:     Event->signal(signal   => "USR2",
                   1703: 		  cb       => \&ToggleTransactionLogging);
1.16      foxr     1704:     Event->signal(signal   => "INT",
                   1705: 		  cb       => \&ToggleDebug,
                   1706: 		  data     => "INT");
1.1       foxr     1707: 
1.62      foxr     1708:     #  Figure out if we got passed a socket or need to open one to listen for
                   1709:     #  client requests.
                   1710: 
1.61      foxr     1711:     my ($socket) = @_;
                   1712:     if (!$socket) {
                   1713: 
                   1714: 	$socket =  SetupLoncListener();
                   1715:     }
1.62      foxr     1716:     #  Establish an event to listen for client connection requests.
                   1717: 
                   1718: 
1.59      foxr     1719:     Event->io(cb   => \&NewClient,
                   1720: 	      poll => 'r',
                   1721: 	      desc => 'Lonc Listener Unix Socket',
                   1722: 	      fd   => $socket);
1.1       foxr     1723:     
                   1724:     $Event::Debuglevel = $DebugLevel;
                   1725:     
                   1726:     Debug(9, "Making initial lond connection for ".$RemoteHost);
                   1727: 
                   1728: # Setup the initial server connection:
                   1729:     
1.62      foxr     1730:      # &MakeLondConnection(); // let first work request do it.
1.10      foxr     1731: 
1.62      foxr     1732:     #  If We are in diwhenidle, need to accept the connection since the
                   1733:     #  event may  not fire.
                   1734: 
                   1735:     if ($DieWhenIdle) {
                   1736: 	&accept_client($socket);
                   1737:     }
1.5       foxr     1738: 
1.1       foxr     1739:     Debug(9,"Entering event loop");
                   1740:     my $ret = Event::loop();		#  Start the main event loop.
                   1741:     
                   1742:     
1.64      foxr     1743:     &child_exit (-1,"Main event loop exited!!!");
1.1       foxr     1744: }
                   1745: 
                   1746: #  Create a new child for host passed in:
                   1747: 
                   1748: sub CreateChild {
1.62      foxr     1749:     my ($host, $socket) = @_;
1.52      foxr     1750: 
1.12      foxr     1751:     my $sigset = POSIX::SigSet->new(SIGINT);
                   1752:     sigprocmask(SIG_BLOCK, $sigset);
1.1       foxr     1753:     $RemoteHost = $host;
1.9       foxr     1754:     Log("CRITICAL", "Forking server for ".$host);
1.23      foxr     1755:     my $pid          = fork;
1.1       foxr     1756:     if($pid) {			# Parent
1.17      foxr     1757: 	$RemoteHost = "Parent";
1.27      foxr     1758: 	$ChildHash{$pid} = $host;
1.26      foxr     1759: 	$HostToPid{$host}= $pid;
1.12      foxr     1760: 	sigprocmask(SIG_UNBLOCK, $sigset);
                   1761: 
1.1       foxr     1762:     } else {			# child.
1.5       foxr     1763: 	ShowStatus("Connected to ".$RemoteHost);
1.23      foxr     1764: 	$SIG{INT} = 'DEFAULT';
1.12      foxr     1765: 	sigprocmask(SIG_UNBLOCK, $sigset);
1.62      foxr     1766: 	if(defined $socket) {
                   1767: 	    &ChildProcess($socket);
                   1768: 	} else {
                   1769: 	    ChildProcess;		# Does not return.
                   1770: 	}
1.1       foxr     1771:     }
1.61      foxr     1772: }
1.1       foxr     1773: 
1.61      foxr     1774: # parent_client_connection:
                   1775: #    Event handler that processes client connections for the parent process.
                   1776: #    This sub is called when the parent is listening on a socket and
                   1777: #    a connection request arrives.  We must:
                   1778: #     Start a child process to accept the connection request.
                   1779: #     Kill our listen on the socket.
                   1780: # Parameter:
                   1781: #    event       - The event object that was created to monitor this socket.
                   1782: #                  event->w->fd is the socket.
                   1783: # Returns:
                   1784: #    NONE
                   1785: #
                   1786: sub parent_client_connection {
1.62      foxr     1787:     if ($I_am_child) {
                   1788: 	#  Should not get here, but seem to anyway:
                   1789: 	&Debug(5," Child caught parent client connection event!!");
                   1790: 	my ($event) = @_;
                   1791: 	my $watcher = $event->w;
                   1792: 	$watcher->cancel();	# Try to kill it off again!!
                   1793:     } else {
                   1794: 	&Debug(9, "parent_client_connection");
                   1795: 	my ($event)   = @_;
                   1796: 	my $watcher   = $event->w;
                   1797: 	my $socket    = $watcher->fd;
                   1798: 	
                   1799: 	# Lookup the host associated with this socket:
                   1800: 	
                   1801: 	my $host = $listening_to{$socket};
                   1802: 	
                   1803: 	# Start the child:
                   1804: 	
                   1805: 	
                   1806: 	
                   1807: 	&Debug(9,"Creating child for $host (parent_client_connection)");
                   1808: 	&CreateChild($host, $socket);
                   1809: 	
                   1810: 	# Clean up the listen since now the child takes over until it exits.
                   1811: 	
                   1812: 	$watcher->cancel();		# Nolonger listening to this event
                   1813: 	delete($listening_to{$socket});
                   1814: 	delete($parent_dispatchers{$host});
                   1815: 	$socket->close();
                   1816:     }
1.61      foxr     1817: }
                   1818: 
                   1819: # parent_listen:
                   1820: #    Opens a socket and starts a listen for the parent process on a client UNIX
                   1821: #    domain socket.
                   1822: #
                   1823: #    This involves:
                   1824: #       Creating a socket for listen.
                   1825: #       Removing any socket lock file
                   1826: #       Adding an event handler for this socket becoming readable
                   1827: #         To the parent's event dispatcher.
                   1828: # Parameters:
                   1829: #    loncapa_host    - LonCAPA cluster name of the host represented by the client
                   1830: #                      socket.
                   1831: # Returns:
                   1832: #    NONE
                   1833: #
                   1834: sub parent_listen {
                   1835:     my ($loncapa_host) = @_;
                   1836:     Debug(5, "parent_listen: $loncapa_host");
                   1837: 
                   1838:     my $socket    = &SetupLoncListener($loncapa_host);
1.62      foxr     1839:     $listening_to{$socket} = $loncapa_host;
1.61      foxr     1840:     if (!$socket) {
                   1841: 	die "Unable to create a listen socket for $loncapa_host";
                   1842:     }
                   1843:     
1.62      foxr     1844:     my $lock_file = &GetLoncSocketPath($loncapa_host).".lock";
1.61      foxr     1845:     unlink($lock_file);		# No problem if it doesn't exist yet [startup e.g.]
                   1846: 
1.62      foxr     1847:     my $watcher = Event->io(cb    => \&parent_client_connection,
1.61      foxr     1848: 	      poll  => 'r',
1.62      foxr     1849: 	      desc  => "Parent listener unix socket ($loncapa_host)",
1.61      foxr     1850: 	      fd    => $socket);
1.62      foxr     1851:     $parent_dispatchers{$loncapa_host} = $watcher;
1.61      foxr     1852: 
                   1853: }
                   1854: 
                   1855: 
                   1856: # listen_on_all_unix_sockets:
                   1857: #    This sub initiates a listen on all unix domain lonc client sockets.
                   1858: #    This will be called in the case where we are trimming idle processes.
                   1859: #    When idle processes are trimmed, loncnew starts up with no children,
                   1860: #    and only spawns off children when a connection request occurs on the
                   1861: #    client unix socket.  The spawned child continues to run until it has
                   1862: #    been idle a while at which point it eventually exits and once more
                   1863: #    the parent picks up the listen.
                   1864: #
                   1865: #  Parameters:
                   1866: #      NONE
                   1867: #  Implicit Inputs:
                   1868: #    The configuration file that has been read in by LondConnection.
                   1869: #  Returns:
                   1870: #     NONE
                   1871: #
                   1872: sub listen_on_all_unix_sockets {
                   1873:     Debug(5, "listen_on_all_unix_sockets");
                   1874:     my $host_iterator      =   &LondConnection::GetHostIterator();
                   1875:     while (!$host_iterator->end()) {
                   1876: 	my $host_entry_ref =   $host_iterator->get();
                   1877: 	my $host_name      = $host_entry_ref->[0];
                   1878: 	Debug(9, "Listen for $host_name");
                   1879: 	&parent_listen($host_name);
                   1880: 	$host_iterator->next();
                   1881:     }
1.1       foxr     1882: }
1.61      foxr     1883: 
1.63      foxr     1884: #   server_died is called whenever a child process exits.
                   1885: #   Since this is dispatched via a signal, we must process all
                   1886: #   dead children until there are no more left.  The action
                   1887: #   is to:
                   1888: #      - Remove the child from the bookeeping hashes
                   1889: #      - Re-establish a listen on the unix domain socket associated
                   1890: #        with that host.
                   1891: # Parameters:
                   1892: #    The event, but we don't actually care about it.
                   1893: sub server_died {
                   1894:     &Debug(9, "server_died called...");
                   1895:     
                   1896:     while(1) {			# Loop until waitpid nowait fails.
                   1897: 	my $pid = waitpid(-1, WNOHANG);
                   1898: 	if($pid <= 0) {
                   1899: 	    return;		# Nothing left to wait for.
                   1900: 	}
                   1901: 	# need the host to restart:
                   1902: 
                   1903: 	my $host = $ChildHash{$pid};
                   1904: 	if($host) {		# It's for real...
                   1905: 	    &Debug(9, "Caught sigchild for $host");
                   1906: 	    delete($ChildHash{$pid});
                   1907: 	    delete($HostToPid{$host});
                   1908: 	    &parent_listen($host);
                   1909: 
                   1910: 	} else {
                   1911: 	    &Debug(5, "Caught sigchild for pid not in hosts hash: $pid");
                   1912: 	}
                   1913:     }
                   1914: 
                   1915: }
                   1916: 
1.1       foxr     1917: #
                   1918: #  Parent process logic pass 1:
                   1919: #   For each entry in the hosts table, we will
                   1920: #  fork off an instance of ChildProcess to service the transactions
                   1921: #  to that host.  Each pid will be entered in a global hash
                   1922: #  with the value of the key, the host.
                   1923: #  The parent will then enter a loop to wait for process exits.
                   1924: #  Each exit gets logged and the child gets restarted.
                   1925: #
                   1926: 
1.5       foxr     1927: #
                   1928: #   Fork and start in new session so hang-up isn't going to 
                   1929: #   happen without intent.
                   1930: #
                   1931: 
                   1932: 
1.6       foxr     1933: 
                   1934: 
1.8       foxr     1935: 
1.6       foxr     1936: 
                   1937: ShowStatus("Forming new session");
                   1938: my $childpid = fork;
                   1939: if ($childpid != 0) {
                   1940:     sleep 4;			# Give child a chacne to break to
                   1941:     exit 0;			# a new sesion.
                   1942: }
1.8       foxr     1943: #
                   1944: #   Write my pid into the pid file so I can be located
                   1945: #
                   1946: 
                   1947: ShowStatus("Parent writing pid file:");
1.23      foxr     1948: my $execdir = $perlvar{'lonDaemons'};
1.8       foxr     1949: open (PIDSAVE, ">$execdir/logs/lonc.pid");
                   1950: print PIDSAVE "$$\n";
                   1951: close(PIDSAVE);
1.6       foxr     1952: 
1.17      foxr     1953: 
                   1954: 
1.6       foxr     1955: if (POSIX::setsid() < 0) {
                   1956:     print "Could not create new session\n";
                   1957:     exit -1;
                   1958: }
1.5       foxr     1959: 
                   1960: ShowStatus("Forking node servers");
                   1961: 
1.9       foxr     1962: Log("CRITICAL", "--------------- Starting children ---------------");
                   1963: 
1.31      foxr     1964: LondConnection::ReadConfig;               # Read standard config files.
1.1       foxr     1965: my $HostIterator = LondConnection::GetHostIterator;
                   1966: 
1.60      foxr     1967: if ($DieWhenIdle) {
1.61      foxr     1968:     $RemoteHost = "[parent]";
                   1969:     &listen_on_all_unix_sockets();
1.60      foxr     1970: } else {
                   1971:     
                   1972:     while (! $HostIterator->end()) {
                   1973: 	
                   1974: 	my $hostentryref = $HostIterator->get();
                   1975: 	CreateChild($hostentryref->[0]);
                   1976: 	$HostHash{$hostentryref->[0]} = $hostentryref->[4];
                   1977: 	$HostIterator->next();
                   1978:     }
1.1       foxr     1979: }
1.60      foxr     1980: 
1.12      foxr     1981: $RemoteHost = "Parent Server";
1.1       foxr     1982: 
                   1983: # Maintain the population:
1.5       foxr     1984: 
                   1985: ShowStatus("Parent keeping the flock");
1.1       foxr     1986: 
1.12      foxr     1987: 
1.60      foxr     1988: if ($DieWhenIdle) {
1.63      foxr     1989:     # We need to setup a SIGChild event to handle the exit (natural or otherwise)
                   1990:     # of the children.
                   1991: 
                   1992:     Event->signal(cb       => \&server_died,
                   1993: 		   desc     => "Child exit handler",
                   1994: 		   signal   => "CHLD");
                   1995: 
                   1996: 
1.65      foxr     1997:     # Set up all the other signals we set up.  We'll vector them off to the
                   1998:     # same subs as we would for DieWhenIdle false and, if necessary, conditionalize
                   1999:     # the code there.
                   2000: 
                   2001:     $parent_handlers{INT} = Event->signal(cb       => \&Terminate,
                   2002: 					  desc     => "Parent INT handler",
                   2003: 					  signal   => "INT");
                   2004:     $parent_handlers{TERM} = Event->signal(cb       => \&Terminate,
                   2005: 					   desc     => "Parent TERM handler",
                   2006: 					   signal   => "TERM");
                   2007:     $parent_handlers{HUP}  = Event->signal(cb       => \&Restart,
                   2008: 					   desc     => "Parent HUP handler.",
                   2009: 					   signal   => "HUP");
                   2010:     $parent_handlers{USR1} = Event->signal(cb       => \&CheckKids,
                   2011: 					   desc     => "Parent USR1 handler",
                   2012: 					   signal   => "USR1");
                   2013:     $parent_handlers{USR2} = Event->signal(cb       => \&UpdateKids,
                   2014: 					   desc     => "Parent USR2 handler.",
                   2015: 					   signal   => "USR2");
                   2016:     
                   2017:     #  Start procdesing events.
                   2018: 
1.61      foxr     2019:     $Event::DebugLevel = $DebugLevel;
                   2020:     Debug(9, "Parent entering event loop");
                   2021:     my $ret = Event::loop();
                   2022:     die "Main Event loop exited: $ret";
                   2023: 
                   2024: 
1.60      foxr     2025: } else {
1.61      foxr     2026:     #
                   2027:     #   Set up parent signals:
                   2028:     #
1.60      foxr     2029:     
                   2030:     $SIG{INT}  = \&Terminate;
                   2031:     $SIG{TERM} = \&Terminate; 
                   2032:     $SIG{HUP}  = \&Restart;
                   2033:     $SIG{USR1} = \&CheckKids; 
                   2034:     $SIG{USR2} = \&UpdateKids;	# LonManage update request.
                   2035:     
                   2036:     while(1) {
                   2037: 	my $deadchild = wait();
                   2038: 	if(exists $ChildHash{$deadchild}) {	# need to restart.
                   2039: 	    my $deadhost = $ChildHash{$deadchild};
                   2040: 	    delete($HostToPid{$deadhost});
                   2041: 	    delete($ChildHash{$deadchild});
                   2042: 	    Log("WARNING","Lost child pid= ".$deadchild.
                   2043: 		"Connected to host ".$deadhost);
                   2044: 	    Log("INFO", "Restarting child procesing ".$deadhost);
                   2045: 	    CreateChild($deadhost);
                   2046: 	}
1.1       foxr     2047:     }
1.13      foxr     2048: }
                   2049: 
1.14      foxr     2050: 
                   2051: =pod
                   2052: 
                   2053: =head1 CheckKids
                   2054: 
                   2055:   Since kids do not die as easily in this implementation
                   2056: as the previous one, there  is no need to restart the
                   2057: dead ones (all dead kids get restarted when they die!!)
                   2058: The only thing this function does is to pass USR1 to the
                   2059: kids so that they report their status.
                   2060: 
                   2061: =cut
                   2062: 
                   2063: sub CheckKids {
                   2064:     Debug(2, "Checking status of children");
                   2065:     my $docdir = $perlvar{'lonDocRoot'};
                   2066:     my $fh = IO::File->new(">$docdir/lon-status/loncstatus.txt");
                   2067:     my $now=time;
                   2068:     my $local=localtime($now);
                   2069:     print $fh "LONC status $local - parent $$ \n\n";
1.65      foxr     2070:     foreach my $host (keys %parent_dispatchers) {
                   2071: 	print $fh "LONC Parent process listening for $host\n";
                   2072:     }
1.23      foxr     2073:     foreach my $pid (keys %ChildHash) {
1.14      foxr     2074: 	Debug(2, "Sending USR1 -> $pid");
                   2075: 	kill 'USR1' => $pid;	# Tell Child to report status.
                   2076:     }
1.65      foxr     2077: 
1.14      foxr     2078: }
1.24      foxr     2079: 
                   2080: =pod
                   2081: 
                   2082: =head1  UpdateKids
                   2083: 
1.25      foxr     2084: parent's SIGUSR2 handler.  This handler:
1.24      foxr     2085: 
                   2086: =item
                   2087: 
                   2088: Rereads the hosts file.
                   2089: 
                   2090: =item
                   2091:  
                   2092: Kills off (via sigint) children for hosts that have disappeared.
                   2093: 
                   2094: =item
                   2095: 
1.27      foxr     2096: QUITs  children for hosts that already exist (this just forces a status display
1.24      foxr     2097: and resets the connection retry count for that host.
                   2098: 
                   2099: =item
                   2100: 
                   2101: Starts new children for hosts that have been added to the hosts.tab file since
                   2102: the start of the master program and maintains them.
                   2103: 
                   2104: =cut
                   2105: 
                   2106: sub UpdateKids {
1.27      foxr     2107: 
1.25      foxr     2108:     Log("INFO", "Updating connections via SIGUSR2");
1.27      foxr     2109: 
1.65      foxr     2110:     #  I'm not sure what I was thinking in the first implementation.
                   2111:     # someone will have to work hard to convince me the effect is any
                   2112:     # different than Restart, especially now that we don't start up 
                   2113:     # per host servers automatically, may as well just restart.
                   2114:     # The down side is transactions that are in flight will get timed out
                   2115:     # (lost unless they are critical).
1.27      foxr     2116: 
1.65      foxr     2117:     &Restart();
1.27      foxr     2118: 
1.24      foxr     2119: }
                   2120: 
1.14      foxr     2121: 
1.13      foxr     2122: =pod
                   2123: 
                   2124: =head1 Restart
                   2125: 
                   2126: Signal handler for HUP... all children are killed and
                   2127: we self restart.  This is an el-cheapo way to re read
                   2128: the config file.
                   2129: 
                   2130: =cut
                   2131: 
                   2132: sub Restart {
1.23      foxr     2133:     &KillThemAll;		# First kill all the children.
1.13      foxr     2134:     Log("CRITICAL", "Restarting");
                   2135:     my $execdir = $perlvar{'lonDaemons'};
                   2136:     unlink("$execdir/logs/lonc.pid");
1.65      foxr     2137:     exec("$executable");
1.10      foxr     2138: }
1.12      foxr     2139: 
                   2140: =pod
                   2141: 
                   2142: =head1 KillThemAll
                   2143: 
                   2144: Signal handler that kills all children by sending them a 
1.17      foxr     2145: SIGHUP.  Responds to sigint and sigterm.
1.12      foxr     2146: 
                   2147: =cut
                   2148: 
1.10      foxr     2149: sub KillThemAll {
1.12      foxr     2150:     Debug(2, "Kill them all!!");
                   2151:     local($SIG{CHLD}) = 'IGNORE';      # Our children >will< die.
1.23      foxr     2152:     foreach my $pid (keys %ChildHash) {
1.12      foxr     2153: 	my $serving = $ChildHash{$pid};
1.52      foxr     2154: 	ShowStatus("Nicely Killing lonc for $serving pid = $pid");
                   2155: 	Log("CRITICAL", "Nicely Killing lonc for $serving pid = $pid");
1.17      foxr     2156: 	kill 'QUIT' => $pid;
1.12      foxr     2157:     }
1.52      foxr     2158: 
1.17      foxr     2159: 
1.1       foxr     2160: }
1.12      foxr     2161: 
1.52      foxr     2162: 
                   2163: #
                   2164: #  Kill all children via KILL.  Just in case the
                   2165: #  first shot didn't get them.
                   2166: 
                   2167: sub really_kill_them_all_dammit
                   2168: {
                   2169:     Debug(2, "Kill them all Dammit");
                   2170:     local($SIG{CHLD} = 'IGNORE'); # In case some purist reenabled them.
                   2171:     foreach my $pid (keys %ChildHash) {
                   2172: 	my $serving = $ChildHash{$pid};
                   2173: 	&ShowStatus("Nastily killing lonc for $serving pid = $pid");
                   2174: 	Log("CRITICAL", "Nastily killing lonc for $serving pid = $pid");
                   2175: 	kill 'KILL' => $pid;
                   2176: 	delete($ChildHash{$pid});
                   2177: 	my $execdir = $perlvar{'lonDaemons'};
                   2178: 	unlink("$execdir/logs/lonc.pid");
                   2179:     }
                   2180: }
1.69    ! matthew  2181: 
1.14      foxr     2182: =pod
                   2183: 
                   2184: =head1 Terminate
                   2185:  
                   2186: Terminate the system.
                   2187: 
                   2188: =cut
                   2189: 
                   2190: sub Terminate {
1.52      foxr     2191:     &Log("CRITICAL", "Asked to kill children.. first be nice...");
                   2192:     &KillThemAll;
                   2193:     #
                   2194:     #  By now they really should all be dead.. but just in case 
                   2195:     #  send them all SIGKILL's after a bit of waiting:
                   2196: 
                   2197:     sleep(4);
                   2198:     &Log("CRITICAL", "Now kill children nasty");
                   2199:     &really_kill_them_all_dammit;
1.17      foxr     2200:     Log("CRITICAL","Master process exiting");
                   2201:     exit 0;
1.14      foxr     2202: 
                   2203: }
1.12      foxr     2204: =pod
1.1       foxr     2205: 
                   2206: =head1 Theory
1.3       albertel 2207: 
                   2208: The event class is used to build this as a single process with an
                   2209: event driven model.  The following events are handled:
1.1       foxr     2210: 
                   2211: =item UNIX Socket connection Received
                   2212: 
                   2213: =item Request data arrives on UNIX data transfer socket.
                   2214: 
                   2215: =item lond connection becomes writable.
                   2216: 
                   2217: =item timer fires at 1 second intervals.
                   2218: 
                   2219: All sockets are run in non-blocking mode.  Timeouts managed by the timer
                   2220: handler prevents hung connections.
                   2221: 
                   2222: Key data structures:
                   2223: 
1.3       albertel 2224: =item RequestQueue
                   2225: 
                   2226: A queue of requests received from UNIX sockets that are
                   2227: waiting for a chance to be forwarded on a lond connection socket.
                   2228: 
                   2229: =item ActiveConnections
                   2230: 
                   2231: A hash of lond connections that have transactions in process that are
                   2232: available to be timed out.
                   2233: 
                   2234: =item ActiveTransactions
                   2235: 
                   2236: A hash indexed by lond connections that contain the client reply
                   2237: socket for each connection that has an active transaction on it.
                   2238: 
                   2239: =item IdleConnections
                   2240: 
                   2241: A hash of lond connections that have no work to do.  These connections
                   2242: can be closed if they are idle for a long enough time.
1.1       foxr     2243: 
                   2244: =cut

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>
500 Internal Server Error

Internal Server Error

The server encountered an internal error or misconfiguration and was unable to complete your request.

Please contact the server administrator at root@localhost to inform them of the time this error occurred, and the actions you performed just before this error.

More information about this error may be available in the server error log.