File:  [LON-CAPA] / loncom / loncron
Revision 1.103.2.12: download - view: text, annotated - select for diffs
Tue Feb 2 21:27:34 2021 UTC (3 years, 4 months ago) by raeburn
Branches: version_2_11_X
CVS tags: version_2_11_4_uiuc, version_2_11_4_msu, version_2_11_4, version_2_11_3_uiuc, version_2_11_3_msu, version_2_11_3
Diff to branchpoint 1.103: preferred, unified
- For 2.11
  Correction to 1.103.2.11 backport.

    1: #!/usr/bin/perl
    2: 
    3: # Housekeeping program, started by cron, loncontrol and loncron.pl
    4: #
    5: # $Id: loncron,v 1.103.2.12 2021/02/02 21:27:34 raeburn Exp $
    6: #
    7: # Copyright Michigan State University Board of Trustees
    8: #
    9: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
   10: #
   11: # LON-CAPA is free software; you can redistribute it and/or modify
   12: # it under the terms of the GNU General Public License as published by
   13: # the Free Software Foundation; either version 2 of the License, or
   14: # (at your option) any later version.
   15: #
   16: # LON-CAPA is distributed in the hope that it will be useful,
   17: # but WITHOUT ANY WARRANTY; without even the implied warranty of
   18: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   19: # GNU General Public License for more details.
   20: #
   21: # You should have received a copy of the GNU General Public License
   22: # along with LON-CAPA; if not, write to the Free Software
   23: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   24: #
   25: # /home/httpd/html/adm/gpl.txt
   26: #
   27: # http://www.lon-capa.org/
   28: #
   29: 
   30: $|=1;
   31: use strict;
   32: 
   33: use lib '/home/httpd/lib/perl/';
   34: use LONCAPA::Configuration;
   35: use LONCAPA::Checksumming;
   36: use LONCAPA;
   37: use Apache::lonnet;
   38: use Apache::loncommon;
   39: 
   40: use LWP::UserAgent();
   41: use HTTP::Request();
   42: use IO::File;
   43: use IO::Socket;
   44: use HTML::Entities;
   45: use Getopt::Long;
   46: use GDBM_File qw(GDBM_READER);
   47: use Storable qw(thaw);
   48: use File::ReadBackwards;
   49: use File::Copy;
   50: use Sys::Hostname::FQDN();
   51: 
   52: #globals
   53: use vars qw (%perlvar %simplestatus $errors $warnings $notices $totalcount);
   54: 
   55: my $statusdir="/home/httpd/html/lon-status";
   56: 
   57: 
   58: # --------------------------------------------------------- Output error status
   59: 
   60: sub log {
   61:     my $fh=shift;
   62:     if ($fh) {	print $fh @_  }
   63: }
   64: 
   65: sub errout {
   66:    my $fh=shift;
   67:    &log($fh,(<<ENDERROUT));
   68:      <table border="2" bgcolor="#CCCCCC">
   69:      <tr><td>Notices</td><td>$notices</td></tr>
   70:      <tr><td>Warnings</td><td>$warnings</td></tr>
   71:      <tr><td>Errors</td><td>$errors</td></tr>
   72:      </table><p><a href="#top">Top</a></p>
   73: ENDERROUT
   74: }
   75: 
   76: sub rotate_logfile {
   77:     my ($file,$fh,$description) = @_;
   78:     my $size=(stat($file))[7];
   79:     if ($size>40000) {
   80: 	&log($fh,"<p>Rotating $description ...</p>");
   81: 	rename("$file.2","$file.3");
   82: 	rename("$file.1","$file.2");
   83: 	rename("$file","$file.1");
   84:     }
   85: }
   86: 
   87: sub start_daemon {
   88:     my ($fh,$daemon,$pidfile,$args) = @_;
   89:     my $progname=$daemon;
   90:     if ($daemon eq 'lonc') {
   91: 	$progname='loncnew';
   92:     }
   93:     my $error_fname="$perlvar{'lonDaemons'}/logs/${daemon}_errors";
   94:     &rotate_logfile($error_fname,$fh,'error logs');
   95:     if ($daemon eq 'lonc') {
   96: 	&clean_sockets($fh);
   97:     }
   98:     system("$perlvar{'lonDaemons'}/$progname 2>$perlvar{'lonDaemons'}/logs/${daemon}_errors");
   99:     sleep 1;
  100:     if (-e $pidfile) {
  101: 	&log($fh,"<p>Seems like it started ...</p>");
  102: 	my $lfh=IO::File->new("$pidfile");
  103: 	my $daemonpid=<$lfh>;
  104: 	chomp($daemonpid);
  105: 	if ($daemonpid =~ /^\d+$/ && kill 0 => $daemonpid) {
  106: 	    return 1;
  107: 	} else {
  108: 	    return 0;
  109: 	}
  110:     }
  111:     &log($fh,"<p>Seems like that did not work!</p>");
  112:     $errors++;
  113:     return 0;
  114: }
  115: 
  116: sub checkon_daemon {
  117:     my ($fh,$daemon,$maxsize,$send,$args)=@_;
  118: 
  119:     my $result;
  120:     &log($fh,'<hr /><a name="'.$daemon.'" /><h2>'.$daemon.'</h2><h3>Log</h3><p style="white-space: pre;"><tt>');
  121:     printf("%-15s ",$daemon);
  122:     if ($fh) {
  123:         if (-e "$perlvar{'lonDaemons'}/logs/$daemon.log"){
  124: 	    if (open(DFH,"tail -n25 $perlvar{'lonDaemons'}/logs/$daemon.log|")) {
  125: 	        while (my $line=<DFH>) {
  126: 	            &log($fh,"$line");
  127: 	            if ($line=~/INFO/) { $notices++; }
  128: 	            if ($line=~/WARNING/) { $notices++; }
  129: 	            if ($line=~/CRITICAL/) { $warnings++; }
  130: 	        }
  131: 	        close (DFH);
  132:             }
  133:         }
  134:         &log($fh,"</tt></p>");
  135:     }
  136: 
  137:     my $pidfile="$perlvar{'lonDaemons'}/logs/$daemon.pid";
  138:  
  139:     my $restartflag=1;
  140:     my $daemonpid;
  141:     if (-e $pidfile) {
  142: 	my $lfh=IO::File->new("$pidfile");
  143: 	$daemonpid=<$lfh>;
  144: 	chomp($daemonpid);
  145: 	if ($daemonpid =~ /^\d+$/ && kill 0 => $daemonpid) {
  146: 	    &log($fh,"<h3>$daemon at pid $daemonpid responding");
  147: 	    if ($send) { &log($fh,", sending $send"); }
  148: 	    &log($fh,"</h3>");
  149: 	    if ($send eq 'USR1') { kill USR1 => $daemonpid; }
  150: 	    if ($send eq 'USR2') { kill USR2 => $daemonpid; }
  151: 	    $restartflag=0;
  152: 	    if ($send eq 'USR2') {
  153: 		$result = 'reloaded';
  154: 		print "reloaded\n";
  155: 	    } else {
  156: 		$result = 'running';
  157: 		print "running\n";
  158: 	    }
  159: 	} else {
  160: 	    $errors++;
  161: 	    &log($fh,"<h3>$daemon at pid $daemonpid not responding</h3>");
  162: 	    $restartflag=1;
  163: 	    &log($fh,"<h3>Decided to clean up stale .pid file and restart $daemon</h3>");
  164: 	}
  165:     }
  166:     if ($restartflag==1) {
  167: 	$simplestatus{$daemon}='off';
  168: 	$errors++;
  169: 	my $kadaemon=$daemon;
  170: 	if ($kadaemon eq 'lonmemcached') { $kadaemon='memcached'; }
  171: 	&log($fh,'<br /><font color="red">Killall '.$daemon.': '.
  172: 	    `killall $kadaemon 2>&1`.' - ');
  173: 	sleep 1;
  174: 	&log($fh,unlink($pidfile).' - '.
  175: 	    `killall -9 $kadaemon 2>&1`.
  176: 	    '</font><br />');
  177: 	&log($fh,"<h3>$daemon not running, trying to start</h3>");
  178: 
  179: 	if (&start_daemon($fh,$daemon,$pidfile,$args)) {
  180: 	    &log($fh,"<h3>$daemon at pid $daemonpid responding</h3>");
  181: 	    $simplestatus{$daemon}='restarted';
  182: 	    $result = 'started';
  183: 	    print "started\n";
  184: 	} else {
  185: 	    $errors++;
  186: 	    &log($fh,"<h3>$daemon at pid $daemonpid not responding</h3>");
  187: 	    &log($fh,"<p>Give it one more try ...</p>");
  188: 	    print " ";
  189: 	    if (&start_daemon($fh,$daemon,$pidfile,$args)) {
  190: 		&log($fh,"<h3>$daemon at pid $daemonpid responding</h3>");
  191: 		$simplestatus{$daemon}='restarted';
  192: 		$result = 'started';
  193: 		print "started\n";
  194: 	    } else {
  195: 		$result = 'failed';
  196: 		print " failed\n";
  197: 		$simplestatus{$daemon}='failed';
  198: 		$errors++; $errors++;
  199: 		&log($fh,"<h3>$daemon at pid $daemonpid not responding</h3>");
  200: 		&log($fh,"<p>Unable to start $daemon</p>");
  201: 	    }
  202: 	}
  203:         if ($fh) {
  204: 	    if (-e "$perlvar{'lonDaemons'}/logs/$daemon.log"){
  205: 	        &log($fh,"<p><pre>");
  206: 	        if (open(DFH,"tail -n100 $perlvar{'lonDaemons'}/logs/$daemon.log|")) {
  207: 	            while (my $line=<DFH>) { 
  208: 		        &log($fh,"$line");
  209: 		        if ($line=~/WARNING/) { $notices++; }
  210: 		        if ($line=~/CRITICAL/) { $notices++; }
  211: 	            }
  212: 	            close (DFH);
  213:                 }
  214: 	        &log($fh,"</pre></p>");
  215: 	    }
  216:         }
  217:     }
  218: 
  219:     my $fname="$perlvar{'lonDaemons'}/logs/$daemon.log";
  220:     &rotate_logfile($fname,$fh,'logs');
  221: 
  222:     &errout($fh);
  223:     return $result;
  224: }
  225: 
  226: # --------------------------------------------------------------------- Machine
  227: sub log_machine_info {
  228:     my ($fh)=@_;
  229:     &log($fh,'<hr /><a name="machine" /><h2>Machine Information</h2>');
  230:     &log($fh,"<h3>loadavg</h3>");
  231: 
  232:     my $cpucount;
  233:     if (open(PIPE,"lscpu |grep '^CPU(s)' 2>&1 |")) {
  234:         my $info = <PIPE>;
  235:         chomp($info);
  236:         ($cpucount) = ($info =~ /^\QCPU(s):\E\s+(\d+)$/);
  237:         close(PIPE);
  238:     }
  239:     if (!$cpucount) {
  240:         $cpucount = 1;
  241:     }
  242:     my %loadtarget = (
  243:                         error => 4.0*$cpucount,
  244:                         warn  => 2.0*$cpucount,
  245:                         note  => 1.0*$cpucount,
  246:                      );
  247:     open (LOADAVGH,"/proc/loadavg");
  248:     my $loadavg=<LOADAVGH>;
  249:     close (LOADAVGH);
  250: 
  251:     &log($fh,"<tt>$loadavg</tt>");
  252: 
  253:     my @parts=split(/\s+/,$loadavg);
  254:     if ($parts[1]>$loadtarget{'error'}) {
  255: 	$errors++;
  256:     } elsif ($parts[1]>$loadtarget{'warn'}) {
  257: 	$warnings++;
  258:     } elsif ($parts[1]>$loadtarget{'note'}) {
  259: 	$notices++;
  260:     }
  261: 
  262:     &log($fh,"<h3>df</h3>");
  263:     &log($fh,"<pre>");
  264: 
  265:     open (DFH,"df|");
  266:     while (my $line=<DFH>) {
  267: 	&log($fh,&encode_entities($line,'<>&"'));
  268: 	@parts=split(/\s+/,$line);
  269: 	my $usage=$parts[4];
  270: 	$usage=~s/\W//g;
  271: 	if ($usage>90) {
  272: 	    $warnings++;
  273: 	    $notices++;
  274: 	} elsif ($usage>80) {
  275: 	    $warnings++;
  276: 	} elsif ($usage>60) {
  277: 	    $notices++;
  278: 	}
  279: 	if ($usage>95) { $warnings++; $warnings++; $simplestatus{'diskfull'}++; }
  280:     }
  281:     close (DFH);
  282:     &log($fh,"</pre>");
  283: 
  284: 
  285:     &log($fh,"<h3>ps</h3>");
  286:     &log($fh,"<pre>");
  287:     my $psproc=0;
  288: 
  289:     open (PSH,"ps aux --cols 140 |");
  290:     while (my $line=<PSH>) {
  291: 	&log($fh,&encode_entities($line,'<>&"'));
  292: 	$psproc++;
  293:     }
  294:     close (PSH);
  295:     &log($fh,"</pre>");
  296: 
  297:     if ($psproc>200) { $notices++; }
  298:     if ($psproc>250) { $notices++; }
  299: 
  300:     &log($fh,"<h3>distprobe</h3>");
  301:     &log($fh,"<pre>");
  302:     &log($fh,&encode_entities(&LONCAPA::distro(),'<>&"'));
  303:     &log($fh,"</pre>");
  304: 
  305:     &errout($fh);
  306: }
  307: 
  308: sub start_logging {
  309:     my $fh=IO::File->new(">$statusdir/newstatus.html");
  310:     my %simplestatus=();
  311:     my $now=time;
  312:     my $date=localtime($now);
  313: 
  314: 
  315:     &log($fh,(<<ENDHEADERS));
  316: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  317: <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  318: <head>
  319: <title>LON Status Report $perlvar{'lonHostID'}</title>
  320: <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  321: </head>
  322: <body bgcolor="#AAAAAA">
  323: <a name="top" />
  324: <h1>LON Status Report $perlvar{'lonHostID'}</h1>
  325: <h2>$date ($now)</h2>
  326: <ol>
  327: <li><a href="#configuration">Configuration</a></li>
  328: <li><a href="#machine">Machine Information</a></li>
  329: <li><a href="#tmp">Temporary Files</a></li>
  330: <li><a href="#tokens">Session Tokens</a></li>
  331: <li><a href="#webdav">WebDAV Session Tokens</a></li>
  332: <li><a href="#httpd">httpd</a></li>
  333: <li><a href="#lonsql">lonsql</a></li>
  334: <li><a href="#lond">lond</a></li>
  335: <li><a href="#lonc">lonc</a></li>
  336: <li><a href="#lonnet">lonnet</a></li>
  337: <li><a href="#connections">Connections</a></li>
  338: <li><a href="#delayed">Delayed Messages</a></li>
  339: <li><a href="#errcount">Error Count</a></li>
  340: </ol>
  341: <hr />
  342: <a name="configuration" />
  343: <h2>Configuration</h2>
  344: <h3>PerlVars</h3>
  345: <table border="2">
  346: ENDHEADERS
  347: 
  348:     foreach my $varname (sort(keys(%perlvar))) {
  349: 	&log($fh,"<tr><td>$varname</td><td>".
  350: 	     &encode_entities($perlvar{$varname},'<>&"')."</td></tr>\n");
  351:     }
  352:     &log($fh,"</table><h3>Hosts</h3><table border='2'>");
  353:     my %hostname = &Apache::lonnet::all_hostnames();
  354:     foreach my $id (sort(keys(%hostname))) {
  355: 	my $role = (&Apache::lonnet::is_library($id) ? 'library'
  356: 		                                     : 'access');
  357: 	&log($fh,
  358: 	    "<tr><td>$id</td><td>".&Apache::lonnet::host_domain($id).
  359: 	    "</td><td>".$role.
  360: 	    "</td><td>".&Apache::lonnet::hostname($id)."</td></tr>\n");
  361:     }
  362:     &log($fh,"</table><h3>Spare Hosts</h3>");
  363:     if (keys(%Apache::lonnet::spareid) > 0) {
  364:         &log($fh,"<ul>");
  365:         foreach my $type (sort(keys(%Apache::lonnet::spareid))) {
  366: 	    &log($fh,"<li>$type\n<ol>");
  367: 	    foreach my $id (@{ $Apache::lonnet::spareid{$type} }) {
  368: 	        &log($fh,"<li>$id</li>\n");
  369: 	    }
  370: 	    &log($fh,"</ol>\n</li>\n");
  371:         }
  372:         &log($fh,"</ul>\n");
  373:     } else {
  374:         &log($fh,"No spare hosts specified<br />\n");
  375:     }
  376:     return $fh;
  377: }
  378: 
  379: # --------------------------------------------------------------- clean out tmp
  380: sub clean_tmp {
  381:     my ($fh)=@_;
  382:     &log($fh,'<hr /><a name="tmp" /><h2>Temporary Files</h2>');
  383:     my ($cleaned,$old,$removed) = (0,0,0);
  384:     my %errors = (
  385:                      dir       => [],
  386:                      file      => [],
  387:                      failopen  => [],
  388:                  );
  389:     my %error_titles = (
  390:                          dir       => 'failed to remove empty directory:',
  391:                          file      => 'failed to unlike stale file',
  392:                          failopen  => 'failed to open file or directory'
  393:                        );
  394:     ($cleaned,$old,$removed) = &recursive_clean_tmp('',$cleaned,$old,$removed,\%errors);
  395:     &log($fh,"Cleaned up: ".$cleaned." files; removed: $removed empty directories; (found: $old old checkout tokens)");
  396:     foreach my $key (sort(keys(%errors))) {
  397:         if (ref($errors{$key}) eq 'ARRAY') {
  398:             if (@{$errors{$key}} > 0) {
  399:                 &log($fh,"Error during cleanup ($error_titles{$key}):<ul><li>".
  400:                      join('</li><li><tt>',@{$errors{$key}}).'</tt></li></ul><br />');
  401:             }
  402:         }
  403:     }
  404: }
  405: 
  406: sub recursive_clean_tmp {
  407:     my ($subdir,$cleaned,$old,$removed,$errors) = @_;
  408:     my $base = "$perlvar{'lonDaemons'}/tmp";
  409:     my $path = $base;
  410:     next if ($subdir =~ m{\.\./});
  411:     next unless (ref($errors) eq 'HASH');
  412:     unless ($subdir eq '') {
  413:         $path .= '/'.$subdir;
  414:     }
  415:     if (opendir(my $dh,"$path")) {
  416:         while (my $file = readdir($dh)) {
  417:             next if ($file =~ /^\.\.?$/);
  418:             my $fname = "$path/$file";
  419:             if (-d $fname) {
  420:                 my $innerdir;
  421:                 if ($subdir eq '') {
  422:                     $innerdir = $file;
  423:                 } else {
  424:                     $innerdir = $subdir.'/'.$file;
  425:                 }
  426:                 ($cleaned,$old,$removed) = 
  427:                      &recursive_clean_tmp($innerdir,$cleaned,$old,$removed,$errors);
  428:                 my @doms = &Apache::lonnet::current_machine_domains();
  429: 
  430:                 if (open(my $dirhandle,$fname)) {
  431:                     unless (($innerdir eq 'helprequests') ||
  432:                             (($innerdir =~ /^addcourse/) && ($innerdir !~ m{/\d+$}))) {
  433:                         my @contents = grep {!/^\.\.?$/} readdir($dirhandle);
  434:                                       join('&&',@contents)."\n";
  435:                         if (scalar(grep {!/^\.\.?$/} readdir($dirhandle)) == 0) {
  436:                             closedir($dirhandle);
  437:                             if ($fname =~ m{^\Q$perlvar{'lonDaemons'}\E/tmp/}) {
  438:                                 if (rmdir($fname)) {
  439:                                     $removed ++;
  440:                                 } elsif (ref($errors->{dir}) eq 'ARRAY') {
  441:                                     push(@{$errors->{dir}},$fname);
  442:                                 }
  443:                             }
  444:                         }
  445:                     } else {
  446:                         closedir($dirhandle);
  447:                     }
  448:                 }
  449:             } else {
  450:                 my ($dev,$ino,$mode,$nlink,
  451:                     $uid,$gid,$rdev,$size,
  452:                     $atime,$mtime,$ctime,
  453:                     $blksize,$blocks)=stat($fname);
  454:                 my $now=time;
  455:                 my $since=$now-$mtime;
  456:                 if ($since>$perlvar{'lonExpire'}) {
  457:                     if ($subdir eq '') {
  458:                         my $line='';
  459:                         if ($fname =~ /\.db$/) {
  460:                             if (unlink($fname)) {
  461:                                 $cleaned++;
  462:                             } elsif (ref($errors->{file}) eq 'ARRAY') {
  463:                                 push(@{$errors->{file}},$fname);
  464:                             }
  465:                         } elsif (open(PROBE,$fname)) {
  466:                             my $line='';
  467:                             $line=<PROBE>;
  468:                             close(PROBE);
  469:                             if ($line=~/^CHECKOUTTOKEN\&/) {
  470:                                 if ($since>365*$perlvar{'lonExpire'}) {
  471:                                     if (unlink($fname)) {
  472:                                         $cleaned++; 
  473:                                     } elsif (ref($errors->{file}) eq 'ARRAY') {
  474:                                         push(@{$errors->{file}},$fname);
  475:                                     }
  476:                                 } else {
  477:                                     $old++;
  478:                                 }
  479:                             } else {
  480:                                 if (unlink($fname)) {
  481:                                     $cleaned++;
  482:                                 } elsif (ref($errors->{file}) eq 'ARRAY') {
  483:                                     push(@{$errors->{file}},$fname);
  484:                                 }
  485:                             }
  486:                         } elsif (ref($errors->{failopen}) eq 'ARRAY') {
  487:                             push(@{$errors->{failopen}},$fname);
  488:                         }
  489:                     } else {
  490:                         if (unlink($fname)) {
  491:                             $cleaned++;
  492:                         } elsif (ref($errors->{file}) eq 'ARRAY') {
  493:                             push(@{$errors->{file}},$fname);
  494:                         }
  495:                     }
  496:                 }
  497:             }
  498:         }
  499:         closedir($dh);
  500:     } elsif (ref($errors->{failopen}) eq 'ARRAY') {
  501:         push(@{$errors->{failopen}},$path);
  502:     }
  503:     return ($cleaned,$old,$removed);
  504: }
  505: 
  506: # ------------------------------------------------------------ clean out lonIDs
  507: sub clean_lonIDs {
  508:     my ($fh)=@_;
  509:     &log($fh,'<hr /><a name="tokens" /><h2>Session Tokens</h2>');
  510:     my $cleaned=0;
  511:     my $active=0;
  512:     while (my $fname=<$perlvar{'lonIDsDir'}/*>) {
  513:         my $now=time;
  514:         if (-l $fname) {
  515:             my $linkfname = readlink($fname);
  516:             if (-f $linkfname) {
  517:                 if ($linkfname =~ m{^$perlvar{'lonIDsDir'}/[^/]+\.id$}) {
  518:                     my @data = stat($linkfname);
  519:                     my $mtime = $data[9];
  520:                     my $since=$now-$mtime;
  521:                     if ($since>$perlvar{'lonExpire'}) {
  522:                         if (unlink($linkfname)) {
  523:                             $cleaned++;
  524:                             &log($fh,"Unlinking $linkfname<br />");
  525:                             unlink($fname);
  526:                         }
  527:                     }
  528:                 }
  529:             } else {
  530:                unlink($fname);
  531:             }
  532:         } elsif (-f $fname) {
  533:             my @data = stat($fname);
  534:             my $mtime = $data[9];
  535:             my $since=$now-$mtime;
  536:             if ($since>$perlvar{'lonExpire'}) {
  537:                 if (unlink($fname)) {
  538:                     $cleaned++;
  539:                     &log($fh,"Unlinking $fname<br />");
  540:                 }
  541:             } else {
  542:                 $active++;
  543:             }
  544:         }
  545:     }
  546:     &log($fh,"<p>Cleaned up ".$cleaned." stale session token(s).</p>");
  547:     &log($fh,"<h3>$active open session(s)</h3>");
  548: }
  549: 
  550: # -------------------------------------------------------- clean out balanceIDs
  551: 
  552: sub clean_balanceIDs {
  553:     my ($fh)=@_;
  554:     &log($fh,'<hr /><a name="balcookies" /><h2>Session Tokens</h2>');
  555:     my $cleaned=0;
  556:     my $active=0;
  557:     if (-d $perlvar{'lonBalanceDir'}) {
  558:         while (my $fname=<$perlvar{'lonBalanceDir'}/*.id>) {
  559:             my ($dev,$ino,$mode,$nlink,
  560:                 $uid,$gid,$rdev,$size,
  561:                 $atime,$mtime,$ctime,
  562:                 $blksize,$blocks)=stat($fname);
  563:             my $now=time;
  564:             my $since=$now-$mtime;
  565:             if ($since>$perlvar{'lonExpire'}) {
  566:                 $cleaned++;
  567:                 &log($fh,"Unlinking $fname<br />");
  568:                 unlink("$fname");
  569:             } else {
  570:                 $active++;
  571:             }
  572:         }
  573:     }
  574:     &log($fh,"<p>Cleaned up ".$cleaned." stale balancer files</p>");
  575:     &log($fh,"<h3>$active unexpired balancer files</h3>");
  576: }
  577: 
  578: # ------------------------------------------------ clean out webDAV Session IDs
  579: sub clean_webDAV_sessionIDs {
  580:     my ($fh)=@_;
  581:     if ($perlvar{'lonRole'} eq 'library') {
  582:         &log($fh,'<hr /><a name="webdav" /><h2>WebDAV Session Tokens</h2>');
  583:         my $cleaned=0;
  584:         my $active=0;
  585:         my $now = time;
  586:         if (-d $perlvar{'lonDAVsessDir'}) {
  587:             while (my $fname=<$perlvar{'lonDAVsessDir'}/*>) {
  588:                 my @stats = stat($fname);
  589:                 my $since=$now-$stats[9];
  590:                 if ($since>$perlvar{'lonExpire'}) {
  591:                     $cleaned++;
  592:                     &log($fh,"Unlinking $fname<br />");
  593:                     unlink("$fname");
  594:                 } else {
  595:                     $active++;
  596:                 }
  597:             }
  598:             &log($fh,"<p>Cleaned up ".$cleaned." stale webDAV session token(s).</p>");
  599:             &log($fh,"<h3>$active open webDAV session(s)</h3>");
  600:         }
  601:     }
  602: }
  603: 
  604: # ----------------------------------------------------------- clean out sockets
  605: sub clean_sockets {
  606:     my ($fh)=@_;
  607:     my $cleaned=0;
  608:     opendir(SOCKETS,$perlvar{'lonSockDir'});
  609:     while (my $fname=readdir(SOCKETS)) {
  610: 	next if (-d $fname 
  611: 		 || $fname=~/(mysqlsock|maximasock|rsock|\Q$perlvar{'lonSockDir'}\E)/);
  612: 	$cleaned++;
  613: 	&log($fh,"Unlinking $fname<br />");
  614: 	unlink("/home/httpd/sockets/$fname");
  615:     }
  616:     &log($fh,"<p>Cleaned up ".$cleaned." stale sockets.</p>");
  617: }
  618: 
  619: 
  620: # ----------------------------------------------------------------------- httpd
  621: sub check_httpd_logs {
  622:     my ($fh)=@_;
  623:     if (open(PIPE,"./lchttpdlogs|")) {
  624:         while (my $line=<PIPE>) {
  625:             &log($fh,$line);
  626:             if ($line=~/\[error\]/) { $notices++; }
  627:         }
  628:         close(PIPE);
  629:     }
  630:     &errout($fh);
  631: }
  632: 
  633: # ---------------------------------------------------------------------- lonnet
  634: 
  635: sub rotate_lonnet_logs {
  636:     my ($fh)=@_;
  637:     &log($fh,'<hr /><a name="lonnet" /><h2>lonnet</h2><h3>Temp Log</h3><pre>');
  638:     print "Checking logs.\n";
  639:     if (-e "$perlvar{'lonDaemons'}/logs/lonnet.log"){
  640: 	open (DFH,"tail -n50 $perlvar{'lonDaemons'}/logs/lonnet.log|");
  641: 	while (my $line=<DFH>) {
  642: 	    &log($fh,&encode_entities($line,'<>&"'));
  643: 	}
  644: 	close (DFH);
  645:     }
  646:     &log($fh,"</pre><h3>Perm Log</h3><pre>");
  647: 
  648:     if (-e "$perlvar{'lonDaemons'}/logs/lonnet.perm.log") {
  649: 	open(DFH,"tail -n10 $perlvar{'lonDaemons'}/logs/lonnet.perm.log|");
  650: 	while (my $line=<DFH>) {
  651: 	    &log($fh,&encode_entities($line,'<>&"'));
  652: 	}
  653: 	close (DFH);
  654:     } else { &log($fh,"No perm log\n") }
  655: 
  656:     my $fname="$perlvar{'lonDaemons'}/logs/lonnet.log";
  657:     &rotate_logfile($fname,$fh,'lonnet log');
  658: 
  659:     &log($fh,"</pre>");
  660:     &errout($fh);
  661: }
  662: 
  663: sub rotate_other_logs {
  664:     my ($fh) = @_;
  665:     my %logs = (
  666:                   autoenroll          => 'Auto Enroll log',
  667:                   autocreate          => 'Create Course log',
  668:                   searchcat           => 'Search Cataloguing log',
  669:                   autoupdate          => 'Auto Update log',
  670:                   refreshcourseids_db => 'Refresh CourseIDs db log',
  671:                );
  672:     foreach my $item (keys(%logs)) {
  673:         my $fname=$perlvar{'lonDaemons'}.'/logs/'.$item.'.log';
  674:         &rotate_logfile($fname,$fh,$logs{$item});
  675:     }
  676: }
  677: 
  678: # ----------------------------------------------------------------- Connections
  679: sub test_connections {
  680:     my ($fh)=@_;
  681:     &log($fh,'<hr /><a name="connections" /><h2>Connections</h2>');
  682:     print "Testing connections.\n";
  683:     &log($fh,"<table border='2'>");
  684:     my ($good,$bad)=(0,0);
  685:     my %hostname = &Apache::lonnet::all_hostnames();
  686:     foreach my $tryserver (sort(keys(%hostname))) {
  687: 	print(".");
  688: 	my $result;
  689: 	my $answer=&Apache::lonnet::reply("ping",$tryserver);
  690: 	if ($answer eq "$tryserver:$perlvar{'lonHostID'}") {
  691: 	    $result="<b>ok</b>";
  692: 	    $good++;
  693: 	} else {
  694: 	    $result=$answer;
  695: 	    $warnings++;
  696: 	    if ($answer eq 'con_lost') {
  697: 		$bad++;
  698: 		$warnings++;
  699: 	    } else {
  700: 		$good++; #self connection
  701: 	    }
  702: 	}
  703: 	if ($answer =~ /con_lost/) { print(" $tryserver down\n"); }
  704: 	&log($fh,"<tr><td>$tryserver</td><td>$result</td></tr>\n");
  705:     }
  706:     &log($fh,"</table>");
  707:     print "\n$good good, $bad bad connections\n";
  708:     &errout($fh);
  709: }
  710: 
  711: 
  712: # ------------------------------------------------------------ Delayed messages
  713: sub check_delayed_msg {
  714:     my ($fh,$weightsref,$exclusionsref)=@_;
  715:     &log($fh,'<hr /><a name="delayed" /><h2>Delayed Messages</h2>');
  716:     print "Checking buffers.\n";
  717:     
  718:     &log($fh,'<h3>Scanning Permanent Log</h3>');
  719: 
  720:     my $unsend=0;
  721:     my $ignored=0;
  722: 
  723:     my %hostname = &Apache::lonnet::all_hostnames();
  724:     my $numhosts = scalar(keys(%hostname));
  725:     my $checkbackwards = 0;
  726:     my $checkfrom = 0;
  727:     my $checkexcluded = 0;
  728:     my (%bymachine,%weights,%exclusions,%serverhomes);
  729:     if (ref($weightsref) eq 'HASH') {
  730:         %weights = %{$weightsref};
  731:     }
  732:     if (ref($exclusionsref) eq 'HASH') {
  733:         %exclusions = %{$exclusionsref};
  734:         if (keys(%exclusions)) {
  735:             $checkexcluded = 1;
  736:             %serverhomes = &read_serverhomeIDs();
  737:         }
  738:     }
  739: 
  740: #
  741: # For LON-CAPA 1.2.0 to 2.1.3 (release dates: 8/31/2004 and 3/31/2006) any
  742: # entry logged in lonnet.perm.log for completion of a delayed (critical)
  743: # transaction lacked the hostID for the remote node to which the command
  744: # to be completed was sent.
  745: #
  746: # Because of this, exclusion of items in lonnet.perm.log for nodes which are
  747: # no longer part of the cluster from adding to the overall "unsend" count
  748: # needs additional effort besides the changes made in loncron rev. 1.105.
  749: #
  750: # For "S" (completion) events logging in LON-CAPA 1.2.0 through 2.1.3 included
  751: # "LondTransaction=HASH(hexadecimal)->getClient() :$cmd, where the hexadecimal
  752: # is a memory location, and $cmd is the command sent to the remote node.
  753: #
  754: # Starting with 2.2.0 (released 8/21/2006) logging for "S" (completion) events
  755: # had sethost:$host_id:$cmd after LondTransaction=HASH(hexadecimal)->getClient()
  756: #
  757: # Starting with 2.4.1 (released 6/13/2007) logging for "S" replaced echoing the
  758: # getClient() call with the result of the Transaction->getClient() call itself
  759: # undef for completion of delivery of a delayed message.
  760: #
  761: # The net effect of these changes is that lonnet.perm.log is now accessed three
  762: # times: (a) oldest record is checked, if earlier than release date for 2.5.0
  763: # then (b) file is read backwards, with timestamp recorded for most recent
  764: # instance of logged "S" event for "update" command without "sethost:$host_id:"
  765: # then (c) file is read forward with records ignored which predate the timestamp
  766: # recorded in (b), if one was found.
  767: #
  768: # In (c), when calculating the unsend total, i.e., the difference between delayed
  769: # transactions ("D") and sent transactions ("S"), transactions are ignored if the
  770: # target node is no longer in the cluster, and also (for "update" commands), if
  771: # the target node is in the list of nodes excluded from the count, in the domain
  772: # configuration for this machine's default domain.  The idea here is to remove
  773: # delayed "update" commands for nodes for which inbound access to port 5663,
  774: # is blocked, but are still part of the LON-CAPA network, (i.e., they can still
  775: # replicate content from other nodes).
  776: #
  777: 
  778:     my $dfh=IO::File->new("$perlvar{'lonDaemons'}/logs/lonnet.perm.log","r");
  779:     if (defined($dfh)) {
  780:         while (my $line=<$dfh>) {
  781: 	    my ($time,$sdf,$rest)=split(/:/,$line,3);
  782:             if ($time < 1541185772) {
  783:                 $checkbackwards = 1;
  784:             }
  785:             last;
  786:         }
  787:         undef $dfh;
  788:     }
  789: 
  790:     if ($checkbackwards) {
  791:         if (tie *BW, 'File::ReadBackwards', "$perlvar{'lonDaemons'}/logs/lonnet.perm.log") {
  792:             while(my $line=<BW>) {
  793:                 if ($line =~ /\QLondTransaction=HASH\E[^:]+:update:/) {
  794:                     ($checkfrom) = split(/:/,$line,2);
  795:                     last;
  796:                 }
  797:             }
  798:             close(BW);
  799:         }
  800:     }
  801:     $dfh=IO::File->new("$perlvar{'lonDaemons'}/logs/lonnet.perm.log","r");
  802:     if (defined($dfh)) {
  803:         while (my $line=<$dfh>) {
  804:             my ($time,$sdf,$rest)=split(/:/,$line,3);
  805:             next unless (($sdf eq 'F') || ($sdf eq 'S') || ($sdf eq 'D'));
  806:             next if (($checkfrom) && ($time <= $checkfrom));
  807:             my ($dserv,$dcmd);
  808:             if ($sdf eq 'S') {
  809:                 my ($serva,$cmda,$servb,$cmdb) = split(/:/,$rest);
  810:                 if ($cmda eq 'sethost') {
  811:                     chomp($cmdb);
  812:                     $dcmd = $cmdb;
  813:                 } else {
  814:                     $dcmd = $cmda;
  815:                 }
  816:                 if (($serva =~ /^LondTransaction/) || ($serva eq '')) {
  817:                     unless (($servb eq '') || ($servb =~ m{^/})) {
  818:                         $dserv = $servb;
  819:                     }
  820:                 } else {
  821:                     $dserv = $serva;
  822:                 }
  823:             } else {
  824:                 ($dserv,$dcmd) = split(/:/,$rest);
  825:             }
  826:             if ($sdf eq 'F') {
  827:                 my $local=localtime($time);
  828:                 &log($fh,"<b>Failed: $time, $dserv, $dcmd</b><br />");
  829:                 $warnings++;
  830:             }
  831:             next if ((($dserv eq '') || ($dcmd eq '')) && ($sdf ne 'F'));
  832:             if ($sdf eq 'S') {
  833:                 if ($dcmd eq 'update') {
  834:                     if ($hostname{$dserv}) {
  835:                         if ($exclusions{$serverhomes{$hostname{$dserv}}}) {
  836:                             $ignored --;
  837:                         } else {
  838:                             $unsend --;
  839:                         }
  840:                     }
  841:                     if (exists($bymachine{$dserv})) {
  842:                         $bymachine{$dserv} --;
  843:                     } else {
  844:                         $bymachine{$dserv} = -1;
  845:                     }
  846:                 } else {
  847:                     if ($hostname{$dserv}) {
  848:                         $unsend --;
  849:                     }
  850:                 }
  851:             } elsif ($sdf eq 'D') {
  852:                 if ($dcmd eq 'update') {
  853:                     if ($hostname{$dserv}) {
  854:                         if ($exclusions{$serverhomes{$hostname{$dserv}}}) {
  855:                             $ignored ++;
  856:                         } else {
  857:                             $unsend ++;
  858:                         }
  859:                     }
  860:                     if (exists($bymachine{$dserv})) {
  861:                         $bymachine{$dserv} ++;
  862:                     } else {
  863:                         $bymachine{$dserv} = 1;
  864:                     }
  865:                 } else {
  866:                     if ($hostname{$dserv}) {
  867:                         $unsend ++;
  868:                     }
  869:                 }
  870:             }
  871:         }
  872:         undef $dfh;
  873:         my $nodest = 0;
  874:         my $retired = 0;
  875:         my %active;
  876:         if (keys(%bymachine)) {
  877:             unless ($checkexcluded) {
  878:                 %serverhomes = &read_serverhomeIDs();
  879:             }
  880:             foreach my $key (keys(%bymachine)) {
  881:                 if ($bymachine{$key} > 0) {
  882:                     if ($hostname{$key}) {
  883:                         $active{$serverhomes{$hostname{$key}}} += $bymachine{$key};
  884:                     } else {
  885:                         $retired ++;
  886:                         $nodest += $bymachine{$key};
  887:                     }
  888:                 }
  889:             }
  890:         }
  891:         if (keys(%active)) {
  892:             &log($fh,"<p>Unsend messages by node, active (undegraded) nodes in cluster</p>\n");
  893:             foreach my $key (sort(keys(%active))) {
  894:                 &log($fh,&encode_entities("$key => $active{$key}",'<>&"')."\n");
  895:             }
  896:         }
  897:         &log($fh,"<p>Total unsend messages: <b>$unsend</b> for ".scalar(keys(%active))." active (undegraded) nodes in cluster.</p>\n");
  898:         if (keys(%exclusions) > 0) {
  899:             &log($fh,"<p>Total incomplete updates <b>$ignored</b> for ".scalar(keys(%exclusions))." degraded nodes in cluster.</p>\n");
  900:         }
  901:         if ($retired) {
  902:             &log($fh,"<p>Total unsent <b>$nodest</b> for $retired nodes no longer in cluster.</p>\n");
  903:         }
  904:         if ($unsend > 0) {
  905:             $warnings=$warnings+$weights{'U'}*$unsend;
  906:         }
  907:     }
  908: 
  909:     if ($unsend) { $simplestatus{'unsend'}=$unsend; }
  910:     &log($fh,"<h3>Outgoing Buffer</h3>\n<pre>");
  911: # list directory with delayed messages and remember offline servers
  912:     my %servers=();
  913:     open (DFH,"ls -lF $perlvar{'lonSockDir'}/delayed|");
  914:     while (my $line=<DFH>) {
  915:         my ($server)=($line=~/\.(\w+)$/);
  916:         if ($server) { $servers{$server}=1; }
  917: 	&log($fh,&encode_entities($line,'<>&"'));
  918:     }
  919:     &log($fh,"</pre>\n");
  920:     close (DFH);
  921: # pong to all servers that have delayed messages
  922: # this will trigger a reverse connection, which should flush the buffers
  923:     foreach my $tryserver (sort(keys(%servers))) {
  924:         if ($hostname{$tryserver} || !$numhosts) {
  925:             my $answer;
  926:             eval {
  927:                 local $SIG{ ALRM } = sub { die "TIMEOUT" };
  928:                 alarm(20);
  929:                 $answer = &Apache::lonnet::reply("pong",$tryserver);
  930:                 alarm(0);
  931:             };
  932:             if ($@ && $@ =~ m/TIMEOUT/) {
  933:                 &log($fh,"Attempted pong to $tryserver timed out<br />");
  934:                 print "Time out while contacting: $tryserver for pong.\n";
  935:             } else {
  936:                 &log($fh,"Pong to $tryserver: $answer<br />");
  937:             }
  938:         } else {
  939:             &log($fh,"$tryserver has delayed messages, but is not part of the cluster -- skipping 'Pong'.<br />");
  940:         }
  941:     }
  942: }
  943: 
  944: sub finish_logging {
  945:     my ($fh,$weightsref)=@_;
  946:     my %weights;
  947:     if (ref($weightsref) eq 'HASH') {
  948:         %weights = %{$weightsref};
  949:     }
  950:     &log($fh,"<a name='errcount' />\n");
  951:     $totalcount=($weights{'N'}*$notices)+($weights{'W'}*$warnings)+($weights{'E'}*$errors);
  952:     &errout($fh);
  953:     &log($fh,"<h1>Total Error Count: $totalcount</h1>");
  954:     my $now=time;
  955:     my $date=localtime($now);
  956:     &log($fh,"<hr />$date ($now)</body></html>\n");
  957:     print "lon-status webpage updated.\n";
  958:     $fh->close();
  959: 
  960:     if ($errors) { $simplestatus{'errors'}=$errors; }
  961:     if ($warnings) { $simplestatus{'warnings'}=$warnings; }
  962:     if ($notices) { $simplestatus{'notices'}=$notices; }
  963:     $simplestatus{'time'}=time;
  964: }
  965: 
  966: sub log_simplestatus {
  967:     rename("$statusdir/newstatus.html","$statusdir/index.html");
  968:     
  969:     my $sfh=IO::File->new(">$statusdir/loncron_simple.txt");
  970:     foreach (keys %simplestatus) {
  971: 	print $sfh $_.'='.$simplestatus{$_}.'&';
  972:     }
  973:     print $sfh "\n";
  974:     $sfh->close();
  975: }
  976: 
  977: sub write_loncaparevs {
  978:     print "Retrieving LON-CAPA version information.\n";
  979:     my %hostname = &Apache::lonnet::all_hostnames();
  980:     my $output;
  981:     foreach my $id (sort(keys(%hostname))) {
  982:         if ($id ne '') {
  983:             my $loncaparev;
  984:             eval {
  985:                 local $SIG{ ALRM } = sub { die "TIMEOUT" };
  986:                 alarm(10);
  987:                 $loncaparev =
  988:                     &Apache::lonnet::get_server_loncaparev('',$id,1,'loncron');
  989:                 alarm(0);
  990:             };
  991:             if ($@ && $@ =~ m/TIMEOUT/) {
  992:                 print "Time out while contacting lonHost: $id for version.\n";   
  993:             }
  994:             if ($loncaparev =~ /^[\w.\-]+$/) {
  995:                 $output .= $id.':'.$loncaparev."\n";
  996:             }
  997:         }
  998:     }
  999:     if ($output) {
 1000:         if (open(my $fh,">$perlvar{'lonTabDir'}/loncaparevs.tab")) {
 1001:             print $fh $output;
 1002:             close($fh);
 1003:             &Apache::lonnet::load_loncaparevs();
 1004:         }
 1005:     }
 1006:     return;
 1007: }
 1008: 
 1009: sub write_serverhomeIDs {
 1010:     print "Retrieving LON-CAPA lonHostID information.\n";
 1011:     my %name_to_host = &Apache::lonnet::all_names();
 1012:     my $output;
 1013:     foreach my $name (sort(keys(%name_to_host))) {
 1014:         if ($name ne '') {
 1015:             if (ref($name_to_host{$name}) eq 'ARRAY') {
 1016:                 my $serverhomeID;
 1017:                 eval {
 1018:                     local $SIG{ ALRM } = sub { die "TIMEOUT" };
 1019:                     alarm(10);
 1020:                     $serverhomeID =
 1021:                         &Apache::lonnet::get_server_homeID($name,1,'loncron');
 1022:                     alarm(0);
 1023:                 };
 1024:                 if ($@ && $@ =~ m/TIMEOUT/) {
 1025:                     print "Time out while contacting server: $name\n"; 
 1026:                 }
 1027:                 if ($serverhomeID ne '') {
 1028:                     $output .= $name.':'.$serverhomeID."\n";
 1029:                 } else {
 1030:                     $output .= $name.':'.$name_to_host{$name}->[0]."\n";
 1031:                 }
 1032:             }
 1033:         }
 1034:     }
 1035:     if ($output) {
 1036:         if (open(my $fh,">$perlvar{'lonTabDir'}/serverhomeIDs.tab")) {
 1037:             print $fh $output;
 1038:             close($fh);
 1039:             &Apache::lonnet::load_serverhomeIDs();
 1040:         }
 1041:     }
 1042:     return;
 1043: }
 1044: 
 1045: sub write_checksums {
 1046:     my $distro = &LONCAPA::distro();
 1047:     if ($distro) {
 1048:         print "Retrieving file version and checksumming.\n";
 1049:         my $numchksums = 0;
 1050:         my ($chksumsref,$versionsref) =
 1051:             &LONCAPA::Checksumming::get_checksums($distro,$perlvar{'lonDaemons'},
 1052:                                                   $perlvar{'lonLib'},
 1053:                                                   $perlvar{'lonIncludes'},
 1054:                                                   $perlvar{'lonTabDir'});
 1055:         if (ref($chksumsref) eq 'HASH') {
 1056:             $numchksums = scalar(keys(%{$chksumsref}));
 1057:         }
 1058:         print "File version retrieved and checksumming completed for $numchksums files.\n";
 1059:     } else {
 1060:         print "File version retrieval and checksumming skipped - could not determine Linux distro.\n"; 
 1061:     }
 1062:     return;
 1063: }
 1064: 
 1065: sub write_hostips {
 1066:     my $lontabdir = $perlvar{'lonTabDir'};
 1067:     my $defdom = $perlvar{'lonDefDomain'};
 1068:     my $lonhost = $perlvar{'lonHostID'};
 1069:     my $newfile = "$lontabdir/currhostips.tab";
 1070:     my $oldfile = "$lontabdir/prevhostips.tab";
 1071:     my (%prevhosts,%currhosts,%ipchange);
 1072:     if ((-e $newfile) && (-s $newfile)) {
 1073:         move($newfile,$oldfile);
 1074:         chmod(0644,$oldfile);
 1075:         if (open(my $fh,'<',$oldfile)) {
 1076:             while (my $line=<$fh>) {
 1077:                 chomp($line);
 1078:                 if ($line =~ /^([^:]+):([\d.]+)$/) {
 1079:                     $prevhosts{$1} = $2;
 1080:                 }
 1081:             }
 1082:             close($fh);
 1083:         }
 1084:     }
 1085:     my ($ip_info,$cached) =
 1086:         &Apache::lonnet::is_cached_new('iphost','iphost');
 1087:     if (!$cached) {
 1088:         &Apache::lonnet::get_iphost();
 1089:         ($ip_info,$cached) =
 1090:         &Apache::lonnet::is_cached_new('iphost','iphost');
 1091:     }
 1092:     if (ref($ip_info) eq 'ARRAY') {
 1093:         %currhosts = %{$ip_info->[1]};
 1094:         if (open(my $fh,'>',$newfile)) {
 1095:             foreach my $key (keys(%currhosts)) {
 1096:                 print $fh "$key:$currhosts{$key}\n";
 1097:             }
 1098:             close($fh);
 1099:             chmod(0644,$newfile);
 1100:         }
 1101:     }
 1102:     if (keys(%prevhosts) && keys(%currhosts)) {
 1103:         foreach my $key (keys(%prevhosts)) {
 1104:             unless ($currhosts{$key} eq $prevhosts{$key}) {
 1105:                 $ipchange{$key} = $prevhosts{$key}.' | '.$currhosts{$key};
 1106:             }
 1107:         }
 1108:         foreach my $key (keys(%currhosts)) {
 1109:             unless ($currhosts{$key} eq $prevhosts{$key}) {
 1110:                 $ipchange{$key} = $prevhosts{$key}.' | '.$currhosts{$key};
 1111:             }
 1112:         }
 1113:     }
 1114:     if (&Apache::lonnet::domain($defdom,'primary') eq $lonhost) {
 1115:         if (keys(%ipchange)) {
 1116:             if (open(my $fh,'>>',$perlvar{'lonDaemons'}.'/logs/hostip.log')) {
 1117:                print $fh "********************\n".localtime(time).' Changes --'."\n".
 1118:                          "| Hostname | Previous IP | New IP |\n".
 1119:                          " --------------------------------- \n";
 1120:                foreach my $hostname (sort(keys(%ipchange))) {
 1121:                     print $fh "| $hostname | $ipchange{$hostname} |\n";
 1122:                 }
 1123:                 print $fh "\n*******************\n\n";
 1124:                 close($fh);
 1125:             }
 1126:             my $emailto = &Apache::loncommon::build_recipient_list(undef,
 1127:                                    'hostipmail',$defdom);
 1128:             if ($emailto) {
 1129:                 my $subject = "LON-CAPA Hostname to IP change ($perlvar{'lonHostID'})";
 1130:                 my $chgmail = "To: $emailto\n".
 1131:                               "Subject: $subject\n".
 1132:                               "Content-type: text/plain\; charset=UTF-8\n".
 1133:                               "MIME-Version: 1.0\n\n".
 1134:                               "Host/IP changes\n".
 1135:                               " \n".
 1136:                               "| Hostname | Previous IP | New IP |\n".
 1137:                               " --------------------------------- \n";
 1138:                 foreach my $hostname (sort(keys(%ipchange))) {
 1139:                     $chgmail .= "| $hostname | $ipchange{$hostname} |\n";
 1140:                 }
 1141:                 $chgmail .= "\n\n";
 1142:                 if (open(my $mailh, "|/usr/lib/sendmail -oi -t -odb")) {
 1143:                     print $mailh $chgmail;
 1144:                     close($mailh);
 1145:                     print "Sending mail notification of hostname/IP changes.\n";
 1146:                 }
 1147:             }
 1148:         }
 1149:     }
 1150:     return;
 1151: }
 1152: 
 1153: sub get_domain_config {
 1154:     my ($dom,$primlibserv,$isprimary,$url,%confhash);
 1155:     $dom = $perlvar{'lonDefDomain'};
 1156:     $primlibserv = &Apache::lonnet::domain($dom,'primary');
 1157:     if ($primlibserv eq $perlvar{'lonHostID'}) {
 1158:         $isprimary = 1;
 1159:     } elsif ($primlibserv ne '') {
 1160:         my $protocol = $Apache::lonnet::protocol{$primlibserv};
 1161:         my $hostname = &Apache::lonnet::hostname($primlibserv);
 1162:         unless ($protocol eq 'https') {
 1163:             $protocol = 'http';
 1164:         }
 1165:         $url = $protocol.'://'.$hostname.'/cgi-bin/listdomconfig.pl?primary='.$primlibserv.'&format=raw';
 1166:     }
 1167:     if ($isprimary) {
 1168:         my $lonusersdir = $perlvar{'lonUsersDir'};
 1169:         my $fname = $lonusersdir.'/'.$dom.'/configuration.db';
 1170:         if (-e $fname) {
 1171:             my $dbref=&LONCAPA::locking_hash_tie($fname,&GDBM_READER());
 1172:             if (ref($dbref) eq 'HASH') {
 1173:                 foreach my $key (sort(keys(%{$dbref}))) {
 1174:                     my $value = $dbref->{$key};
 1175:                     if ($value =~ s/^__FROZEN__//) {
 1176:                         $value = thaw(&LONCAPA::unescape($value));
 1177:                     } else {
 1178:                         $value = &LONCAPA::unescape($value);
 1179:                     }
 1180:                     $confhash{$key} = $value;
 1181:                 }
 1182:                 &LONCAPA::locking_hash_untie($dbref);
 1183:             }
 1184:         }
 1185:     } else {
 1186:         my $ua=new LWP::UserAgent;
 1187:         $ua->timeout(5);
 1188:         my $request=new HTTP::Request('GET',$url);
 1189:         my $response=$ua->request($request);
 1190:         unless ($response->is_error()) {
 1191:             my $content = $response->content;
 1192:             if ($content) {
 1193:                 my @pairs=split(/\&/,$content);
 1194:                 foreach my $item (@pairs) {
 1195:                     my ($key,$value)=split(/=/,$item,2);
 1196:                     my $what = &LONCAPA::unescape($key);
 1197:                     if ($value =~ s/^__FROZEN__//) {
 1198:                         $value = thaw(&LONCAPA::unescape($value));
 1199:                     } else {
 1200:                         $value = &LONCAPA::unescape($value);
 1201:                     }
 1202:                     $confhash{$what}=$value;
 1203:                 }
 1204:             }
 1205:         }
 1206:     }
 1207:     return \%confhash;
 1208: }
 1209: 
 1210: sub get_permcount_settings {
 1211:     my ($domconf) = @_;
 1212:     my ($defaults,$names) = &Apache::loncommon::lon_status_items();
 1213:     my (%weights,$threshold,$sysmail,$reportstatus,%exclusions);
 1214:     foreach my $type ('E','W','N','U') {
 1215:         $weights{$type} = $defaults->{$type};
 1216:     }
 1217:     $threshold = $defaults->{'threshold'};
 1218:     $sysmail = $defaults->{'sysmail'};
 1219:     $reportstatus = 1;
 1220:     if (ref($domconf) eq 'HASH') {
 1221:         if (ref($domconf->{'contacts'}) eq 'HASH') {
 1222:             if ($domconf->{'contacts'}{'reportstatus'} == 0) {
 1223:                 $reportstatus = 0;
 1224:             }
 1225:             if (ref($domconf->{'contacts'}{'lonstatus'}) eq 'HASH') {
 1226:                 if (ref($domconf->{'contacts'}{'lonstatus'}{weights}) eq 'HASH') {
 1227:                     foreach my $type ('E','W','N','U') {
 1228:                         if (exists($domconf->{'contacts'}{'lonstatus'}{weights}{$type})) {
 1229:                             $weights{$type} = $domconf->{'contacts'}{'lonstatus'}{weights}{$type};
 1230:                         }
 1231:                     }
 1232:                 }
 1233:                 if (ref($domconf->{'contacts'}{'lonstatus'}{'excluded'}) eq 'ARRAY') {
 1234:                     my @excluded = @{$domconf->{'contacts'}{'lonstatus'}{'excluded'}};
 1235:                     if (@excluded) {
 1236:                         map { $exclusions{$_} = 1; } @excluded;
 1237:                     }
 1238:                 }
 1239:                 if (exists($domconf->{'contacts'}{'lonstatus'}{'threshold'})) {
 1240:                     $threshold = $domconf->{'contacts'}{'lonstatus'}{'threshold'};
 1241:                 }
 1242:                 if (exists($domconf->{'contacts'}{'lonstatus'}{'sysmail'})) {
 1243:                     $sysmail = $domconf->{'contacts'}{'lonstatus'}{'sysmail'};
 1244:                 }
 1245:             }
 1246:         }
 1247:     }
 1248:     return ($threshold,$sysmail,$reportstatus,\%weights,\%exclusions);
 1249: }
 1250: 
 1251: sub read_serverhomeIDs {
 1252:     my %server;
 1253:     if (-e "$perlvar{'lonTabDir'}/serverhomeIDs.tab") {
 1254:         if (open(my $fh,'<',"$perlvar{'lonTabDir'}/serverhomeIDs.tab")) {
 1255:             while (<$fh>) {
 1256:                 my($host,$id) = split(/:/);
 1257:                 chomp($id);
 1258:                 $server{$host} = $id;
 1259:             }
 1260:             close($fh);
 1261:         }
 1262:     }
 1263:     return %server;
 1264: }
 1265: 
 1266: sub send_mail {
 1267:     my ($sysmail,$reportstatus) = @_;
 1268:     my $defdom = $perlvar{'lonDefDomain'};
 1269:     my $origmail = $perlvar{'lonAdmEMail'};
 1270:     my $emailto = &Apache::loncommon::build_recipient_list(undef,
 1271:                                    'lonstatusmail',$defdom,$origmail);
 1272:     if (($totalcount>$sysmail) && ($reportstatus)) {
 1273: 	$emailto.=",$perlvar{'lonSysEMail'}";
 1274:     }
 1275:     my $from;
 1276:     my $hostname=`/bin/hostname`;
 1277:     chop($hostname);
 1278:     $hostname=~s/[^\w\.]//g;
 1279:     if ($hostname) {
 1280:         $from = 'www@'.$hostname;
 1281:     }
 1282:     my $subj="LON: $perlvar{'lonHostID'} E:$errors W:$warnings N:$notices";
 1283:     my $loncronmail = "To: $emailto\n".
 1284:                       "From: $from\n".
 1285:                       "Subject: ".$subj."\n".
 1286:                       "Content-type: text/html\; charset=UTF-8\n".
 1287:                       "MIME-Version: 1.0\n\n";
 1288:     if (open(my $fh,"<$statusdir/index.html")) {
 1289:         while (<$fh>) {
 1290:             $loncronmail .= $_;
 1291:         }
 1292:         close($fh);
 1293:     } else {
 1294:         $loncronmail .= "Failed to read from http://$hostname/lon-status/index.html\n";
 1295:     }
 1296:     $loncronmail .= "\n\n";
 1297:     if (open(my $mailh, "|/usr/lib/sendmail -oi -t -odb")) {
 1298:         print $mailh $loncronmail;
 1299:         close($mailh);
 1300:         print "Sending mail.\n";
 1301:     } else {
 1302:         print "Sending mail failed.\n";
 1303:     }
 1304: }
 1305: 
 1306: sub usage {
 1307:     print(<<USAGE);
 1308: loncron - housekeeping program that checks up on various parts of LON-CAPA
 1309: 
 1310: Options:
 1311:    --help     Display 
 1312:    --noemail  Do not send the status email
 1313:    --justcheckconnections  Only check the current status of the lonc/d
 1314:                                 connections, do not send emails do not
 1315:                                 check if the daemons are running, do not
 1316:                                 generate lon-status
 1317:    --justcheckdaemons      Only check that all of the Lon-CAPA daemons are
 1318:                                 running, do not send emails do not
 1319:                                 check the lonc/d connections, do not
 1320:                                 generate lon-status
 1321:    --justreload            Only tell the daemons to reload the config files,
 1322: 				do not send emails do not
 1323:                                 check if the daemons are running, do not
 1324:                                 generate lon-status
 1325:    --justiptables          Only update the dynamic iptables rules for the
 1326:                                 lond port; do not send emails, do not
 1327:                                 check if the daemons are running, do not
 1328:                                 generate lon-status
 1329: USAGE
 1330: }
 1331: 
 1332: # ================================================================ Main Program
 1333: sub main () {
 1334:     my ($help,$justcheckdaemons,$noemail,$justcheckconnections,
 1335: 	$justreload,$justiptables);
 1336:     &GetOptions("help"                 => \$help,
 1337: 		"justcheckdaemons"     => \$justcheckdaemons,
 1338: 		"noemail"              => \$noemail,
 1339: 		"justcheckconnections" => \$justcheckconnections,
 1340: 		"justreload"           => \$justreload,
 1341:                 "justiptables"         => \$justiptables
 1342: 		);
 1343:     if ($help) { &usage(); return; }
 1344: # --------------------------------- Read loncapa_apache.conf and loncapa.conf
 1345:     my $perlvarref=LONCAPA::Configuration::read_conf('loncapa.conf');
 1346:     %perlvar=%{$perlvarref};
 1347:     undef $perlvarref;
 1348:     delete $perlvar{'lonReceipt'}; # remove since sensitive and not needed
 1349:     delete $perlvar{'lonSqlAccess'}; # remove since sensitive and not needed
 1350:     chdir($perlvar{'lonDaemons'});
 1351: # --------------------------------------- Make sure that LON-CAPA is configured
 1352: # I only test for one thing here (lonHostID).  This is just a safeguard.
 1353:     if ('{[[[[lonHostID]]]]}' eq $perlvar{'lonHostID'}) {
 1354: 	print("Unconfigured machine.\n");
 1355: 	my $emailto=$perlvar{'lonSysEMail'};
 1356: 	my $hostname = Sys::Hostname::FQDN::fqdn();
 1357: 	$hostname=~s/\.+/./g;
 1358: 	$hostname=~s/\-+/-/g;
 1359: 	$hostname=~s/[^\w\.-]//g; # make sure is safe to pass through shell
 1360: 	my $subj="LON: Unconfigured machine $hostname";
 1361: 	system("echo 'Unconfigured machine $hostname.' |".
 1362:                " mail -s '$subj' $emailto > /dev/null");
 1363: 	exit 1;
 1364:     }
 1365: 
 1366: # ----------------------------- Make sure this process is running from user=www
 1367:     my $wwwid=getpwnam('www');
 1368:     if ($wwwid!=$<) {
 1369: 	print("User ID mismatch. This program must be run as user 'www'.\n");
 1370: 	my $emailto="$perlvar{'lonAdmEMail'},$perlvar{'lonSysEMail'}";
 1371: 	my $subj="LON: $perlvar{'lonHostID'} User ID mismatch";
 1372: 	system("echo 'User ID mismatch. loncron must be run as user www.' |".
 1373:                " mail -s '$subj' $emailto > /dev/null");
 1374: 	exit 1;
 1375:     }
 1376: 
 1377: # -------------------------------------------- Force reload of host information
 1378:     my $nomemcache;
 1379:     if ($justcheckdaemons) {
 1380:         $nomemcache=1;
 1381:         my $memcachepidfile="$perlvar{'lonDaemons'}/logs/memcached.pid";
 1382:         my $memcachepid;
 1383:         if (-e $memcachepidfile) {
 1384:             my $memfh=IO::File->new($memcachepidfile);
 1385:             $memcachepid=<$memfh>;
 1386:             chomp($memcachepid);
 1387:             if ($memcachepid =~ /^\d+$/ && kill 0 => $memcachepid) {
 1388:                 undef($nomemcache);
 1389:             }
 1390:         }
 1391:     }
 1392:     if (!$justiptables) {
 1393:         &Apache::lonnet::load_hosts_tab(1,$nomemcache);
 1394:         &Apache::lonnet::load_domain_tab(1,$nomemcache);
 1395:         &Apache::lonnet::get_iphost(1,$nomemcache);
 1396:     }
 1397: 
 1398: # ----------------------------------------- Force firewall update for lond port
 1399: 
 1400:     if ((!$justcheckdaemons) && (!$justreload)) {
 1401:         my $now = time;
 1402:         my $tmpfile = $perlvar{'lonDaemons'}.'/tmp/lciptables_iphost_'.
 1403:                       $now.$$.int(rand(10000));
 1404:         if (open(my $fh,">$tmpfile")) {
 1405:             my %iphosts = &Apache::lonnet::get_iphost();
 1406:             foreach my $key (keys(%iphosts)) {
 1407:                 print $fh "$key\n";
 1408:             }
 1409:             close($fh);
 1410:             if (&LONCAPA::try_to_lock('/tmp/lock_lciptables')) {
 1411:                 my $execpath = $perlvar{'lonDaemons'}.'/lciptables';
 1412:                 system("$execpath $tmpfile");
 1413:                 unlink('/tmp/lock_lciptables');  # Remove the lock file.
 1414:             }
 1415:             unlink($tmpfile);
 1416:         }
 1417:     }
 1418: 
 1419: # ---------------------------------------------------------------- Start report
 1420: 
 1421:     $errors=0;
 1422:     $warnings=0;
 1423:     $notices=0;
 1424: 
 1425: 
 1426:     my $fh;
 1427:     if (!$justcheckdaemons && !$justcheckconnections && !$justreload && !$justiptables) {
 1428: 	$fh=&start_logging();
 1429: 
 1430: 	&log_machine_info($fh);
 1431: 	&clean_tmp($fh);
 1432: 	&clean_lonIDs($fh);
 1433:         &clean_balanceIDs($fh);
 1434:         &clean_webDAV_sessionIDs($fh);
 1435: 	&check_httpd_logs($fh);
 1436: 	&rotate_lonnet_logs($fh);
 1437: 	&rotate_other_logs($fh);
 1438:     }
 1439:     if (!$justcheckconnections && !$justreload && !$justiptables) {
 1440: 	&checkon_daemon($fh,'lonmemcached',40000);
 1441: 	&checkon_daemon($fh,'lonsql',200000);
 1442: 	if ( &checkon_daemon($fh,'lond',40000,'USR1') eq 'running') {
 1443: 	    &checkon_daemon($fh,'lond',40000,'USR2');
 1444: 	}
 1445: 	&checkon_daemon($fh,'lonc',40000,'USR1');
 1446:         &checkon_daemon($fh,'lonmaxima',40000);
 1447:         &checkon_daemon($fh,'lonr',40000);
 1448:     }
 1449:     if ($justreload) {
 1450: 	&checkon_daemon($fh,'lond',40000,'USR2');
 1451: 	&checkon_daemon($fh,'lonc',40000,'USR2');
 1452:     }
 1453:     if ($justcheckconnections) {
 1454: 	&test_connections($fh);
 1455:     }
 1456:     if (!$justcheckdaemons && !$justcheckconnections && !$justreload && !$justiptables) {
 1457:         my $domconf = &get_domain_config();
 1458:         my ($threshold,$sysmail,$reportstatus,$weightsref,$exclusionsref) =
 1459:             &get_permcount_settings($domconf);
 1460: 	&check_delayed_msg($fh,$weightsref,$exclusionsref);
 1461: 	&finish_logging($fh,$weightsref);
 1462: 	&log_simplestatus();
 1463:         &write_loncaparevs();
 1464:         &write_serverhomeIDs();
 1465: 	&write_checksums();
 1466:         &write_hostips();
 1467:         if ($totalcount>$threshold && !$noemail) { &send_mail($sysmail,$reportstatus); }
 1468:     }
 1469: }
 1470: 
 1471: &main();
 1472: 1;
 1473: 

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

Internal Server Error

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

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

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