Diff for /loncom/loncnew between versions 1.15 and 1.28

version 1.15, 2003/07/15 02:07:05 version 1.28, 2003/10/14 15:36:21
Line 7 Line 7
 # Copyright Michigan State University Board of Trustees  # Copyright Michigan State University Board of Trustees
 #  #
 # This file is part of the LearningOnline Network with CAPA (LON-CAPA).  # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
 #  ## LON-CAPA is free software; you can redistribute it and/or modify
 # LON-CAPA is free software; you can redistribute it and/or modify  
 # it under the terms of the GNU General Public License as published by  # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation; either version 2 of the License, or  # the Free Software Foundation; either version 2 of the License, or
 # (at your option) any later version.  # (at your option) any later version.
Line 46 Line 45
   
 # Change log:  # Change log:
 #    $Log$  #    $Log$
   #    Revision 1.28  2003/10/14 15:36:21  albertel
   #    - making it easier to run loncnew,
   #       /etc/init.d/loncontrol startnew
   #       /etc/init.d/loncontrol restartnew
   #      will now start loncnew in place of lonc
   #
   #    Revision 1.27  2003/10/07 11:23:03  foxr
   #    Installed and tested code to process reinit in parent server.
   #
   #    Revision 1.26  2003/09/30 11:11:17  foxr
   #    Add book-keeping hashes to support the re-init procedure.
   #
   #    Revision 1.25  2003/09/23 11:22:14  foxr
   #    Tested ability to receive sigusr2  This is now logged and must be
   #    properly implemented as a re-read of hosts and re-init of appropriate
   #    children.
   #
   #    Revision 1.24  2003/09/16 09:46:42  foxr
   #    Added skeletal infrastructure to support SIGUSR2 update hosts request.
   #
   #    Revision 1.23  2003/09/15 09:24:49  foxr
   #    Add use strict and fix all the fallout from that.
   #
   #    Revision 1.22  2003/09/02 10:34:47  foxr
   #    - Fix errors in host dead detection logic (too many cases where the
   #      retries left were not getting incremented or just not checked).
   #    - Added some additional status to the ps axuww display:
   #      o Remaining retries on a host.
   #      o >>> DEAD <<< indicator if I've given up on a host.
   #    - Tested the SIGHUP will reset the retries remaining count (thanks to
   #      the above status stuff, and get allow the loncnew to re-try again
   #      on the host (thanks to the log).
   #
   #    Revision 1.21  2003/08/26 09:19:51  foxr
   #    How embarrassing... put in the SocketTimeout function in loncnew and forgot
   #    to actually hook it into the LondTransaction.  Added this to MakeLondConnection
   #    where it belongs... hopefully transactions (not just connection attempts) will
   #    timeout more speedily than the socket errors will catch it.
   #
   #    Revision 1.20  2003/08/25 18:48:11  albertel
   #    - fixing a forgotten ;
   #
   #    Revision 1.19  2003/08/19 09:31:46  foxr
   #    Get socket directory from configuration rather than the old hard coded test
   #    way that I forgot to un-hard code.
   #
   #    Revision 1.18  2003/08/06 09:52:29  foxr
   #    Also needed to remember to fail in-flight transactions if their sends fail.
   #
   #    Revision 1.17  2003/08/03 00:44:31  foxr
   #    1. Correct handling of connection failure: Assume it means the host is
   #       unreachable and fail all of the queued transactions.  Note that the
   #       inflight transactions should fail on their own time due either to timeout
   #       or send/receive failures.
   #    2. Correct handling of logs for forced death signals.  Pull the signal
   #       from the event watcher.
   #
   #    Revision 1.16  2003/07/29 02:33:05  foxr
   #    Add SIGINT processing to child processes to toggle annoying trace mode
   #    on/off.. will try to use this to isolate the compute boud process issue.
   #
 #    Revision 1.15  2003/07/15 02:07:05  foxr  #    Revision 1.15  2003/07/15 02:07:05  foxr
 #    Added code for lonc/lond transaction timeouts.  Who knows if it works right.  #    Added code for lonc/lond transaction timeouts.  Who knows if it works right.
 #    The intent is for a timeout to fail any transaction in progress and kill  #    The intent is for a timeout to fail any transaction in progress and kill
