--- loncom/lond 2003/10/08 20:39:42 1.132.2.3 +++ loncom/lond 2003/09/30 10:16:06 1.150 @@ -2,7 +2,7 @@ # The LearningOnline Network # lond "LON Daemon" Server (port "LOND" 5663) # -# $Id: lond,v 1.132.2.3 2003/10/08 20:39:42 albertel Exp $ +# $Id: lond,v 1.150 2003/09/30 10:16:06 foxr Exp $ # # Copyright Michigan State University Board of Trustees # @@ -50,8 +50,63 @@ # population). Since the time averaged connection rate is close to zero # because lonc's purpose is to maintain near continuous connnections, # preforking is not really needed. -### +# 08/xx/2003 Ron Fox: Add management requests. Management requests +# will be validated via a call to ValidateManager. At present, this +# is done by simple host verification. In the future we can modify +# this function to do a certificate check. +# Management functions supported include: +# - pushing /home/httpd/lonTabs/hosts.tab +# - pushing /home/httpd/lonTabs/domain.tab +# 09/08/2003 Ron Fox: Told lond to take care of change logging so we +# don't have to remember it: +# $Log: lond,v $ +# Revision 1.150 2003/09/30 10:16:06 foxr +# Added invocation of apachereload in ReloadApache sub. +# This completes the addtion of the reinit functionality. +# +# Revision 1.149 2003/09/30 09:44:13 foxr +# Tested UpdateHosts ability to +# - Remove live children for hosts that are no longer in the hosts.tab +# - Remove live children for hosts whose IPs have changed in the hosts.tab +# +# Revision 1.148 2003/09/29 10:09:18 foxr +# Put in logic to reinit lond itself (except for apache reload). I don't believe +# this logic works correctly yet, however lond still does everything it used to doso I'll do the commit anyway. +# +# Revision 1.147 2003/09/23 11:23:31 foxr +# Comlplete implementation of reinit functionality. Must still implement +# the actual initialization functionality, but the process can now +# receive the request and either invoke the appropriate internal function or +# signal the correct lonc. +# +# Revision 1.146 2003/09/16 10:28:14 foxr +# ReinitProcess - decode the process selector and produce the associated pid +# filename. Note: While it is possible to test that valid process selectors are +# handled properly I am not able to test that invalid process selectors produce +# the appropriate error as lonManage also blocks the use of invalid process selectors. +# +# Revision 1.145 2003/09/16 10:13:20 foxr +# Added ReinitProcess function to oversee the parsing and processing of the +# reinit: client request. +# +# Revision 1.144 2003/09/16 09:47:01 foxr +# Added skeletal support for SIGUSR2 (update hosts.tab) +# +# Revision 1.143 2003/09/15 10:03:52 foxr +# Completed and tested code for pushfile. +# +# Revision 1.142 2003/09/09 20:47:46 www +# Permanently store chatroom entries in chatroom.log +# +# Revision 1.141 2003/09/08 10:32:07 foxr +# Added PushFile sub This sub oversees the push of a new configuration table file +# Currently supported files are: +# - hosts.tab (transaction pushfile:hosts:contents) +# - domain.tab (transaction pushfile:domain:contents) +# + +use strict; use lib '/home/httpd/lib/perl/'; use LONCAPA::Configuration; @@ -67,16 +122,29 @@ use Authen::Krb4; use Authen::Krb5; use lib '/home/httpd/lib/perl/'; use localauth; +use File::Copy; my $DEBUG = 0; # Non zero to enable debug log entries. my $status=''; my $lastlog=''; -my $VERSION='$Revision: 1.132.2.3 $'; #' stupid emacs +my $VERSION='$Revision: 1.150 $'; #' stupid emacs my $remoteVERSION; my $currenthostid; my $currentdomainid; + +my $client; +my $clientip; + +my $server; +my $thisserver; + +my %hostid; +my %hostdom; +my %hostip; +my %perlvar; # Will have the apache conf defined perl vars. + # # The array below are password error strings." # @@ -116,6 +184,252 @@ my @adderrors = ("ok", # +# GetCertificate: Given a transaction that requires a certificate, +# this function will extract the certificate from the transaction +# request. Note that at this point, the only concept of a certificate +# is the hostname to which we are connected. +# +# Parameter: +# request - The request sent by our client (this parameterization may +# need to change when we really use a certificate granting +# authority. +# +sub GetCertificate { + my $request = shift; + + return $clientip; +} + + +# +# ValidManager: Determines if a given certificate represents a valid manager. +# in this primitive implementation, the 'certificate' is +# just the connecting loncapa client name. This is checked +# against a valid client list in the configuration. +# +# +sub ValidManager { + my $certificate = shift; + + my $hostentry = $hostid{$certificate}; + if ($hostentry ne undef) { + &logthis('Authenticating manager'. + " $hostentry"); + return 1; + } else { + &logthis(' Failed manager authentication '. + "$certificate "); + } +} +# +# CopyFile: Called as part of the process of installing a +# new configuration file. This function copies an existing +# file to a backup file. +# Parameters: +# oldfile - Name of the file to backup. +# newfile - Name of the backup file. +# Return: +# 0 - Failure (errno has failure reason). +# 1 - Success. +# +sub CopyFile { + my $oldfile = shift; + my $newfile = shift; + + # The file must exist: + + if(-e $oldfile) { + + # Read the old file. + + my $oldfh = IO::File->new("< $oldfile"); + if(!$oldfh) { + return 0; + } + my @contents = <$oldfh>; # Suck in the entire file. + + # write the backup file: + + my $newfh = IO::File->new("> $newfile"); + if(!(defined $newfh)){ + return 0; + } + my $lines = scalar @contents; + for (my $i =0; $i < $lines; $i++) { + print $newfh ($contents[$i]); + } + + $oldfh->close; + $newfh->close; + + chmod(0660, $newfile); + + return 1; + + } else { + return 0; + } +} + +# +# InstallFile: Called to install an administrative file: +# - The file is created with .tmp +# - The .tmp file is then mv'd to +# This lugubrious procedure is done to ensure that we are never without +# a valid, even if dated, version of the file regardless of who crashes +# and when the crash occurs. +# +# Parameters: +# Name of the file +# File Contents. +# Return: +# nonzero - success. +# 0 - failure and $! has an errno. +# +sub InstallFile { + my $Filename = shift; + my $Contents = shift; + my $TempFile = $Filename.".tmp"; + + # Open the file for write: + + my $fh = IO::File->new("> $TempFile"); # Write to temp. + if(!(defined $fh)) { + &logthis(' Unable to create '.$TempFile.""); + return 0; + } + # write the contents of the file: + + print $fh ($Contents); + $fh->close; # In case we ever have a filesystem w. locking + + chmod(0660, $TempFile); + + # Now we can move install the file in position. + + move($TempFile, $Filename); + + return 1; +} + +# +# PushFile: Called to do an administrative push of a file. +# - Ensure the file being pushed is one we support. +# - Backup the old file to +# - Separate the contents of the new file out from the +# rest of the request. +# - Write the new file. +# Parameter: +# Request - The entire user request. This consists of a : separated +# string pushfile:tablename:contents. +# NOTE: The contents may have :'s in it as well making things a bit +# more interesting... but not much. +# Returns: +# String to send to client ("ok" or "refused" if bad file). +# +sub PushFile { + my $request = shift; + my ($command, $filename, $contents) = split(":", $request, 3); + + # At this point in time, pushes for only the following tables are + # supported: + # hosts.tab ($filename eq host). + # domain.tab ($filename eq domain). + # Construct the destination filename or reject the request. + # + # lonManage is supposed to ensure this, however this session could be + # part of some elaborate spoof that managed somehow to authenticate. + # + + my $tablefile = $perlvar{'lonTabDir'}.'/'; # need to precede with dir. + if ($filename eq "host") { + $tablefile .= "hosts.tab"; + } elsif ($filename eq "domain") { + $tablefile .= "domain.tab"; + } else { + return "refused"; + } + # + # >copy< the old table to the backup table + # don't rename in case system crashes/reboots etc. in the time + # window between a rename and write. + # + my $backupfile = $tablefile; + $backupfile =~ s/\.tab$/.old/; + if(!CopyFile($tablefile, $backupfile)) { + &logthis(' CopyFile from '.$tablefile." to ".$backupfile." failed "); + return "error:$!"; + } + &logthis(' Pushfile: backed up ' + .$tablefile." to $backupfile"); + + # Install the new file: + + if(!InstallFile($tablefile, $contents)) { + &logthis(' Pushfile: unable to install ' + .$tablefile." $! "); + return "error:$!"; + } + else { + &logthis(' Installed new '.$tablefile + .""); + + } + + + # Indicate success: + + return "ok"; + +} + +# +# Called to re-init either lonc or lond. +# +# Parameters: +# request - The full request by the client. This is of the form +# reinit: +# where is allowed to be either of +# lonc or lond +# +# Returns: +# The string to be sent back to the client either: +# ok - Everything worked just fine. +# error:why - There was a failure and why describes the reason. +# +# +sub ReinitProcess { + my $request = shift; + + + # separate the request (reinit) from the process identifier and + # validate it producing the name of the .pid file for the process. + # + # + my ($junk, $process) = split(":", $request); + my $processpidfile = $perlvar{'lonDaemons'}.'/logs/'; + if($process eq 'lonc') { + $processpidfile = $processpidfile."lonc.pid"; + if (!open(PIDFILE, "< $processpidfile")) { + return "error:Open failed for $processpidfile"; + } + my $loncpid = ; + close(PIDFILE); + logthis(' Reinitializing lonc pid='.$loncpid + .""); + kill("USR2", $loncpid); + } elsif ($process eq 'lond') { + logthis(' Reinitializing self (lond) '); + &UpdateHosts; # Lond is us!! + } else { + &logthis('"); + return "error:Invalid process identifier $process"; + } + return 'ok'; +} + +# # Convert an error return code from lcpasswd to a string value. # sub lcpasswdstrerror { @@ -145,7 +459,7 @@ sub catchexception { $SIG{'QUIT'}='DEFAULT'; $SIG{__DIE__}='DEFAULT'; &logthis("CRITICAL: " - ."ABNORMAL EXIT. Child $$ for server $wasserver died through " + ."ABNORMAL EXIT. Child $$ for server $thisserver died through " ."a crash with this error msg->[$error]"); &logthis('Famous last words: '.$status.' - '.$lastlog); if ($client) { print $client "error: $error\n"; } @@ -165,14 +479,14 @@ $SIG{__DIE__}=\&catchexception; # ---------------------------------- Read loncapa_apache.conf and loncapa.conf &status("Read loncapa.conf and loncapa_apache.conf"); my $perlvarref=LONCAPA::Configuration::read_conf('loncapa.conf'); -my %perlvar=%{$perlvarref}; +%perlvar=%{$perlvarref}; undef $perlvarref; # ----------------------------- Make sure this process is running from user=www my $wwwid=getpwnam('www'); if ($wwwid!=$<) { - $emailto="$perlvar{'lonAdmEMail'},$perlvar{'lonSysEMail'}"; - $subj="LON: $currenthostid User ID mismatch"; + my $emailto="$perlvar{'lonAdmEMail'},$perlvar{'lonSysEMail'}"; + my $subj="LON: $currenthostid User ID mismatch"; system("echo 'User ID mismatch. lond must be run as user www.' |\ mailto $emailto -s '$subj' > /dev/null"); exit 1; @@ -189,22 +503,9 @@ if (-e $pidfile) { if (kill 0 => $pide) { die "already running"; } } -$PREFORK=4; # number of children to maintain, at least four spare - # ------------------------------------------------------------- Read hosts file -open (CONFIG,"$perlvar{'lonTabDir'}/hosts.tab") || die "Can't read host file"; -while ($configline=) { - my ($id,$domain,$role,$name,$ip)=split(/:/,$configline); - chomp($ip); $ip=~s/\D+$//; - $hostid{$ip}=$id; - $hostdom{$id}=$domain; - $hostip{$id}=$ip; - if ($id eq $perlvar{'lonHostID'}) { $thisserver=$name; } - $PREFORK++; -} -close(CONFIG); # establish SERVER socket, bind and listen. $server = IO::Socket::INET->new(LocalPort => $perlvar{'londPort'}, @@ -218,10 +519,8 @@ $server = IO::Socket::INET->new(LocalPor # global variables -$MAX_CLIENTS_PER_CHILD = 50; # number of clients each child should - # process -%children = (); # keys are current child process IDs -$children = 0; # current number of children +my %children = (); # keys are current child process IDs +my $children = 0; # current number of children sub REAPER { # takes care of dead children $SIG{CHLD} = \&REAPER; @@ -250,16 +549,101 @@ sub HUPSMAN { # sig kill 'INT' => keys %children; &logthis("Free socket: ".shutdown($server,2)); # free up socket &logthis("CRITICAL: Restarting"); - unlink("$execdir/logs/lond.pid"); my $execdir=$perlvar{'lonDaemons'}; + unlink("$execdir/logs/lond.pid"); exec("$execdir/lond"); # here we go again } +# +# Kill off hashes that describe the host table prior to re-reading it. +# Hashes affected are: +# %hostid, %hostdom %hostip +# +sub KillHostHashes { + foreach my $key (keys %hostid) { + delete $hostid{$key}; + } + foreach my $key (keys %hostdom) { + delete $hostdom{$key}; + } + foreach my $key (keys %hostip) { + delete $hostip{$key}; + } +} +# +# Read in the host table from file and distribute it into the various hashes: +# +# - %hostid - Indexed by IP, the loncapa hostname. +# - %hostdom - Indexed by loncapa hostname, the domain. +# - %hostip - Indexed by hostid, the Ip address of the host. +sub ReadHostTable { + + open (CONFIG,"$perlvar{'lonTabDir'}/hosts.tab") || die "Can't read host file"; + + while (my $configline=) { + my ($id,$domain,$role,$name,$ip)=split(/:/,$configline); + chomp($ip); $ip=~s/\D+$//; + $hostid{$ip}=$id; + $hostdom{$id}=$domain; + $hostip{$id}=$ip; + if ($id eq $perlvar{'lonHostID'}) { $thisserver=$name; } + } + close(CONFIG); +} +# +# Reload the Apache daemon's state. +# This is done by invoking /home/httpd/perl/apachereload +# a setuid perl script that can be root for us to do this job. +# +sub ReloadApache { + my $execdir = $perlvar{'lonDaemons'}; + my $script = $execdir."/apachereload"; + system($script); +} + +# +# Called in response to a USR2 signal. +# - Reread hosts.tab +# - All children connected to hosts that were removed from hosts.tab +# are killed via SIGINT +# - All children connected to previously existing hosts are sent SIGUSR1 +# - Our internal hosts hash is updated to reflect the new contents of +# hosts.tab causing connections from hosts added to hosts.tab to +# now be honored. +# +sub UpdateHosts { + logthis(' Updating connections '); + # + # The %children hash has the set of IP's we currently have children + # on. These need to be matched against records in the hosts.tab + # Any ip's no longer in the table get killed off they correspond to + # either dropped or changed hosts. Note that the re-read of the table + # will take care of new and changed hosts as connections come into being. + + + KillHostHashes; + ReadHostTable; + + foreach my $child (keys %children) { + my $childip = $children{$child}; + if(!$hostid{$childip}) { + logthis(' UpdateHosts killing child ' + ." $child for ip $childip "); + kill('INT', $child); + } else { + logthis(' keeping child for ip ' + ." $childip (pid=$child) "); + } + } + ReloadApache; +} + + sub checkchildren { &initnewstatus(); &logstatus(); &logthis('Going to check on the children'); - $docdir=$perlvar{'lonDocRoot'}; + my $docdir=$perlvar{'lonDocRoot'}; foreach (sort keys %children) { sleep 1; unless (kill 'USR1' => $_) { @@ -286,7 +670,7 @@ sub checkchildren { } } $SIG{ALRM} = 'DEFAULT'; - $SIG{__DIE__} = \&catchexception; + $SIG{__DIE__} = \&cathcexception; } # --------------------------------------------------------------------- Logging @@ -331,7 +715,7 @@ sub initnewstatus { my $local=localtime($now); print $fh "LOND status $local - parent $$\n\n"; opendir(DIR,"$docdir/lon-status/londchld"); - while ($filename=readdir(DIR)) { + while (my $filename=readdir(DIR)) { unlink("$docdir/lon-status/londchld/$filename"); } closedir(DIR); @@ -473,7 +857,7 @@ sub ishome { # ======================================================= Continue main program # ---------------------------------------------------- Fork once and dissociate -$fpid=fork; +my $fpid=fork; exit if $fpid; die "Couldn't fork: $!" unless defined ($fpid); @@ -481,7 +865,7 @@ POSIX::setsid() or die "Can't start new # ------------------------------------------------------- Write our PID on disk -$execdir=$perlvar{'lonDaemons'}; +my $execdir=$perlvar{'lonDaemons'}; open (PIDSAVE,">$execdir/logs/lond.pid"); print PIDSAVE "$$\n"; close(PIDSAVE); @@ -497,8 +881,11 @@ $SIG{CHLD} = \&REAPER; $SIG{INT} = $SIG{TERM} = \&HUNTSMAN; $SIG{HUP} = \&HUPSMAN; $SIG{USR1} = \&checkchildren; +$SIG{USR2} = \&UpdateHosts; +# Read the host hashes: +ReadHostTable; # -------------------------------------------------------------- # Accept connections. When a connection comes in, it is validated @@ -511,7 +898,6 @@ while (1) { } sub make_new_child { - my $client; my $pid; my $cipher; my $sigset; @@ -522,14 +908,25 @@ sub make_new_child { $sigset = POSIX::SigSet->new(SIGINT); sigprocmask(SIG_BLOCK, $sigset) or die "Can't block SIGINT for fork: $!\n"; - + die "fork: $!" unless defined ($pid = fork); + + $client->sockopt(SO_KEEPALIVE, 1); # Enable monitoring of + # connection liveness. + + # + # Figure out who we're talking to so we can record the peer in + # the pid hash. + # + my $caller = getpeername($client); + my ($port,$iaddr)=unpack_sockaddr_in($caller); + $clientip=inet_ntoa($iaddr); if ($pid) { # Parent records the child's birth and returns. sigprocmask(SIG_UNBLOCK, $sigset) or die "Can't unblock SIGINT for fork: $!\n"; - $children{$pid} = 1; + $children{$pid} = $clientip; $children++; &status('Started child '.$pid); return; @@ -547,7 +944,7 @@ sub make_new_child { sigprocmask(SIG_UNBLOCK, $sigset) or die "Can't unblock SIGINT for fork: $!\n"; - $tmpsnum=0; + my $tmpsnum=0; #---------------------------------------------------- kerberos 5 initialization &Authen::Krb5::init_context(); &Authen::Krb5::init_ets(); @@ -556,12 +953,8 @@ sub make_new_child { # ============================================================================= # do something with the connection # ----------------------------------------------------------------------------- - $client->sockopt(SO_KEEPALIVE, 1);# Enable monitoring of - # connection liveness. - # see if we know client and check for spoof IP by challenge - my $caller = getpeername($client); - my ($port,$iaddr)=unpack_sockaddr_in($caller); - my $clientip=inet_ntoa($iaddr); + # see if we know client and check for spoof IP by challenge + my $clientrec=($hostid{$clientip} ne undef); &logthis( "INFO: Connection, $clientip ($hostid{$clientip})" @@ -640,8 +1033,8 @@ sub make_new_child { if ($userinput =~ /^ping/) { print $client "$currenthostid\n"; # ------------------------------------------------------------------------ pong - } elsif ($userinput =~ /^pong/) { - $reply=reply("ping",$hostid{$clientip}); + }elsif ($userinput =~ /^pong/) { + my $reply=&reply("ping",$hostid{$clientip}); print $client "$currenthostid:$reply\n"; # ------------------------------------------------------------------------ ekey } elsif ($userinput =~ /^ekey/) { @@ -671,6 +1064,10 @@ sub make_new_child { } elsif ($userinput =~ /^userload/) { my $userloadpercent=&userload(); print $client "$userloadpercent\n"; + +# +# Transactions requiring encryption: +# # ----------------------------------------------------------------- currentauth } elsif ($userinput =~ /^currentauth/) { if ($wasenc==1) { @@ -685,6 +1082,33 @@ sub make_new_child { } else { print $client "refused\n"; } +#--------------------------------------------------------------------- pushfile + } elsif($userinput =~ /^pushfile/) { + if($wasenc == 1) { + my $cert = GetCertificate($userinput); + if(ValidManager($cert)) { + my $reply = PushFile($userinput); + print $client "$reply\n"; + } else { + print $client "refused\n"; + } + } else { + print $client "refused\n"; + } +#--------------------------------------------------------------------- reinit + } elsif($userinput =~ /^reinit/) { + if ($wasenc == 1) { + my $cert = GetCertificate($userinput); + if(ValidManager($cert)) { + chomp($userinput); + my $reply = ReinitProcess($userinput); + print $client "$reply\n"; + } else { + print $client "refused\n"; + } + } else { + print $client "refused\n"; + } # ------------------------------------------------------------------------ auth } elsif ($userinput =~ /^auth/) { if ($wasenc==1) { @@ -725,7 +1149,7 @@ sub make_new_child { } } } elsif ($howpwd eq 'krb4') { - $null=pack("C",0); + my $null=pack("C",0); unless ($upass=~/$null/) { my $krb4_error = &Authen::Krb4::get_pw_in_tkt ($uname,"",$contentpwd,'krbtgt', @@ -742,7 +1166,7 @@ sub make_new_child { } } } elsif ($howpwd eq 'krb5') { - $null=pack("C",0); + my $null=pack("C",0); unless ($upass=~/$null/) { my $krbclient=&Authen::Krb5::parse_name($uname.'@'.$contentpwd); my $krbservice="krbtgt/".$contentpwd."\@".$contentpwd; @@ -796,10 +1220,18 @@ sub make_new_child { my $salt=time; $salt=substr($salt,6,2); my $ncpass=crypt($npass,$salt); - { my $pf = IO::File->new(">$passfilename"); - print $pf "internal:$ncpass\n"; } - &logthis("Result of password change for $uname: pwchange_success"); - print $client "ok\n"; + { + my $pf; + if ($pf = IO::File->new(">$passfilename")) { + print $pf "internal:$ncpass\n"; + &logthis("Result of password change for $uname: pwchange_success"); + print $client "ok\n"; + } else { + &logthis("Unable to open $uname passwd to change password"); + print $client "non_authorized\n"; + } + } + } else { print $client "non_authorized\n"; } @@ -867,10 +1299,10 @@ sub make_new_child { } elsif ($udom ne $currentdomainid) { print $client "not_right_domain\n"; } else { - @fpparts=split(/\//,$proname); - $fpnow=$fpparts[0].'/'.$fpparts[1].'/'.$fpparts[2]; - $fperror=''; - for ($i=3;$i<=$#fpparts;$i++) { + my @fpparts=split(/\//,$proname); + my $fpnow=$fpparts[0].'/'.$fpparts[1].'/'.$fpparts[2]; + my $fperror=''; + for (my $i=3;$i<=$#fpparts;$i++) { $fpnow.='/'.$fpparts[$i]; unless (-e $fpnow) { unless (mkdir($fpnow,0777)) { @@ -934,11 +1366,11 @@ sub make_new_child { $uid,$gid,$rdev,$size, $atime,$mtime,$ctime, $blksize,$blocks)=stat($fname); - $now=time; - $since=$now-$atime; + my $now=time; + my $since=$now-$atime; if ($since>$perlvar{'lonExpire'}) { - $reply= - reply("unsub:$fname","$hostid{$clientip}"); + my $reply= + &reply("unsub:$fname","$hostid{$clientip}"); unlink("$fname"); } else { my $transname="$fname.in.transfer"; @@ -1009,16 +1441,16 @@ sub make_new_child { } elsif ($userinput =~ /^tokenauthuserfile/) { my ($cmd,$fname,$session)=split(/:/,$userinput); chomp($session); - $reply='non_auth'; + my $reply='non_auth'; if (open(ENVIN,$perlvar{'lonIDsDir'}.'/'. - $session.'.id')) { - while ($line=) { - if ($line=~/userfile\.$fname\=/) { $reply='ok'; } - } - close(ENVIN); - print $client $reply."\n"; + $session.'.id')) { + while (my $line=) { + if ($line=~/userfile\.$fname\=/) { $reply='ok'; } + } + close(ENVIN); + print $client $reply."\n"; } else { - print $client "invalid_token\n"; + print $client "invalid_token\n"; } # ----------------------------------------------------------------- unsubscribe } elsif ($userinput =~ /^unsub/) { @@ -1069,9 +1501,10 @@ sub make_new_child { ) { print $hfh "P:$now:$what\n"; } } my @pairs=split(/\&/,$what); - if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_WRCREAT(),0640)) { - foreach $pair (@pairs) { - ($key,$value)=split(/=/,$pair); + my %hash; + if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_WRCREAT(),0640)) { + foreach my $pair (@pairs) { + my ($key,$value)=split(/=/,$pair); $hash{$key}=$value; } if (untie(%hash)) { @@ -1111,14 +1544,14 @@ sub make_new_child { } } my @pairs=split(/\&/,$what); - if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_WRCREAT(),0640)) { - foreach $pair (@pairs) { - ($key,$value)=split(/=/,$pair); + my %hash; + if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_WRCREAT(),0640)) { + foreach my $pair (@pairs) { + my ($key,$value)=split(/=/,$pair); &ManagePermissions($key, $udom, $uname, &GetAuthType( $udom, $uname)); $hash{$key}=$value; - } if (untie(%hash)) { print $client "ok\n"; @@ -1157,10 +1590,10 @@ sub make_new_child { } } my @rolekeys=split(/\&/,$what); - if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_WRCREAT(),0640)) { - foreach $key (@rolekeys) { + my %hash; + if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_WRCREAT(),0640)) { + foreach my $key (@rolekeys) { delete $hash{$key}; - } if (untie(%hash)) { print $client "ok\n"; @@ -1187,8 +1620,9 @@ sub make_new_child { my @queries=split(/\&/,$what); my $proname=propath($udom,$uname); my $qresult=''; - if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_READER(),0640)) { - for ($i=0;$i<=$#queries;$i++) { + my %hash; + if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_READER(),0640)) { + for (my $i=0;$i<=$#queries;$i++) { $qresult.="$hash{$queries[$i]}&"; } if (untie(%hash)) { @@ -1219,8 +1653,9 @@ sub make_new_child { my @queries=split(/\&/,$what); my $proname=propath($udom,$uname); my $qresult=''; - if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_READER(),0640)) { - for ($i=0;$i<=$#queries;$i++) { + my %hash; + if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_READER(),0640)) { + for (my $i=0;$i<=$#queries;$i++) { $qresult.="$hash{$queries[$i]}&"; } if (untie(%hash)) { @@ -1265,8 +1700,9 @@ sub make_new_child { ) { print $hfh "D:$now:$what\n"; } } my @keys=split(/\&/,$what); - if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_WRCREAT(),0640)) { - foreach $key (@keys) { + my %hash; + if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_WRCREAT(),0640)) { + foreach my $key (@keys) { delete($hash{$key}); } if (untie(%hash)) { @@ -1289,8 +1725,9 @@ sub make_new_child { $namespace=~s/\W//g; my $proname=propath($udom,$uname); my $qresult=''; - if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_READER(),0640)) { - foreach $key (keys %hash) { + my %hash; + if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_READER(),0640)) { + foreach my $key (keys %hash) { $qresult.="$key&"; } if (untie(%hash)) { @@ -1314,6 +1751,7 @@ sub make_new_child { $namespace=~s/\W//g; my $qresult=''; my $proname=propath($udom,$uname); + my %hash; if (tie(%hash,'GDBM_File', "$proname/$namespace.db", &GDBM_READER(),0640)) { @@ -1364,9 +1802,10 @@ sub make_new_child { } my $qresult=''; my $proname=propath($udom,$uname); - if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_READER(),0640)) { + my %hash; + if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_READER(),0640)) { study($regexp); - while (($key,$value) = each(%hash)) { + while (my ($key,$value) = each(%hash)) { if ($regexp eq '.') { $qresult.=$key.'='.$value.'&'; } else { @@ -1406,15 +1845,15 @@ sub make_new_child { ) { print $hfh "P:$now:$rid:$what\n"; } } my @pairs=split(/\&/,$what); - - if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_WRCREAT(),0640)) { + my %hash; + if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_WRCREAT(),0640)) { my @previouskeys=split(/&/,$hash{"keys:$rid"}); my $key; $hash{"version:$rid"}++; my $version=$hash{"version:$rid"}; my $allkeys=''; - foreach $pair (@pairs) { - ($key,$value)=split(/=/,$pair); + foreach my $pair (@pairs) { + my ($key,$value)=split(/=/,$pair); $allkeys.=$key.':'; $hash{"$version:$rid:$key"}=$value; } @@ -1445,7 +1884,8 @@ sub make_new_child { chomp($rid); my $proname=propath($udom,$uname); my $qresult=''; - if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_READER(),0640)) { + my %hash; + if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_READER(),0640)) { my $version=$hash{"version:$rid"}; $qresult.="version=$version&"; my $scope; @@ -1522,9 +1962,10 @@ sub make_new_child { "$perlvar{'lonUsersDir'}/$udom/nohist_courseids"; my $now=time; my @pairs=split(/\&/,$what); - if (tie(%hash,'GDBM_File',"$proname.db",&GDBM_WRCREAT(),0640)) { - foreach $pair (@pairs) { - ($key,$value)=split(/=/,$pair); + my %hash; + if (tie(%hash,'GDBM_File',"$proname.db",&GDBM_WRCREAT(),0640)) { + foreach my $pair (@pairs) { + my ($key,$value)=split(/=/,$pair); $hash{$key}=$value.':'.$now; } if (untie(%hash)) { @@ -1552,11 +1993,12 @@ sub make_new_child { my $qresult=''; my $proname= "$perlvar{'lonUsersDir'}/$udom/nohist_courseids"; - if (tie(%hash,'GDBM_File',"$proname.db",&GDBM_READER(),0640)) { - while (($key,$value) = each(%hash)) { + my %hash; + if (tie(%hash,'GDBM_File',"$proname.db",&GDBM_READER(),0640)) { + while (my ($key,$value) = each(%hash)) { my ($descr,$lasttime)=split(/\:/,$value); if ($lasttime<$since) { next; } - if ($regexp eq '.') { + if ($description eq '.') { $qresult.=$key.'='.$descr.'&'; } else { my $unescapeVal = &unescape($descr); @@ -1592,9 +2034,10 @@ sub make_new_child { ) { print $hfh "P:$now:$what\n"; } } my @pairs=split(/\&/,$what); - if (tie(%hash,'GDBM_File',"$proname.db",&GDBM_WRCREAT(),0640)) { - foreach $pair (@pairs) { - ($key,$value)=split(/=/,$pair); + my %hash; + if (tie(%hash,'GDBM_File',"$proname.db",&GDBM_WRCREAT(),0640)) { + foreach my $pair (@pairs) { + my ($key,$value)=split(/=/,$pair); $hash{$key}=$value; } if (untie(%hash)) { @@ -1617,17 +2060,18 @@ sub make_new_child { my $proname="$perlvar{'lonUsersDir'}/$udom/ids"; my @queries=split(/\&/,$what); my $qresult=''; - if (tie(%hash,'GDBM_File',"$proname.db",&GDBM_READER(),0640)) { - for ($i=0;$i<=$#queries;$i++) { + my %hash; + if (tie(%hash,'GDBM_File',"$proname.db",&GDBM_READER(),0640)) { + for (my $i=0;$i<=$#queries;$i++) { $qresult.="$hash{$queries[$i]}&"; } if (untie(%hash)) { - $qresult=~s/\&$//; - print $client "$qresult\n"; + $qresult=~s/\&$//; + print $client "$qresult\n"; } else { - print $client "error: ".($!+0) - ." untie(GDBM) Failed ". - "while attempting idget\n"; + print $client "error: ".($!+0) + ." untie(GDBM) Failed ". + "while attempting idget\n"; } } else { print $client "error: ".($!+0) @@ -1709,6 +2153,19 @@ sub make_new_child { } if ($ulsout eq '') { $ulsout='empty'; } print $client "$ulsout\n"; +# ----------------------------------------------------------------- setannounce + } elsif ($userinput =~ /^setannounce/) { + my ($cmd,$announcement)=split(/:/,$userinput); + chomp($announcement); + $announcement=&unescape($announcement); + if (my $store=IO::File->new('>'.$perlvar{'lonDocRoot'}. + '/announcement.txt')) { + print $store $announcement; + close $store; + print $client "ok\n"; + } else { + print $client "error: ".($!+0)."\n"; + } # ------------------------------------------------------------------ Hanging up } elsif (($userinput =~ /^exit/) || ($userinput =~ /^init/)) { @@ -1772,11 +2229,11 @@ sub ManagePermissions my $authtype= shift; # See if the request is of the form /$domain/_au - + &logthis("ruequest is $request"); if($request =~ /^(\/$domain\/_au)$/) { # It's an author rolesput... my $execdir = $perlvar{'lonDaemons'}; my $userhome= "/home/$user" ; - Debug("system $execdir/lchtmldir $userhome $system $authtype"); + &logthis("system $execdir/lchtmldir $userhome $user $authtype"); system("$execdir/lchtmldir $userhome $user $authtype"); } } @@ -1819,6 +2276,7 @@ sub addline { my $found=0; my $expr='^'.$hostid.':'.$ip.':'; $expr =~ s/\./\\\./g; + my $sh; if ($sh=IO::File->new("$fname.subscription")) { while (my $subline=<$sh>) { if ($subline !~ /$expr/) {$contents.= $subline;} else {$found=1;} @@ -1843,7 +2301,7 @@ sub getchat { untie %hash; } my @participants=(); - $cutoff=time-60; + my $cutoff=time-60; if (tie(%hash,'GDBM_File',"$proname/nohist_inchatroom.db", &GDBM_WRCREAT(),0640)) { $hash{$uname.':'.$udom}=time; @@ -1862,10 +2320,10 @@ sub chatadd { my %hash; my $proname=&propath($cdom,$cname); my @entries=(); + my $time=time; if (tie(%hash,'GDBM_File',"$proname/nohist_chatroom.db", &GDBM_WRCREAT(),0640)) { @entries=map { $_.':'.$hash{$_} } sort keys %hash; - my $time=time; my ($lastid)=($entries[$#entries]=~/^(\w+)\:/); my ($thentime,$idnum)=split(/\_/,$lastid); my $newid=$time.'_000000'; @@ -1885,6 +2343,12 @@ sub chatadd { } untie %hash; } + { + my $hfh; + if ($hfh=IO::File->new(">>$proname/chatroom.log")) { + print $hfh "$time:".&unescape($newchat)."\n"; + } + } } sub unsub { @@ -1921,24 +2385,24 @@ sub currentversion { } if (-e $fname) { $version=1; } if (-e $ulsdir) { - if(-d $ulsdir) { - if (opendir(LSDIR,$ulsdir)) { - - while ($ulsfn=readdir(LSDIR)) { + if(-d $ulsdir) { + if (opendir(LSDIR,$ulsdir)) { + my $ulsfn; + while ($ulsfn=readdir(LSDIR)) { # see if this is a regular file (ignore links produced earlier) - my $thisfile=$ulsdir.'/'.$ulsfn; - unless (-l $thisfile) { - if ($thisfile=~/\Q$fnamere1\E(\d+)\Q$fnamere2\E/) { - if ($1>$version) { $version=$1; } - } - } - } - closedir(LSDIR); - $version++; - } - } - } - return $version; + my $thisfile=$ulsdir.'/'.$ulsfn; + unless (-l $thisfile) { + if ($thisfile=~/\Q$fnamere1\E(\d+)\Q$fnamere2\E/) { + if ($1>$version) { $version=$1; } + } + } + } + closedir(LSDIR); + $version++; + } + } + } + return $version; } sub thisversion { @@ -1977,7 +2441,7 @@ sub subscribe { $result="directory\n"; } else { if (-e "$fname.$hostid{$clientip}") {&unsub($fname,$clientip);} - $now=time; + my $now=time; my $found=&addline($fname,$hostid{$clientip},$clientip, "$hostid{$clientip}:$clientip:$now\n"); if ($found) { $result="$fname\n"; } @@ -2203,6 +2667,17 @@ each connection is logged. =item * +SIGUSR2 + +Parent Signal assignment: + $SIG{USR2} = \&UpdateHosts + +Child signal assignment: + NONE + + +=item * + SIGCHLD Parent signal assignment: @@ -2363,6 +2838,17 @@ Send along temporarily stored informatio List part of a user's directory. +=item pushtable + +Pushes a file in /home/httpd/lonTab directory. Currently limited to: +hosts.tab and domain.tab. The old file is copied to *.tab.backup but +must be restored manually in case of a problem with the new table file. +pushtable requires that the request be encrypted and validated via +ValidateManager. The form of the command is: +enc:pushtable tablename \n +where pushtable, tablename and will be encrypted, but \n is a +cleartext newline. + =item Hanging up (exit or init) What to do when a client tells the server that they (the client) @@ -2373,6 +2859,7 @@ are leaving the network. If B is sent an unknown command (not in the list above), it replys to the client "unknown_cmd". + =item UNKNOWN CLIENT If the anti-spoofing algorithm cannot verify the client,