Diff for /loncom/lonsql between versions 1.11 and 1.51

version 1.11, 2001/03/26 19:46:47 version 1.51, 2002/08/06 13:48:47
Line 1 Line 1
 #!/usr/bin/perl  #!/usr/bin/perl
 # lonsql-based on the preforker:harsha jagasia:date:5/10/00  
 # 7/25 Gerd Kortemeyer  # The LearningOnline Network
 # many different dates Scott Harrison  # lonsql - LON TCP-MySQL-Server Daemon for handling database requests.
 # 03/22/2001 Scott Harrison  #
   # $Id$
   #
   # 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/
   #
   
   =pod
   
   =head1 NAME
   
   lonsql - LON TCP-MySQL-Server Daemon for handling database requests.
   
   =head1 SYNOPSIS
   
   This script should be run as user=www.  
   Note that a lonsql.pid file contains the pid of the parent process.
   
   =head1 DESCRIPTION
   
   lonsql is many things to many people.  To me, it is a source file in need
   of documentation.
   
   =head1 Internals
   
   =over 4
   
   =cut
   
   use strict;
   
   use lib '/home/httpd/lib/perl/';
   use LONCAPA::Configuration;
   
 use IO::Socket;  use IO::Socket;
 use Symbol;  use Symbol;
 use POSIX;  use POSIX;