Line 63 Line 123
 #    Revision 1.10  2003/06/24 02:46:04  foxr  #    Revision 1.10  2003/06/24 02:46:04  foxr
 #    Put a limit on  the number of times we'll retry a connection.  #    Put a limit on  the number of times we'll retry a connection.
 #    Start getting the signal stuff put in as well...note that need to get signals  #    Start getting the signal stuff put in as well...note that need to get signals
 #    going or else 6the client will permanently give up on dead servers.  #    going or else the client will permanently give up on dead servers.
 #  #
 #    Revision 1.9  2003/06/13 02:38:43  foxr  #    Revision 1.9  2003/06/13 02:38:43  foxr
 #    Add logging in 'expected format'  #    Add logging in 'expected format'
Line 77 Line 137
 #    complete coding to support deferred transactions.  #    complete coding to support deferred transactions.
 #  #
 #  #
   use strict;
 use lib "/home/httpd/lib/perl/";  use lib "/home/httpd/lib/perl/";
 use lib "/home/foxr/newloncapa/types";  use lib "/home/foxr/newloncapa/types";
 use Event qw(:DEFAULT );  use Event qw(:DEFAULT );
Line 101  use LONCAPA::HashIterator; Line 161  use LONCAPA::HashIterator;
 #  #
 #   Disable all signals we might receive from outside for now.  #   Disable all signals we might receive from outside for now.
 #  #
 #$SIG{QUIT}  = IGNORE;  
 #$SIG{HUP}   = IGNORE;  
 #$SIG{USR1}  = IGNORE;  
 #$SIG{INT}   = IGNORE;  
 #$SIG{CHLD}  = IGNORE;  
 #$SIG{__DIE__}  = IGNORE;  
   
   
 # Read the httpd configuration file to get perl variables  # Read the httpd configuration file to get perl variables
Line 119  my %perlvar    = %{$perlvarref}; Line 173  my %perlvar    = %{$perlvarref};
 #  parent and shared variables.  #  parent and shared variables.
   
 my %ChildHash; # by pid -> host.  my %ChildHash; # by pid -> host.
   my %HostToPid; # By host -> pid.
   my %HostHash; # by loncapaname -> IP.
   
   
 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= 10; # So Sigint can toggle this.
 my $IdleTimeout= 3600; # Wait an hour before pruning connections.  my $IdleTimeout= 3600; # Wait an hour before pruning connections.
   
 #  #
 #  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; # Name of host child is talking to.
 my $UnixSocketDir= "/home/httpd/sockets";   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.
 my %ActiveTransactions; # LondTransactions in flight.  my %ActiveTransactions; # LondTransactions in flight.
