--- loncom/lond	2000/07/21 00:40:37	1.18
+++ loncom/lond	2002/02/06 14:18:09	1.68.2.1
@@ -1,14 +1,55 @@
 #!/usr/bin/perl
 # The LearningOnline Network
 # lond "LON Daemon" Server (port "LOND" 5663)
+#
+# $Id: lond,v 1.68.2.1 2002/02/06 14:18:09 albertel 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,
 # 12/7,12/15,01/06,01/11,01/12,01/14,2/8,
 # 03/07,05/31 Gerd Kortemeyer
 # 06/26 Scott Harrison
-# 06/29,06/30,07/14,07/15,07/17,07/20 Gerd Kortemeyer
-#
+# 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
+# 03/24 Gerd Kortemeyer
+# 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
+# YEAR=2002
+# 01/20/02,02/05 Gerd Kortemeyer
+###
+
 # based on "Perl Cookbook" ISBN 1-56592-243-3
 # preforker - server who forks first
 # runs as a daemon
@@ -24,6 +65,34 @@ use Crypt::IDEA;
 use LWP::UserAgent();
 use GDBM_File;
 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)=@_;
+    $SIG{'QUIT'}='DEFAULT';
+    $SIG{__DIE__}='DEFAULT';
+    &logthis("<font color=red>CRITICAL: "
+     ."ABNORMAL EXIT. Child $$ for server $wasserver died through "
+     ."a crash with this error msg->[$error]</font>");
+    &logthis('Famous last words: '.$status.' - '.$lastlog);
+    if ($client) { print $client "error: $error\n"; }
+    $server->close();
+    die($error);
+}
+
+sub timeout {
+    &logthis("<font color=ref>CRITICAL: TIME OUT ".$$."</font>");
+    &catchexception('Timeout');
+}
+# -------------------------------- Set signal handlers to record abnormal exits
+
+$SIG{'QUIT'}=\&catchexception;
+$SIG{__DIE__}=\&catchexception;
 
 # ------------------------------------ Read httpd access.conf and get variables
 
@@ -38,6 +107,27 @@ while ($configline=<CONFIG>) {
 }
 close(CONFIG);
 
+# ----------------------------- Make sure this process is running from user=www
+my $wwwid=getpwnam('www');
+if ($wwwid!=$<) {
+   $emailto="$perlvar{'lonAdmEMail'},$perlvar{'lonSysEMail'}";
+   $subj="LON: $perlvar{'lonHostID'} User ID mismatch";
+   system("echo 'User ID mismatch.  lond must be run as user www.' |\
+ mailto $emailto -s '$subj' > /dev/null");
+   exit 1;
+}
+
+# --------------------------------------------- Check if other instance running
+
+my $pidfile="$perlvar{'lonDaemons'}/logs/lond.pid";
+
+if (-e $pidfile) {
+   my $lfh=IO::File->new("$pidfile");
+   my $pide=<$lfh>;
+   chomp($pide);
+   if (kill 0 => $pide) { die "already running"; }
+}
+
 $PREFORK=4; # number of children to maintain, at least four spare
 
 # ------------------------------------------------------------- Read hosts file
@@ -73,14 +163,19 @@ $children               = 0;        # cu
 sub REAPER {                        # takes care of dead children
     $SIG{CHLD} = \&REAPER;
     my $pid = wait;
-    $children --;
-    &logthis("Child $pid died");
-    delete $children{$pid};
+    if (defined($children{$pid})) {
+	&logthis("Child $pid died");
+	$children --;
+	delete $children{$pid};
+    } else {
+	&logthis("Unknown Child $pid died");
+    }
 }
 
 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("<font color=red>CRITICAL: Shutting down</font>");
