File:  [LON-CAPA] / loncom / Attic / lonc
Revision 1.28: download - view: text, annotated - select for diffs
Tue Feb 19 22:51:13 2002 UTC (22 years, 4 months ago) by www
Branches: MAIN
CVS tags: HEAD
Skip empty lines in broken hosts.tabs

    1: #!/usr/bin/perl
    2: 
    3: # The LearningOnline Network
    4: # lonc - LON TCP-Client Domain-Socket-Server
    5: # provides persistent TCP connections to the other servers in the network
    6: # through multiplexed domain sockets
    7: #
    8: # $Id: lonc,v 1.28 2002/02/19 22:51:13 www Exp $
    9: #
   10: # Copyright Michigan State University Board of Trustees
   11: #
   12: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
   13: #
   14: # LON-CAPA is free software; you can redistribute it and/or modify
   15: # it under the terms of the GNU General Public License as published by
   16: # the Free Software Foundation; either version 2 of the License, or
   17: # (at your option) any later version.
   18: #
   19: # LON-CAPA is distributed in the hope that it will be useful,
   20: # but WITHOUT ANY WARRANTY; without even the implied warranty of
   21: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   22: # GNU General Public License for more details.
   23: #
   24: # You should have received a copy of the GNU General Public License
   25: # along with LON-CAPA; if not, write to the Free Software
   26: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   27: #
   28: # /home/httpd/html/adm/gpl.txt
   29: #
   30: # http://www.lon-capa.org/
   31: #
   32: # PID in subdir logs/lonc.pid
   33: # kill kills
   34: # HUP restarts
   35: # USR1 tries to open connections again
   36: 
   37: # 6/4/99,6/5,6/7,6/8,6/9,6/10,6/11,6/12,7/14,7/19,
   38: # 10/8,10/9,10/15,11/18,12/22,
   39: # 2/8,7/25 Gerd Kortemeyer
   40: # 12/05 Scott Harrison
   41: # 12/05 Gerd Kortemeyer
   42: # YEAR=2001
   43: # 01/10/01 Scott Harrison
   44: # 03/14/01,03/15,06/12,11/26,11/27,11/28 Gerd Kortemeyer
   45: # 12/20 Scott Harrison
   46: # YEAR=2002
   47: # 2/19/02
   48: # 
   49: # based on nonforker from Perl Cookbook
   50: # - server who multiplexes without forking
   51: 
   52: use POSIX;
   53: use IO::Socket;
   54: use IO::Select;
   55: use IO::File;
   56: use Socket;
   57: use Fcntl;
   58: use Tie::RefHash;
   59: use Crypt::IDEA;
   60: use Net::Ping;
   61: use LWP::UserAgent();
   62: 
   63: my $status='';
   64: my $lastlog='';
   65: 
   66: # grabs exception and records it to log before exiting
   67: sub catchexception {
   68:     my ($signal)=@_;
   69:     $SIG{'QUIT'}='DEFAULT';
   70:     $SIG{__DIE__}='DEFAULT';
   71:     &logthis("<font color=red>CRITICAL: "
   72:      ."ABNORMAL EXIT. Child $$ for server $wasserver died through "
   73:      ."\"$signal\" with this parameter->[$@]</font>");
   74:     die($@);
   75: }
   76: 
   77: $childmaxattempts=5;
   78: 
   79: # -------------------------------------- Routines to see if other box available
   80: 
   81: sub online {
   82:     my $host=shift;
   83:     my $p=Net::Ping->new("tcp",20);
   84:     my $online=$p->ping("$host");
   85:     $p->close();
   86:     undef ($p);
   87:     return $online;
   88: }
   89: 
   90: sub connected {
   91:     my ($local,$remote)=@_;
   92:     $local=~s/\W//g;
   93:     $remote=~s/\W//g;
   94: 
   95:     unless ($hostname{$local}) { return 'local_unknown'; }
   96:     unless ($hostname{$remote}) { return 'remote_unknown'; }
   97: 
   98:     unless (&online($hostname{$local})) { return 'local_offline'; }
   99: 
  100:     my $ua=new LWP::UserAgent;
  101:     
  102:     my $request=new HTTP::Request('GET',
  103:       "http://".$hostname{$local}.'/cgi-bin/ping.pl?'.$remote);
  104: 
  105:     my $response=$ua->request($request);
  106: 
  107:     unless ($response->is_success) { return 'local_error'; }
  108: 
  109:     my $reply=$response->content;
  110:     $reply=(split("\n",$reply))[0];
  111:     $reply=~s/\W//g;
  112:     if ($reply ne $remote) { return $reply; }
  113:     return 'ok';
  114: }
  115: 
  116: 
  117: # -------------------------------- Set signal handlers to record abnormal exits
  118: 
  119: $SIG{QUIT}=\&catchexception;
  120: $SIG{__DIE__}=\&catchexception;
  121: 
  122: # ------------------------------------ Read httpd access.conf and get variables
  123: 
  124: open (CONFIG,"/etc/httpd/conf/access.conf") || die "Can't read access.conf";
  125: 
  126: while ($configline=<CONFIG>) {
  127:     if ($configline =~ /PerlSetVar/) {
  128: 	my ($dummy,$varname,$varvalue)=split(/\s+/,$configline);
  129:         chomp($varvalue);
  130:         $perlvar{$varname}=$varvalue;
  131:     }
  132: }
  133: close(CONFIG);
  134: 
  135: # ----------------------------- Make sure this process is running from user=www
  136: my $wwwid=getpwnam('www');
  137: if ($wwwid!=$<) {
  138:    $emailto="$perlvar{'lonAdmEMail'},$perlvar{'lonSysEMail'}";
  139:    $subj="LON: $perlvar{'lonHostID'} User ID mismatch";
  140:    system("echo 'User ID mismatch.  lonc must be run as user www.' |\
  141:  mailto $emailto -s '$subj' > /dev/null");
  142:    exit 1;
  143: }
  144: 
  145: # --------------------------------------------- Check if other instance running
  146: 
  147: my $pidfile="$perlvar{'lonDaemons'}/logs/lonc.pid";
  148: 
  149: if (-e $pidfile) {
  150:    my $lfh=IO::File->new("$pidfile");
  151:    my $pide=<$lfh>;
  152:    chomp($pide);
  153:    if (kill 0 => $pide) { die "already running"; }
  154: }
  155: 
  156: # ------------------------------------------------------------- Read hosts file
  157: 
  158: open (CONFIG,"$perlvar{'lonTabDir'}/hosts.tab") || die "Can't read host file";
  159: 
  160: while ($configline=<CONFIG>) {
  161:     my ($id,$domain,$role,$name,$ip)=split(/:/,$configline);
  162:     chomp($ip);
  163:     if ($ip) {
  164:      $hostip{$id}=$ip;
  165:      $hostname{$id}=$name;
  166:     }
  167: }
  168: 
  169: close(CONFIG);
  170: 
  171: # -------------------------------------------------------- Routines for forking
  172: 
  173: %children               = ();       # keys are current child process IDs,
  174:                                     # values are hosts
  175: %childpid               = ();       # the other way around
  176: 
  177: %childatt               = ();       # number of attempts to start server
  178:                                     # for ID
  179: 
  180: sub REAPER {                        # takes care of dead children
  181:     $SIG{CHLD} = \&REAPER;
  182:     my $pid = wait;
  183:     my $wasserver=$children{$pid};
  184:     &logthis("<font color=red>CRITICAL: "
  185:      ."Child $pid for server $wasserver died ($childatt{$wasserver})</font>");
  186:     delete $children{$pid};
  187:     delete $childpid{$wasserver};
  188:     my $port = "$perlvar{'lonSockDir'}/$wasserver";
  189:     unlink($port);
  190: }
  191: 
  192: sub hangup {
  193:     foreach (keys %children) {
  194:         $wasserver=$children{$_};
  195:         &status("Closing $wasserver");
  196:         &logthis('Closing '.$wasserver.': '.&subreply('exit',$wasserver));
  197:         &status("Kill PID $_ for $wasserver");
  198: 	kill ('INT',$_);
  199:     }
  200: }
  201: 
  202: sub HUNTSMAN {                      # signal handler for SIGINT
  203:     local($SIG{CHLD}) = 'IGNORE';   # we're going to kill our children
  204:     &hangup();
  205:     my $execdir=$perlvar{'lonDaemons'};
  206:     unlink("$execdir/logs/lonc.pid");
  207:     &logthis("<font color=red>CRITICAL: Shutting down</font>");
  208:     exit;                           # clean up with dignity
  209: }
  210: 
  211: sub HUPSMAN {                      # signal handler for SIGHUP
  212:     local($SIG{CHLD}) = 'IGNORE';  # we're going to kill our children
  213:     &hangup();
  214:     &logthis("<font color=red>CRITICAL: Restarting</font>");
  215:     unlink("$execdir/logs/lonc.pid");
  216:     my $execdir=$perlvar{'lonDaemons'};
  217:     exec("$execdir/lonc");         # here we go again
  218: }
  219: 
  220: sub checkchildren {
  221:     &initnewstatus();
  222:     &logstatus();
  223:     &logthis('Going to check on the children');
  224:     foreach (sort keys %children) {
  225: 	sleep 1;
  226:         unless (kill 'USR1' => $_) {
  227: 	    &logthis ('Child '.$_.' is dead');
  228:             &logstatus($$.' is dead');
  229:         } 
  230:     }
  231: }
  232: 
  233: sub USRMAN {
  234:     &logthis("USR1: Trying to establish connections again");
  235:     %childatt=();
  236:     &checkchildren();
  237: }
  238: 
  239: # -------------------------------------------------- Non-critical communication
  240: sub subreply { 
  241:  my ($cmd,$server)=@_;
  242:  my $answer='';
  243:  if ($server ne $perlvar{'lonHostID'}) { 
  244:     my $peerfile="$perlvar{'lonSockDir'}/$server";
  245:     my $sclient=IO::Socket::UNIX->new(Peer    =>"$peerfile",
  246:                                       Type    => SOCK_STREAM,
  247:                                       Timeout => 10)
  248:        or return "con_lost";
  249: 
  250: 
  251:     $SIG{ALRM}=sub { die "timeout" };
  252:     $SIG{__DIE__}='DEFAULT';
  253:     eval {
  254:      alarm(10);
  255:      print $sclient "$cmd\n";
  256:      $answer=<$sclient>;
  257:      chomp($answer);
  258:      alarm(0);
  259:     };
  260:     if ((!$answer) || ($@=~/timeout/)) { $answer="con_lost"; }
  261:     $SIG{ALRM}='DEFAULT';
  262:     $SIG{__DIE__}=\&catchexception;
  263:  } else { $answer='self_reply'; }
  264:  return $answer;
  265: }
  266: 
  267: # --------------------------------------------------------------------- Logging
  268: 
  269: sub logthis {
  270:     my $message=shift;
  271:     my $execdir=$perlvar{'lonDaemons'};
  272:     my $fh=IO::File->new(">>$execdir/logs/lonc.log");
  273:     my $now=time;
  274:     my $local=localtime($now);
  275:     $lastlog=$local.': '.$message;
  276:     print $fh "$local ($$): $message\n";
  277: }
  278: 
  279: 
  280: sub logperm {
  281:     my $message=shift;
  282:     my $execdir=$perlvar{'lonDaemons'};
  283:     my $now=time;
  284:     my $local=localtime($now);
  285:     my $fh=IO::File->new(">>$execdir/logs/lonnet.perm.log");
  286:     print $fh "$now:$message:$local\n";
  287: }
  288: # ------------------------------------------------------------------ Log status
  289: 
  290: sub logstatus {
  291:     my $docdir=$perlvar{'lonDocRoot'};
  292:     my $fh=IO::File->new(">>$docdir/lon-status/loncstatus.txt");
  293:     print $fh $$."\t".$status."\t".$lastlog."\n";
  294: }
  295: 
  296: sub initnewstatus {
  297:     my $docdir=$perlvar{'lonDocRoot'};
  298:     my $fh=IO::File->new(">$docdir/lon-status/loncstatus.txt");
  299:     my $now=time;
  300:     my $local=localtime($now);
  301:     print $fh "LONC status $local - parent $$\n\n";
  302: }
  303: 
  304: # -------------------------------------------------------------- Status setting
  305: 
  306: sub status {
  307:     my $what=shift;
  308:     my $now=time;
  309:     my $local=localtime($now);
  310:     $status=$local.': '.$what;
  311: }
  312: 
  313: 
  314: # ---------------------------------------------------- Fork once and dissociate
  315: 
  316: $fpid=fork;
  317: exit if $fpid;
  318: die "Couldn't fork: $!" unless defined ($fpid);
  319: 
  320: POSIX::setsid() or die "Can't start new session: $!";
  321: 
  322: # ------------------------------------------------------- Write our PID on disk
  323: 
  324: $execdir=$perlvar{'lonDaemons'};
  325: open (PIDSAVE,">$execdir/logs/lonc.pid");
  326: print PIDSAVE "$$\n";
  327: close(PIDSAVE);
  328: &logthis("<font color=red>CRITICAL: ---------- Starting ----------</font>");
  329: 
  330: # ----------------------------- Ignore signals generated during initial startup
  331: $SIG{HUP}=$SIG{USR1}='IGNORE';
  332: # ------------------------------------------------------- Now we are on our own
  333:     
  334: # Fork off our children, one for every server
  335: 
  336: &status("Forking ...");
  337: 
  338: foreach $thisserver (keys %hostip) {
  339:     if (&online($hostname{$thisserver})) {
  340:        make_new_child($thisserver);
  341:     }
  342: }
  343: 
  344: &logthis("Done starting initial servers");
  345: # ----------------------------------------------------- Install signal handlers
  346: 
  347: $SIG{CHLD} = \&REAPER;
  348: $SIG{INT}  = $SIG{TERM} = \&HUNTSMAN;
  349: $SIG{HUP}  = \&HUPSMAN;
  350: $SIG{USR1} = \&USRMAN;
  351: 
  352: # And maintain the population.
  353: while (1) {
  354:     &status("Sleeping");
  355:     sleep;                          # wait for a signal (i.e., child's death)
  356:                                     # See who died and start new one
  357:     &status("Woke up");
  358:     foreach $thisserver (keys %hostip) {
  359:         if (!$childpid{$thisserver}) {
  360: 	    if (($childatt{$thisserver}<$childmaxattempts) &&
  361:                 (&online($hostname{$thisserver}))) {
  362: 	       $childatt{$thisserver}++;
  363:                &logthis(
  364:    "<font color=yellow>INFO: Trying to reconnect for $thisserver "
  365:   ."($childatt{$thisserver} of $childmaxattempts attempts)</font>"); 
  366:                make_new_child($thisserver);
  367: 	   } else {
  368:                &logthis(
  369:    "<font color=yellow>INFO: Skipping $thisserver "
  370:   ."($childatt{$thisserver} of $childmaxattempts attempts)</font>");
  371:            } 
  372:                
  373:         }       
  374:     }
  375: }
  376: 
  377: 
  378: sub make_new_child {
  379:    
  380:     my $conserver=shift;
  381:     my $pid;
  382:     my $sigset;
  383:     &logthis("Attempting to start child for server $conserver");
  384:     # block signal for fork
  385:     $sigset = POSIX::SigSet->new(SIGINT);
  386:     sigprocmask(SIG_BLOCK, $sigset)
  387:         or die "Can't block SIGINT for fork: $!\n";
  388:     
  389:     die "fork: $!" unless defined ($pid = fork);
  390:     
  391:     if ($pid) {
  392:         # Parent records the child's birth and returns.
  393:         sigprocmask(SIG_UNBLOCK, $sigset)
  394:             or die "Can't unblock SIGINT for fork: $!\n";
  395:         $children{$pid} = $conserver;
  396:         $childpid{$conserver} = $pid;
  397:         return;
  398:     } else {
  399:         # Child can *not* return from this subroutine.
  400:         $SIG{INT} = 'DEFAULT';      # make SIGINT kill us as it did before
  401:         $SIG{USR1}= \&logstatus;
  402:    
  403:         # unblock signals
  404:         sigprocmask(SIG_UNBLOCK, $sigset)
  405:             or die "Can't unblock SIGINT for fork: $!\n";
  406: 
  407: # ----------------------------- This is the modified main program of non-forker
  408: 
  409: $port = "$perlvar{'lonSockDir'}/$conserver";
  410: 
  411: unlink($port);
  412: 
  413: # ---------------------------------------------------- Client to network server
  414: 
  415: &status("Opening TCP: $conserver");
  416: 
  417: unless (
  418:   $remotesock = IO::Socket::INET->new(PeerAddr => $hostip{$conserver},
  419:                                       PeerPort => $perlvar{'londPort'},
  420:                                       Proto    => "tcp",
  421:                                       Type     => SOCK_STREAM)
  422:    ) { 
  423:        my $st=120+int(rand(240));
  424:        &logthis(
  425: "<font color=blue>WARNING: Couldn't connect $conserver ($st secs): $@</font>");
  426:        sleep($st);
  427:        exit; 
  428:      };
  429: # ----------------------------------------------------------------- Init dialog
  430: 
  431: &status("Init dialogue: $conserver");
  432: 
  433:      $SIG{ALRM}=sub { die "timeout" };
  434:      $SIG{__DIE__}='DEFAULT';
  435:      eval {
  436:          alarm(60);
  437: print $remotesock "init\n";
  438: $answer=<$remotesock>;
  439: print $remotesock "$answer";
  440: $answer=<$remotesock>;
  441: chomp($answer);
  442:           alarm(0);
  443:      };
  444:      $SIG{ALRM}='DEFAULT';
  445:      $SIG{__DIE__}=\&catchexception;
  446:  
  447:      if ($@=~/timeout/) {
  448: 	 &logthis("Timed out during init: $conserver");
  449:          exit;
  450:      }
  451: 
  452: 
  453: &logthis("Init reply for $conserver: >$answer<");
  454: if ($answer ne 'ok') {
  455:        my $st=120+int(rand(240));
  456:        &logthis(
  457: "<font color=blue>WARNING: Init failed $conserver ($st secs)</font>");
  458:        sleep($st);
  459:        exit; 
  460: }
  461: sleep 5;
  462: &status("Ponging $conserver");
  463: print $remotesock "pong\n";
  464: $answer=<$remotesock>;
  465: chomp($answer);
  466: &logthis("Pong reply for $conserver: >$answer<");
  467: # ----------------------------------------------------------- Initialize cipher
  468: 
  469: &status("Initialize cipher: $conserver");
  470: print $remotesock "ekey\n";
  471: my $buildkey=<$remotesock>;
  472: my $key=$conserver.$perlvar{'lonHostID'};
  473: $key=~tr/a-z/A-Z/;
  474: $key=~tr/G-P/0-9/;
  475: $key=~tr/Q-Z/0-9/;
  476: $key=$key.$buildkey.$key.$buildkey.$key.$buildkey;
  477: $key=substr($key,0,32);
  478: my $cipherkey=pack("H32",$key);
  479: if ($cipher=new IDEA $cipherkey) {
  480:    &logthis("Secure connection initialized: $conserver");
  481: } else {
  482:    my $st=120+int(rand(240));
  483:    &logthis(
  484:      "<font color=blue>WARNING: ".
  485:      "Could not establish secure connection, $conserver ($st secs)!</font>");
  486:    sleep($st);
  487:    exit;
  488: }
  489: 
  490: # ----------------------------------------- We're online, send delayed messages
  491:     &status("Checking for delayed messages");
  492:     my @allbuffered;
  493:     my $path="$perlvar{'lonSockDir'}/delayed";
  494:     opendir(DIRHANDLE,$path);
  495:     @allbuffered=grep /\.$conserver$/, readdir DIRHANDLE;
  496:     closedir(DIRHANDLE);
  497:     my $dfname;
  498:     foreach (@allbuffered) {
  499:         &status("Sending delayed $conserver $_");
  500:         $dfname="$path/$_";
  501:         &logthis($dfname);
  502:         my $wcmd;
  503:         {
  504:          my $dfh=IO::File->new($dfname);
  505:          $cmd=<$dfh>;
  506:         }
  507:         chomp($cmd);
  508:         my $bcmd=$cmd;
  509:         if ($cmd =~ /^encrypt\:/) {
  510: 	    my $rcmd=$cmd;
  511:             $rcmd =~ s/^encrypt\://;
  512:             chomp($rcmd);
  513:             my $cmdlength=length($rcmd);
  514:             $rcmd.="         ";
  515:             my $encrequest='';
  516:             for (my $encidx=0;$encidx<=$cmdlength;$encidx+=8) {
  517:                 $encrequest.=
  518:                     unpack("H16",$cipher->encrypt(substr($rcmd,$encidx,8)));
  519:             }
  520:             $cmd="enc:$cmdlength:$encrequest\n";
  521:         }
  522:     $SIG{ALRM}=sub { die "timeout" };
  523:     $SIG{__DIE__}='DEFAULT';
  524:     eval {
  525:         alarm(60);
  526:         print $remotesock "$cmd\n";
  527:         $answer=<$remotesock>;
  528: 	chomp($answer);
  529:         alarm(0);
  530:     };
  531:     $SIG{ALRM}='DEFAULT';
  532:     $SIG{__DIE__}=\&catchexception;
  533: 
  534:         if (($answer ne '') && ($@!~/timeout/)) {
  535: 	    unlink("$dfname");
  536:             &logthis("Delayed $cmd to $conserver: >$answer<");
  537:             &logperm("S:$conserver:$bcmd");
  538:         }        
  539:     }
  540: 
  541: # ------------------------------------------------------- Listen to UNIX socket
  542: &status("Opening socket $conserver");
  543: unless (
  544:   $server = IO::Socket::UNIX->new(Local  => $port,
  545:                                   Type   => SOCK_STREAM,
  546:                                   Listen => 10 )
  547:    ) { 
  548:        my $st=120+int(rand(240));
  549:        &logthis(
  550:          "<font color=blue>WARNING: ".
  551:          "Can't make server socket $conserver ($st secs): $@</font>");
  552:        sleep($st);
  553:        exit; 
  554:      };
  555: 
  556: # -----------------------------------------------------------------------------
  557: 
  558: &logthis("<font color=green>$conserver online</font>");
  559: 
  560: # -----------------------------------------------------------------------------
  561: # begin with empty buffers
  562: %inbuffer  = ();
  563: %outbuffer = ();
  564: %ready     = ();
  565: 
  566: tie %ready, 'Tie::RefHash';
  567: 
  568: nonblock($server);
  569: $select = IO::Select->new($server);
  570: 
  571: # Main loop: check reads/accepts, check writes, check ready to process
  572: while (1) {
  573:     my $client;
  574:     my $rv;
  575:     my $data;
  576: 
  577:     # check for new information on the connections we have
  578: 
  579:     # anything to read or accept?
  580:     foreach $client ($select->can_read(0.1)) {
  581: 
  582:         if ($client == $server) {
  583:             # accept a new connection
  584:             &status("Accept new connection: $conserver");
  585:             $client = $server->accept();
  586:             $select->add($client);
  587:             nonblock($client);
  588:         } else {
  589:             # read data
  590:             $data = '';
  591:             $rv   = $client->recv($data, POSIX::BUFSIZ, 0);
  592: 
  593:             unless (defined($rv) && length $data) {
  594:                 # This would be the end of file, so close the client
  595:                 delete $inbuffer{$client};
  596:                 delete $outbuffer{$client};
  597:                 delete $ready{$client};
  598: 
  599:                 &status("Idle $conserver");
  600:                 $select->remove($client);
  601:                 close $client;
  602:                 next;
  603:             }
  604: 
  605:             $inbuffer{$client} .= $data;
  606: 
  607:             # test whether the data in the buffer or the data we
  608:             # just read means there is a complete request waiting
  609:             # to be fulfilled.  If there is, set $ready{$client}
  610:             # to the requests waiting to be fulfilled.
  611:             while ($inbuffer{$client} =~ s/(.*\n)//) {
  612:                 push( @{$ready{$client}}, $1 );
  613:             }
  614:         }
  615:     }
  616: 
  617:     # Any complete requests to process?
  618:     foreach $client (keys %ready) {
  619:         handle($client);
  620:     }
  621: 
  622:     # Buffers to flush?
  623:     foreach $client ($select->can_write(1)) {
  624:         # Skip this client if we have nothing to say
  625:         next unless exists $outbuffer{$client};
  626: 
  627:         $rv = $client->send($outbuffer{$client}, 0);
  628:         unless (defined $rv) {
  629:             # Whine, but move on.
  630:             &logthis("I was told I could write, but I can't.\n");
  631:             next;
  632:         }
  633:         $errno=$!;
  634:         if (($rv == length $outbuffer{$client}) ||
  635:             ($errno == POSIX::EWOULDBLOCK) || ($errno == 0)) {
  636:             substr($outbuffer{$client}, 0, $rv) = '';
  637:             delete $outbuffer{$client} unless length $outbuffer{$client};
  638:         } else {
  639:             # Couldn't write all the data, and it wasn't because
  640:             # it would have blocked.  Shutdown and move on.
  641: 
  642: 	    &logthis("Dropping data with ".$errno.": ".
  643:                      length($outbuffer{$client}).", $rv");
  644: 
  645:             delete $inbuffer{$client};
  646:             delete $outbuffer{$client};
  647:             delete $ready{$client};
  648: 
  649:             $select->remove($client);
  650:             close($client);
  651:             next;
  652:         }
  653:     }
  654: }
  655: }
  656: 
  657: # ------------------------------------------------------- End of make_new_child
  658: 
  659: # handle($socket) deals with all pending requests for $client
  660: sub handle {
  661:     # requests are in $ready{$client}
  662:     # send output to $outbuffer{$client}
  663:     my $client = shift;
  664:     my $request;
  665: 
  666:     foreach $request (@{$ready{$client}}) {
  667: # ============================================================= Process request
  668:         # $request is the text of the request
  669:         # put text of reply into $outbuffer{$client}
  670: # -----------------------------------------------------------------------------
  671:         if ($request =~ /^encrypt\:/) {
  672: 	    my $cmd=$request;
  673:             $cmd =~ s/^encrypt\://;
  674:             chomp($cmd);
  675:             my $cmdlength=length($cmd);
  676:             $cmd.="         ";
  677:             my $encrequest='';
  678:             for (my $encidx=0;$encidx<=$cmdlength;$encidx+=8) {
  679:                 $encrequest.=
  680:                     unpack("H16",$cipher->encrypt(substr($cmd,$encidx,8)));
  681:             }
  682:             $request="enc:$cmdlength:$encrequest\n";
  683:         }
  684: # --------------------------------------------------------------- Main exchange
  685:     $SIG{ALRM}=sub { die "timeout" };
  686:     $SIG{__DIE__}='DEFAULT';
  687:     eval {
  688:         alarm(300);
  689:         &status("Sending $conserver: $request");
  690:         print $remotesock "$request";
  691:         &status("Waiting for reply from $conserver: $request");
  692:         $answer=<$remotesock>;
  693:         &status("Received reply: $request");
  694:         alarm(0);
  695:     };
  696:     if ($@=~/timeout/) { 
  697:        $answer='';
  698:        &logthis(
  699:         "<font color=red>CRITICAL: Timeout $conserver: $request</font>");
  700:     }  
  701:     $SIG{ALRM}='DEFAULT';
  702:     $SIG{__DIE__}=\&catchexception;
  703: 
  704: 
  705:         if ($answer) {
  706: 	   if ($answer =~ /^enc/) {
  707:                my ($cmd,$cmdlength,$encinput)=split(/:/,$answer);
  708:                chomp($encinput);
  709: 	       $answer='';
  710:                for (my $encidx=0;$encidx<length($encinput);$encidx+=16) {
  711:                   $answer.=$cipher->decrypt(
  712:                    pack("H16",substr($encinput,$encidx,16))
  713:                   );
  714: 	       }
  715: 	      $answer=substr($answer,0,$cmdlength);
  716: 	      $answer.="\n";
  717: 	   }
  718:            $outbuffer{$client} .= $answer;
  719:         } else {
  720:            $outbuffer{$client} .= "con_lost\n";
  721:         }
  722: 
  723: # ===================================================== Done processing request
  724:     }
  725:     delete $ready{$client};
  726:     &status("Completed $conserver: $request");
  727: # -------------------------------------------------------------- End non-forker
  728: }
  729: # ---------------------------------------------------------- End make_new_child
  730: }
  731: 
  732: # nonblock($socket) puts socket into nonblocking mode
  733: sub nonblock {
  734:     my $socket = shift;
  735:     my $flags;
  736: 
  737:     
  738:     $flags = fcntl($socket, F_GETFL, 0)
  739:             or die "Can't get flags for socket: $!\n";
  740:     fcntl($socket, F_SETFL, $flags | O_NONBLOCK)
  741:             or die "Can't make socket nonblocking: $!\n";
  742: }
  743: 
  744: # ----------------------------------- POD (plain old documentation, CPAN style)
  745: 
  746: =head1 NAME
  747: 
  748: lonc - LON TCP-MySQL-Server Daemon for handling database requests.
  749: 
  750: =head1 SYNOPSIS
  751: 
  752: Should only be run as user=www.  This is a command-line script which
  753: is invoked by loncron.
  754: 
  755: =head1 DESCRIPTION
  756: 
  757: Provides persistent TCP connections to the other servers in the network
  758: through multiplexed domain sockets
  759: 
  760:  PID in subdir logs/lonc.pid
  761:  kill kills
  762:  HUP restarts
  763:  USR1 tries to open connections again
  764: 
  765: =head1 README
  766: 
  767: Not yet written.
  768: 
  769: =head1 PREREQUISITES
  770: 
  771: POSIX
  772: IO::Socket
  773: IO::Select
  774: IO::File
  775: Socket
  776: Fcntl
  777: Tie::RefHash
  778: Crypt::IDEA
  779: 
  780: =head1 COREQUISITES
  781: 
  782: =head1 OSNAMES
  783: 
  784: linux
  785: 
  786: =head1 SCRIPT CATEGORIES
  787: 
  788: Server/Process
  789: 
  790: =cut

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