--- loncom/loncnew 2007/05/01 01:04:23 1.85 +++ loncom/loncnew 2017/10/20 20:20:20 1.102 @@ -2,7 +2,7 @@ # The LearningOnline Network with CAPA # lonc maintains the connections to remote computers # -# $Id: loncnew,v 1.85 2007/05/01 01:04:23 albertel Exp $ +# $Id: loncnew,v 1.102 2017/10/20 20:20:20 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -26,7 +26,7 @@ # http://www.lon-capa.org/ # # -# new lonc handles n request out bver m connections to londs. +# new lonc handles n request out over m connections to londs. # This module is based on the Event class. # Development iterations: # - Setup basic event loop. (done) @@ -85,7 +85,7 @@ my $ClientConnection = 0; # Uniquifier f my $DebugLevel = 0; my $NextDebugLevel= 2; # So Sigint can toggle this. -my $IdleTimeout= 600; # Wait 10 minutes before pruning connections. +my $IdleTimeout= 5*60; # Seconds to wait prior to pruning connections. my $LogTransactions = 0; # When True, all transactions/replies get logged. my $executable = $0; # Get the full path to me. @@ -158,6 +158,7 @@ sub LogPerm { my $now=time; my $local=localtime($now); my $fh=IO::File->new(">>$execdir/logs/lonnet.perm.log"); + chomp($message); print $fh "$now:$message:$local\n"; } @@ -348,7 +349,7 @@ sub child_exit { =head2 Tick -Invoked each timer tick. +Invoked each timer tick. =cut @@ -440,7 +441,8 @@ Trigger disconnections of idle sockets. sub SetupTimer { Debug(6, "SetupTimer"); - Event->timer(interval => 1, cb => \&Tick ); + Event->timer(interval => 1, cb => \&Tick, + hard => 1); } =pod @@ -486,8 +488,8 @@ sub ServerToIdle { Event callback for when a client socket is writable. -This callback is established when a transaction reponse is -avaiable from lond. The response is forwarded to the unix socket +This callback is established when a transaction response is +available from lond. The response is forwarded to the unix socket as it becomes writable in this sub. Parameters: @@ -557,7 +559,8 @@ sub ClientWritable { if($errno == POSIX::EWOULDBLOCK || $errno == POSIX::EAGAIN || $errno == POSIX::EINTR) { - # No action taken? + # No action taken...the socket will be writable firing the event again + # which will result in a retry of the write. } else { # Unanticipated errno. &Debug(5,"ClientWritable error or peer shutdown".$RemoteHost); $Watcher->cancel; # Stop the watcher. @@ -586,8 +589,8 @@ Parameters: =item Socket -Socket on which the lond transaction occured. This is a -LondConnection. The data received is in the TransactionReply member. +Socket on which the lond transaction occurred. This is a +LondConnection. The data received are in the TransactionReply member. =item Transaction @@ -608,8 +611,8 @@ sub CompleteTransaction { StartClientReply($Transaction, $data); } else { # Delete deferred transaction file. Log("SUCCESS", "A delayed transaction was completed"); - LogPerm("S:$Transaction->getClient() :".$Transaction->getRequest()); - unlink $Transaction->getFile(); + LogPerm("S:".$Socket->PeerLoncapaHim().":".$Transaction->getRequest()); + unlink($Transaction->getFile()); } } @@ -627,7 +630,7 @@ sub CompleteTransaction { =item data - The data to send to apached client. + The data to send to apache client. =cut @@ -740,7 +743,7 @@ Parameters: =item Restart -nonzero if we are allowed to create a new connection. +non-zero if we are allowed to create a new connection. =cut @@ -760,6 +763,7 @@ sub KillSocket { delete ($ActiveTransactions{$Socket}); } if(exists($ActiveConnections{$Socket})) { + $ActiveConnections{$Socket}->cancel; delete($ActiveConnections{$Socket}); $ConnectionCount--; if ($ConnectionCount < 0) { $ConnectionCount = 0; } @@ -768,9 +772,11 @@ sub KillSocket { # work queue, the work all gets failed with con_lost. # if($ConnectionCount == 0) { + $LondConnecting = 0; # No connections so also not connecting. EmptyQueue(); CloseAllLondConnections; # Should all already be closed but... } + UpdateStatus(); } =pod @@ -782,7 +788,7 @@ is readable. The action is state depend =head3 State=Initialized -We''re waiting for the challenge, this is a no-op until the +We are waiting for the challenge, this is a no-op until the state changes. =head3 State=Challenged @@ -792,7 +798,7 @@ The connection must echo the challenge b =head3 State=ChallengeReplied -The challenge has been replied to. The we are receiveing the +The challenge has been replied to. Then we are receiving the 'ok' from the partner. =head3 State=ReadingVersionString @@ -818,9 +824,9 @@ The the key has been requested, now we a =head3 State=Idle The encryption key has been negotiated or we have finished -reading data from the a transaction. If the callback data has -a client as well as the socket iformation, then we are -doing a transaction and the data received is relayed to the client +reading data from the a transaction. If the callback data have +a client as well as the socket information, then we are +doing a transaction and the data received are relayed to the client before the socket is put on the idle list. =head3 State=SendingRequest @@ -837,7 +843,7 @@ to readable to receive the reply. The parameter to this function are: The event. Implicit in this is the watcher and its data. The data -contains at least the lond connection object and, if a +contain at least the lond connection object and, if a transaction is in progress, the socket attached to the local client. =cut @@ -942,7 +948,8 @@ sub LondReadable { CompleteTransaction($Socket, $ActiveTransactions{$Socket}); } else { - Log("SUCCESS", "Connection ".$ConnectionCount." to " + my $count = $Socket->GetClientData(); + Log("SUCCESS", "Connection ".$count." to " .$RemoteHost." now ready for action"); } ServerToIdle($Socket); # Next work unit or idle. @@ -979,9 +986,9 @@ event. The action taken is very state d =head3 State = Connected The connection is in the process of sending the 'init' hailing to the -lond on the remote end. The connection object''s Writable member is -called. On error, ConnectionError is called to destroy the connection -and remove it from the ActiveConnections hash +lond on the remote end. The Writable member of the connection object +is called. On error, call ConnectionError to destroy the connection +and remove it from the ActiveConnections hash. =head3 Initialized @@ -1190,7 +1197,7 @@ sub QueueDelayed { =head2 MakeLondConnection Create a new lond connection object, and start it towards its initial -idleness. Once idle, it becomes elligible to receive transactions +idleness. Once idle, it becomes eligible to receive transactions from the work queue. If the work queue is not empty when the connection is completed and becomes idle, it will dequeue an entry and start off on it. @@ -1205,7 +1212,7 @@ sub MakeLondConnection { &GetServerPort(), &GetHostId()); - if($Connection eq undef) { # Needs to be more robust later. + if($Connection eq undef) { Log("CRITICAL","Failed to make a connection with lond."); $ConnectionRetriesLeft--; return 0; # Failure. @@ -1236,11 +1243,12 @@ sub MakeLondConnection { &SetupTimer; # Need to handle timeouts with connections... } $ConnectionCount++; + $Connection->SetClientData($ConnectionCount); Debug(4, "Connection count = ".$ConnectionCount); if($ConnectionCount == 1) { # First Connection: QueueDelayed; } - Log("SUCESS", "Created connection ".$ConnectionCount + Log("SUCCESS", "Created connection ".$ConnectionCount ." to host ".GetServerHost()); return 1; # Return success. } @@ -1261,7 +1269,7 @@ reply. =item $Client -Connection to the client that is making this request We got the +Connection to the client that is making this request. We got the request from this socket, and when the request has been relayed to lond and we get a reply back from lond it will get sent to this socket. @@ -1347,11 +1355,11 @@ sub QueueTransaction { } } -#-------------------------- Lonc UNIX socket handling --------------------- - +#-------------------------- Lonc UNIX socket handling ------------------- =pod =head2 ClientRequest + Callback that is called when data can be read from the UNIX domain socket connecting us with an apache server process. @@ -1382,14 +1390,23 @@ sub ClientRequest { $data = $data.$thisread; # Append new data. $watcher->data($data); if($data =~ /\n$/) { # Request entirely read. - if($data eq "close_connection_exit\n") { + if ($data eq "close_connection_exit\n") { Log("CRITICAL", "Request Close Connection ... exiting"); CloseAllLondConnections(); exit; + } elsif ($data eq "reset_retries\n") { + Log("INFO", "Resetting Connection Retries."); + $ConnectionRetriesLeft = $ConnectionRetries; + &UpdateStatus(); + my $Transaction = LondTransaction->new($data); + $Transaction->SetClient($socket); + StartClientReply($Transaction, "ok\n"); + $watcher->cancel(); + return; } Debug(8, "Complete transaction received: ".$data); - if($LogTransactions) { + if ($LogTransactions) { Log("SUCCESS", "Transaction: '$data'"); # Transaction has \n. } my $Transaction = LondTransaction->new($data); @@ -1442,7 +1459,7 @@ sub accept_client { Callback that is called when a connection is received on the unix socket for a new client of lonc. The callback is parameterized by the event.. which is a-priori assumed to be an io event, and therefore has -an fd member that is the Listener socket. We Accept the connection +an fd member that is the Listener socket. We accept the connection and register a new event on the readability of that socket: =cut @@ -1522,7 +1539,7 @@ sub GetServerPort { Setup a lonc listener event. The event is called when the socket becomes readable.. that corresponds to the receipt of a new connection. The event handler established will accept the connection -(creating a communcations channel), that int turn will establish +(creating a communications channel), that in turn will establish another event handler to subess requests. =head2 Parameters: @@ -1662,8 +1679,8 @@ sub ToggleDebug { This sub implements a child process for a single lonc daemon. Optional parameter: - $socket - if provided, this is a socket already open for listen - on the client socket. Otherwise, a new listen is set up. + $socket - if provided, this is a socket already open for listening + on the client socket. Otherwise, a new listener is set up. =cut @@ -1720,6 +1737,13 @@ sub ChildProcess { cb => \&ToggleDebug, data => "INT"); + # Block the pipe signal we'll get when the socket disconnects. We detect + # socket disconnection via send/receive failures. On disconnect, the + # socket becomes readable .. which will force the disconnect detection. + + my $set = POSIX::SigSet->new(SIGPIPE); + sigprocmask(SIG_BLOCK, $set); + # Figure out if we got passed a socket or need to open one to listen for # client requests. @@ -1763,6 +1787,7 @@ sub CreateChild { my $sigset = POSIX::SigSet->new(SIGINT); sigprocmask(SIG_BLOCK, $sigset); $RemoteHost = $host; + ShowStatus('Parent keeping the flock'); # Update time in status message. Log("CRITICAL", "Forking server for ".$host); my $pid = fork; if($pid) { # Parent @@ -1897,7 +1922,7 @@ sub parent_listen { sub parent_clean_up { my ($loncapa_host) = @_; - Debug(-1, "parent_clean_up: $loncapa_host"); + Debug(1, "parent_clean_up: $loncapa_host"); my $socket_file = &GetLoncSocketPath($loncapa_host); unlink($socket_file); # No problem if it doesn't exist yet [startup e.g.] @@ -2055,7 +2080,7 @@ die "Main Event loop exited: $ret"; =head1 CheckKids Since kids do not die as easily in this implementation -as the previous one, there is no need to restart the +as the previous one, there is no need to restart the dead ones (all dead kids get restarted when they die!!) The only thing this function does is to pass USR1 to the kids so that they report their status. @@ -2117,6 +2142,7 @@ sub UpdateKids { # (lost unless they are critical). &KillThemAll(); + LondConnection->ResetReadConfig(); } @@ -2125,13 +2151,14 @@ sub UpdateKids { =head1 Restart Signal handler for HUP... all children are killed and -we self restart. This is an el-cheapo way to re read +we self restart. This is an el-cheapo way to re-read the config file. =cut sub Restart { &KillThemAll; # First kill all the children. + LondConnection->ResetReadConfig(); Log("CRITICAL", "Restarting"); my $execdir = $perlvar{'lonDaemons'}; unlink("$execdir/logs/lonc.pid"); @@ -2255,3 +2282,183 @@ A hash of lond connections that have no can be closed if they are idle for a long enough time. =cut + +=pod + +=head1 Log messages + +The following is a list of log messages that can appear in the +lonc.log file. Each log file has a severity and a message. + +=over 2 + +=item Warning A socket timeout was detected + +If there are pending transactions in the socket's queue, +they are failed (saved if critical). If the connection +retry count gets exceeded by this, the +remote host is marked as dead. +Called when timeouts occurred during the connection and +connection dialog with a remote host. + +=item Critical Host makred DEAD + +The numer of retry counts for contacting a host was +exceeded. The host is marked dead an no +further attempts will be made by that child. + +=item Info lonc pipe client hung up on us + +Write to the client pipe indicated no data transferred +Socket to remote host is shut down. Reply to the client +is discarded. Note: This is commented out in &ClientWriteable + +=item Success Reply from lond: + +Can be enabled for debugging by setting LogTransactions to nonzero. +Indicates a successful transaction with lond, is the data received +from the remote lond. + +=item Success A delayed transaction was completed + +A transaction that must be reliable was executed and completed +as lonc restarted. This is followed by a mesage of the form + + S: client-name : request + +=item WARNING Failing transaction : + +Transaction failed on a socket, but the failure retry count for the remote +node has not yet been exhausted (the node is not yet marked dead). +cmd is the command, subcmd is the subcommand. This results from a con_lost +when communicating with lond. + +=item WARNING Shutting down a socket + +Called when a socket is being closed to lond. This is emitted both when +idle pruning is being done and when the socket has been disconnected by the remote. + +=item WARNING Lond connection lost. + +Called when a read from lond's socket failed indicating lond has closed the +connection or died. This should be followed by one or more + + "WARNING Failing transaction..." msgs for each in-flight or queued transaction. + +=item INFO Connected to lond version: + +When connection negotiation is complete, the lond version is requested and logged here. + +=item SUCCESS Connection n to host now ready for action + +Emitted when connection has been completed with lond. n is then number of +concurrent connections and host, the host to which the connection has just +been established. + +=item WARNING Connection to host has been disconnected + +Write to a lond resulted in failure status. Connection to lond is dropped. + +=item SUCCESS Created connection n to host host + +Initial connection request to host..(before negotiation). + +=item CRITICAL Request Close Connection ... exiting + +Client has sent "close_connection_exit" The loncnew server is exiting. + +=item INFO Resetting Connection Retries + +Client has sent "reset_retries" The lond connection retries are reset to zero for the +corresponding lond. + +=item SUCCESS Transaction + +Only emitted if the global variable $LogTransactions was set to true. +A client has requested a lond transaction is the contents of the request. + +=item SUCCESS Toggled transaction logging + +The state of the $LogTransactions global has been toggled, and its current value +(after being toggled) is displayed. When non zero additional logging of transactions +is enabled for debugging purposes. Transaction logging is toggled on receipt of a USR2 +signal. + +=item CRITICAL Abnormal exit. Child for died thorugh signal. + +QUIT signal received. lonc child process is exiting. + +=item SUCCESS New debugging level for now + +Debugging toggled for the host loncnew is talking with. +Currently debugging is a level based scheme with higher number +conveying more information. The daemon starts out at +DebugLevel 0 and can toggle back and forth between that and +DebugLevel 2 These are controlled by +the global variables $DebugLevel and $NextDebugLevel +The debug level can go up to 9. +SIGINT toggles the debug level. The higher the debug level the +more debugging information is spewed. See the Debug +sub in loncnew. + +=item CRITICAL Forking server for host + +A child is being created to service requests for the specified host. + + +=item WARNING Request for a second child on hostname + +Somehow loncnew was asked to start a second child on a host that already had a child +servicing it. This request is not honored, but themessage is emitted. This could happen +due to a race condition. When a client attempts to contact loncnew for a new host, a child +is forked off to handle the requests for that server. The parent then backs off the Unix +domain socket leaving it for the child to service all requests. If in the time between +creating the child, and backing off, a new connection request comes in to the unix domain +socket, this could trigger (unlikely but remotely possible),. + +=item CRITICAL ------ Starting Children ---- + +This message should probably be changed to "Entering event loop" as the loncnew only starts +children as needed. This message is emitted as new events are established and +the event processing loop is entered. + +=item INFO Updating connections via SIGUSR2 + +SIGUSR2 received. The original code would kill all clients, re-read the host file, +then restart children for each host. Now that children are started on demand, this +just kills all child processes and lets requests start them as needed again. + + +=item CRITICAL Restarting + +SigHUP received. all the children are killed and the script exec's itself to start again. + +=item CRITICAL Nicely killing lonc for host pid = + +Attempting to kill the child that is serving the specified host (pid given) cleanly via +SIGQUIT. The child should handle that, clean up nicely and exit. + +=item CRITICAL Nastily killing lonc for host pid = + +The child specified did not die when requested via SIGQUIT. Therefore it is killed +via SIGKILL. + +=item CRITICAL Asked to kill children.. first be nice.. + +In the parent's INT handler. INT kills the child processes. This inidicate loncnew +is about to attempt to kill all known children via SIGQUIT. This message should be followed +by one "Nicely killing" message for each extant child. + +=item CRITICAL Now kill children nasty + +In the parent's INT handler. remaining children are about to be killed via +SIGKILL. Should be followed by a Nastily killing... for each lonc child that +refused to die. + +=item CRITICAL Master process exiting + +In the parent's INT handler. just prior to the exit 0 call. + +=back + +=cut