@@ -90,12 +185,39 @@ 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("<font color=red>CRITICAL: Restarting</font>");
+    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');
+    $docdir=$perlvar{'lonDocRoot'};
+    foreach (sort keys %children) {
+	sleep 1;
+        unless (kill 'USR1' => $_) {
+	    &logthis ('Child '.$_.' is dead');
+            &logstatus($$.' is dead');
+        } 
+    }
+    sleep 5;
+    foreach (sort keys %children) {
+        unless (-e "$docdir/lon-status/londchld/$_.txt") {
+	    &logthis('Child '.$_.' did not respond');
+	    kill 9 => $_;
+	    $emailto="$perlvar{'lonAdmEMail'},$perlvar{'lonSysEMail'}";
+	    $subj="LON: $perlvar{'lonHostID'} killed lond process $_";
+	    my $result=`echo 'Killed lond process $_.' | mailto $emailto -s '$subj' > /dev/null`;
+	    $execdir=$perlvar{'lonDaemons'};
+	    $result=`/bin/cp $execdir/logs/lond.log $execdir/logs/lond.log.$_`
+        }
+    }
+}
+
 # --------------------------------------------------------------------- Logging
 
 sub logthis {
@@ -104,9 +226,47 @@ 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";
+    $fh->close();
+    }
+    {
+	my $fh=IO::File->new(">$docdir/lon-status/londchld/$$.txt");
+        print $fh $status."\n".$lastlog."\n".time;
+        $fh->close();
+    }
+}
+
+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";
+    opendir(DIR,"$docdir/lon-status/londchld");
+    while ($filename=readdir(DIR)) {
+        unlink("$docdir/lon-status/londchld/$filename");
+    }
+    closedir(DIR);
+}
+
+# -------------------------------------------------------------- Status setting
+
+sub status {
+    my $what=shift;
+    my $now=time;
+    my $local=localtime($now);
+    $status=$local.': '.$what;
+}
 
 # -------------------------------------------------------- Escape Special Chars
 
@@ -253,6 +413,7 @@ open (PIDSAVE,">$execdir/logs/lond.pid")
 print PIDSAVE "$$\n";
 close(PIDSAVE);
 &logthis("<font color=red>CRITICAL: ---------- Starting ----------</font>");
+&status('Starting');
 
 # ------------------------------------------------------- Now we are on our own
     
@@ -263,13 +424,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
     }
@@ -293,11 +460,16 @@ 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;
+        $SIG{ALRM}= \&timeout;
+        $lastlog='Forked ';
+        $status='Forked';
+
         # unblock signals
         sigprocmask(SIG_UNBLOCK, $sigset)
             or die "Can't unblock SIGINT for fork: $!\n";
@@ -306,8 +478,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
 # -----------------------------------------------------------------------------
