version 1.51, 2004/08/26 12:35:10
|
version 1.107, 2018/12/10 17:34:22
|
Line 26
|
Line 26
|
# http://www.lon-capa.org/ |
# 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. |
# This module is based on the Event class. |
# Development iterations: |
# Development iterations: |
# - Setup basic event loop. (done) |
# - Setup basic event loop. (done) |
Line 60 use LONCAPA::Stack;
|
Line 60 use LONCAPA::Stack;
|
use LONCAPA::LondConnection; |
use LONCAPA::LondConnection; |
use LONCAPA::LondTransaction; |
use LONCAPA::LondTransaction; |
use LONCAPA::Configuration; |
use LONCAPA::Configuration; |
use LONCAPA::HashIterator; |
use Fcntl qw(:flock); |
|
|
|
|
# Read the httpd configuration file to get perl variables |
# Read the httpd configuration file to get perl variables |
Line 72 my %perlvar = %{$perlvarref};
|
Line 72 my %perlvar = %{$perlvarref};
|
# |
# |
# parent and shared variables. |
# parent and shared variables. |
|
|
my %ChildHash; # by pid -> host. |
my %ChildPid; # by pid -> host. |
my %HostToPid; # By host -> pid. |
my %ChildHost; # by host. |
my %HostHash; # by loncapaname -> IP. |
my %ChildKeyMode; # by pid -> keymode |
|
my %listening_to; # Socket->host table for who the parent |
|
# is listening to. |
|
my %parent_dispatchers; # host-> listener watcher events. |
|
|
|
my %parent_handlers; # Parent signal handlers... |
|
|
my $MaxConnectionCount = 10; # Will get from config later. |
my $MaxConnectionCount = 10; # Will get from config later. |
my $ClientConnection = 0; # Uniquifier for client events. |
my $ClientConnection = 0; # Uniquifier for client events. |
|
|
my $DebugLevel = 0; |
my $DebugLevel = 0; |
my $NextDebugLevel= 2; # So Sigint can toggle this. |
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 $LogTransactions = 0; # When True, all transactions/replies get logged. |
|
my $executable = $0; # Get the full path to me. |
|
|
# |
# |
# The variables below are only used by the child processes. |
# The variables below are only used by the child processes. |
# |
# |
my $RemoteHost; # Name of host child is talking to. |
my $RemoteHost; # Hostname of host child is talking to. |
|
my $RemoteHostId; # lonid of host child is talking to. |
|
my $RemoteDefHostId; # default lonhostID of host child is talking to. |
|
my @all_host_ids; |
my $UnixSocketDir= $perlvar{'lonSockDir'}; |
my $UnixSocketDir= $perlvar{'lonSockDir'}; |
my $IdleConnections = Stack->new(); # Set of idle connections |
my $IdleConnections = Stack->new(); # Set of idle connections |
my %ActiveConnections; # Connections to the remote lond. |
my %ActiveConnections; # Connections to the remote lond. |
Line 100 my $ConnectionCount = 0;
|
Line 108 my $ConnectionCount = 0;
|
my $IdleSeconds = 0; # Number of seconds idle. |
my $IdleSeconds = 0; # Number of seconds idle. |
my $Status = ""; # Current status string. |
my $Status = ""; # Current status string. |
my $RecentLogEntry = ""; |
my $RecentLogEntry = ""; |
my $ConnectionRetries=2; # Number of connection retries allowed. |
my $ConnectionRetries=5; # Number of connection retries allowed. |
my $ConnectionRetriesLeft=2; # Number of connection retries remaining. |
my $ConnectionRetriesLeft=5; # Number of connection retries remaining. |
my $LondVersion = "unknown"; # Version of lond we talk with. |
my $LondVersion = "unknown"; # Version of lond we talk with. |
my $KeyMode = ""; # e.g. ssl, local, insecure from last connect. |
my $KeyMode = ""; # e.g. ssl, local, insecure from last connect. |
|
my $LondConnecting = 0; # True when a connection is being built. |
|
|
|
|
|
|
|
my $I_am_child = 0; # True if this is the child process. |
|
|
my $LongTickLength = 10000000; #Tick Frequency when Idle |
|
my $ShortTickLength = 1; #Tick Frequency when Active (many places in |
|
# the code assume this is one) |
|
my $TickLength = $ShortTickLength;#number of seconds to wait until ticking |
|
# |
# |
# The hash below gives the HTML format for log messages |
# The hash below gives the HTML format for log messages |
# given a severity. |
# given a severity. |
Line 122 $LogFormats{"WARNING"} = "<font color='
|
Line 131 $LogFormats{"WARNING"} = "<font color='
|
$LogFormats{"DEFAULT"} = " %s "; |
$LogFormats{"DEFAULT"} = " %s "; |
|
|
|
|
|
# UpdateStatus; |
|
# Update the idle status display to show how many connections |
|
# are left, retries and other stuff. |
|
# |
|
sub UpdateStatus { |
|
if ($ConnectionRetriesLeft > 0) { |
|
ShowStatus(GetServerHost()." Connection count: ".$ConnectionCount |
|
." Retries remaining: ".$ConnectionRetriesLeft |
|
." ($KeyMode)"); |
|
} else { |
|
ShowStatus(GetServerHost()." >> DEAD <<"); |
|
} |
|
} |
|
|
|
|
=pod |
=pod |
|
|
Line 130 $LogFormats{"DEFAULT"} = " %s ";
|
Line 153 $LogFormats{"DEFAULT"} = " %s ";
|
Makes an entry into the permanent log file. |
Makes an entry into the permanent log file. |
|
|
=cut |
=cut |
|
|
sub LogPerm { |
sub LogPerm { |
my $message=shift; |
my $message=shift; |
my $execdir=$perlvar{'lonDaemons'}; |
my $execdir=$perlvar{'lonDaemons'}; |
my $now=time; |
my $now=time; |
my $local=localtime($now); |
my $local=localtime($now); |
my $fh=IO::File->new(">>$execdir/logs/lonnet.perm.log"); |
my $fh=IO::File->new(">>$execdir/logs/lonnet.perm.log"); |
|
chomp($message); |
print $fh "$now:$message:$local\n"; |
print $fh "$now:$message:$local\n"; |
} |
} |
|
|
Line 175 sub Log {
|
Line 200 sub Log {
|
my $now = time; |
my $now = time; |
my $local = localtime($now); |
my $local = localtime($now); |
my $finalformat = "$local ($$) [$RemoteHost] [$Status] "; |
my $finalformat = "$local ($$) [$RemoteHost] [$Status] "; |
my $finalformat = $finalformat.$format."\n"; |
$finalformat = $finalformat.$format."\n"; |
|
|
# open the file and put the result. |
# open the file and put the result. |
|
|
Line 249 sub SocketDump {
|
Line 274 sub SocketDump {
|
and as what we return in a SIGUSR1 |
and as what we return in a SIGUSR1 |
|
|
=cut |
=cut |
|
|
sub ShowStatus { |
sub ShowStatus { |
my $state = shift; |
my $state = shift; |
my $now = time; |
my $now = time; |
Line 259 sub ShowStatus {
|
Line 285 sub ShowStatus {
|
|
|
=pod |
=pod |
|
|
=head 2 SocketTimeout |
=head2 SocketTimeout |
|
|
Called when an action on the socket times out. The socket is |
Called when an action on the socket times out. The socket is |
destroyed and any active transaction is failed. |
destroyed and any active transaction is failed. |
|
|
|
|
=cut |
=cut |
|
|
sub SocketTimeout { |
sub SocketTimeout { |
my $Socket = shift; |
my $Socket = shift; |
Log("WARNING", "A socket timeout was detected"); |
Log("WARNING", "A socket timeout was detected"); |
Debug(0, " SocketTimeout called: "); |
Debug(5, " SocketTimeout called: "); |
$Socket->Dump(0); |
$Socket->Dump(0); |
if(exists($ActiveTransactions{$Socket})) { |
if(exists($ActiveTransactions{$Socket})) { |
FailTransaction($ActiveTransactions{$Socket}); |
FailTransaction($ActiveTransactions{$Socket}); |
Line 278 sub SocketTimeout {
|
Line 305 sub SocketTimeout {
|
# a connection failure: |
# a connection failure: |
$ConnectionRetriesLeft--; |
$ConnectionRetriesLeft--; |
if($ConnectionRetriesLeft <= 0) { |
if($ConnectionRetriesLeft <= 0) { |
Log("CRITICAL", "Host marked dead: ".GetServerHost()); |
Log("CRITICAL", "Host marked DEAD: ".GetServerHost()); |
|
$LondConnecting = 0; |
} |
} |
|
|
} |
} |
|
|
|
# |
|
# This function should be called by the child in all cases where it must |
|
# exit. The child process must create a lock file for the AF_UNIX socket |
|
# in order to prevent connection requests from lonnet in the time between |
|
# process exit and the parent picking up the listen again. |
|
# |
|
# Parameters: |
|
# exit_code - Exit status value, however see the next parameter. |
|
# message - If this optional parameter is supplied, the exit |
|
# is via a die with this message. |
|
# |
|
sub child_exit { |
|
my ($exit_code, $message) = @_; |
|
|
|
# Regardless of how we exit, we may need to do the lock thing: |
|
|
|
# |
|
# Create a lock file since there will be a time window |
|
# between our exit and the parent's picking up the listen |
|
# during which no listens will be done on the |
|
# lonnet client socket. |
|
# |
|
my $lock_file = &GetLoncSocketPath().".lock"; |
|
open(LOCK,">$lock_file"); |
|
print LOCK "Contents not important"; |
|
close(LOCK); |
|
unlink(&GetLoncSocketPath()); |
|
|
|
if ($message) { |
|
die($message); |
|
} else { |
|
exit($exit_code); |
|
} |
|
} |
#----------------------------- Timer management ------------------------ |
#----------------------------- Timer management ------------------------ |
|
|
=pod |
=pod |
|
|
=head2 Tick |
=head2 Tick |
|
|
Invoked each timer tick. |
Invoked each timer tick. |
|
|
=cut |
=cut |
|
|
|
|
sub Tick { |
sub Tick { |
|
my ($Event) = @_; |
|
my $clock_watcher = $Event->w; |
|
|
my $client; |
my $client; |
if($ConnectionRetriesLeft > 0) { |
UpdateStatus(); |
ShowStatus(GetServerHost()." Connection count: ".$ConnectionCount |
|
." Retries remaining: ".$ConnectionRetriesLeft |
|
." ($KeyMode)"); |
|
} else { |
|
ShowStatus(GetServerHost()." >> DEAD <<"); |
|
} |
|
# Is it time to prune connection count: |
# Is it time to prune connection count: |
|
|
|
|
if($IdleConnections->Count() && |
if($IdleConnections->Count() && |
($WorkQueue->Count() == 0)) { # Idle connections and nothing to do? |
($WorkQueue->Count() == 0)) { # Idle connections and nothing to do? |
$IdleSeconds+=$TickLength; |
$IdleSeconds++; |
if($IdleSeconds > $IdleTimeout) { # Prune a connection... |
if($IdleSeconds > $IdleTimeout) { # Prune a connection... |
my $Socket = $IdleConnections->pop(); |
my $Socket = $IdleConnections->pop(); |
KillSocket($Socket); |
KillSocket($Socket); |
if ($IdleConnections->Count() == 0) { |
$IdleSeconds = 0; # Otherwise all connections get trimmed to fast. |
&SetupTimer($LongTickLength); |
UpdateStatus(); |
|
if(($ConnectionCount == 0)) { |
|
&child_exit(0); |
|
|
} |
} |
} |
} |
} else { |
} else { |
Line 332 sub Tick {
|
Line 396 sub Tick {
|
# Do we have work in the queue, but no connections to service them? |
# Do we have work in the queue, but no connections to service them? |
# If so, try to make some new connections to get things going again. |
# If so, try to make some new connections to get things going again. |
# |
# |
|
# Note this code is dead now... |
|
# |
my $Requests = $WorkQueue->Count(); |
my $Requests = $WorkQueue->Count(); |
if (($ConnectionCount == 0) && ($Requests > 0)) { |
if (($ConnectionCount == 0) && ($Requests > 0) && (!$LondConnecting)) { |
if ($ConnectionRetriesLeft > 0) { |
if ($ConnectionRetriesLeft > 0) { |
my $Connections = ($Requests <= $MaxConnectionCount) ? |
Debug(5,"Work but no connections, Make a new one"); |
$Requests : $MaxConnectionCount; |
my $success; |
Debug(5,"Work but no connections, start ".$Connections." of them"); |
$success = &MakeLondConnection; |
my $successCount = 0; |
if($success == 0) { # All connections failed: |
for (my $i =0; $i < $Connections; $i++) { |
|
$successCount += MakeLondConnection(); |
|
} |
|
if($successCount == 0) { # All connections failed: |
|
Debug(5,"Work in queue failed to make any connectiouns\n"); |
Debug(5,"Work in queue failed to make any connectiouns\n"); |
EmptyQueue(); # Fail pending transactions with con_lost. |
EmptyQueue(); # Fail pending transactions with con_lost. |
CloseAllLondConnections(); # Should all be closed but.... |
CloseAllLondConnections(); # Should all be closed but.... |
} |
} |
} else { |
} else { |
|
$LondConnecting = 0; |
ShowStatus(GetServerHost()." >>> DEAD!!! <<<"); |
ShowStatus(GetServerHost()." >>> DEAD!!! <<<"); |
Debug(5,"Work in queue, but gave up on connections..flushing\n"); |
Debug(5,"Work in queue, but gave up on connections..flushing\n"); |
EmptyQueue(); # Connections can't be established. |
EmptyQueue(); # Connections can't be established. |
Line 358 sub Tick {
|
Line 420 sub Tick {
|
} |
} |
if ($ConnectionCount == 0) { |
if ($ConnectionCount == 0) { |
$KeyMode = ""; |
$KeyMode = ""; |
|
$clock_watcher->cancel(); |
} |
} |
|
&UpdateStatus(); |
} |
} |
|
|
=pod |
=pod |
Line 377 Trigger disconnections of idle sockets.
|
Line 441 Trigger disconnections of idle sockets.
|
|
|
=cut |
=cut |
|
|
my $timer; |
|
sub SetupTimer { |
sub SetupTimer { |
my ($newLength)=@_; |
Debug(6, "SetupTimer"); |
Debug(6, "SetupTimer $TickLength->$newLength"); |
Event->timer(interval => 1, cb => \&Tick, |
$TickLength=$newLength; |
hard => 1); |
if ($timer) { $timer->cancel; } |
|
$timer=Event->timer(interval => $TickLength, cb => \&Tick ); |
|
} |
} |
|
|
=pod |
=pod |
Line 429 sub ServerToIdle {
|
Line 490 sub ServerToIdle {
|
|
|
Event callback for when a client socket is writable. |
Event callback for when a client socket is writable. |
|
|
This callback is established when a transaction reponse is |
This callback is established when a transaction response is |
avaiable from lond. The response is forwarded to the unix socket |
available from lond. The response is forwarded to the unix socket |
as it becomes writable in this sub. |
as it becomes writable in this sub. |
|
|
Parameters: |
Parameters: |
Line 445 the data and Event->w->fd is the socket
|
Line 506 the data and Event->w->fd is the socket
|
sub ClientWritable { |
sub ClientWritable { |
my $Event = shift; |
my $Event = shift; |
my $Watcher = $Event->w; |
my $Watcher = $Event->w; |
|
if (!defined($Watcher)) { |
|
&child_exit(-1,'No watcher for event in ClientWritable'); |
|
} |
my $Data = $Watcher->data; |
my $Data = $Watcher->data; |
my $Socket = $Watcher->fd; |
my $Socket = $Watcher->fd; |
|
|
Line 483 sub ClientWritable {
|
Line 547 sub ClientWritable {
|
} else { # Partial string sent. |
} else { # Partial string sent. |
$Watcher->data(substr($Data, $result)); |
$Watcher->data(substr($Data, $result)); |
if($result == 0) { # client hung up on us!! |
if($result == 0) { # client hung up on us!! |
Log("INFO", "lonc pipe client hung up on us!"); |
# Log("INFO", "lonc pipe client hung up on us!"); |
$Watcher->cancel; |
$Watcher->cancel; |
$Socket->shutdown(2); |
$Socket->shutdown(2); |
$Socket->close(); |
$Socket->close(); |
Line 497 sub ClientWritable {
|
Line 561 sub ClientWritable {
|
if($errno == POSIX::EWOULDBLOCK || |
if($errno == POSIX::EWOULDBLOCK || |
$errno == POSIX::EAGAIN || |
$errno == POSIX::EAGAIN || |
$errno == POSIX::EINTR) { |
$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. |
} else { # Unanticipated errno. |
&Debug(5,"ClientWritable error or peer shutdown".$RemoteHost); |
&Debug(5,"ClientWritable error or peer shutdown".$RemoteHost); |
$Watcher->cancel; # Stop the watcher. |
$Watcher->cancel; # Stop the watcher. |
Line 508 sub ClientWritable {
|
Line 573 sub ClientWritable {
|
} |
} |
} else { |
} else { |
$Watcher->cancel(); # A delayed request...just cancel. |
$Watcher->cancel(); # A delayed request...just cancel. |
|
return; |
} |
} |
} |
} |
|
|
Line 525 Parameters:
|
Line 591 Parameters:
|
|
|
=item Socket |
=item Socket |
|
|
Socket on which the lond transaction occured. This is a |
Socket on which the lond transaction occurred. This is a |
LondConnection. The data received is in the TransactionReply member. |
LondConnection. The data received are in the TransactionReply member. |
|
|
=item Transaction |
=item Transaction |
|
|
Line 547 sub CompleteTransaction {
|
Line 613 sub CompleteTransaction {
|
StartClientReply($Transaction, $data); |
StartClientReply($Transaction, $data); |
} else { # Delete deferred transaction file. |
} else { # Delete deferred transaction file. |
Log("SUCCESS", "A delayed transaction was completed"); |
Log("SUCCESS", "A delayed transaction was completed"); |
LogPerm("S:$Transaction->getClient() :".$Transaction->getRequest()); |
LogPerm("S:".$Socket->PeerLoncapaHim().":".$Transaction->getRequest()); |
unlink $Transaction->getFile(); |
unlink($Transaction->getFile()); |
} |
} |
} |
} |
|
|
Line 566 sub CompleteTransaction {
|
Line 632 sub CompleteTransaction {
|
|
|
=item data |
=item data |
|
|
The data to send to apached client. |
The data to send to apache client. |
|
|
=cut |
=cut |
|
|
Line 611 Parameters:
|
Line 677 Parameters:
|
|
|
sub FailTransaction { |
sub FailTransaction { |
my $transaction = shift; |
my $transaction = shift; |
Log("WARNING", "Failing transaction ".$transaction->getRequest()); |
|
Debug(1, "Failing transaction: ".$transaction->getRequest()); |
# If the socket is dead, that's already logged. |
|
|
|
if ($ConnectionRetriesLeft > 0) { |
|
Log("WARNING", "Failing transaction " |
|
.$transaction->getLoggableRequest()); |
|
} |
|
Debug(1, "Failing transaction: ".$transaction->getLoggableRequest()); |
if (!$transaction->isDeferred()) { # If the transaction is deferred we'll get to it. |
if (!$transaction->isDeferred()) { # If the transaction is deferred we'll get to it. |
my $client = $transaction->getClient(); |
my $client = $transaction->getClient(); |
Debug(1," Replying con_lost to ".$transaction->getRequest()); |
Debug(1," Replying con_lost to ".$transaction->getRequest()); |
Line 622 sub FailTransaction {
|
Line 694 sub FailTransaction {
|
} |
} |
|
|
=pod |
=pod |
|
|
=head1 EmptyQueue |
=head1 EmptyQueue |
|
|
Fails all items in the work queue with con_lost. |
Fails all items in the work queue with con_lost. |
Note that each item in the work queue is a transaction. |
Note that each item in the work queue is a transaction. |
|
|
=cut |
=cut |
|
|
sub EmptyQueue { |
sub EmptyQueue { |
$ConnectionRetriesLeft--; # Counts as connection failure too. |
$ConnectionRetriesLeft--; # Counts as connection failure too. |
while($WorkQueue->Count()) { |
while($WorkQueue->Count()) { |
Line 643 sub EmptyQueue {
|
Line 717 sub EmptyQueue {
|
Close all connections open on lond prior to exit e.g. |
Close all connections open on lond prior to exit e.g. |
|
|
=cut |
=cut |
|
|
sub CloseAllLondConnections { |
sub CloseAllLondConnections { |
foreach my $Socket (keys %ActiveConnections) { |
foreach my $Socket (keys %ActiveConnections) { |
if(exists($ActiveTransactions{$Socket})) { |
if(exists($ActiveTransactions{$Socket})) { |
Line 651 sub CloseAllLondConnections {
|
Line 726 sub CloseAllLondConnections {
|
KillSocket($Socket); |
KillSocket($Socket); |
} |
} |
} |
} |
=cut |
|
|
|
=pod |
=pod |
|
|
Line 669 Parameters:
|
Line 743 Parameters:
|
|
|
The socket to kill off. |
The socket to kill off. |
|
|
=item Restart |
=item restart |
|
|
nonzero if we are allowed to create a new connection. |
|
|
|
|
non-zero if we are allowed to create a new connection. |
|
|
=cut |
=cut |
|
|
sub KillSocket { |
sub KillSocket { |
my $Socket = shift; |
my $Socket = shift; |
|
my $restart = shift; |
|
|
Log("WARNING", "Shutting down a socket"); |
Log("WARNING", "Shutting down a socket"); |
$Socket->Shutdown(); |
$Socket->Shutdown(); |
Line 691 sub KillSocket {
|
Line 766 sub KillSocket {
|
delete ($ActiveTransactions{$Socket}); |
delete ($ActiveTransactions{$Socket}); |
} |
} |
if(exists($ActiveConnections{$Socket})) { |
if(exists($ActiveConnections{$Socket})) { |
|
$ActiveConnections{$Socket}->cancel; |
delete($ActiveConnections{$Socket}); |
delete($ActiveConnections{$Socket}); |
$ConnectionCount--; |
# Decrement ConnectionCount unless we will immediately |
|
# re-connect (i.e., $restart is true), because this was |
|
# a connection where the SSL channel for exchange of the |
|
# shared key failed, and we may use an insecure channel. |
|
unless ($restart) { |
|
$ConnectionCount--; |
|
} |
if ($ConnectionCount < 0) { $ConnectionCount = 0; } |
if ($ConnectionCount < 0) { $ConnectionCount = 0; } |
} |
} |
# If the connection count has gone to zero and there is work in the |
# If the connection count has gone to zero and there is work in the |
# work queue, the work all gets failed with con_lost. |
# work queue, the work all gets failed with con_lost. |
# |
# |
|
|
if($ConnectionCount == 0) { |
if($ConnectionCount == 0) { |
|
$LondConnecting = 0; # No connections so also not connecting. |
EmptyQueue(); |
EmptyQueue(); |
CloseAllLondConnections; # Should all already be closed but... |
CloseAllLondConnections(); # Should all already be closed but... |
|
&clear_childpid($$); |
} |
} |
|
UpdateStatus(); |
} |
} |
|
|
=pod |
=pod |
Line 713 is readable. The action is state depend
|
Line 799 is readable. The action is state depend
|
|
|
=head3 State=Initialized |
=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. |
state changes. |
|
|
=head3 State=Challenged |
=head3 State=Challenged |
Line 723 The connection must echo the challenge b
|
Line 809 The connection must echo the challenge b
|
|
|
=head3 State=ChallengeReplied |
=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. |
'ok' from the partner. |
|
|
=head3 State=ReadingVersionString |
=head3 State=ReadingVersionString |
Line 749 The the key has been requested, now we a
|
Line 835 The the key has been requested, now we a
|
=head3 State=Idle |
=head3 State=Idle |
|
|
The encryption key has been negotiated or we have finished |
The encryption key has been negotiated or we have finished |
reading data from the a transaction. If the callback data has |
reading data from the a transaction. If the callback data have |
a client as well as the socket iformation, then we are |
a client as well as the socket information, then we are |
doing a transaction and the data received is relayed to the client |
doing a transaction and the data received are relayed to the client |
before the socket is put on the idle list. |
before the socket is put on the idle list. |
|
|
=head3 State=SendingRequest |
=head3 State=SendingRequest |
Line 768 to readable to receive the reply.
|
Line 854 to readable to receive the reply.
|
The parameter to this function are: |
The parameter to this function are: |
|
|
The event. Implicit in this is the watcher and its data. The data |
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. |
transaction is in progress, the socket attached to the local client. |
|
|
=cut |
=cut |
Line 796 sub LondReadable {
|
Line 882 sub LondReadable {
|
|
|
Log("WARNING", |
Log("WARNING", |
"Lond connection lost."); |
"Lond connection lost."); |
|
my $state_on_exit = $Socket->GetState(); |
if(exists($ActiveTransactions{$Socket})) { |
if(exists($ActiveTransactions{$Socket})) { |
FailTransaction($ActiveTransactions{$Socket}); |
FailTransaction($ActiveTransactions{$Socket}); |
|
} else { |
|
# Socket is connecting and failed... need to mark |
|
# no longer connecting. |
|
$LondConnecting = 0; |
} |
} |
$Watcher->cancel(); |
$Watcher->cancel(); |
KillSocket($Socket); |
if ($state_on_exit eq 'ReInitNoSSL') { |
$ConnectionRetriesLeft--; # Counts as connection failure |
# SSL certificate verification failed, and insecure connection |
|
# allowed. Send restart arg to KillSocket(), so EmptyQueue() |
|
# is not called, as we still hope to process queued request. |
|
|
|
KillSocket($Socket,1); |
|
|
|
# Re-initiate creation of Lond Connection for use with queued |
|
# request. |
|
|
|
ShowStatus("Connected to ".$RemoteHost); |
|
Log("WARNING","No SSL channel (verification failed), will try with insecure channel"); |
|
&MakeLondConnection(1); |
|
|
|
} else { |
|
KillSocket($Socket); |
|
$ConnectionRetriesLeft--; # Counts as connection failure |
|
} |
return; |
return; |
} |
} |
SocketDump(6,$Socket); |
SocketDump(6,$Socket); |
Line 812 sub LondReadable {
|
Line 919 sub LondReadable {
|
if($State eq "Initialized") { |
if($State eq "Initialized") { |
|
|
|
|
|
} elsif ($State eq "ReInitNoSSL") { |
|
|
} elsif ($State eq "ChallengeReceived") { |
} elsif ($State eq "ChallengeReceived") { |
# The challenge must be echoed back; The state machine |
# The challenge must be echoed back; The state machine |
# in the connection takes care of setting that up. Just |
# in the connection takes care of setting that up. Just |
Line 848 sub LondReadable {
|
Line 957 sub LondReadable {
|
} elsif ($State eq "ReceivingKey") { |
} elsif ($State eq "ReceivingKey") { |
|
|
} elsif ($State eq "Idle") { |
} elsif ($State eq "Idle") { |
|
|
|
if ($ConnectionCount == 1) { |
|
# Write child Pid file to keep track of ssl and insecure |
|
# connections |
|
|
|
&record_childpid($Socket); |
|
} |
|
|
# This is as good a spot as any to get the peer version |
# This is as good a spot as any to get the peer version |
# string: |
# string: |
|
|
Line 868 sub LondReadable {
|
Line 984 sub LondReadable {
|
CompleteTransaction($Socket, |
CompleteTransaction($Socket, |
$ActiveTransactions{$Socket}); |
$ActiveTransactions{$Socket}); |
} else { |
} else { |
Log("SUCCESS", "Connection ".$ConnectionCount." to " |
my $count = $Socket->GetClientData(); |
|
Log("SUCCESS", "Connection ".$count." to " |
.$RemoteHost." now ready for action"); |
.$RemoteHost." now ready for action"); |
} |
} |
ServerToIdle($Socket); # Next work unit or idle. |
ServerToIdle($Socket); # Next work unit or idle. |
|
|
|
# |
|
$LondConnecting = 0; # Best spot I can think of for this. |
|
# |
|
|
} elsif ($State eq "SendingRequest") { |
} elsif ($State eq "SendingRequest") { |
# We need to be writable for this and probably don't belong |
# We need to be writable for this and probably don't belong |
# here inthe first place. |
# here inthe first place. |
|
|
Deubg(6, "SendingRequest state encountered in readable"); |
Debug(6, "SendingRequest state encountered in readable"); |
$Watcher->poll("w"); |
$Watcher->poll("w"); |
$Watcher->cb(\&LondWritable); |
$Watcher->cb(\&LondWritable); |
|
|
Line 901 event. The action taken is very state d
|
Line 1022 event. The action taken is very state d
|
=head3 State = Connected |
=head3 State = Connected |
|
|
The connection is in the process of sending the 'init' hailing to the |
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 |
lond on the remote end. The Writable member of the connection object |
called. On error, ConnectionError is called to destroy the connection |
is called. On error, call ConnectionError to destroy the connection |
and remove it from the ActiveConnections hash |
and remove it from the ActiveConnections hash. |
|
|
=head3 Initialized |
=head3 Initialized |
|
|
Line 985 sub LondWritable {
|
Line 1106 sub LondWritable {
|
" has been disconnected"); |
" has been disconnected"); |
if(exists($ActiveTransactions{$Socket})) { |
if(exists($ActiveTransactions{$Socket})) { |
FailTransaction($ActiveTransactions{$Socket}); |
FailTransaction($ActiveTransactions{$Socket}); |
|
} else { |
|
# In the process of conneting, so need to turn that off. |
|
|
|
$LondConnecting = 0; |
} |
} |
$Watcher->cancel(); |
$Watcher->cancel(); |
KillSocket($Socket); |
KillSocket($Socket); |
Line 1004 sub LondWritable {
|
Line 1129 sub LondWritable {
|
|
|
$Watcher->cb(\&LondReadable); |
$Watcher->cb(\&LondReadable); |
$Watcher->poll("r"); |
$Watcher->poll("r"); |
|
|
|
} elsif ($State eq "ReInitNoSSL") { |
|
|
} elsif ($State eq "ChallengeReceived") { |
} elsif ($State eq "ChallengeReceived") { |
# We received the challenge, now we |
# We received the challenge, now we |
# are echoing it back. This is a no-op, |
# are echoing it back. This is a no-op, |
Line 1071 sub LondWritable {
|
Line 1198 sub LondWritable {
|
} |
} |
|
|
} |
} |
|
|
=pod |
=pod |
|
|
=cut |
=cut |
|
|
|
|
sub QueueDelayed { |
sub QueueDelayed { |
Debug(3,"QueueDelayed called"); |
Debug(3,"QueueDelayed called"); |
|
|
Line 1081 sub QueueDelayed {
|
Line 1211 sub QueueDelayed {
|
|
|
Debug(4, "Delayed path: ".$path); |
Debug(4, "Delayed path: ".$path); |
opendir(DIRHANDLE, $path); |
opendir(DIRHANDLE, $path); |
|
|
my @alldelayed = grep /\.$RemoteHost$/, readdir DIRHANDLE; |
my $host_id_re = '(?:'.join('|',map {quotemeta($_)} (@all_host_ids)).')'; |
|
my @alldelayed = grep(/\.$host_id_re$/, readdir(DIRHANDLE)); |
closedir(DIRHANDLE); |
closedir(DIRHANDLE); |
my $dfname; |
foreach my $dfname (sort(@alldelayed)) { |
my $reqfile; |
my $reqfile = "$path/$dfname"; |
foreach $dfname (sort @alldelayed) { |
my ($host_id) = ($dfname =~ /\.([^.]*)$/); |
$reqfile = "$path/$dfname"; |
Debug(4, "queueing ".$reqfile." for $host_id"); |
Debug(4, "queueing ".$reqfile); |
|
my $Handle = IO::File->new($reqfile); |
my $Handle = IO::File->new($reqfile); |
my $cmd = <$Handle>; |
my $cmd = <$Handle>; |
chomp $cmd; # There may or may not be a newline... |
chomp $cmd; # There may or may not be a newline... |
$cmd = $cmd."\n"; # now for sure there's exactly one newline. |
$cmd = $cmd."\n"; # now for sure there's exactly one newline. |
my $Transaction = LondTransaction->new($cmd); |
my $Transaction = LondTransaction->new("sethost:$host_id:$cmd"); |
$Transaction->SetDeferred($reqfile); |
$Transaction->SetDeferred($reqfile); |
QueueTransaction($Transaction); |
QueueTransaction($Transaction); |
} |
} |
Line 1105 sub QueueDelayed {
|
Line 1235 sub QueueDelayed {
|
=head2 MakeLondConnection |
=head2 MakeLondConnection |
|
|
Create a new lond connection object, and start it towards its initial |
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 |
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 |
connection is completed and becomes idle, it will dequeue an entry and |
start off on it. |
start off on it. |
|
|
=cut |
=cut |
|
|
sub MakeLondConnection { |
sub MakeLondConnection { |
|
my ($restart) = @_; |
Debug(4,"MakeLondConnection to ".GetServerHost()." on port " |
Debug(4,"MakeLondConnection to ".GetServerHost()." on port " |
.GetServerPort()); |
.GetServerPort()); |
|
|
my $Connection = LondConnection->new(&GetServerHost(), |
my $Connection = LondConnection->new(&GetServerHost(), |
&GetServerPort()); |
&GetServerPort(), |
|
&GetHostId(), |
|
&GetDefHostId()); |
|
|
if($Connection eq undef) { # Needs to be more robust later. |
if($Connection eq undef) { |
Log("CRITICAL","Failed to make a connection with lond."); |
Log("CRITICAL","Failed to make a connection with lond."); |
$ConnectionRetriesLeft--; |
$ConnectionRetriesLeft--; |
return 0; # Failure. |
return 0; # Failure. |
} else { |
} else { |
|
$LondConnecting = 1; # Connection in progress. |
# The connection needs to have writability |
# The connection needs to have writability |
# monitored in order to send the init sequence |
# monitored in order to send the init sequence |
# that starts the whole authentication/key |
# that starts the whole authentication/key |
Line 1132 sub MakeLondConnection {
|
Line 1265 sub MakeLondConnection {
|
# |
# |
my $Socket = $Connection->GetSocket(); |
my $Socket = $Connection->GetSocket(); |
if($Socket eq undef) { |
if($Socket eq undef) { |
die "did not get a socket from the connection"; |
&child_exit(-1, "did not get a socket from the connection"); |
} else { |
} else { |
&Debug(9,"MakeLondConnection got socket: ".$Socket); |
&Debug(9,"MakeLondConnection got socket: ".$Socket); |
} |
} |
Line 1145 sub MakeLondConnection {
|
Line 1278 sub MakeLondConnection {
|
data => $Connection, |
data => $Connection, |
desc => 'Connection to lond server'); |
desc => 'Connection to lond server'); |
$ActiveConnections{$Connection} = $event; |
$ActiveConnections{$Connection} = $event; |
|
if ($ConnectionCount == 0) { |
$ConnectionCount++; |
&SetupTimer; # Need to handle timeouts with connections... |
|
} |
|
unless ($restart) { |
|
$ConnectionCount++; |
|
} |
|
$Connection->SetClientData($ConnectionCount); |
Debug(4, "Connection count = ".$ConnectionCount); |
Debug(4, "Connection count = ".$ConnectionCount); |
if($ConnectionCount == 1) { # First Connection: |
if($ConnectionCount == 1) { # First Connection: |
QueueDelayed; |
QueueDelayed; |
} |
} |
Log("SUCESS", "Created connection ".$ConnectionCount |
Log("SUCCESS", "Created connection ".$ConnectionCount |
." to host ".GetServerHost()); |
." to host ".GetServerHost()); |
return 1; # Return success. |
return 1; # Return success. |
} |
} |
Line 1172 reply.
|
Line 1310 reply.
|
|
|
=item $Client |
=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 |
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 |
lond and we get a reply back from lond it will get sent to this |
socket. |
socket. |
Line 1236 sub QueueTransaction {
|
Line 1374 sub QueueTransaction {
|
if(!defined $LondSocket) { # Need to queue request. |
if(!defined $LondSocket) { # Need to queue request. |
Debug(5,"Must queue..."); |
Debug(5,"Must queue..."); |
$WorkQueue->enqueue($requestData); |
$WorkQueue->enqueue($requestData); |
if($ConnectionCount < $MaxConnectionCount) { |
Debug(5, "Queue Transaction startnew $ConnectionCount $LondConnecting"); |
|
if(($ConnectionCount < $MaxConnectionCount) && (! $LondConnecting)) { |
|
|
if($ConnectionRetriesLeft > 0) { |
if($ConnectionRetriesLeft > 0) { |
Debug(5,"Starting additional lond connection"); |
Debug(5,"Starting additional lond connection"); |
if(MakeLondConnection() == 0) { |
if(&MakeLondConnection() == 0) { |
EmptyQueue(); # Fail transactions, can't make connection. |
EmptyQueue(); # Fail transactions, can't make connection. |
CloseAllLondConnections; # Should all be closed but... |
CloseAllLondConnections; # Should all be closed but... |
} |
} |
&SetupTimer($ShortTickLength); |
|
} else { |
} else { |
ShowStatus(GetServerHost()." >>> DEAD !!!! <<<"); |
ShowStatus(GetServerHost()." >>> DEAD !!!! <<<"); |
|
$LondConnecting = 0; |
EmptyQueue(); # It's worse than that ... he's dead Jim. |
EmptyQueue(); # It's worse than that ... he's dead Jim. |
CloseAllLondConnections; # Should all be closed but.. |
CloseAllLondConnections; # Should all be closed but.. |
} |
} |
Line 1256 sub QueueTransaction {
|
Line 1396 sub QueueTransaction {
|
} |
} |
} |
} |
|
|
#-------------------------- Lonc UNIX socket handling --------------------- |
#-------------------------- Lonc UNIX socket handling ------------------- |
|
|
=pod |
=pod |
|
|
=head2 ClientRequest |
=head2 ClientRequest |
|
|
Callback that is called when data can be read from the UNIX domain |
Callback that is called when data can be read from the UNIX domain |
socket connecting us with an apache server process. |
socket connecting us with an apache server process. |
|
|
Line 1291 sub ClientRequest {
|
Line 1431 sub ClientRequest {
|
$data = $data.$thisread; # Append new data. |
$data = $data.$thisread; # Append new data. |
$watcher->data($data); |
$watcher->data($data); |
if($data =~ /\n$/) { # Request entirely read. |
if($data =~ /\n$/) { # Request entirely read. |
if($data eq "close_connection_exit\n") { |
if ($data eq "close_connection_exit\n") { |
Log("CRITICAL", |
Log("CRITICAL", |
"Request Close Connection ... exiting"); |
"Request Close Connection ... exiting"); |
CloseAllLondConnections(); |
CloseAllLondConnections(); |
exit; |
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); |
Debug(8, "Complete transaction received: ".$data); |
if($LogTransactions) { |
if ($LogTransactions) { |
Log("SUCCESS", "Transaction: '$data'"); # Transaction has \n. |
Log("SUCCESS", "Transaction: '$data'"); # Transaction has \n. |
} |
} |
my $Transaction = LondTransaction->new($data); |
my $Transaction = LondTransaction->new($data); |
Line 1309 sub ClientRequest {
|
Line 1458 sub ClientRequest {
|
|
|
} |
} |
|
|
|
# |
|
# Accept a connection request for a client (lonc child) and |
|
# start up an event watcher to keep an eye on input from that |
|
# Event. This can be called both from NewClient and from |
|
# ChildProcess. |
|
# Parameters: |
|
# $socket - The listener socket. |
|
# Returns: |
|
# NONE |
|
# Side Effects: |
|
# An event is made to watch the accepted connection. |
|
# Active clients hash is updated to reflect the new connection. |
|
# The client connection count is incremented. |
|
# |
|
sub accept_client { |
|
my ($socket) = @_; |
|
|
|
Debug(8, "Entering accept for lonc UNIX socket\n"); |
|
my $connection = $socket->accept(); # Accept the client connection. |
|
Debug(8,"Connection request accepted from " |
|
.GetPeername($connection, AF_UNIX)); |
|
|
|
|
|
my $description = sprintf("Connection to lonc client %d", |
|
$ClientConnection); |
|
Debug(9, "Creating event named: ".$description); |
|
Event->io(cb => \&ClientRequest, |
|
poll => 'r', |
|
desc => $description, |
|
data => "", |
|
fd => $connection); |
|
$ActiveClients{$connection} = $ClientConnection; |
|
$ClientConnection++; |
|
} |
|
|
=pod |
=pod |
|
|
Line 1317 sub ClientRequest {
|
Line 1500 sub ClientRequest {
|
Callback that is called when a connection is received on the unix |
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 |
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 |
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: |
and register a new event on the readability of that socket: |
|
|
=cut |
=cut |
Line 1327 sub NewClient {
|
Line 1510 sub NewClient {
|
my $event = shift; # Get the event parameters. |
my $event = shift; # Get the event parameters. |
my $watcher = $event->w; |
my $watcher = $event->w; |
my $socket = $watcher->fd; # Get the event' socket. |
my $socket = $watcher->fd; # Get the event' socket. |
my $connection = $socket->accept(); # Accept the client connection. |
|
Debug(8,"Connection request accepted from " |
|
.GetPeername($connection, AF_UNIX)); |
|
|
|
|
&accept_client($socket); |
my $description = sprintf("Connection to lonc client %d", |
|
$ClientConnection); |
|
Debug(9, "Creating event named: ".$description); |
|
Event->io(cb => \&ClientRequest, |
|
poll => 'r', |
|
desc => $description, |
|
data => "", |
|
fd => $connection); |
|
$ActiveClients{$connection} = $ClientConnection; |
|
$ClientConnection++; |
|
} |
} |
|
|
=pod |
=pod |
Line 1351 sub NewClient {
|
Line 1521 sub NewClient {
|
Returns the name of the UNIX socket on which to listen for client |
Returns the name of the UNIX socket on which to listen for client |
connections. |
connections. |
|
|
|
=head2 Parameters: |
|
|
|
host (optional) - Name of the host socket to return.. defaults to |
|
the return from GetServerHost(). |
|
|
=cut |
=cut |
|
|
sub GetLoncSocketPath { |
sub GetLoncSocketPath { |
return $UnixSocketDir."/".GetServerHost(); |
|
|
my $host = GetServerHost(); # Default host. |
|
if (@_) { |
|
($host) = @_; # Override if supplied. |
|
} |
|
return $UnixSocketDir."/".$host; |
} |
} |
|
|
=pod |
=pod |
Line 1371 sub GetServerHost {
|
Line 1551 sub GetServerHost {
|
|
|
=pod |
=pod |
|
|
|
=head2 GetHostId |
|
|
|
Returns the hostid whose lond we talk with. |
|
|
|
=cut |
|
|
|
sub GetHostId { |
|
return $RemoteHostId; # Setup by the fork. |
|
} |
|
|
|
=pod |
|
|
|
=head2 GetDefHostId |
|
|
|
Returns the default hostid for the node whose lond we talk with. |
|
|
|
=cut |
|
|
|
sub GetDefHostId { # Setup by the fork. |
|
return $RemoteDefHostId; |
|
} |
|
|
|
=pod |
|
|
=head2 GetServerPort |
=head2 GetServerPort |
|
|
Returns the lond port number. |
Returns the lond port number. |
Line 1388 sub GetServerPort {
|
Line 1592 sub GetServerPort {
|
Setup a lonc listener event. The event is called when the socket |
Setup a lonc listener event. The event is called when the socket |
becomes readable.. that corresponds to the receipt of a new |
becomes readable.. that corresponds to the receipt of a new |
connection. The event handler established will accept the connection |
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. |
another event handler to subess requests. |
|
|
|
=head2 Parameters: |
|
|
|
host (optional) Name of the host to set up a unix socket to. |
|
|
=cut |
=cut |
|
|
sub SetupLoncListener { |
sub SetupLoncListener { |
|
my ($host,$SocketName) = @_; |
|
if (!$host) { $host = &GetServerHost(); } |
|
if (!$SocketName) { $SocketName = &GetLoncSocketPath($host); } |
|
|
|
|
my $socket; |
|
my $SocketName = GetLoncSocketPath(); |
|
unlink($SocketName); |
unlink($SocketName); |
|
|
|
my $socket; |
unless ($socket =IO::Socket::UNIX->new(Local => $SocketName, |
unless ($socket =IO::Socket::UNIX->new(Local => $SocketName, |
Listen => 10, |
Listen => 250, |
Type => SOCK_STREAM)) { |
Type => SOCK_STREAM)) { |
die "Failed to create a lonc listner socket"; |
if($I_am_child) { |
|
&child_exit(-1, "Failed to create a lonc listener socket"); |
|
} else { |
|
die "Failed to create a lonc listner socket"; |
|
} |
} |
} |
Event->io(cb => \&NewClient, |
return $socket; |
poll => 'r', |
|
desc => 'Lonc listener Unix Socket', |
|
fd => $socket); |
|
} |
} |
|
|
# |
# |
Line 1436 into the status file.
|
Line 1649 into the status file.
|
|
|
We also use this to reset the retries count in order to allow the |
We also use this to reset the retries count in order to allow the |
client to retry connections with a previously dead server. |
client to retry connections with a previously dead server. |
|
|
=cut |
=cut |
|
|
sub ChildStatus { |
sub ChildStatus { |
Line 1444 sub ChildStatus {
|
Line 1658 sub ChildStatus {
|
|
|
Debug(2, "Reporting child status because : ".$watcher->data); |
Debug(2, "Reporting child status because : ".$watcher->data); |
my $docdir = $perlvar{'lonDocRoot'}; |
my $docdir = $perlvar{'lonDocRoot'}; |
my $fh = IO::File->new(">>$docdir/lon-status/loncstatus.txt"); |
|
print $fh $$."\t".$RemoteHost."\t".$Status."\t". |
open(LOG,">>$docdir/lon-status/loncstatus.txt"); |
|
flock(LOG,LOCK_EX); |
|
print LOG $$."\t".$RemoteHost."\t".$Status."\t". |
$RecentLogEntry."\n"; |
$RecentLogEntry."\n"; |
# |
# |
# Write out information about each of the connections: |
# Write out information about each of the connections: |
# |
# |
if ($DebugLevel > 2) { |
if ($DebugLevel > 2) { |
print $fh "Active connection statuses: \n"; |
print LOG "Active connection statuses: \n"; |
my $i = 1; |
my $i = 1; |
print STDERR "================================= Socket Status Dump:\n"; |
print STDERR "================================= Socket Status Dump:\n"; |
foreach my $item (keys %ActiveConnections) { |
foreach my $item (keys %ActiveConnections) { |
my $Socket = $ActiveConnections{$item}->data; |
my $Socket = $ActiveConnections{$item}->data; |
my $state = $Socket->GetState(); |
my $state = $Socket->GetState(); |
print $fh "Connection $i State: $state\n"; |
print LOG "Connection $i State: $state\n"; |
print STDERR "---------------------- Connection $i \n"; |
print STDERR "---------------------- Connection $i \n"; |
$Socket->Dump(-1); # Ensure it gets dumped.. |
$Socket->Dump(-1); # Ensure it gets dumped.. |
$i++; |
$i++; |
} |
} |
} |
} |
|
flock(LOG,LOCK_UN); |
|
close(LOG); |
$ConnectionRetriesLeft = $ConnectionRetries; |
$ConnectionRetriesLeft = $ConnectionRetries; |
|
UpdateStatus(); |
} |
} |
|
|
=pod |
=pod |
Line 1484 sub SignalledToDeath {
|
Line 1703 sub SignalledToDeath {
|
chomp($signal); |
chomp($signal); |
Log("CRITICAL", "Abnormal exit. Child $$ for $RemoteHost " |
Log("CRITICAL", "Abnormal exit. Child $$ for $RemoteHost " |
."died through "."\"$signal\""); |
."died through "."\"$signal\""); |
LogPerm("F:lonc: $$ on $RemoteHost signalled to death: " |
#LogPerm("F:lonc: $$ on $RemoteHost signalled to death: " |
."\"$signal\""); |
# ."\"$signal\""); |
|
&clear_childpid($$); |
exit 0; |
exit 0; |
|
|
} |
} |
|
|
|
=pod |
|
|
=head2 ToggleDebug |
=head2 ToggleDebug |
|
|
This sub toggles trace debugging on and off. |
This sub toggles trace debugging on and off. |
Line 1505 sub ToggleDebug {
|
Line 1727 sub ToggleDebug {
|
|
|
} |
} |
|
|
|
=pod |
|
|
=head2 ChildProcess |
=head2 ChildProcess |
|
|
This sub implements a child process for a single lonc daemon. |
This sub implements a child process for a single lonc daemon. |
|
Optional parameter: |
|
$socket - if provided, this is a socket already open for listening |
|
on the client socket. Otherwise, a new listener is set up. |
|
|
=cut |
=cut |
|
|
sub ChildProcess { |
sub ChildProcess { |
|
# We've inherited all the |
|
# events of our parent and those have to be cancelled or else |
|
# all holy bloody chaos will result.. trust me, I already made |
|
# >that< mistake. |
|
|
|
my $host = GetServerHost(); |
|
foreach my $listener (keys %parent_dispatchers) { |
|
my $watcher = $parent_dispatchers{$listener}; |
|
my $s = $watcher->fd; |
|
if ($listener ne $host) { # Close everyone but me. |
|
Debug(5, "Closing listen socket for $listener"); |
|
$s->close(); |
|
} |
|
Debug(5, "Killing watcher for $listener"); |
|
|
|
$watcher->cancel(); |
|
delete($parent_dispatchers{$listener}); |
|
|
|
} |
|
|
|
# kill off the parent's signal handlers too! |
|
# |
|
|
|
for my $handler (keys %parent_handlers) { |
|
my $watcher = $parent_handlers{$handler}; |
|
$watcher->cancel(); |
|
delete($parent_handlers{$handler}); |
|
} |
|
|
|
$I_am_child = 1; # Seems like in spite of it all I may still getting |
|
# parent event dispatches.. flag I'm a child. |
|
|
|
|
# |
# |
# Signals must be handled by the Event framework... |
# Signals must be handled by the Event framework... |
# |
# |
|
|
Event->signal(signal => "QUIT", |
Event->signal(signal => "QUIT", |
cb => \&SignalledToDeath, |
cb => \&SignalledToDeath, |
Line 1533 sub ChildProcess {
|
Line 1791 sub ChildProcess {
|
cb => \&ToggleDebug, |
cb => \&ToggleDebug, |
data => "INT"); |
data => "INT"); |
|
|
SetupTimer($LongTickLength); |
# Block the pipe signal we'll get when the socket disconnects. We detect |
|
# socket disconnection via send/receive failures. On disconnect, the |
SetupLoncListener(); |
# 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. |
|
|
|
my ($socket) = @_; |
|
if (!$socket) { |
|
|
|
$socket = SetupLoncListener(); |
|
} |
|
# Establish an event to listen for client connection requests. |
|
|
|
|
|
Event->io(cb => \&NewClient, |
|
poll => 'r', |
|
desc => 'Lonc Listener Unix Socket', |
|
fd => $socket); |
|
|
$Event::Debuglevel = $DebugLevel; |
$Event::DebugLevel = $DebugLevel; |
|
|
Debug(9, "Making initial lond connection for ".$RemoteHost); |
Debug(9, "Making initial lond connection for ".$RemoteHost); |
|
|
# Setup the initial server connection: |
# Setup the initial server connection: |
|
|
# &MakeLondConnection(); // let first work requirest do it. |
# &MakeLondConnection(); // let first work request do it. |
|
|
|
# need to accept the connection since the event may not fire. |
|
|
|
&accept_client($socket); |
|
|
Debug(9,"Entering event loop"); |
Debug(9,"Entering event loop"); |
my $ret = Event::loop(); # Start the main event loop. |
my $ret = Event::loop(); # Start the main event loop. |
|
|
|
|
die "Main event loop exited!!!"; |
&child_exit (-1,"Main event loop exited!!!"); |
} |
} |
|
|
# Create a new child for host passed in: |
# Create a new child for host passed in: |
|
|
sub CreateChild { |
sub CreateChild { |
|
my ($host, $hostid, $defhostid) = @_; |
|
|
my $sigset = POSIX::SigSet->new(SIGINT); |
my $sigset = POSIX::SigSet->new(SIGINT); |
sigprocmask(SIG_BLOCK, $sigset); |
sigprocmask(SIG_BLOCK, $sigset); |
my $host = shift; |
|
$RemoteHost = $host; |
$RemoteHost = $host; |
|
ShowStatus('Parent keeping the flock'); # Update time in status message. |
Log("CRITICAL", "Forking server for ".$host); |
Log("CRITICAL", "Forking server for ".$host); |
my $pid = fork; |
my $pid = fork; |
if($pid) { # Parent |
if($pid) { # Parent |
$RemoteHost = "Parent"; |
$RemoteHost = "Parent"; |
$ChildHash{$pid} = $host; |
$ChildPid{$pid} = $host; |
$HostToPid{$host}= $pid; |
|
sigprocmask(SIG_UNBLOCK, $sigset); |
sigprocmask(SIG_UNBLOCK, $sigset); |
|
undef(@all_host_ids); |
} else { # child. |
} else { # child. |
|
$RemoteHostId = $hostid; |
|
$RemoteDefHostId = $defhostid; |
ShowStatus("Connected to ".$RemoteHost); |
ShowStatus("Connected to ".$RemoteHost); |
$SIG{INT} = 'DEFAULT'; |
$SIG{INT} = 'DEFAULT'; |
sigprocmask(SIG_UNBLOCK, $sigset); |
sigprocmask(SIG_UNBLOCK, $sigset); |
ChildProcess; # Does not return. |
&ChildProcess(); # Does not return. |
|
} |
|
} |
|
|
|
# parent_client_connection: |
|
# Event handler that processes client connections for the parent process. |
|
# This sub is called when the parent is listening on a socket and |
|
# a connection request arrives. We must: |
|
# Start a child process to accept the connection request. |
|
# Kill our listen on the socket. |
|
# Parameter: |
|
# event - The event object that was created to monitor this socket. |
|
# event->w->fd is the socket. |
|
# Returns: |
|
# NONE |
|
# |
|
sub parent_client_connection { |
|
if ($I_am_child) { |
|
# Should not get here, but seem to anyway: |
|
&Debug(5," Child caught parent client connection event!!"); |
|
my ($event) = @_; |
|
my $watcher = $event->w; |
|
$watcher->cancel(); # Try to kill it off again!! |
|
} else { |
|
&Debug(9, "parent_client_connection"); |
|
my ($event) = @_; |
|
my $watcher = $event->w; |
|
my $socket = $watcher->fd; |
|
my $connection = $socket->accept(); # Accept the client connection. |
|
Event->io(cb => \&get_remote_hostname, |
|
poll => 'r', |
|
data => "", |
|
fd => $connection); |
|
} |
|
} |
|
|
|
sub get_remote_hostname { |
|
my ($event) = @_; |
|
my $watcher = $event->w; |
|
my $socket = $watcher->fd; |
|
|
|
my $thisread; |
|
my $rv = $socket->recv($thisread, POSIX::BUFSIZ, 0); |
|
Debug(8, "rcv: data length = ".length($thisread)." read =".$thisread); |
|
if (!defined($rv) || length($thisread) == 0) { |
|
# Likely eof on socket. |
|
Debug(5,"Client Socket closed on lonc for p_c_c"); |
|
close($socket); |
|
$watcher->cancel(); |
|
return; |
|
} |
|
|
|
my $data = $watcher->data().$thisread; |
|
$watcher->data($data); |
|
if($data =~ /\n$/) { # Request entirely read. |
|
chomp($data); |
|
} else { |
|
return; |
|
} |
|
|
|
&Debug(5,"Creating child for $data (parent_client_connection)"); |
|
(my $hostname,my $lonid,@all_host_ids) = split(':',$data); |
|
$ChildHost{$hostname}++; |
|
if ($ChildHost{$hostname} == 1) { |
|
&CreateChild($hostname,$lonid,$all_host_ids[-1]); |
|
} else { |
|
&Log('WARNING',"Request for a second child on $hostname"); |
|
} |
|
# Clean up the listen since now the child takes over until it exits. |
|
$watcher->cancel(); # Nolonger listening to this event |
|
$socket->send("done\n"); |
|
$socket->close(); |
|
} |
|
|
|
# parent_listen: |
|
# Opens a socket and starts a listen for the parent process on a client UNIX |
|
# domain socket. |
|
# |
|
# This involves: |
|
# Creating a socket for listen. |
|
# Removing any socket lock file |
|
# Adding an event handler for this socket becoming readable |
|
# To the parent's event dispatcher. |
|
# Parameters: |
|
# loncapa_host - LonCAPA cluster name of the host represented by the client |
|
# socket. |
|
# Returns: |
|
# NONE |
|
# |
|
sub parent_listen { |
|
my ($loncapa_host) = @_; |
|
Debug(5, "parent_listen: $loncapa_host"); |
|
|
|
my ($socket,$file); |
|
if (!$loncapa_host) { |
|
$loncapa_host = 'common_parent'; |
|
$file = $perlvar{'lonSockCreate'}; |
|
} else { |
|
$file = &GetLoncSocketPath($loncapa_host); |
|
} |
|
$socket = &SetupLoncListener($loncapa_host,$file); |
|
|
|
$listening_to{$socket} = $loncapa_host; |
|
if (!$socket) { |
|
die "Unable to create a listen socket for $loncapa_host"; |
|
} |
|
|
|
my $lock_file = $file.".lock"; |
|
unlink($lock_file); # No problem if it doesn't exist yet [startup e.g.] |
|
|
|
my $watcher = |
|
Event->io(cb => \&parent_client_connection, |
|
poll => 'r', |
|
desc => "Parent listener unix socket ($loncapa_host)", |
|
data => "", |
|
fd => $socket); |
|
$parent_dispatchers{$loncapa_host} = $watcher; |
|
|
|
} |
|
|
|
sub parent_clean_up { |
|
my ($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.] |
|
my $lock_file = $socket_file.".lock"; |
|
unlink($lock_file); # No problem if it doesn't exist yet [startup e.g.] |
|
} |
|
|
|
|
|
|
|
# This sub initiates a listen on the common unix domain lonc client socket. |
|
# loncnew starts up with no children, and only spawns off children when a |
|
# connection request occurs on the common client unix socket. The spawned |
|
# child continues to run until it has been idle a while at which point it |
|
# eventually exits and once more the parent picks up the listen. |
|
# |
|
# Parameters: |
|
# NONE |
|
# Implicit Inputs: |
|
# The configuration file that has been read in by LondConnection. |
|
# Returns: |
|
# NONE |
|
# |
|
sub listen_on_common_socket { |
|
Debug(5, "listen_on_common_socket"); |
|
&parent_listen(); |
|
} |
|
|
|
# server_died is called whenever a child process exits. |
|
# Since this is dispatched via a signal, we must process all |
|
# dead children until there are no more left. The action |
|
# is to: |
|
# - Remove the child from the bookeeping hashes |
|
# - Re-establish a listen on the unix domain socket associated |
|
# with that host. |
|
# Parameters: |
|
# The event, but we don't actually care about it. |
|
sub server_died { |
|
&Debug(9, "server_died called..."); |
|
|
|
while(1) { # Loop until waitpid nowait fails. |
|
my $pid = waitpid(-1, WNOHANG); |
|
if($pid <= 0) { |
|
return; # Nothing left to wait for. |
|
} |
|
# need the host to restart: |
|
|
|
my $host = $ChildPid{$pid}; |
|
if($host) { # It's for real... |
|
&Debug(9, "Caught sigchild for $host"); |
|
&clear_childpid($pid); |
|
delete($ChildPid{$pid}); |
|
delete($ChildHost{$host}); |
|
&parent_clean_up($host); |
|
|
|
} else { |
|
&Debug(5, "Caught sigchild for pid not in hosts hash: $pid"); |
|
} |
} |
} |
|
|
} |
} |
|
|
# |
# |
# Parent process logic pass 1: |
# Parent process logic pass 1: |
# For each entry in the hosts table, we will |
# For each entry in the hosts table, we will |
Line 1624 ShowStatus("Forking node servers");
|
Line 2087 ShowStatus("Forking node servers");
|
Log("CRITICAL", "--------------- Starting children ---------------"); |
Log("CRITICAL", "--------------- Starting children ---------------"); |
|
|
LondConnection::ReadConfig; # Read standard config files. |
LondConnection::ReadConfig; # Read standard config files. |
my $HostIterator = LondConnection::GetHostIterator; |
|
while (! $HostIterator->end()) { |
|
|
|
my $hostentryref = $HostIterator->get(); |
$RemoteHost = "[parent]"; |
CreateChild($hostentryref->[0]); |
&listen_on_common_socket(); |
$HostHash{$hostentryref->[0]} = $hostentryref->[4]; |
|
$HostIterator->next(); |
|
} |
|
$RemoteHost = "Parent Server"; |
$RemoteHost = "Parent Server"; |
|
|
# Maintain the population: |
# Maintain the population: |
|
|
ShowStatus("Parent keeping the flock"); |
ShowStatus("Parent keeping the flock"); |
|
|
# |
|
# Set up parent signals: |
|
# |
|
|
|
$SIG{INT} = \&Terminate; |
|
$SIG{TERM} = \&Terminate; |
|
$SIG{HUP} = \&Restart; |
|
$SIG{USR1} = \&CheckKids; |
|
$SIG{USR2} = \&UpdateKids; # LonManage update request. |
|
|
|
while(1) { |
|
my $deadchild = wait(); |
|
if(exists $ChildHash{$deadchild}) { # need to restart. |
|
my $deadhost = $ChildHash{$deadchild}; |
|
delete($HostToPid{$deadhost}); |
|
delete($ChildHash{$deadchild}); |
|
Log("WARNING","Lost child pid= ".$deadchild. |
|
"Connected to host ".$deadhost); |
|
Log("INFO", "Restarting child procesing ".$deadhost); |
|
CreateChild($deadhost); |
|
} |
|
} |
|
|
|
|
# We need to setup a SIGChild event to handle the exit (natural or otherwise) |
|
# of the children. |
|
|
|
Event->signal(cb => \&server_died, |
|
desc => "Child exit handler", |
|
signal => "CHLD"); |
|
|
|
|
|
# Set up all the other signals we set up. |
|
|
|
$parent_handlers{INT} = Event->signal(cb => \&Terminate, |
|
desc => "Parent INT handler", |
|
signal => "INT"); |
|
$parent_handlers{TERM} = Event->signal(cb => \&Terminate, |
|
desc => "Parent TERM handler", |
|
signal => "TERM"); |
|
$parent_handlers{HUP} = Event->signal(cb => \&KillThemAll, |
|
desc => "Parent HUP handler.", |
|
signal => "HUP"); |
|
$parent_handlers{USR1} = Event->signal(cb => \&CheckKids, |
|
desc => "Parent USR1 handler", |
|
signal => "USR1"); |
|
$parent_handlers{USR2} = Event->signal(cb => \&UpdateKids, |
|
desc => "Parent USR2 handler.", |
|
signal => "USR2"); |
|
|
|
# Start procdesing events. |
|
|
|
$Event::DebugLevel = $DebugLevel; |
|
Debug(9, "Parent entering event loop"); |
|
my $ret = Event::loop(); |
|
die "Main Event loop exited: $ret"; |
|
|
=pod |
=pod |
|
|
=head1 CheckKids |
=head1 CheckKids |
|
|
Since kids do not die as easily in this implementation |
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!!) |
dead ones (all dead kids get restarted when they die!!) |
The only thing this function does is to pass USR1 to the |
The only thing this function does is to pass USR1 to the |
kids so that they report their status. |
kids so that they report their status. |
Line 1682 sub CheckKids {
|
Line 2150 sub CheckKids {
|
my $now=time; |
my $now=time; |
my $local=localtime($now); |
my $local=localtime($now); |
print $fh "LONC status $local - parent $$ \n\n"; |
print $fh "LONC status $local - parent $$ \n\n"; |
foreach my $pid (keys %ChildHash) { |
foreach my $host (keys %parent_dispatchers) { |
|
print $fh "LONC Parent process listening for $host\n"; |
|
} |
|
foreach my $pid (keys %ChildPid) { |
Debug(2, "Sending USR1 -> $pid"); |
Debug(2, "Sending USR1 -> $pid"); |
kill 'USR1' => $pid; # Tell Child to report status. |
kill 'USR1' => $pid; # Tell Child to report status. |
sleep 1; # Wait so file doesn't intermix. |
|
} |
} |
|
|
} |
} |
|
|
=pod |
=pod |
Line 1719 sub UpdateKids {
|
Line 2190 sub UpdateKids {
|
|
|
Log("INFO", "Updating connections via SIGUSR2"); |
Log("INFO", "Updating connections via SIGUSR2"); |
|
|
# Just in case we need to kill our own lonc, we wait a few seconds to |
# I'm not sure what I was thinking in the first implementation. |
# give it a chance to receive and relay lond's response to the |
# someone will have to work hard to convince me the effect is any |
# re-init command. |
# different than Restart, especially now that we don't start up |
# |
# per host servers automatically, may as well just restart. |
|
# The down side is transactions that are in flight will get timed out |
sleep(2); # Wait a couple of seconds. |
# (lost unless they are critical). |
|
|
my %hosts; # Indexed by loncapa hostname, value=ip. |
&KillThemAll(); |
|
LondConnection->ResetReadConfig(); |
# Need to re-read the host table: |
ShowStatus('Parent keeping the flock'); |
|
|
|
|
LondConnection::ReadConfig(); |
|
my $I = LondConnection::GetHostIterator; |
|
while (! $I->end()) { |
|
my $item = $I->get(); |
|
$hosts{$item->[0]} = $item->[4]; |
|
$I->next(); |
|
} |
|
|
|
# The logic below is written for clarity not for efficiency. |
|
# Since I anticipate that this function is only rarely called, that's |
|
# appropriate. There are certainly ways to combine the loops below, |
|
# and anyone wishing to obscure the logic is welcome to go for it. |
|
# Note that we don't re-direct sigchild. Instead we do what's needed |
|
# to the data structures that keep track of children to ensure that |
|
# when sigchild is honored, no new child is born. |
|
# |
|
|
|
# For each existing child; if it's host doesn't exist, kill the child. |
|
|
|
foreach my $child (keys %ChildHash) { |
|
my $oldhost = $ChildHash{$child}; |
|
if (!(exists $hosts{$oldhost})) { |
|
Log("CRITICAL", "Killing child for $oldhost host no longer exists"); |
|
delete $ChildHash{$child}; |
|
delete $HostToPid{$oldhost}; |
|
kill 'QUIT' => $child; |
|
} |
|
} |
|
# For each remaining existing child; if it's host's ip has changed, |
|
# Restart the child on the new IP. |
|
|
|
foreach my $child (keys %ChildHash) { |
|
my $oldhost = $ChildHash{$child}; |
|
my $oldip = $HostHash{$oldhost}; |
|
if ($hosts{$oldhost} ne $oldip) { |
|
|
|
# kill the old child. |
|
|
|
Log("CRITICAL", "Killing child for $oldhost host ip has changed..."); |
|
delete $ChildHash{$child}; |
|
delete $HostToPid{$oldhost}; |
|
kill 'QUIT' => $child; |
|
|
|
# Do the book-keeping needed to start a new child on the |
|
# new ip. |
|
|
|
$HostHash{$oldhost} = $hosts{$oldhost}; |
|
CreateChild($oldhost); |
|
} |
|
} |
|
# Finally, for each new host, not in the host hash, create a |
|
# enter the host and create a new child. |
|
# Force a status display of any existing process. |
|
|
|
foreach my $host (keys %hosts) { |
|
if(!(exists $HostHash{$host})) { |
|
Log("INFO", "New host $host discovered in hosts.tab..."); |
|
$HostHash{$host} = $hosts{$host}; |
|
CreateChild($host); |
|
} else { |
|
kill 'HUP' => $HostToPid{$host}; # status display. |
|
} |
|
} |
|
} |
} |
|
|
|
|
Line 1802 sub UpdateKids {
|
Line 2208 sub UpdateKids {
|
=head1 Restart |
=head1 Restart |
|
|
Signal handler for HUP... all children are killed and |
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. |
the config file. |
|
|
=cut |
=cut |
|
|
sub Restart { |
sub Restart { |
&KillThemAll; # First kill all the children. |
&KillThemAll; # First kill all the children. |
|
LondConnection->ResetReadConfig(); |
Log("CRITICAL", "Restarting"); |
Log("CRITICAL", "Restarting"); |
my $execdir = $perlvar{'lonDaemons'}; |
my $execdir = $perlvar{'lonDaemons'}; |
unlink("$execdir/logs/lonc.pid"); |
unlink("$execdir/logs/lonc.pid"); |
exec("$execdir/loncnew"); |
exec("$executable"); |
} |
} |
|
|
=pod |
=pod |
Line 1826 SIGHUP. Responds to sigint and sigterm.
|
Line 2233 SIGHUP. Responds to sigint and sigterm.
|
|
|
sub KillThemAll { |
sub KillThemAll { |
Debug(2, "Kill them all!!"); |
Debug(2, "Kill them all!!"); |
local($SIG{CHLD}) = 'IGNORE'; # Our children >will< die. |
|
foreach my $pid (keys %ChildHash) { |
#local($SIG{CHLD}) = 'IGNORE'; |
my $serving = $ChildHash{$pid}; |
# Our children >will< die. |
Debug(2, "Killing lonc for $serving pid = $pid"); |
# but we need to catch their death and cleanup after them in case this is |
ShowStatus("Killing lonc for $serving pid = $pid"); |
# a restart set of kills |
Log("CRITICAL", "Killing lonc for $serving pid = $pid"); |
my @allpids = keys(%ChildPid); |
|
foreach my $pid (@allpids) { |
|
my $serving = $ChildPid{$pid}; |
|
ShowStatus("Nicely Killing lonc for $serving pid = $pid"); |
|
Log("CRITICAL", "Nicely Killing lonc for $serving pid = $pid"); |
kill 'QUIT' => $pid; |
kill 'QUIT' => $pid; |
delete($ChildHash{$pid}); |
&clear_childpid($pid); |
} |
} |
my $execdir = $perlvar{'lonDaemons'}; |
ShowStatus("Finished killing child processes off."); |
unlink("$execdir/logs/lonc.pid"); |
} |
|
|
|
|
|
# |
|
# Kill all children via KILL. Just in case the |
|
# first shot didn't get them. |
|
|
|
sub really_kill_them_all_dammit |
|
{ |
|
Debug(2, "Kill them all Dammit"); |
|
local($SIG{CHLD} = 'IGNORE'); # In case some purist reenabled them. |
|
foreach my $pid (keys %ChildPid) { |
|
my $serving = $ChildPid{$pid}; |
|
&ShowStatus("Nastily killing lonc for $serving pid = $pid"); |
|
Log("CRITICAL", "Nastily killing lonc for $serving pid = $pid"); |
|
kill 'KILL' => $pid; |
|
delete($ChildPid{$pid}); |
|
delete($ChildKeyMode{$pid}); |
|
my $execdir = $perlvar{'lonDaemons'}; |
|
unlink("$execdir/logs/lonc.pid"); |
|
} |
} |
} |
|
|
=pod |
=pod |
Line 1849 Terminate the system.
|
Line 2279 Terminate the system.
|
=cut |
=cut |
|
|
sub Terminate { |
sub Terminate { |
KillThemAll; |
&Log("CRITICAL", "Asked to kill children.. first be nice..."); |
|
&KillThemAll; |
|
# |
|
# By now they really should all be dead.. but just in case |
|
# send them all SIGKILL's after a bit of waiting: |
|
|
|
sleep(4); |
|
&Log("CRITICAL", "Now kill children nasty"); |
|
&really_kill_them_all_dammit; |
Log("CRITICAL","Master process exiting"); |
Log("CRITICAL","Master process exiting"); |
exit 0; |
exit 0; |
|
|
} |
} |
|
|
|
=pod |
|
|
|
=cut |
|
|
|
sub my_hostname { |
|
use Sys::Hostname::FQDN(); |
|
my $name = Sys::Hostname::FQDN::fqdn(); |
|
&Debug(9,"Name is $name"); |
|
return $name; |
|
} |
|
|
|
sub record_childpid { |
|
my ($Socket) = @_; |
|
my $docdir = $perlvar{'lonDocRoot'}; |
|
my $authmode = $Socket->GetKeyMode(); |
|
my $peer = $Socket->PeerLoncapaHim(); |
|
if (($authmode eq 'ssl') || ($authmode eq 'insecure')) { |
|
my $childpid = $$; |
|
if ($childpid) { |
|
unless (exists($ChildKeyMode{$childpid})) { |
|
$ChildKeyMode{$childpid} = $authmode; |
|
} |
|
if (-d "$docdir/lon-status/loncchld") { |
|
unless (-e "$docdir/lon-status/loncchld/$childpid") { |
|
if (open (my $pidfh,'>',"$docdir/lon-status/loncchld/$childpid")) { |
|
print $pidfh "$peer:$authmode\n"; |
|
close($pidfh); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
return; |
|
} |
|
|
|
sub clear_childpid { |
|
my ($childpid) = @_; |
|
my $docdir = $perlvar{'lonDocRoot'}; |
|
if (-d "$docdir/lon-status/loncchld") { |
|
if ($childpid =~ /^\d+$/) { |
|
if (($ChildKeyMode{$childpid} eq 'insecure') || |
|
($ChildKeyMode{$childpid} eq 'ssl')) { |
|
if (-e "$docdir/lon-status/loncchld/$childpid") { |
|
unlink("$docdir/lon-status/loncchld/$childpid"); |
|
} |
|
} |
|
} |
|
} |
|
if (exists($ChildKeyMode{$childpid})) { |
|
delete($ChildKeyMode{$childpid}); |
|
} |
|
return; |
|
} |
|
|
=pod |
=pod |
|
|
=head1 Theory |
=head1 Theory |
Line 1895 A hash of lond connections that have no
|
Line 2388 A hash of lond connections that have no
|
can be closed if they are idle for a long enough time. |
can be closed if they are idle for a long enough time. |
|
|
=cut |
=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 <hostname> |
|
|
|
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: <data> |
|
|
|
Can be enabled for debugging by setting LogTransactions to nonzero. |
|
Indicates a successful transaction with lond, <data> 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 <cmd>:<subcmd> |
|
|
|
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 WARNING No SSL channel (verification failed), will try with insecure channel. |
|
|
|
Called when promotion of a socket to SSL failed because SSL certificate verification failed. |
|
Domain configuration must also permit insecure channel use for key exchange. Connection |
|
negotiation will start again from the beginning, but with Authentication Mode not set to ssl. |
|
|
|
=item INFO Connected to lond version: <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 <data> |
|
|
|
Only emitted if the global variable $LogTransactions was set to true. |
|
A client has requested a lond transaction <data> is the contents of the request. |
|
|
|
=item SUCCESS Toggled transaction logging <LogTransactions> |
|
|
|
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 <pid> for <host> died thorugh signal. |
|
|
|
QUIT signal received. lonc child process is exiting. |
|
|
|
=item SUCCESS New debugging level for <RemoteHost> now <DebugLevel> |
|
|
|
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 = <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 = <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 |