Line 12  use Socket; Line 64  use Socket;
 use Fcntl;  use Fcntl;
 use Tie::RefHash;  use Tie::RefHash;
 use DBI;  use DBI;
   use File::Find;
   
 my @metalist;  ########################################################
 # ----------------- Code to enable 'find' subroutine listing of the .meta files  ########################################################
 require "find.pl";  
 sub wanted {  
     (($dev,$ino,$mode,$nlink,$uid,$gid) = lstat($_)) &&  
     -f _ &&  
     /^.*\.meta$/ &&  
     push(@metalist,"$dir/$_");  
 }  
   
   =pod
   
 $childmaxattempts=10;  =item Global Variables
 $run =0;#running counter to generate the query-id  
   
 # ------------------------------------ Read httpd access.conf and get variables  
 open (CONFIG,"/etc/httpd/conf/access.conf") || die "Can't read access.conf";  
   
 while ($configline=<CONFIG>) {  
     if ($configline =~ /PerlSetVar/) {  
  my ($dummy,$varname,$varvalue)=split(/\s+/,$configline);  
         chomp($varvalue);  
         $perlvar{$varname}=$varvalue;  
     }  
 }  
 close(CONFIG);  
   
 # --------------------------------------------- Check if other instance running  =over 4
   
 my $pidfile="$perlvar{'lonDaemons'}/logs/lonsql.pid";  =item dbh
   
 if (-e $pidfile) {  =back
    my $lfh=IO::File->new("$pidfile");  
    my $pide=<$lfh>;  
    chomp($pide);  
    if (kill 0 => $pide) { die "already running"; }  
 }  
   
 # ------------------------------------------------------------- Read hosts file  =cut
 $PREFORK=4; # number of children to maintain, at least four spare  
   
 open (CONFIG,"$perlvar{'lonTabDir'}/hosts.tab") || die "Can't read host file";  ########################################################
   ########################################################
   my $dbh;
   
 while ($configline=<CONFIG>) {  ########################################################
     my ($id,$domain,$role,$name,$ip)=split(/:/,$configline);  ########################################################
     chomp($ip);  
   
     $hostip{$ip}=$id;  =pod 
   
     if ($id eq $perlvar{'lonHostID'}) { $thisserver=$name; }  =item Variables required for forking
   
     $PREFORK++;  =over 4
 }  
 close(CONFIG);  
   
 $unixsock = "mysqlsock";  =item $MAX_CLIENTS_PER_CHILD
 my $localfile="$perlvar{'lonSockDir'}/$unixsock";  
 my $server;  The number of clients each child should process.
 unlink ($localfile);  
 unless ($server=IO::Socket::UNIX->new(Local    =>"$localfile",  =item %children 
   Type    => SOCK_STREAM,  
   Listen => 10))  The keys to %children  are the current child process IDs
 {  
     print "in socket error:$@\n";  =item $children
 }  
   The current number of children
   
   =back
   
   =cut 
   
   ########################################################
   ########################################################
   my $MAX_CLIENTS_PER_CHILD  = 5;   # number of clients each child should process
   my %children               = ();  # keys are current child process IDs
   my $children               = 0;   # current number of children
                                  
   ########################################################
   ########################################################
   
   =pod
   
   =item Functions required for forking
   
   =over 4
   
   =item REAPER
   
   REAPER takes care of dead children.
   
 # -------------------------------------------------------- Routines for forking  =item HUNTSMAN
 # global variables  
 $MAX_CLIENTS_PER_CHILD  = 5;        # number of clients each child should process  
 %children               = ();       # keys are current child process IDs  
 $children               = 0;        # current number of children  
   
 sub REAPER {                        # takes care of dead children  Signal handler for SIGINT.
   
   =item HUPSMAN
   
   Signal handler for SIGHUP
   
   =item DISCONNECT
   
   Disconnects from database.
   
   =back
   
   =cut
   
   ########################################################
   ########################################################
   sub REAPER {                   # takes care of dead children
     $SIG{CHLD} = \&REAPER;      $SIG{CHLD} = \&REAPER;
     my $pid = wait;      my $pid = wait;
     $children --;      $children --;
Line 100  sub HUNTSMAN {                      # si Line 163  sub HUNTSMAN {                      # si
     &logthis("<font color=red>CRITICAL: Shutting down</font>");      &logthis("<font color=red>CRITICAL: Shutting down</font>");
     $unixsock = "mysqlsock";      $unixsock = "mysqlsock";
     my $port="$perlvar{'lonSockDir'}/$unixsock";      my $port="$perlvar{'lonSockDir'}/$unixsock";
     unlink(port);      unlink($port);
     exit;                           # clean up with dignity      exit;                           # clean up with dignity
 }  }
   
Line 112  sub HUPSMAN {                      # sig Line 175  sub HUPSMAN {                      # sig
     my $execdir=$perlvar{'lonDaemons'};      my $execdir=$perlvar{'lonDaemons'};
     $unixsock = "mysqlsock";      $unixsock = "mysqlsock";
     my $port="$perlvar{'lonSockDir'}/$unixsock";      my $port="$perlvar{'lonSockDir'}/$unixsock";
     unlink(port);      unlink($port);
     exec("$execdir/lonsql");         # here we go again      exec("$execdir/lonsql");         # here we go again
 }  }
   
 sub logthis {  sub DISCONNECT {
     my $message=shift;      $dbh->disconnect or 
     my $execdir=$perlvar{'lonDaemons'};      &logthis("<font color=blue>WARNING: Couldn't disconnect from database ".
     my $fh=IO::File->new(">>$execdir/logs/lonsqlfinal.log");               " $DBI::errstr : $@</font>");
     my $now=time;      exit;
     my $local=localtime($now);  
     print $fh "$local ($$): $message\n";  
 }  }
 # ---------------------------------------------------- Fork once and dissociate  
 $fpid=fork;  
 exit if $fpid;  
 die "Couldn't fork: $!" unless defined ($fpid);  
   
 POSIX::setsid() or die "Can't start new session: $!";  ###################################################################
   ###################################################################
   
   =pod
   
   =item Main body of code.
   
   =over 4
   
   =item Read data from loncapa_apache.conf and loncapa.conf.
   
   =item Ensure we can access the database.
   
   =item Determine if there are other instances of lonsql running.
   
   =item Read the hosts file.
   
   =item Create a socket for lonsql.
   
   =item Fork once and dissociate from parent.
   
 # ------------------------------------------------------- Write our PID on disk  =item Write PID to disk.
   
 $execdir=$perlvar{'lonDaemons'};  =item Prefork children and maintain the population of children.
   
   =back
   
   =cut
   
   ###################################################################
   ###################################################################
   my $childmaxattempts=10;
   my $run =0;              # running counter to generate the query-id
   #
   # Read loncapa_apache.conf and loncapa.conf
   #
   my $perlvarref=LONCAPA::Configuration::read_conf('loncapa_apache.conf',
                                                    'loncapa.conf');
   my %perlvar=%{$perlvarref};
   #
   # Make sure that database can be accessed
   #
   my $dbh;
   unless ($dbh = DBI->connect("DBI:mysql:loncapa","www",
                               $perlvar{'lonSqlAccess'},
                               { RaiseError =>0,PrintError=>0})) { 
       print "Cannot connect to database!\n";
       my $emailto="$perlvar{'lonAdmEMail'},$perlvar{'lonSysEMail'}";
       my $subj="LON: $perlvar{'lonHostID'} Cannot connect to database!";
       system("echo 'Cannot connect to MySQL database!' |".
              " mailto $emailto -s '$subj' > /dev/null");
       exit 1;
   } else {
       $dbh->disconnect;
   }
   #
   # Check if other instance running
   #
   my $pidfile="$perlvar{'lonDaemons'}/logs/lonsql.pid";
   if (-e $pidfile) {
      my $lfh=IO::File->new("$pidfile");
      my $pide=<$lfh>;
      chomp($pide);
      if (kill 0 => $pide) { die "already running"; }
   }
   #
   # Read hosts file
   #
   my %hostip;
   my $thisserver;
   my $PREFORK=4; # number of children to maintain, at least four spare
   open (CONFIG,"$perlvar{'lonTabDir'}/hosts.tab") || die "Can't read host file";
   while (my $configline=<CONFIG>) {
       my ($id,$domain,$role,$name,$ip)=split(/:/,$configline);
       chomp($ip);
       $hostip{$ip}=$id;
       $thisserver=$name if ($id eq $perlvar{'lonHostID'});
       $PREFORK++;
   }
   close(CONFIG);
   #
   $PREFORK=int($PREFORK/4);
   #
   # Create a socket to talk to lond
   #
   my $unixsock = "mysqlsock";
   my $localfile="$perlvar{'lonSockDir'}/$unixsock";
   my $server;
   unlink ($localfile);
   unless ($server=IO::Socket::UNIX->new(Local    =>"$localfile",
                                         Type    => SOCK_STREAM,
                                         Listen => 10)) {
       print "in socket error:$@\n";
   }
   ########################################################
   ########################################################
   #
   # Fork once and dissociate
   my $fpid=fork;
   exit if $fpid;
   die "Couldn't fork: $!" unless defined ($fpid);
   POSIX::setsid() or die "Can't start new session: $!";
   #
   # Write our PID on disk
   my $execdir=$perlvar{'lonDaemons'};
 open (PIDSAVE,">$execdir/logs/lonsql.pid");  open (PIDSAVE,">$execdir/logs/lonsql.pid");
 print PIDSAVE "$$\n";  print PIDSAVE "$$\n";
 close(PIDSAVE);  close(PIDSAVE);
 &logthis("<font color=red>CRITICAL: ---------- Starting ----------</font>");  &logthis("<font color=red>CRITICAL: ---------- Starting ----------</font>");
   #
 # ----------------------------- Ignore signals generated during initial startup  # Ignore signals generated during initial startup
 $SIG{HUP}=$SIG{USR1}='IGNORE';  $SIG{HUP}=$SIG{USR1}='IGNORE';
 # ------------------------------------------------------- Now we are on our own      # Now we are on our own    
 # Fork off our children.  #    Fork off our children.
 for (1 .. $PREFORK) {  for (1 .. $PREFORK) {
     make_new_child();      make_new_child();
 }  }
   #
 # Install signal handlers.  # Install signal handlers.
 $SIG{CHLD} = \&REAPER;  $SIG{CHLD} = \&REAPER;
 $SIG{INT}  = $SIG{TERM} = \&HUNTSMAN;  $SIG{INT}  = $SIG{TERM} = \&HUNTSMAN;
 $SIG{HUP}  = \&HUPSMAN;  $SIG{HUP}  = \&HUPSMAN;
   #
 # And maintain the population.  # And maintain the population.
 while (1) {  while (1) {
     sleep;                          # wait for a signal (i.e., child's death)      sleep;                          # wait for a signal (i.e., child's death)
     for ($i = $children; $i < $PREFORK; $i++) {      for (my $i = $children; $i < $PREFORK; $i++) {
         make_new_child();           # top up the child pool          make_new_child();           # top up the child pool
     }      }
 }  }
   
   ########################################################
   ########################################################
   
   =pod
   
   =item &make_new_child
   
   Inputs: None
   
   Returns: None
   
   =cut
   
   ########################################################
   ########################################################
 sub make_new_child {  sub make_new_child {
     my $pid;      my $pid;
     my $sigset;      my $sigset;
           #
     # block signal for fork      # block signal for fork
     $sigset = POSIX::SigSet->new(SIGINT);      $sigset = POSIX::SigSet->new(SIGINT);
     sigprocmask(SIG_BLOCK, $sigset)      sigprocmask(SIG_BLOCK, $sigset)
         or die "Can't block SIGINT for fork: $!\n";          or die "Can't block SIGINT for fork: $!\n";
           #
     die "fork: $!" unless defined ($pid = fork);      die "fork: $!" unless defined ($pid = fork);
           #
     if ($pid) {      if ($pid) {
         # Parent records the child's birth and returns.          # Parent records the child's birth and returns.
         sigprocmask(SIG_UNBLOCK, $sigset)          sigprocmask(SIG_UNBLOCK, $sigset)
Line 182  sub make_new_child { Line 353  sub make_new_child {
     } else {      } else {
         # Child can *not* return from this subroutine.          # Child can *not* return from this subroutine.
         $SIG{INT} = 'DEFAULT';      # make SIGINT kill us as it did before          $SIG{INT} = 'DEFAULT';      # make SIGINT kill us as it did before
       
         # unblock signals          # unblock signals
         sigprocmask(SIG_UNBLOCK, $sigset)          sigprocmask(SIG_UNBLOCK, $sigset)
             or die "Can't unblock SIGINT for fork: $!\n";              or die "Can't unblock SIGINT for fork: $!\n";
   
   
         #open database handle          #open database handle
  # making dbh global to avoid garbage collector   # making dbh global to avoid garbage collector
  unless (   unless ($dbh = DBI->connect("DBI:mysql:loncapa","www",
  $dbh = DBI->connect("DBI:mysql:loncapa","www","123",{ RaiseError =>0,PrintError=>0})                                      $perlvar{'lonSqlAccess'},
  ) {                                       { RaiseError =>0,PrintError=>0})) { 
             my $st=120+int(rand(240));              sleep(10+int(rand(20)));
     &logthis("<font color=blue>WARNING: Couldn't connect to database  ($st secs): $@</font>");              &logthis("<font color=blue>WARNING: Couldn't connect to database".
     print "database handle error\n";                       ": $@</font>");
     sleep($st);                       #  "($st secs): $@</font>");
     exit;              print "database handle error\n";
               exit;
   };          }
  # make sure that a database disconnection occurs with ending kill signals   # make sure that a database disconnection occurs with 
           # ending kill signals
  $SIG{TERM}=$SIG{INT}=$SIG{QUIT}=$SIG{__DIE__}=\&DISCONNECT;   $SIG{TERM}=$SIG{INT}=$SIG{QUIT}=$SIG{__DIE__}=\&DISCONNECT;
   
         # handle connections until we've reached $MAX_CLIENTS_PER_CHILD          # handle connections until we've reached $MAX_CLIENTS_PER_CHILD
         for ($i=0; $i < $MAX_CLIENTS_PER_CHILD; $i++) {          for (my $i=0; $i < $MAX_CLIENTS_PER_CHILD; $i++) {
             $client = $server->accept()     or last;              my $client = $server->accept() or last;
               
             # do something with the connection              # do something with the connection
     $run = $run+1;      $run = $run+1;
     my $userinput = <$client>;      my $userinput = <$client>;
     chomp($userinput);      chomp($userinput);
                       #
     my ($conserver,$querytmp,$customtmp)=split(/&/,$userinput);      my ($conserver,$query,
     my $query=unescape($querytmp);   $arg1,$arg2,$arg3)=split(/&/,$userinput);
     my $custom=unescape($customtmp);      my $query=unescape($query);
               #
             #send query id which is pid_unixdatetime_runningcounter              #send query id which is pid_unixdatetime_runningcounter
     $queryid = $thisserver;      my $queryid = $thisserver;
     $queryid .="_".($$)."_";      $queryid .="_".($$)."_";
     $queryid .= time."_";      $queryid .= time."_";
     $queryid .= $run;      $queryid .= $run;
     print $client "$queryid\n";      print $client "$queryid\n";
           #
             #prepare and execute the query      &logthis("QUERY: $query - $arg1 - $arg2 - $arg3");
     my $sth = $dbh->prepare($query);      sleep 1;
     my $result;              #
     unless ($sth->execute())              my $result='';
     {              #
  &logthis("<font color=blue>WARNING: Could not retrieve from database: $@</font>");              # At this point, query is received, query-ID assigned and sent 
  $result="";              # back, $query eq 'logquery' will mean that this is a query 
     }              # against log-files
     else {              if (($query eq 'userlog') || ($query eq 'courselog')) {
  my $r1=$sth->fetchall_arrayref;                  # beginning of log query
  my @r2; map {my $a=$_; my @b=map {escape($_)} @$a; push @r2,join(",", @b)} (@$r1);                  my $udom    = &unescape($arg1);
  $result=join("&",@r2) . "\n";                  my $uname   = &unescape($arg2);
     }                  my $command = &unescape($arg3);
                   my $path    = &propath($udom,$uname);
     # do custom metadata searching here and build into result                  if (-e "$path/activity.log") {
     if ($custom) {                      if ($query eq 'userlog') {
  &logthis("am going to do custom query for $custom");                          $result=&userlog($path,$command);
  @metalist=(); pop @metalist;                      } else {
  &find("$perlvar{'lonDocRoot'}/res");                          $result=&courselog($path,$command);
  &logthis("FILELIST:" . join(":::",@metalist));                      }
  # if file is indicated in sql database and                  } else {
  # not part of sql-relevant query, do not pattern match.                      &logthis('Unable to do log query: '.$uname.'@'.$udom);
  # if file is not in sql database, output error.                      $result='no_such_file';
  # if file is indicated in sql database and is                  }
  # part of query result list, then do the pattern match.                  # end of log query
  foreach my $m (@metalist) {              } else {
     my $fh=IO::File->new($m);                  # Do an sql query
     my @lines=<$fh>;                  $result = &do_sql_query($query,$arg1,$arg2);
     my $stuff=join('',@lines);              }
     if ($stuff=~/$custom/s) {              # result does not need to be escaped because it has already been
  &logthis("found: $stuff");              # escaped.
     }              #$result=&escape($result);
  }              # reply with result, append \n unless already there
     }      $result.="\n" unless ($result=~/\n$/);
     # reply with result  
             &reply("queryreply:$queryid:$result",$conserver);              &reply("queryreply:$queryid:$result",$conserver);
   
         }          }
       
         # tidy up gracefully and finish          # tidy up gracefully and finish
           #
         #close the database handle          # close the database handle
  $dbh->disconnect   $dbh->disconnect
    or &logthis("<font color=blue>WARNING: Couldn't disconnect from database  $DBI::errstr ($st secs): $@</font>");              or &logthis("<font color=blue>WARNING: Couldn't disconnect".
                               " from database  $DBI::errstr : $@</font>");
         # this exit is VERY important, otherwise the child will become          # this exit is VERY important, otherwise the child will become
         # a producer of more and more children, forking yourself into          # a producer of more and more children, forking yourself into
         # process death.          # process death.
Line 275  sub make_new_child { Line 439  sub make_new_child {
     }      }
 }  }
   
 sub DISCONNECT {  ########################################################
     $dbh->disconnect or   ########################################################
     &logthis("<font color=blue>WARNING: Couldn't disconnect from database  $DBI::errstr ($st secs): $@</font>");  
     exit;  =pod
   
   =item &do_sql_query
   
   Runs an sql metadata table query.
   
   Inputs: $query, $custom, $customshow
   
   Returns: A string containing escaped results.
   
   =cut
   
   ########################################################
   ########################################################
   {
       my @metalist;
   
   sub process_file {
       if ( -e $_ &&  # file exists
            -f $_ &&  # and is a normal file
            /\.meta$/ &&  # ends in meta
            ! /^.+\.\d+\.[^\.]+\.meta$/  # is not a previous version
            ) {
           push(@metalist,$File::Find::name);
       }
   }
   
   sub do_sql_query {
       my ($query,$custom,$customshow) = @_;
       $custom     = &unescape($custom);
       $customshow = &unescape($customshow);
       #
       @metalist = ();
       #
       my $result = '';
       my @results = ();
       my @files;
       my $subsetflag=0;
       #
       if ($query) {
           #prepare and execute the query
           my $sth = $dbh->prepare($query);
           unless ($sth->execute()) {
               &logthis("<font color=blue>WARNING: ".
                        "Could not retrieve from database: $@</font>");
           } else {
               my $aref=$sth->fetchall_arrayref;
               foreach my $row (@$aref) {
                   push @files,@{$row}[3] if ($custom or $customshow);
                   my @b=map { &escape($_); } @$row;
                   push @results,join(",", @b);
                   # Build up the @files array with the LON-CAPA urls 
                   # of the resources.
               }
           }
       }
       # do custom metadata searching here and build into result
       return join("&",@results) if (! ($custom or $customshow));
       # Only get here if there is a custom query or custom show request
       &logthis("Doing custom query for $custom");
       if ($query) {
           @metalist=map {
               $perlvar{'lonDocRoot'}.$_.'.meta';
           } @files;
       } else {
           my $dir = "$perlvar{'lonDocRoot'}/res/$perlvar{'lonDefDomain'}";
           @metalist=(); 
           opendir(RESOURCES,$dir);
           my @homeusers=grep {
               &ishome($dir.'/'.$_);
           } grep {!/^\.\.?$/} readdir(RESOURCES);
           closedir RESOURCES;
           # Define the
           foreach my $user (@homeusers) {
               find (\&process_file,$dir.'/'.$user);
           }
       } 
       # if file is indicated in sql database and
       #     not part of sql-relevant query, do not pattern match.
       #
       # if file is not in sql database, output error.
       #
       # if file is indicated in sql database and is
       #     part of query result list, then do the pattern match.
       my $customresult='';
       my @results;
       foreach my $metafile (@metalist) {
           my $fh=IO::File->new($metafile);
           my @lines=<$fh>;
           my $stuff=join('',@lines);
           if ($stuff=~/$custom/s) {
               foreach my $f ('abstract','author','copyright',
                              'creationdate','keywords','language',
                              'lastrevisiondate','mime','notes',
                              'owner','subject','title') {
                   $stuff=~s/\n?\<$f[^\>]*\>.*?<\/$f[^\>]*\>\n?//s;
               }
               my $mfile=$metafile; 
               my $docroot=$perlvar{'lonDocRoot'};
               $mfile=~s/^$docroot//;
               $mfile=~s/\.meta$//;
               unless ($query) {
                   my $q2="SELECT * FROM metadata WHERE url ".
                       " LIKE BINARY '?'";
                   my $sth = $dbh->prepare($q2);
                   $sth->execute($mfile);
                   my $aref=$sth->fetchall_arrayref;
                   foreach my $a (@$aref) {
                       my @b=map { &escape($_)} @$a;
                       push @results,join(",", @b);
                   }
               }
               # &logthis("found: $stuff");
               $customresult.='&custom='.&escape($mfile).','.
                   escape($stuff);
           }
       }
       $result=join("&",@results) unless $query;
       $result.=$customresult;
       #
       return $result;
   } # End of &do_sql_query
   
   } # End of scoping curly braces for &process_file and &do_sql_query
   ########################################################
   ########################################################
   
   =pod
   
   =item &logthis
   
   Inputs: $message, the message to log
   
   Returns: nothing
   
   Writes $message to the logfile.
   
   =cut
   
   ########################################################
   ########################################################
   sub logthis {
       my $message=shift;
       my $execdir=$perlvar{'lonDaemons'};
       my $fh=IO::File->new(">>$execdir/logs/lonsqlfinal.log");
       my $now=time;
       my $local=localtime($now);
       print $fh "$local ($$): $message\n";
 }  }
   
 # -------------------------------------------------- Non-critical communication  # -------------------------------------------------- Non-critical communication
   
   ########################################################
   ########################################################
   
   =pod
   
   =item &subreply
   
   Sends a command to a server.  Called only by &reply.
   
   Inputs: $cmd,$server
   
   Returns: The results of the message or 'con_lost' on error.
   
   =cut
   
   ########################################################
   ########################################################
 sub subreply {  sub subreply {
     my ($cmd,$server)=@_;      my ($cmd,$server)=@_;
     my $peerfile="$perlvar{'lonSockDir'}/$server";      my $peerfile="$perlvar{'lonSockDir'}/$server";
Line 293  sub subreply { Line 621  sub subreply {
     print $sclient "$cmd\n";      print $sclient "$cmd\n";
     my $answer=<$sclient>;      my $answer=<$sclient>;
     chomp($answer);      chomp($answer);
     if (!$answer) { $answer="con_lost"; }      $answer="con_lost" if (!$answer);
     return $answer;      return $answer;
 }  }
   
   ########################################################
   ########################################################
   
   =pod
   
   =item &reply
   
   Sends a command to a server.
   
   Inputs: $cmd,$server
   
   Returns: The results of the message or 'con_lost' on error.
   
   =cut
   
   ########################################################
   ########################################################
 sub reply {  sub reply {
   my ($cmd,$server)=@_;    my ($cmd,$server)=@_;
   my $answer;    my $answer;
Line 308  sub reply { Line 653  sub reply {
     }      }
   } else {    } else {
     $answer='self_reply';      $answer='self_reply';
       $answer=subreply($cmd,$server);
   }     } 
   return $answer;    return $answer;
 }  }
   
 # -------------------------------------------------------- Escape Special Chars  ########################################################
   ########################################################
   
   =pod
   
   =item &escape
   
   Escape special characters in a string.
   
   Inputs: string to escape
   
   Returns: The input string with special characters escaped.
   
   =cut
   
   ########################################################
   ########################################################
 sub escape {  sub escape {
     my $str=shift;      my $str=shift;
     $str =~ s/(\W)/"%".unpack('H2',$1)/eg;      $str =~ s/(\W)/"%".unpack('H2',$1)/eg;
     return $str;      return $str;
 }  }
   
 # ----------------------------------------------------- Un-Escape Special Chars  ########################################################
   ########################################################
   
   =pod
   
   =item &unescape
   
   Unescape special characters in a string.
   
   Inputs: string to unescape
   
   Returns: The input string with special characters unescaped.
   
   =cut
   
   ########################################################
   ########################################################
 sub unescape {  sub unescape {
     my $str=shift;      my $str=shift;
     $str =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg;      $str =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg;
     return $str;      return $str;
 }  }
   
   ########################################################
   ########################################################
   
   =pod
   
   =item &ishome
   
   Determine if the current machine is the home server for a user.
   The determination is made by checking the filesystem for the users information.
   
   Inputs: $author
   
   Returns: 0 - this is not the authors home server, 1 - this is.
   
   =cut
   
   ########################################################
   ########################################################
   sub ishome {
       my $author=shift;
       $author=~s/\/home\/httpd\/html\/res\/([^\/]*)\/([^\/]*).*/$1\/$2/;
       my ($udom,$uname)=split(/\//,$author);
       my $proname=propath($udom,$uname);
       if (-e $proname) {
    return 1;
       } else {
           return 0;
       }
   }
   
   ########################################################
   ########################################################
   
   =pod
   
   =item &propath
   
   Inputs: user name, user domain
   
   Returns: The full path to the users directory.
   
   =cut
   
   ########################################################
   ########################################################
   sub propath {
       my ($udom,$uname)=@_;
       $udom=~s/\W//g;
       $uname=~s/\W//g;
       my $subdir=$uname.'__';
       $subdir =~ s/(.)(.)(.).*/$1\/$2\/$3/;
       my $proname="$perlvar{'lonUsersDir'}/$udom/$subdir/$uname";
       return $proname;
   } 
   
   ########################################################
   ########################################################
   
   =pod
   
   =item &courselog
   
   Inputs: $path, $command
   
   Returns: unescaped string of values.
   
   =cut
   
   ########################################################
   ########################################################
   sub courselog {
       my ($path,$command)=@_;
       my %filters=();
       foreach (split(/\:/,&unescape($command))) {
    my ($name,$value)=split(/\=/,$_);
           $filters{$name}=$value;
       }
       my @results=();
       open(IN,$path.'/activity.log') or return ('file_error');
       while (my $line=<IN>) {
           chomp($line);
           my ($timestamp,$host,$log)=split(/\:/,$line);
   #
   # $log has the actual log entries; currently still escaped, and
   # %26(timestamp)%3a(url)%3a(user)%3a(domain)
   # then additionally
   # %3aPOST%3a(name)%3d(value)%3a(name)%3d(value)
   # or
   # %3aCSTORE%3a(name)%3d(value)%26(name)%3d(value)
   #
   # get delimiter between timestamped entries to be &&&
           $log=~s/\%26(\d+)\%3a/\&\&\&$1\%3a/g;
   # now go over all log entries 
           foreach (split(/\&\&\&/,&unescape($log))) {
       my ($time,$res,$uname,$udom,$action,@values)=split(/\:/,$_);
               my $values=&unescape(join(':',@values));
               $values=~s/\&/\:/g;
               $res=&unescape($res);
               my $include=1;
               if (($filters{'username'}) && ($uname ne $filters{'username'})) 
                                                                  { $include=0; }
               if (($filters{'domain'}) && ($udom ne $filters{'domain'})) 
                                                                  { $include=0; }
               if (($filters{'url'}) && ($res!~/$filters{'url'}/)) 
                                                                  { $include=0; }
               if (($filters{'start'}) && ($time<$filters{'start'})) 
                                                                  { $include=0; }
               if (($filters{'end'}) && ($time>$filters{'end'})) 
                                                                  { $include=0; }
               if (($filters{'action'} eq 'view') && ($action)) 
                                                                  { $include=0; }
               if (($filters{'action'} eq 'submit') && ($action ne 'POST')) 
                                                                  { $include=0; }
               if (($filters{'action'} eq 'grade') && ($action ne 'CSTORE')) 
                                                                  { $include=0; }
               if ($include) {
          push(@results,($time<1000000000?'0':'').$time.':'.$res.':'.
                                               $uname.':'.$udom.':'.
                                               $action.':'.$values);
               }
          }
       }
       close IN;
       return join('&',sort(@results));
   }
   
   ########################################################
   ########################################################
   
   =pod
   
   =item &userlog
   
   Inputs: $path, $command
   
   Returns: unescaped string of values.
   
   =cut
   
   ########################################################
   ########################################################
   sub userlog {
       my ($path,$command)=@_;
       my %filters=();
       foreach (split(/\:/,&unescape($command))) {
    my ($name,$value)=split(/\=/,$_);
           $filters{$name}=$value;
       }
       my @results=();
       open(IN,$path.'/activity.log') or return ('file_error');
       while (my $line=<IN>) {
           chomp($line);
           my ($timestamp,$host,$log)=split(/\:/,$line);
           $log=&unescape($log);
           my $include=1;
           if (($filters{'start'}) && ($timestamp<$filters{'start'})) 
                                                                { $include=0; }
           if (($filters{'end'}) && ($timestamp>$filters{'end'})) 
                                                                { $include=0; }
           if (($filters{'action'} eq 'log') && ($log!~/^Log/)) { $include=0; }
           if (($filters{'action'} eq 'check') && ($log!~/^Check/)) 
                                                                { $include=0; }
           if ($include) {
      push(@results,$timestamp.':'.$log);
           }
       }
       close IN;
       return join('&',sort(@results));
   }
   
   
   
   
   
   
   
   
   
   
   
   # ----------------------------------- POD (plain old documentation, CPAN style)
   
   =pod
   
   =back
   
   =cut

Removed from v.1.11  
changed lines
  Added in v.1.51


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.