@@ -317,14 +490,19 @@ sub make_new_child {
             my $clientip=inet_ntoa($iaddr);
             my $clientrec=($hostid{$clientip} ne undef);
             &logthis(
-"<font color=yellow>INFO: Connect from $clientip ($hostid{$clientip})</font>");
+"<font color=yellow>INFO: Connection $i, $clientip ($hostid{$clientip})</font>"
+            );
+            &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) {
@@ -333,28 +511,32 @@ sub make_new_child {
                   } else {
 		      &logthis(
  "<font color=blue>WARNING: $clientip did not reply challenge</font>");
-                      print $client "bye\n";
+                      &status('No challenge reply '.$clientip);
                   }
               } else {
 		  &logthis(
                     "<font color=blue>WARNING: "
                    ."$clientip failed to initialize: >$remotereq< </font>");
-		  print $client "bye\n";
+                  &status('No init '.$clientip);
               }
 	    } else {
               &logthis(
  "<font color=blue>WARNING: Unknown client $clientip</font>");
-              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(
        "<font color=green>Established connection: $hostid{$clientip}</font>");
+              &status('Will listen to '.$hostid{$clientip});
 # ------------------------------------------------------------ Process requests
               while (my $userinput=<$client>) {
                 chomp($userinput);
+                &status('Processing '.$hostid{$clientip}.': '.$userinput);
+                &logthis('Processing '.$hostid{$clientip}.': '.$userinput);
                 my $wasenc=0;
+                alarm(120);
 # ------------------------------------------------------------ See if encrypted
 		if ($userinput =~ /^enc/) {
 		  if ($cipher) {
@@ -369,7 +551,9 @@ sub make_new_child {
 		    $userinput=substr($userinput,0,$cmdlength);
                     $wasenc=1;
 		  }
-		}
+		  &logthis('Decrypted '.$hostid{$clientip}.': '.$userinput);
+	      }
+
 # ------------------------------------------------------------- Normal commands
 # ------------------------------------------------------------------------ ping
 		   if ($userinput =~ /^ping/) {
@@ -402,6 +586,28 @@ sub make_new_child {
                        $loadavg =~ s/\s.*//g;
                        my $loadpercent=100*$loadavg/$perlvar{'lonLoadLim'};
 		       print $client "$loadpercent\n";
+# ----------------------------------------------------------------- currentauth
+		   } elsif ($userinput =~ /^currentauth/) {
+		     if ($wasenc==1) {
+                       my ($cmd,$udom,$uname)=split(/:/,$userinput);
+                       my $proname=propath($udom,$uname);
+                       my $passfilename="$proname/passwd";
+                       if (-e $passfilename) {
+			   my $pf = IO::File->new($passfilename);
+			   my $realpasswd=<$pf>;
+			   chomp($realpasswd);
+			   my ($howpwd,$contentpwd)=split(/:/,$realpasswd);
+			   my $availablecontent='';
+			   if ($howpwd eq 'krb4') {
+			       $availablecontent=$contentpwd;
+			   }
+			   print $client "$howpwd:$availablecontent\n";
+		       } else {
+                          print $client "unknown_user\n";
+                       }
+		     } else {
+		       print $client "refused\n";
+		     }
 # ------------------------------------------------------------------------ auth
                    } elsif ($userinput =~ /^auth/) {
 		     if ($wasenc==1) {
@@ -421,14 +627,27 @@ sub make_new_child {
 				  (crypt($upass,$contentpwd) eq $contentpwd);
                           } elsif ($howpwd eq 'unix') {
                               $contentpwd=(getpwnam($uname))[1];
-                              $pwdcorrect=
-                                  (crypt($upass,$contentpwd) eq $contentpwd);
+			      my $pwauth_path="/usr/local/sbin/pwauth";
+			      unless ($contentpwd eq 'x') {
+				  $pwdcorrect=
+                                    (crypt($upass,$contentpwd) eq $contentpwd);
+			      }
+			      elsif (-e $pwauth_path) {
+				  open PWAUTH, "|$pwauth_path" or
+				      die "Cannot invoke authentication";
+				  print PWAUTH "$uname\n$upass\n";
+				  close PWAUTH;
+				  $pwdcorrect=!$?;
+			      }
                           } elsif ($howpwd eq 'krb4') {
                               $pwdcorrect=(
                                  Authen::Krb4::get_pw_in_tkt($uname,"",
                                         $contentpwd,'krbtgt',$contentpwd,1,
 							     $upass) == 0);
-                          }
+                          } elsif ($howpwd eq 'localauth') {
+			    $pwdcorrect=&localauth::localauth($uname,$upass,
+							      $contentpwd);
+			  }
                           if ($pwdcorrect) {
                              print $client "authorized\n";
                           } else {
@@ -446,6 +665,8 @@ sub make_new_child {
                        my 
                        ($cmd,$udom,$uname,$upass,$npass)=split(/:/,$userinput);
                        chomp($npass);
+                       $upass=&unescape($upass);
+                       $npass=&unescape($npass);
                        my $proname=propath($udom,$uname);
                        my $passfilename="$proname/passwd";
                        if (-e $passfilename) {
@@ -460,7 +681,7 @@ sub make_new_child {
                              $salt=substr($salt,6,2);
 			     my $ncpass=crypt($npass,$salt);
                              { my $pf = IO::File->new(">$passfilename");
- 	  		       print $pf "internal:$ncpass\n";; }             
+ 	  		       print $pf "internal:$ncpass\n"; }             
                              print $client "ok\n";
                            } else {
                              print $client "non_authorized\n";
@@ -474,6 +695,145 @@ sub make_new_child {
 		     } else {
 		       print $client "refused\n";
 		     }
+# -------------------------------------------------------------------- makeuser
+                   } elsif ($userinput =~ /^makeuser/) {
+    	             my $oldumask=umask(0077);
+		     if ($wasenc==1) {
+                       my 
+                       ($cmd,$udom,$uname,$umode,$npass)=split(/:/,$userinput);
+                       chomp($npass);
+                       $npass=&unescape($npass);
+                       my $proname=propath($udom,$uname);
+                       my $passfilename="$proname/passwd";
+                       if (-e $passfilename) {
+			   print $client "already_exists\n";
+                       } elsif ($udom ne $perlvar{'lonDefDomain'}) {
+                           print $client "not_right_domain\n";
+                       } else {
+                           @fpparts=split(/\//,$proname);
+                           $fpnow=$fpparts[0].'/'.$fpparts[1].'/'.$fpparts[2];
+                           $fperror='';
+                           for ($i=3;$i<=$#fpparts;$i++) {
+                               $fpnow.='/'.$fpparts[$i]; 
+                               unless (-e $fpnow) {
+				   unless (mkdir($fpnow,0777)) {
+                                      $fperror="error:$!";
+                                   }
+                               }
+                           }
+                           unless ($fperror) {
+			     if ($umode eq 'krb4') {
+                               { 
+                                 my $pf = IO::File->new(">$passfilename");
+ 	  		         print $pf "krb4:$npass\n"; 
+                               }             
+                               print $client "ok\n";
+                             } elsif ($umode eq 'internal') {
+			       my $salt=time;
+                               $salt=substr($salt,6,2);
+			       my $ncpass=crypt($npass,$salt);
+                               { 
+                                 my $pf = IO::File->new(">$passfilename");
+ 	  		         print $pf "internal:$ncpass\n"; 
+                               }
+                               print $client "ok\n";
+			     } elsif ($umode eq 'localauth') {
+			       {
+				 my $pf = IO::File->new(">$passfilename");
+  	  		         print $pf "localauth:$npass\n";
+			       }
+			       print $client "ok\n";
+			     } elsif ($umode eq 'unix') {
+			       {
+				 my $execpath="$perlvar{'lonDaemons'}/".
+				              "lcuseradd";
+				 {
+				     my $se = IO::File->new("|$execpath");
+				     print $se "$uname\n";
+				     print $se "$npass\n";
+				     print $se "$npass\n";
+				 }
+                                 my $pf = IO::File->new(">$passfilename");
+ 	  		         print $pf "unix:\n"; 
+			       }
+			       print $client "ok\n";
+			     } elsif ($umode eq 'none') {
+                               { 
+                                 my $pf = IO::File->new(">$passfilename");
+ 	  		         print $pf "none:\n"; 
+                               }             
+                               print $client "ok\n";
+                             } else {
+                               print $client "auth_mode_error\n";
+                             }  
+                           } else {
+                               print $client "$fperror\n";
+                           }
+                       }
+		     } else {
+		       print $client "refused\n";
+		     }
+		     umask($oldumask);
+# -------------------------------------------------------------- changeuserauth
+                   } elsif ($userinput =~ /^changeuserauth/) {
+		     if ($wasenc==1) {
+                       my 
+                       ($cmd,$udom,$uname,$umode,$npass)=split(/:/,$userinput);
+                       chomp($npass);
+                       $npass=&unescape($npass);
+                       my $proname=propath($udom,$uname);
+                       my $passfilename="$proname/passwd";
+		       if ($udom ne $perlvar{'lonDefDomain'}) {
+                           print $client "not_right_domain\n";
+                       } else {
+			   if ($umode eq 'krb4') {
+                               { 
+				   my $pf = IO::File->new(">$passfilename");
+				   print $pf "krb4:$npass\n"; 
+                               }             
+                               print $client "ok\n";
+			   } elsif ($umode eq 'internal') {
+			       my $salt=time;
+                               $salt=substr($salt,6,2);
+			       my $ncpass=crypt($npass,$salt);
+                               { 
+				   my $pf = IO::File->new(">$passfilename");
+				   print $pf "internal:$ncpass\n"; 
+                               }
+                               print $client "ok\n";
+			   } elsif ($umode eq 'localauth') {
+			       {
+				   my $pf = IO::File->new(">$passfilename");
+				   print $pf "localauth:$npass\n";
+			       }
+			       print $client "ok\n";
+			   } elsif ($umode eq 'unix') {
+			       {
+				   my $execpath="$perlvar{'lonDaemons'}/".
+				       "lcuseradd";
+				   {
+				       my $se = IO::File->new("|$execpath");
+				       print $se "$uname\n";
+				       print $se "$npass\n";
+				       print $se "$npass\n";
+				   }
+				   my $pf = IO::File->new(">$passfilename");
+				   print $pf "unix:\n"; 
+			       }
+			       print $client "ok\n";
+			   } elsif ($umode eq 'none') {
+                               { 
+				   my $pf = IO::File->new(">$passfilename");
+				   print $pf "none:\n"; 
+                               }             
+                               print $client "ok\n";
+			   } else {
+                               print $client "auth_mode_error\n";
+			   }  
+                       }
+		     } else {
+		       print $client "refused\n";
+		     }
 # ------------------------------------------------------------------------ home
                    } elsif ($userinput =~ /^home/) {
                        my ($cmd,$udom,$uname)=split(/:/,$userinput);
@@ -511,12 +871,13 @@ sub make_new_child {
                              $response=$ua->request($request,$transname);
 			      }
                              if ($response->is_error()) {
-				 unline($transname);
+				 unlink($transname);
                                  my $message=$response->status_line;
                                  &logthis(
                                   "LWP GET: $message for $fname ($remoteurl)");
                              } else {
 	                         if ($remoteurl!~/\.meta$/) {
+                                  my $ua=new LWP::UserAgent;
                                   my $mrequest=
                                    new HTTP::Request('GET',$remoteurl.'.meta');
                                   my $mresponse=
@@ -558,9 +919,15 @@ sub make_new_child {
                          } else {
                            $now=time;
                            { 
-                            my $sh=IO::File->new(">$fname.$hostid{$clientip}");
-                            print $sh "$clientip:$now\n";
+			    my $sh;
+                            if ($sh=
+                             IO::File->new(">$fname.$hostid{$clientip}")) {
+                               print $sh "$clientip:$now\n";
+			    }
 			   }
+                           unless ($fname=~/\.meta$/) {
+			       unlink("$fname.meta.$hostid{$clientip}");
+                           }
                            $fname=~s/\/home\/httpd\/html\/res/raw/;
                            $fname="http://$thisserver/".$fname;
                            print $client "$fname\n";
@@ -596,7 +963,7 @@ sub make_new_child {
                        chomp($what);
                        my $proname=propath($udom,$uname);
                        my $now=time;
-                       {
+                       unless ($namespace=~/^nohist\_/) {
 			   my $hfh;
 			   if (
                              $hfh=IO::File->new(">>$proname/$namespace.hist")
@@ -663,7 +1030,7 @@ sub make_new_child {
                        my @queries=split(/\&/,$what);
                        my $proname=propath($udom,$uname);
                        my $qresult='';
-      if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_WRCREAT,0640)) {
+      if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_READER,0640)) {
                            for ($i=0;$i<=$#queries;$i++) {
                                $qresult.="$hash{$queries[$i]}&";
                            }
@@ -686,7 +1053,7 @@ sub make_new_child {
                        my @queries=split(/\&/,$what);
                        my $proname=propath($udom,$uname);
                        my $qresult='';
-      if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_WRCREAT,0640)) {
+      if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_READER,0640)) {
                            for ($i=0;$i<=$#queries;$i++) {
                                $qresult.="$hash{$queries[$i]}&";
                            }
@@ -721,7 +1088,7 @@ sub make_new_child {
                        chomp($what);
                        my $proname=propath($udom,$uname);
                        my $now=time;
-                       {
+                       unless ($namespace=~/^nohist\_/) {
 			   my $hfh;
 			   if (
                              $hfh=IO::File->new(">>$proname/$namespace.hist")
@@ -748,7 +1115,7 @@ sub make_new_child {
                        $namespace=~s/\W//g;
                        my $proname=propath($udom,$uname);
                        my $qresult='';
-      if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_WRCREAT,0640)) {
+      if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_READER,0640)) {
                            foreach $key (keys %hash) {
                                $qresult.="$key&";
                            }
@@ -763,15 +1130,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_WRCREAT,0640)) {
+      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/\&$//;
@@ -792,7 +1166,7 @@ sub make_new_child {
                        chomp($what);
                        my $proname=propath($udom,$uname);
                        my $now=time;
-                       {
+                       unless ($namespace=~/^nohist\_/) {
 			   my $hfh;
 			   if (
                              $hfh=IO::File->new(">>$proname/$namespace.hist")
@@ -811,7 +1185,8 @@ sub make_new_child {
                                $allkeys.=$key.':';
                                $hash{"$version:$rid:$key"}=$value;
                            }
-                           $allkeys=~s/:$//;
+                           $hash{"$version:$rid:timestamp"}=$now;
+                           $allkeys.='timestamp';
                            $hash{"$version:keys:$rid"}=$allkeys;
 			   if (untie(%hash)) {
                               print $client "ok\n";
@@ -833,7 +1208,7 @@ sub make_new_child {
                        chomp($rid);
                        my $proname=propath($udom,$uname);
                        my $qresult='';
-      if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_WRCREAT,0640)) {
+      if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_READER,0640)) {
                 	   my $version=$hash{"version:$rid"};
                            $qresult.="version=$version&";
                            my $scope;
@@ -843,7 +1218,7 @@ sub make_new_child {
                               my $key;
                               $qresult.="$scope:keys=$vkeys&";
                               foreach $key (@keys) {
-	     $qresult.="$version:$key=".$hash{"$scope:$rid:$key"}."&";
+	     $qresult.="$scope:$key=".$hash{"$scope:$rid:$key"}."&";
                               }                                  
                            }
 			   if (untie(%hash)) {
@@ -857,17 +1232,30 @@ sub make_new_child {
                        }
 # ------------------------------------------------------------------- querysend
                    } elsif ($userinput =~ /^querysend/) {
-                       my ($cmd,$query)=split(/:/,$userinput);
+                       my ($cmd,$query,
+			   $custom,$customshow)=split(/:/,$userinput);
 		       $query=~s/\n*$//g;
-                     print $client sqlreply("$hostid{$clientip}\&$query")."\n";
+		       unless ($custom or $customshow) {
+			   print $client "".
+			       sqlreply("$hostid{$clientip}\&$query")."\n";
+		       }
+		       else {
+			   print $client "".
+			       sqlreply("$hostid{$clientip}\&$query".
+					"\&$custom"."\&$customshow")."\n";
+		       }
 # ------------------------------------------------------------------ queryreply
                    } elsif ($userinput =~ /^queryreply/) {
                        my ($cmd,$id,$reply)=split(/:/,$userinput); 
 		       my $store;
                        my $execdir=$perlvar{'lonDaemons'};
                        if ($store=IO::File->new(">$execdir/tmp/$id")) {
+			   $reply=~s/\&/\n/g;
 			   print $store $reply;
 			   close $store;
+			   my $store2=IO::File->new(">$execdir/tmp/$id.end");
+			   print $store2 "done\n";
+			   close $store2;
 			   print $client "ok\n";
 		       }
 		       else {
@@ -908,7 +1296,7 @@ sub make_new_child {
                        my $proname="$perlvar{'lonUsersDir'}/$udom/ids";
                        my @queries=split(/\&/,$what);
                        my $qresult='';
-                 if (tie(%hash,'GDBM_File',"$proname.db",&GDBM_WRCREAT,0640)) {
+                 if (tie(%hash,'GDBM_File',"$proname.db",&GDBM_READER,0640)) {
                            for ($i=0;$i<=$#queries;$i++) {
                                $qresult.="$hash{$queries[$i]}&";
                            }
@@ -961,24 +1349,40 @@ sub make_new_child {
                        my $ulsout='';
                        my $ulsfn;
                        if (-e $ulsdir) {
-                          while ($ulsfn=<$ulsdir/*>) {
-			     my @ulsstats=stat($ulsfn);
+			if (opendir(LSDIR,$ulsdir)) {
+                          while ($ulsfn=readdir(LSDIR)) {
+			     my @ulsstats=stat($ulsdir.'/'.$ulsfn);
                              $ulsout.=$ulsfn.'&'.join('&',@ulsstats).':';
                           }
+                          closedir(LSDIR);
+		        }
 		       } else {
                           $ulsout='no_such_dir';
                        }
                        if ($ulsout eq '') { $ulsout='empty'; }
                        print $client "$ulsout\n";
+# ------------------------------------------------------------------ Hanging up
+                   } elsif (($userinput =~ /^exit/) ||
+                            ($userinput =~ /^init/)) {
+                       &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
+		   alarm(0);
+                   &status('Listening to '.$hostid{$clientip});
+                   &logthis('Completed '.$userinput.' Listening to '.$hostid{$clientip});
 	       }
+# --------------------------------------------- client unknown or fishy, refuse
             } else {
 	        print $client "refused\n";
+                $client->close();
                 &logthis("<font color=blue>WARNING: "
                 ."Rejected client $clientip, closing connection</font>");
             }              
@@ -989,6 +1393,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.
@@ -996,6 +1403,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