--- loncom/lond 2001/11/16 16:26:01 1.56 +++ loncom/lond 2001/12/22 21:46:02 1.62 @@ -1,6 +1,31 @@ #!/usr/bin/perl # The LearningOnline Network # lond "LON Daemon" Server (port "LOND" 5663) +# +# $Id: lond,v 1.62 2001/12/22 21:46:02 www Exp $ +# +# Copyright Michigan State University Board of Trustees +# +# This file is part of the LearningOnline Network with CAPA (LON-CAPA). +# +# 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 +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# LON-CAPA is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with LON-CAPA; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# /home/httpd/html/adm/gpl.txt +# +# http://www.lon-capa.org/ +# # 5/26/99,6/4,6/10,6/11,6/14,6/15,6/26,6/28,6/30, # 7/8,7/9,7/10,7/12,7/17,7/19,9/21, # 10/7,10/8,10/9,10/11,10/13,10/15,11/4,11/16, @@ -10,6 +35,7 @@ # 06/29,06/30,07/14,07/15,07/17,07/20,07/25,09/18 Gerd Kortemeyer # 12/05 Scott Harrison # 12/05,12/13,12/29 Gerd Kortemeyer +# YEAR=2001 # Jan 01 Scott Harrison # 02/12 Gerd Kortemeyer # 03/15 Scott Harrison @@ -17,8 +43,10 @@ # 04/02 Scott Harrison # 05/11,05/28,08/30 Gerd Kortemeyer # 9/30,10/22,11/13,11/15,11/16 Scott Harrison +# 11/26,11/27 Gerd Kortemeyer +# 12/20 Scott Harrison +# 12/22 Gerd Kortemeyer # -# $Id: lond,v 1.56 2001/11/16 16:26:01 harris41 Exp $ ### # based on "Perl Cookbook" ISBN 1-56592-243-3 @@ -39,6 +67,9 @@ use Authen::Krb4; use lib '/home/httpd/lib/perl/'; use localauth; +my $status=''; +my $lastlog=''; + # grabs exception and records it to log before exiting sub catchexception { my ($error)=@_; @@ -47,7 +78,9 @@ sub catchexception { &logthis("CRITICAL: " ."ABNORMAL EXIT. Child $$ for server $wasserver died through " ."a crash with this error msg->[$error]"); + &logthis('Famous last words: '.$status.' - '.$lastlog); if ($client) { print $client "error: $error\n"; } + $server->close(); die($error); } @@ -133,6 +166,7 @@ sub REAPER { # ta sub HUNTSMAN { # signal handler for SIGINT local($SIG{CHLD}) = 'IGNORE'; # we're going to kill our children kill 'INT' => keys %children; + &logthis("Free socket: ".shutdown($server,2)); # free up socket my $execdir=$perlvar{'lonDaemons'}; unlink("$execdir/logs/lond.pid"); &logthis("CRITICAL: Shutting down"); @@ -142,13 +176,26 @@ sub HUNTSMAN { # si sub HUPSMAN { # signal handler for SIGHUP local($SIG{CHLD}) = 'IGNORE'; # we're going to kill our children kill 'INT' => keys %children; - close($server); # free up socket + &logthis("Free socket: ".shutdown($server,2)); # free up socket &logthis("CRITICAL: Restarting"); unlink("$execdir/logs/lond.pid"); my $execdir=$perlvar{'lonDaemons'}; exec("$execdir/lond"); # here we go again } +sub checkchildren { + &initnewstatus(); + &logstatus(); + &logthis('Going to check on the children'); + foreach (sort keys %children) { + sleep 1; + unless (kill 'USR1' => $_) { + &logthis ('Child '.$_.' is dead'); + &logstatus($$.' is dead'); + } + } +} + # --------------------------------------------------------------------- Logging sub logthis { @@ -157,9 +204,34 @@ sub logthis { my $fh=IO::File->new(">>$execdir/logs/lond.log"); my $now=time; my $local=localtime($now); + $lastlog=$local.': '.$message; print $fh "$local ($$): $message\n"; } +# ------------------------------------------------------------------ Log status + +sub logstatus { + my $docdir=$perlvar{'lonDocRoot'}; + my $fh=IO::File->new(">>$docdir/lon-status/londstatus.txt"); + print $fh $$."\t".$status."\t".$lastlog."\n"; +} + +sub initnewstatus { + my $docdir=$perlvar{'lonDocRoot'}; + my $fh=IO::File->new(">$docdir/lon-status/londstatus.txt"); + my $now=time; + my $local=localtime($now); + print $fh "LOND status $local - parent $$\n\n"; +} + +# -------------------------------------------------------------- Status setting + +sub status { + my $what=shift; + my $now=time; + my $local=localtime($now); + $status=$local.': '.$what; +} # -------------------------------------------------------- Escape Special Chars @@ -306,6 +378,7 @@ open (PIDSAVE,">$execdir/logs/lond.pid") print PIDSAVE "$$\n"; close(PIDSAVE); &logthis("CRITICAL: ---------- Starting ----------"); +&status('Starting'); # ------------------------------------------------------- Now we are on our own @@ -316,13 +389,19 @@ for (1 .. $PREFORK) { # ----------------------------------------------------- Install signal handlers +&status('Forked children'); + $SIG{CHLD} = \&REAPER; $SIG{INT} = $SIG{TERM} = \&HUNTSMAN; $SIG{HUP} = \&HUPSMAN; +$SIG{USR1} = \&checkchildren; # And maintain the population. while (1) { + &status('Sleeping'); sleep; # wait for a signal (i.e., child's death) + &logthis('Woke up'); + &status('Woke up'); for ($i = $children; $i < $PREFORK; $i++) { make_new_child(); # top up the child pool } @@ -346,11 +425,15 @@ sub make_new_child { or die "Can't unblock SIGINT for fork: $!\n"; $children{$pid} = 1; $children++; + &status('Started child '.$pid); return; } else { # Child can *not* return from this subroutine. $SIG{INT} = 'DEFAULT'; # make SIGINT kill us as it did before - + $SIG{USR1}= \&logstatus; + $lastlog='Forked '; + $status='Forked'; + # unblock signals sigprocmask(SIG_UNBLOCK, $sigset) or die "Can't unblock SIGINT for fork: $!\n"; @@ -359,8 +442,9 @@ sub make_new_child { # handle connections until we've reached $MAX_CLIENTS_PER_CHILD for ($i=0; $i < $MAX_CLIENTS_PER_CHILD; $i++) { + &status('Idle, waiting for connection'); $client = $server->accept() or last; - + &status('Accepted connection'); # ============================================================================= # do something with the connection # ----------------------------------------------------------------------------- @@ -372,13 +456,17 @@ sub make_new_child { &logthis( "INFO: Connection $i, $clientip ($hostid{$clientip})" ); + &status("Connecting $clientip ($hostid{$clientip})"); my $clientok; if ($clientrec) { + &status("Waiting for init from $clientip ($hostid{$clientip})"); my $remotereq=<$client>; $remotereq=~s/\W//g; if ($remotereq eq 'init') { my $challenge="$$".time; print $client "$challenge\n"; + &status( + "Waiting for challenge reply from $clientip ($hostid{$clientip})"); $remotereq=<$client>; $remotereq=~s/\W//g; if ($challenge eq $remotereq) { @@ -387,27 +475,29 @@ sub make_new_child { } else { &logthis( "WARNING: $clientip did not reply challenge"); - print $client "bye\n"; + &status('No challenge reply '.$clientip); } } else { &logthis( "WARNING: " ."$clientip failed to initialize: >$remotereq< "); - print $client "bye\n"; + &status('No init '.$clientip); } } else { &logthis( "WARNING: Unknown client $clientip"); - print $client "bye\n"; + &status('Hung up on '.$clientip); } if ($clientok) { # ---------------- New known client connecting, could mean machine online again &reconlonc("$perlvar{'lonSockDir'}/$hostid{$clientip}"); &logthis( "Established connection: $hostid{$clientip}"); + &status('Will listen to '.$hostid{$clientip}); # ------------------------------------------------------------ Process requests while (my $userinput=<$client>) { chomp($userinput); + &status('Processing '.$hostid{$clientip}.': '.$userinput); my $wasenc=0; # ------------------------------------------------------------ See if encrypted if ($userinput =~ /^enc/) { @@ -1000,15 +1090,22 @@ sub make_new_child { } # ------------------------------------------------------------------------ dump } elsif ($userinput =~ /^dump/) { - my ($cmd,$udom,$uname,$namespace) + my ($cmd,$udom,$uname,$namespace,$regexp) =split(/:/,$userinput); $namespace=~s/\//\_/g; $namespace=~s/\W//g; + if (defined($regexp)) { + $regexp=&unescape($regexp); + } else { + $regexp='.'; + } my $proname=propath($udom,$uname); my $qresult=''; if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_READER,0640)) { foreach $key (keys %hash) { - $qresult.="$key=$hash{$key}&"; + if (eval('$key=~/$regexp/')) { + $qresult.="$key=$hash{$key}&"; + } } if (untie(%hash)) { $qresult=~s/\&$//; @@ -1230,16 +1327,20 @@ sub make_new_child { &logthis( "Client $clientip ($hostid{$clientip}) hanging up: $userinput"); print $client "bye\n"; + $client->close(); last; # ------------------------------------------------------------- unknown command } else { # unknown command print $client "unknown_cmd\n"; } -# ------------------------------------------------------ client unknown, refuse +# -------------------------------------------------------------------- complete + &status('Listening to '.$hostid{$clientip}); } +# --------------------------------------------- client unknown or fishy, refuse } else { print $client "refused\n"; + $client->close(); &logthis("WARNING: " ."Rejected client $clientip, closing connection"); } @@ -1250,6 +1351,9 @@ sub make_new_child { # tidy up gracefully and finish + $client->close(); + $server->close(); + # this exit is VERY important, otherwise the child will become # a producer of more and more children, forking yourself into # process death. @@ -1257,6 +1361,48 @@ sub make_new_child { } } +# ----------------------------------- POD (plain old documentation, CPAN style) + +=head1 NAME + +lond - "LON Daemon" Server (port "LOND" 5663) + +=head1 SYNOPSIS + +Should only be run as user=www. Invoked by loncron. + +=head1 DESCRIPTION + +Preforker - server who forks first. Runs as a daemon. HUPs. +Uses IDEA encryption + +=head1 README + +Not yet written. + +=head1 PREREQUISITES + +IO::Socket +IO::File +Apache::File +Symbol +POSIX +Crypt::IDEA +LWP::UserAgent() +GDBM_File +Authen::Krb4 + +=head1 COREQUISITES + +=head1 OSNAMES + +linux + +=head1 SCRIPT CATEGORIES + +Server/Process + +=cut