Line 240  sub GetPeername { Line 297  sub GetPeername {
     my $peerip;      my $peerip;
     if($AdrFamily == AF_INET) {      if($AdrFamily == AF_INET) {
  ($peerport, $peerip) = sockaddr_in($peer);   ($peerport, $peerip) = sockaddr_in($peer);
  my $peername    = gethostbyaddr($iaddr, $AdrFamily);   my $peername    = gethostbyaddr($peerip, $AdrFamily);
  return $peername;   return $peername;
     } elsif ($AdrFamily == AF_UNIX) {      } elsif ($AdrFamily == AF_UNIX) {
  my $peerfile;   my $peerfile;
Line 261  sub Debug { Line 318  sub Debug {
     my $level   = shift;      my $level   = shift;
     my $message = shift;      my $message = shift;
     if ($level <= $DebugLevel) {      if ($level <= $DebugLevel) {
  print $message." host = ".$RemoteHost."\n";   Log("INFO", "-Debug- $message host = $RemoteHost");
     }      }
 }  }
   
Line 301  sub ShowStatus { Line 358  sub ShowStatus {
 sub SocketTimeout {  sub SocketTimeout {
     my $Socket = shift;      my $Socket = shift;
           
     KillSocket($Socket);      KillSocket($Socket); # A transaction timeout also counts as
                                   # a connection failure:
       $ConnectionRetriesLeft--;
 }  }
   
 =pod  =pod
Line 315  Invoked  each timer tick. Line 374  Invoked  each timer tick.
   
 sub Tick {  sub Tick {
     my $client;      my $client;
     ShowStatus(GetServerHost()." Connection count: ".$ConnectionCount);      if($ConnectionRetriesLeft > 0) {
    ShowStatus(GetServerHost()." Connection count: ".$ConnectionCount
      ." Retries remaining: ".$ConnectionRetriesLeft);
       } else {
    ShowStatus(GetServerHost()." >> DEAD <<");
       }
     # Is it time to prune connection count:      # Is it time to prune connection count:
   
   
Line 324  sub Tick { Line 387  sub Tick {
        ($WorkQueue->Count() == 0)) { # Idle connections and nothing to do?         ($WorkQueue->Count() == 0)) { # Idle connections and nothing to do?
  $IdleSeconds++;   $IdleSeconds++;
  if($IdleSeconds > $IdleTimeout) { # Prune a connection...   if($IdleSeconds > $IdleTimeout) { # Prune a connection...
     $Socket = $IdleConnections->pop();      my $Socket = $IdleConnections->pop();
     KillSocket($Socket);      KillSocket($Socket);
  }   }
     } else {      } else {
Line 333  sub Tick { Line 396  sub Tick {
     #      #
     #  For each inflight transaction, tick down its timeout counter.      #  For each inflight transaction, tick down its timeout counter.
     #      #
     foreach $item (keys %ActiveTransactions) {      foreach my $item (keys %ActiveTransactions) {
  my $Socket = $ActiveTransactions{$item}->getServer();   my $Socket = $ActiveTransactions{$item}->getServer();
  $Socket->Tick();   $Socket->Tick();
     }      }
Line 347  sub Tick { Line 410  sub Tick {
     my $Connections = ($Requests <= $MaxConnectionCount) ?      my $Connections = ($Requests <= $MaxConnectionCount) ?
  $Requests : $MaxConnectionCount;   $Requests : $MaxConnectionCount;
     Debug(1,"Work but no connections, start ".$Connections." of them");      Debug(1,"Work but no connections, start ".$Connections." of them");
     for ($i =0; $i < $Connections; $i++) {      my $successCount = 0;
  MakeLondConnection();      for (my $i =0; $i < $Connections; $i++) {
    $successCount += MakeLondConnection();
       }
       if($successCount == 0) { # All connections failed:
    Debug(1,"Work in queue failed to make any connectiouns\n");
    EmptyQueue(); # Fail pending transactions with con_lost.
     }      }
  } else {   } else {
       ShowStatus(GetServerHost()." >>> DEAD!!! <<<");
     Debug(1,"Work in queue, but gave up on connections..flushing\n");      Debug(1,"Work in queue, but gave up on connections..flushing\n");
     EmptyQueue(); # Connections can't be established.      EmptyQueue(); # Connections can't be established.
  }   }
Line 402  sub ServerToIdle { Line 471  sub ServerToIdle {
   
     #  If there's work to do, start the transaction:      #  If there's work to do, start the transaction:
   
     $reqdata = $WorkQueue->dequeue(); # This is a LondTransaction      my $reqdata = $WorkQueue->dequeue(); # This is a LondTransaction
     unless($reqdata eq undef)  {      unless($reqdata eq undef)  {
  Debug(9, "Queue gave request data: ".$reqdata->getRequest());   Debug(9, "Queue gave request data: ".$reqdata->getRequest());
  &StartRequest($Socket,  $reqdata);   &StartRequest($Socket,  $reqdata);
Line 536  sub CompleteTransaction { Line 605  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:$Client:".$Transaction->getRequest());   LogPerm("S:$Transaction->getClient() :".$Transaction->getRequest());
  unlink $Transaction->getFile();   unlink $Transaction->getFile();
     }      }
 }  }
Line 597  Parameters: Line 666  Parameters:
   
 sub FailTransaction {  sub FailTransaction {
     my $transaction = shift;      my $transaction = shift;
       Log("WARNING", "Failing transaction ".$transaction->getRequest());
     Debug(1, "Failing transaction: ".$transaction->getRequest());      Debug(1, "Failing transaction: ".$transaction->getRequest());
     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());
  StartClientReply($transaction, "con_lost\n");   StartClientReply($transaction, "con_lost\n");
     }      }
       if($ConnectionRetriesLeft <= 0) {
    Log("CRITICAL", "Host marked dead: ".GetServerHost());
       }
   
 }  }
   
Line 614  sub FailTransaction { Line 687  sub FailTransaction {
   
 =cut  =cut
 sub EmptyQueue {  sub EmptyQueue {
       $ConnectionRetriesLeft--; # Counts as connection failure too.
     while($WorkQueue->Count()) {      while($WorkQueue->Count()) {
  my $request = $WorkQueue->dequeue(); # This is a transaction   my $request = $WorkQueue->dequeue(); # This is a transaction
  FailTransaction($request);   FailTransaction($request);
Line 628  Close all connections open on lond prior Line 702  Close all connections open on lond prior
   
 =cut  =cut
 sub CloseAllLondConnections {  sub CloseAllLondConnections {
     foreach $Socket (keys %ActiveConnections) {      foreach my $Socket (keys %ActiveConnections) {
  KillSocket($Socket);   KillSocket($Socket);
     }      }
 }  }
Line 659  nonzero if we are allowed to create a ne Line 733  nonzero if we are allowed to create a ne
 sub KillSocket {  sub KillSocket {
     my $Socket = shift;      my $Socket = shift;
   
       Log("WARNING", "Shutting down a socket");
     $Socket->Shutdown();      $Socket->Shutdown();
   
     #  If the socket came from the active connection set,      #  If the socket came from the active connection set,
Line 679  sub KillSocket { Line 754  sub KillSocket {
     #  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) {
  EmptyQueue;   EmptyQueue();
     }      }
 }  }
   
Line 748  sub LondReadable { Line 823  sub LondReadable {
     my $Socket     = $Watcher->data;      my $Socket     = $Watcher->data;
     my $client     = undef;      my $client     = undef;
   
     &Debug(6,"LondReadable called state = ".$State);      &Debug(6,"LondReadable called state = ".$Socket->GetState());
   
   
     my $State = $Socket->GetState(); # All action depends on the state.      my $State = $Socket->GetState(); # All action depends on the state.
   
     SocketDump(6, $Socket);      SocketDump(6, $Socket);
     my $status = $Socket->Readable();      my $status = $Socket->Readable();
   
     &Debug(2, "Socket->Readable returned: $status");      &Debug(2, "Socket->Readable returned: $status");
   
     if($status != 0) {      if($status != 0) {
  # bad return from socket read. Currently this means that   # bad return from socket read. Currently this means that
  # The socket has become disconnected. We fail the transaction.   # The socket has become disconnected. We fail the transaction.
   
    Log("WARNING",
       "Lond connection lost.");
  if(exists($ActiveTransactions{$Socket})) {   if(exists($ActiveTransactions{$Socket})) {
     Debug(3,"Lond connection lost failing transaction");  
     FailTransaction($ActiveTransactions{$Socket});      FailTransaction($ActiveTransactions{$Socket});
  }   }
  $Watcher->cancel();   $Watcher->cancel();
  KillSocket($Socket);   KillSocket($Socket);
    $ConnectionRetriesLeft--;       # Counts as connection failure
  return;   return;
     }      }
     SocketDump(6,$Socket);      SocketDump(6,$Socket);
Line 800  sub LondReadable { Line 878  sub LondReadable {
     } elsif ($State eq "Idle") {      } elsif ($State eq "Idle") {
  # If necessary, complete a transaction and then go into the   # If necessary, complete a transaction and then go into the
  # idle queue.   # idle queue.
    #  Note that a trasition to idle indicates a live lond
    # on the other end so reset the connection retries.
    #
    $ConnectionRetriesLeft = $ConnectionRetries; # success resets the count
  $Watcher->cancel();   $Watcher->cancel();
  if(exists($ActiveTransactions{$Socket})) {   if(exists($ActiveTransactions{$Socket})) {
     Debug(8,"Completing transaction!!");      Debug(8,"Completing transaction!!");
Line 912  sub LondWritable { Line 994  sub LondWritable {
     # We'll treat this as if the socket got disconnected:      # We'll treat this as if the socket got disconnected:
     Log("WARNING", "Connection to ".$RemoteHost.      Log("WARNING", "Connection to ".$RemoteHost.
  " has been disconnected");   " has been disconnected");
       FailTransaction($ActiveTransactions{$Socket});
     $Watcher->cancel();      $Watcher->cancel();
     KillSocket($Socket);      KillSocket($Socket);
     return;      return;
Line 1011  sub QueueDelayed { Line 1094  sub QueueDelayed {
     Debug(4, "Delayed path: ".$path);      Debug(4, "Delayed path: ".$path);
     opendir(DIRHANDLE, $path);      opendir(DIRHANDLE, $path);
           
     @alldelayed = grep /\.$RemoteHost$/, readdir DIRHANDLE;      my @alldelayed = grep /\.$RemoteHost$/, readdir DIRHANDLE;
     Debug(4, "Got ".$alldelayed." delayed files");  
     closedir(DIRHANDLE);      closedir(DIRHANDLE);
     my $dfname;      my $dfname;
     my $reqfile;      my $reqfile;
Line 1054  sub MakeLondConnection { Line 1136  sub MakeLondConnection {
  $ConnectionRetriesLeft--;   $ConnectionRetriesLeft--;
  return 0; # Failure.   return 0; # Failure.
     }  else {      }  else {
  $ConnectionRetriesLeft = $ConnectionRetries; # success resets the count  
  # 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 1067  sub MakeLondConnection { Line 1149  sub MakeLondConnection {
     &Debug(9,"MakeLondConnection got socket: ".$Socket);      &Debug(9,"MakeLondConnection got socket: ".$Socket);
  }   }
   
    $Connection->SetTimeoutCallback(\&SocketTimeout);
  $event = Event->io(fd       => $Socket,  
    my $event = Event->io(fd       => $Socket,
    poll     => 'w',     poll     => 'w',
    cb       => \&LondWritable,     cb       => \&LondWritable,
    data     => $Connection,     data     => $Connection,
Line 1124  sub StartRequest { Line 1207  sub StartRequest {
     $ActiveTransactions{$Lond} = $Request;      $ActiveTransactions{$Lond} = $Request;
   
     $Lond->InitiateTransaction($Request->getRequest());      $Lond->InitiateTransaction($Request->getRequest());
     $event = Event->io(fd      => $Socket,      my $event = Event->io(fd      => $Socket,
        poll    => "w",         poll    => "w",
        cb      => \&LondWritable,         cb      => \&LondWritable,
        data    => $Lond,         data    => $Lond,
Line 1166  sub QueueTransaction { Line 1249  sub QueueTransaction {
  Debug(8,"Must queue...");   Debug(8,"Must queue...");
  $WorkQueue->enqueue($requestData);   $WorkQueue->enqueue($requestData);
  if($ConnectionCount < $MaxConnectionCount) {   if($ConnectionCount < $MaxConnectionCount) {
     Debug(4,"Starting additional lond connection");      if($ConnectionRetriesLeft > 0) {
     MakeLondConnection();   Debug(4,"Starting additional lond connection");
    if(MakeLondConnection() == 0) {
       EmptyQueue(); # Fail transactions, can't make connection.
    }
       } else {
    ShowStatus(GetServerHost()." >>> DEAD !!!! <<<");
    EmptyQueue(); # It's worse than that ... he's dead Jim.
       }
  }   }
     } else { # Can start the request:      } else { # Can start the request:
  Debug(8,"Can start...");   Debug(8,"Can start...");
Line 1332  sub SetupLoncListener { Line 1422  sub SetupLoncListener {
 Child USR1 signal handler to report the most recent status  Child USR1 signal handler to report the most recent status
 into the status file.  into the status file.
   
   We also use this to reset the retries count in order to allow the
   client to retry connections with a previously dead server.
 =cut  =cut
 sub ChildStatus {  sub ChildStatus {
     my $event = shift;      my $event = shift;
Line 1342  sub ChildStatus { Line 1434  sub ChildStatus {
     my $fh = IO::File->new(">>$docdir/lon-status/loncstatus.txt");      my $fh = IO::File->new(">>$docdir/lon-status/loncstatus.txt");
     print $fh $$."\t".$RemoteHost."\t".$Status."\t".      print $fh $$."\t".$RemoteHost."\t".$Status."\t".
  $RecentLogEntry."\n";   $RecentLogEntry."\n";
       $ConnectionRetriesLeft = $ConnectionRetries;
 }  }
   
 =pod  =pod
Line 1358  sub SignalledToDeath { Line 1451  sub SignalledToDeath {
     my $watcher= $event->w;      my $watcher= $event->w;
   
     Debug(2,"Signalled to death! via ".$watcher->data);      Debug(2,"Signalled to death! via ".$watcher->data);
     my ($signal) = @_;      my ($signal) = $watcher->data;
     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\"");
     die("Signal abnormal end");  
     exit 0;      exit 0;
   
 }  }
   
   =head2 ToggleDebug
   
   This sub toggles trace debugging on and off.
   
   =cut
   
   sub ToggleDebug {
       my $Current    = $DebugLevel;
          $DebugLevel = $NextDebugLevel;
          $NextDebugLevel = $Current;
   
       Log("SUCCESS", "New debugging level for $RemoteHost now $DebugLevel");
   
   }
   
 =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.
Line 1380  sub ChildProcess { Line 1488  sub ChildProcess {
     #      #
     #  Signals must be handled by the Event framework...      #  Signals must be handled by the Event framework...
 #  #
 #    $SIG{QUIT}  = \&SignalledToDeath;  
 #    $SIG{HUP}   = \&ChildStatus;  
 #    $SIG{USR1}  = IGNORE;  
 #    $SIG{INT}   = DEFAULT;  
 #    $SIG{CHLD}  = IGNORE;  
 #    $SIG{__DIE__}  = \&SignalledToDeath;  
   
     Event->signal(signal   => "QUIT",      Event->signal(signal   => "QUIT",
   cb       => \&SignalledToDeath,    cb       => \&SignalledToDeath,
Line 1396  sub ChildProcess { Line 1498  sub ChildProcess {
     Event->signal(signal   => "USR1",      Event->signal(signal   => "USR1",
   cb       => \&ChildStatus,    cb       => \&ChildStatus,
   data     => "USR1");    data     => "USR1");
       Event->signal(signal   => "INT",
     cb       => \&ToggleDebug,
     data     => "INT");
   
     SetupTimer();      SetupTimer();
           
Line 1425  sub CreateChild { Line 1530  sub CreateChild {
     my $host = shift;      my $host = shift;
     $RemoteHost = $host;      $RemoteHost = $host;
     Log("CRITICAL", "Forking server for ".$host);      Log("CRITICAL", "Forking server for ".$host);
     $pid          = fork;      my $pid          = fork;
     if($pid) { # Parent      if($pid) { # Parent
  $ChildHash{$pid} = $RemoteHost;   $RemoteHost = "Parent";
    $ChildHash{$pid} = $host;
    $HostToPid{$host}= $pid;
  sigprocmask(SIG_UNBLOCK, $sigset);   sigprocmask(SIG_UNBLOCK, $sigset);
   
     } else { # child.      } else { # child.
  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.
     }      }
Line 1469  if ($childpid != 0) { Line 1576  if ($childpid != 0) {
 #  #
   
 ShowStatus("Parent writing pid file:");  ShowStatus("Parent writing pid file:");
 $execdir = $perlvar{'lonDaemons'};  my $execdir = $perlvar{'lonDaemons'};
 open (PIDSAVE, ">$execdir/logs/lonc.pid");  open (PIDSAVE, ">$execdir/logs/lonc.pid");
 print PIDSAVE "$$\n";  print PIDSAVE "$$\n";
 close(PIDSAVE);  close(PIDSAVE);
   
   
   
 if (POSIX::setsid() < 0) {  if (POSIX::setsid() < 0) {
     print "Could not create new session\n";      print "Could not create new session\n";
     exit -1;      exit -1;
Line 1486  Log("CRITICAL", "--------------- Startin Line 1595  Log("CRITICAL", "--------------- Startin
 my $HostIterator = LondConnection::GetHostIterator;  my $HostIterator = LondConnection::GetHostIterator;
 while (! $HostIterator->end()) {  while (! $HostIterator->end()) {
   
     $hostentryref = $HostIterator->get();      my $hostentryref = $HostIterator->get();
     CreateChild($hostentryref->[0]);      CreateChild($hostentryref->[0]);
       $HostHash{$hostentryref->[0]} = $hostentryref->[4];
     $HostIterator->next();      $HostIterator->next();
 }  }
 $RemoteHost = "Parent Server";  $RemoteHost = "Parent Server";
Line 1504  $SIG{INT}  = \&Terminate; Line 1614  $SIG{INT}  = \&Terminate;
 $SIG{TERM} = \&Terminate;   $SIG{TERM} = \&Terminate; 
 $SIG{HUP}  = \&Restart;  $SIG{HUP}  = \&Restart;
 $SIG{USR1} = \&CheckKids;   $SIG{USR1} = \&CheckKids; 
   $SIG{USR2} = \&UpdateKids; # LonManage update request.
   
 while(1) {  while(1) {
     $deadchild = wait();      my $deadchild = wait();
     if(exists $ChildHash{$deadchild}) { # need to restart.      if(exists $ChildHash{$deadchild}) { # need to restart.
  $deadhost = $ChildHash{$deadchild};   my $deadhost = $ChildHash{$deadchild};
    delete($HostToPid{$deadhost});
  delete($ChildHash{$deadchild});   delete($ChildHash{$deadchild});
  Log("WARNING","Lost child pid= ".$deadchild.   Log("WARNING","Lost child pid= ".$deadchild.
       "Connected to host ".$deadhost);        "Connected to host ".$deadhost);
Line 1538  sub CheckKids { Line 1650  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 $pid (keys %ChildHash) {      foreach my $pid (keys %ChildHash) {
  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.   sleep 1; # Wait so file doesn't intermix.
Line 1547  sub CheckKids { Line 1659  sub CheckKids {
   
 =pod  =pod
   
   =head1  UpdateKids
   
   parent's SIGUSR2 handler.  This handler:
   
   =item
   
   Rereads the hosts file.
   
   =item
    
   Kills off (via sigint) children for hosts that have disappeared.
   
   =item
   
   QUITs  children for hosts that already exist (this just forces a status display
   and resets the connection retry count for that host.
   
   =item
   
   Starts new children for hosts that have been added to the hosts.tab file since
   the start of the master program and maintains them.
   
   =cut
   
   sub UpdateKids {
   
       Log("INFO", "Updating connections via SIGUSR2");
   
       #  Just in case we need to kill our own lonc, we wait a few seconds to
       #  give it a chance to receive and relay lond's response to the 
       #  re-init command.
       #
   
       sleep(2); # Wait a couple of seconds.
   
       my %hosts;                   # Indexed by loncapa hostname, value=ip.
       
       # Need to re-read  the host table:
       
       
       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.
    }
       }
   }
   
   
   =pod
   
 =head1 Restart  =head1 Restart
   
 Signal handler for HUP... all children are killed and  Signal handler for HUP... all children are killed and
Line 1556  the config file. Line 1776  the config file.
 =cut  =cut
   
 sub Restart {  sub Restart {
     KillThemAll; # First kill all the children.      &KillThemAll; # First kill all the children.
     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/lonc");      exec("$execdir/loncnew");
 }  }
   
 =pod  =pod
Line 1568  sub Restart { Line 1788  sub Restart {
 =head1 KillThemAll  =head1 KillThemAll
   
 Signal handler that kills all children by sending them a   Signal handler that kills all children by sending them a 
 SIGINT.  Responds to sigint and sigterm.  SIGHUP.  Responds to sigint and sigterm.
   
 =cut  =cut
   
 sub KillThemAll {  sub KillThemAll {
     Debug(2, "Kill them all!!");      Debug(2, "Kill them all!!");
     local($SIG{CHLD}) = 'IGNORE';      # Our children >will< die.      local($SIG{CHLD}) = 'IGNORE';      # Our children >will< die.
     foreach $pid (keys %ChildHash) {      foreach my $pid (keys %ChildHash) {
  my $serving = $ChildHash{$pid};   my $serving = $ChildHash{$pid};
  Debug(2, "Killing lonc for $serving pid = $pid");   Debug(2, "Killing lonc for $serving pid = $pid");
  ShowStatus("Killing lonc for $serving pid = $pid");   ShowStatus("Killing lonc for $serving pid = $pid");
  Log("CRITICAL", "Killing lonc for $serving pid = $pid");   Log("CRITICAL", "Killing lonc for $serving pid = $pid");
  kill('INT', $pid);   kill 'QUIT' => $pid;
  delete($ChildeHash{$pid});   delete($ChildHash{$pid});
     }      }
     my $execdir = $perlvar{'lonDaemons'};      my $execdir = $perlvar{'lonDaemons'};
     unlink("$execdir/logs/lonc.pid");      unlink("$execdir/logs/lonc.pid");
     ShowStatus("Killing the master process");  
     Log("CRITICAL", "Killing the master process.");  
 }  }
   
 =pod  =pod
Line 1599  Terminate the system. Line 1818  Terminate the system.
   
 sub Terminate {  sub Terminate {
     KillThemAll;      KillThemAll;
     exit;      Log("CRITICAL","Master process exiting");
       exit 0;
   
 }  }
 =pod  =pod

Removed from v.1.15  
changed lines
  Added in v.1.28


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>