version 1.166, 2003/12/22 11:03:37
|
version 1.184, 2004/03/16 20:48:49
|
Line 10
|
Line 10
|
# |
# |
# LON-CAPA is free software; you can redistribute it and/or modify |
# 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 |
# it under the terms of the GNU General Public License as published by |
# the Free Software Foundation; either version 2 of the License, or |
# the Free Software Foundation; either version 2 of the License, or |
# (at your option) any later version. |
# (at your option) any later version. |
# |
# |
# LON-CAPA is distributed in the hope that it will be useful, |
# LON-CAPA is distributed in the hope that it will be useful, |
Line 46 use Authen::Krb5;
|
Line 46 use Authen::Krb5;
|
use lib '/home/httpd/lib/perl/'; |
use lib '/home/httpd/lib/perl/'; |
use localauth; |
use localauth; |
use File::Copy; |
use File::Copy; |
|
use LONCAPA::ConfigFileEdit; |
|
|
my $DEBUG = 0; # Non zero to enable debug log entries. |
my $DEBUG = 0; # Non zero to enable debug log entries. |
|
|
Line 349 sub InstallFile {
|
Line 350 sub InstallFile {
|
|
|
return 1; |
return 1; |
} |
} |
|
# |
|
# ConfigFileFromSelector: converts a configuration file selector |
|
# (one of host or domain at this point) into a |
|
# configuration file pathname. |
|
# |
|
# Parameters: |
|
# selector - Configuration file selector. |
|
# Returns: |
|
# Full path to the file or undef if the selector is invalid. |
|
# |
|
sub ConfigFileFromSelector { |
|
my $selector = shift; |
|
my $tablefile; |
|
|
|
my $tabledir = $perlvar{'lonTabDir'}.'/'; |
|
if ($selector eq "hosts") { |
|
$tablefile = $tabledir."hosts.tab"; |
|
} elsif ($selector eq "domain") { |
|
$tablefile = $tabledir."domain.tab"; |
|
} else { |
|
return undef; |
|
} |
|
return $tablefile; |
|
|
|
} |
# |
# |
# PushFile: Called to do an administrative push of a file. |
# PushFile: Called to do an administrative push of a file. |
# - Ensure the file being pushed is one we support. |
# - Ensure the file being pushed is one we support. |
Line 379 sub PushFile {
|
Line 404 sub PushFile {
|
# part of some elaborate spoof that managed somehow to authenticate. |
# part of some elaborate spoof that managed somehow to authenticate. |
# |
# |
|
|
my $tablefile = $perlvar{'lonTabDir'}.'/'; # need to precede with dir. |
|
if ($filename eq "host") { |
my $tablefile = ConfigFileFromSelector($filename); |
$tablefile .= "hosts.tab"; |
if(! (defined $tablefile)) { |
} elsif ($filename eq "domain") { |
|
$tablefile .= "domain.tab"; |
|
} else { |
|
return "refused"; |
return "refused"; |
} |
} |
# |
# |
Line 476 sub ReinitProcess {
|
Line 498 sub ReinitProcess {
|
} |
} |
return 'ok'; |
return 'ok'; |
} |
} |
|
# Validate a line in a configuration file edit script: |
|
# Validation includes: |
|
# - Ensuring the command is valid. |
|
# - Ensuring the command has sufficient parameters |
|
# Parameters: |
|
# scriptline - A line to validate (\n has been stripped for what it's worth). |
|
# |
|
# Return: |
|
# 0 - Invalid scriptline. |
|
# 1 - Valid scriptline |
|
# NOTE: |
|
# Only the command syntax is checked, not the executability of the |
|
# command. |
|
# |
|
sub isValidEditCommand { |
|
my $scriptline = shift; |
|
|
|
# Line elements are pipe separated: |
|
|
|
my ($command, $key, $newline) = split(/\|/, $scriptline); |
|
&logthis('<font color="green"> isValideditCommand checking: '. |
|
"Command = '$command', Key = '$key', Newline = '$newline' </font>\n"); |
|
|
|
if ($command eq "delete") { |
|
# |
|
# key with no newline. |
|
# |
|
if( ($key eq "") || ($newline ne "")) { |
|
return 0; # Must have key but no newline. |
|
} else { |
|
return 1; # Valid syntax. |
|
} |
|
} elsif ($command eq "replace") { |
|
# |
|
# key and newline: |
|
# |
|
if (($key eq "") || ($newline eq "")) { |
|
return 0; |
|
} else { |
|
return 1; |
|
} |
|
} elsif ($command eq "append") { |
|
if (($key ne "") && ($newline eq "")) { |
|
return 1; |
|
} else { |
|
return 0; |
|
} |
|
} else { |
|
return 0; # Invalid command. |
|
} |
|
return 0; # Should not get here!!! |
|
} |
|
# |
|
# ApplyEdit - Applies an edit command to a line in a configuration |
|
# file. It is the caller's responsiblity to validate the |
|
# edit line. |
|
# Parameters: |
|
# $directive - A single edit directive to apply. |
|
# Edit directives are of the form: |
|
# append|newline - Appends a new line to the file. |
|
# replace|key|newline - Replaces the line with key value 'key' |
|
# delete|key - Deletes the line with key value 'key'. |
|
# $editor - A config file editor object that contains the |
|
# file being edited. |
|
# |
|
sub ApplyEdit { |
|
my $directive = shift; |
|
my $editor = shift; |
|
|
|
# Break the directive down into its command and its parameters |
|
# (at most two at this point. The meaning of the parameters, if in fact |
|
# they exist depends on the command). |
|
|
|
my ($command, $p1, $p2) = split(/\|/, $directive); |
|
|
|
if($command eq "append") { |
|
$editor->Append($p1); # p1 - key p2 null. |
|
} elsif ($command eq "replace") { |
|
$editor->ReplaceLine($p1, $p2); # p1 - key p2 = newline. |
|
} elsif ($command eq "delete") { |
|
$editor->DeleteLine($p1); # p1 - key p2 null. |
|
} else { # Should not get here!!! |
|
die "Invalid command given to ApplyEdit $command" |
|
} |
|
} |
|
# |
|
# AdjustOurHost: |
|
# Adjusts a host file stored in a configuration file editor object |
|
# for the true IP address of this host. This is necessary for hosts |
|
# that live behind a firewall. |
|
# Those hosts have a publicly distributed IP of the firewall, but |
|
# internally must use their actual IP. We assume that a given |
|
# host only has a single IP interface for now. |
|
# Formal Parameters: |
|
# editor - The configuration file editor to adjust. This |
|
# editor is assumed to contain a hosts.tab file. |
|
# Strategy: |
|
# - Figure out our hostname. |
|
# - Lookup the entry for this host. |
|
# - Modify the line to contain our IP |
|
# - Do a replace for this host. |
|
sub AdjustOurHost { |
|
my $editor = shift; |
|
|
|
# figure out who I am. |
|
|
|
my $myHostName = $perlvar{'lonHostID'}; # LonCAPA hostname. |
|
|
|
# Get my host file entry. |
|
|
|
my $ConfigLine = $editor->Find($myHostName); |
|
if(! (defined $ConfigLine)) { |
|
die "AdjustOurHost - no entry for me in hosts file $myHostName"; |
|
} |
|
# figure out my IP: |
|
# Use the config line to get my hostname. |
|
# Use gethostbyname to translate that into an IP address. |
|
# |
|
my ($id,$domain,$role,$name,$ip,$maxcon,$idleto,$mincon) = split(/:/,$ConfigLine); |
|
my $BinaryIp = gethostbyname($name); |
|
my $ip = inet_ntoa($ip); |
|
# |
|
# Reassemble the config line from the elements in the list. |
|
# Note that if the loncnew items were not present before, they will |
|
# be now even if they would be empty |
|
# |
|
my $newConfigLine = $id; |
|
foreach my $item ($domain, $role, $name, $ip, $maxcon, $idleto, $mincon) { |
|
$newConfigLine .= ":".$item; |
|
} |
|
# Replace the line: |
|
|
|
$editor->ReplaceLine($id, $newConfigLine); |
|
|
|
} |
|
# |
|
# ReplaceConfigFile: |
|
# Replaces a configuration file with the contents of a |
|
# configuration file editor object. |
|
# This is done by: |
|
# - Copying the target file to <filename>.old |
|
# - Writing the new file to <filename>.tmp |
|
# - Moving <filename.tmp> -> <filename> |
|
# This laborious process ensures that the system is never without |
|
# a configuration file that's at least valid (even if the contents |
|
# may be dated). |
|
# Parameters: |
|
# filename - Name of the file to modify... this is a full path. |
|
# editor - Editor containing the file. |
|
# |
|
sub ReplaceConfigFile { |
|
my $filename = shift; |
|
my $editor = shift; |
|
|
|
CopyFile ($filename, $filename.".old"); |
|
|
|
my $contents = $editor->Get(); # Get the contents of the file. |
|
|
|
InstallFile($filename, $contents); |
|
} |
|
# |
|
# |
|
# Called to edit a configuration table file |
|
# Parameters: |
|
# request - The entire command/request sent by lonc or lonManage |
|
# Return: |
|
# The reply to send to the client. |
|
# |
|
sub EditFile { |
|
my $request = shift; |
|
|
|
# Split the command into it's pieces: edit:filetype:script |
|
|
|
my ($request, $filetype, $script) = split(/:/, $request,3); # : in script |
|
|
|
# Check the pre-coditions for success: |
|
|
|
if($request != "edit") { # Something is amiss afoot alack. |
|
return "error:edit request detected, but request != 'edit'\n"; |
|
} |
|
if( ($filetype ne "hosts") && |
|
($filetype ne "domain")) { |
|
return "error:edit requested with invalid file specifier: $filetype \n"; |
|
} |
|
|
|
# Split the edit script and check it's validity. |
|
|
|
my @scriptlines = split(/\n/, $script); # one line per element. |
|
my $linecount = scalar(@scriptlines); |
|
for(my $i = 0; $i < $linecount; $i++) { |
|
chomp($scriptlines[$i]); |
|
if(!isValidEditCommand($scriptlines[$i])) { |
|
return "error:edit with bad script line: '$scriptlines[$i]' \n"; |
|
} |
|
} |
|
|
|
# Execute the edit operation. |
|
# - Create a config file editor for the appropriate file and |
|
# - execute each command in the script: |
|
# |
|
my $configfile = ConfigFileFromSelector($filetype); |
|
if (!(defined $configfile)) { |
|
return "refused\n"; |
|
} |
|
my $editor = ConfigFileEdit->new($configfile); |
|
|
|
for (my $i = 0; $i < $linecount; $i++) { |
|
ApplyEdit($scriptlines[$i], $editor); |
|
} |
|
# If the file is the host file, ensure that our host is |
|
# adjusted to have our ip: |
|
# |
|
if($filetype eq "host") { |
|
AdjustOurHost($editor); |
|
} |
|
# Finally replace the current file with our file. |
|
# |
|
ReplaceConfigFile($configfile, $editor); |
|
|
|
return "ok\n"; |
|
} |
# |
# |
# Convert an error return code from lcpasswd to a string value. |
# Convert an error return code from lcpasswd to a string value. |
# |
# |
Line 570 $server = IO::Socket::INET->new(LocalPor
|
Line 812 $server = IO::Socket::INET->new(LocalPor
|
# global variables |
# global variables |
|
|
my %children = (); # keys are current child process IDs |
my %children = (); # keys are current child process IDs |
my $children = 0; # current number of children |
|
|
|
sub REAPER { # takes care of dead children |
sub REAPER { # takes care of dead children |
$SIG{CHLD} = \&REAPER; |
$SIG{CHLD} = \&REAPER; |
&status("Handling child death"); |
&status("Handling child death"); |
my $pid = wait; |
my $pid; |
if (defined($children{$pid})) { |
do { |
&logthis("Child $pid died"); |
$pid = waitpid(-1,&WNOHANG()); |
$children --; |
if (defined($children{$pid})) { |
delete $children{$pid}; |
&logthis("Child $pid died"); |
} else { |
delete($children{$pid}); |
&logthis("Unknown Child $pid died"); |
} elsif ($pid > 0) { |
|
&logthis("Unknown Child $pid died"); |
|
} |
|
} while ( $pid > 0 ); |
|
foreach my $child (keys(%children)) { |
|
$pid = waitpid($child,&WNOHANG()); |
|
if ($pid > 0) { |
|
&logthis("Child $child - $pid looks like we missed it's death"); |
|
delete($children{$pid}); |
|
} |
} |
} |
&status("Finished Handling child death"); |
&status("Finished Handling child death"); |
} |
} |
Line 637 sub ReadHostTable {
|
Line 887 sub ReadHostTable {
|
open (CONFIG,"$perlvar{'lonTabDir'}/hosts.tab") || die "Can't read host file"; |
open (CONFIG,"$perlvar{'lonTabDir'}/hosts.tab") || die "Can't read host file"; |
|
|
while (my $configline=<CONFIG>) { |
while (my $configline=<CONFIG>) { |
my ($id,$domain,$role,$name,$ip)=split(/:/,$configline); |
if (!($configline =~ /^\s*\#/)) { |
chomp($ip); $ip=~s/\D+$//; |
my ($id,$domain,$role,$name,$ip)=split(/:/,$configline); |
$hostid{$ip}=$id; |
chomp($ip); $ip=~s/\D+$//; |
$hostdom{$id}=$domain; |
$hostid{$ip}=$id; |
$hostip{$id}=$ip; |
$hostdom{$id}=$domain; |
if ($id eq $perlvar{'lonHostID'}) { $thisserver=$name; } |
$hostip{$id}=$ip; |
|
if ($id eq $perlvar{'lonHostID'}) { $thisserver=$name; } |
|
} |
} |
} |
close(CONFIG); |
close(CONFIG); |
} |
} |
Line 778 sub logstatus {
|
Line 1030 sub logstatus {
|
my $docdir=$perlvar{'lonDocRoot'}; |
my $docdir=$perlvar{'lonDocRoot'}; |
{ |
{ |
my $fh=IO::File->new(">>$docdir/lon-status/londstatus.txt"); |
my $fh=IO::File->new(">>$docdir/lon-status/londstatus.txt"); |
print $fh $$."\t".$currenthostid."\t".$status."\t".$lastlog."\n"; |
print $fh $$."\t".$clientname."\t".$currenthostid."\t".$status."\t".$lastlog."\n"; |
$fh->close(); |
$fh->close(); |
} |
} |
&status("Finished londstatus.txt"); |
&status("Finished londstatus.txt"); |
Line 1006 sub make_new_child {
|
Line 1258 sub make_new_child {
|
# the pid hash. |
# the pid hash. |
# |
# |
my $caller = getpeername($client); |
my $caller = getpeername($client); |
my ($port,$iaddr)=unpack_sockaddr_in($caller); |
my ($port,$iaddr); |
$clientip=inet_ntoa($iaddr); |
if (defined($caller) && length($caller) > 0) { |
|
($port,$iaddr)=unpack_sockaddr_in($caller); |
|
} else { |
|
&logthis("Unable to determine who caller was, getpeername returned nothing"); |
|
} |
|
if (defined($iaddr)) { |
|
$clientip=inet_ntoa($iaddr); |
|
} else { |
|
&logthis("Unable to determine clinetip"); |
|
$clientip='Unavailable'; |
|
} |
|
|
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) |
or die "Can't unblock SIGINT for fork: $!\n"; |
or die "Can't unblock SIGINT for fork: $!\n"; |
$children{$pid} = $clientip; |
$children{$pid} = $clientip; |
$children++; |
|
&status('Started child '.$pid); |
&status('Started child '.$pid); |
return; |
return; |
} else { |
} else { |
Line 1237 sub make_new_child {
|
Line 1498 sub make_new_child {
|
my($command, $filetype, $script) = split(/:/, $userinput); |
my($command, $filetype, $script) = split(/:/, $userinput); |
if (($filetype eq "hosts") || ($filetype eq "domain")) { |
if (($filetype eq "hosts") || ($filetype eq "domain")) { |
if($script ne "") { |
if($script ne "") { |
Reply($client,"ok\n",$userinput); # DEBUG: Call EditFile here. |
Reply($client, EditFile($userinput)); |
} else { |
} else { |
Reply($client,"refused\n",$userinput); |
Reply($client,"refused\n",$userinput); |
} |
} |
Line 1451 sub make_new_child {
|
Line 1712 sub make_new_child {
|
unless (mkdir($fpnow,0777)) { |
unless (mkdir($fpnow,0777)) { |
$fperror="error: ".($!+0) |
$fperror="error: ".($!+0) |
." mkdir failed while attempting " |
." mkdir failed while attempting " |
."makeuser\n"; |
."makeuser"; |
} |
} |
} |
} |
} |
} |
Line 1567 sub make_new_child {
|
Line 1828 sub make_new_child {
|
} elsif ($userinput =~ /^fetchuserfile/) { # Client clear or enc. |
} elsif ($userinput =~ /^fetchuserfile/) { # Client clear or enc. |
if(isClient) { |
if(isClient) { |
my ($cmd,$fname)=split(/:/,$userinput); |
my ($cmd,$fname)=split(/:/,$userinput); |
my ($udom,$uname,$ufile)=split(/\//,$fname); |
my ($udom,$uname,$ufile) = ($fname =~ /^([^\/]+)\/([^\/]+)\/(.+)$/); |
my $udir=propath($udom,$uname).'/userfiles'; |
my $udir=propath($udom,$uname).'/userfiles'; |
unless (-e $udir) { mkdir($udir,0770); } |
unless (-e $udir) { mkdir($udir,0770); } |
if (-e $udir) { |
if (-e $udir) { |
$ufile=~s/^[\.\~]+//; |
$ufile=~s/^[\.\~]+//; |
$ufile=~s/\///g; |
my $path = $udir; |
|
if ($ufile =~/(.+)\/([^\/]+)$/) { |
|
my @parts=split(/\//,$1); |
|
foreach my $part (@parts) { |
|
$path .= '/'.$part; |
|
if ((-e $path)!=1) { |
|
mkdir($path,0770); |
|
} |
|
} |
|
} |
my $destname=$udir.'/'.$ufile; |
my $destname=$udir.'/'.$ufile; |
my $transname=$udir.'/'.$ufile.'.in.transit'; |
my $transname=$udir.'/'.$ufile.'.in.transit'; |
my $remoteurl='http://'.$clientip.'/userfiles/'.$fname; |
my $remoteurl='http://'.$clientip.'/userfiles/'.$fname; |
Line 1601 sub make_new_child {
|
Line 1871 sub make_new_child {
|
} |
} |
} else { |
} else { |
Reply($client, "refused\n", $userinput); |
Reply($client, "refused\n", $userinput); |
|
|
} |
} |
# ------------------------------------------ authenticate access to a user file |
# ------------------------------------------ authenticate access to a user file |
} elsif ($userinput =~ /^tokenauthuserfile/) { # Client only |
} elsif ($userinput =~ /^tokenauthuserfile/) { # Client only |
Line 1612 sub make_new_child {
|
Line 1881 sub make_new_child {
|
if (open(ENVIN,$perlvar{'lonIDsDir'}.'/'. |
if (open(ENVIN,$perlvar{'lonIDsDir'}.'/'. |
$session.'.id')) { |
$session.'.id')) { |
while (my $line=<ENVIN>) { |
while (my $line=<ENVIN>) { |
if ($line=~/userfile\.$fname\=/) { $reply='ok'; } |
if ($line=~ m|userfile\.$fname\=|) { $reply='ok'; } |
} |
} |
close(ENVIN); |
close(ENVIN); |
print $client $reply."\n"; |
print $client $reply."\n"; |
Line 1755 sub make_new_child {
|
Line 2024 sub make_new_child {
|
} else { |
} else { |
print $client "error: ".($!+0) |
print $client "error: ".($!+0) |
." untie(GDBM) failed ". |
." untie(GDBM) failed ". |
"while attempting put\n"; |
"while attempting inc\n"; |
} |
} |
} else { |
} else { |
print $client "error: ".($!) |
print $client "error: ".($!) |
." tie(GDBM) Failed ". |
." tie(GDBM) Failed ". |
"while attempting put\n"; |
"while attempting inc\n"; |
} |
} |
} else { |
} else { |
print $client "refused\n"; |
print $client "refused\n"; |
Line 2086 sub make_new_child {
|
Line 2355 sub make_new_child {
|
my $proname=propath($udom,$uname); |
my $proname=propath($udom,$uname); |
my %hash; |
my %hash; |
if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_READER(),0640)) { |
if (tie(%hash,'GDBM_File',"$proname/$namespace.db",&GDBM_READER(),0640)) { |
study($regexp); |
|
while (my ($key,$value) = each(%hash)) { |
while (my ($key,$value) = each(%hash)) { |
if ($regexp eq '.') { |
if ($regexp eq '.') { |
$qresult.=$key.'='.$value.'&'; |
$qresult.=$key.'='.$value.'&'; |
Line 2482 sub make_new_child {
|
Line 2750 sub make_new_child {
|
# -------------------------------------------------------------------------- ls |
# -------------------------------------------------------------------------- ls |
} elsif ($userinput =~ /^ls/) { |
} elsif ($userinput =~ /^ls/) { |
if(isClient) { |
if(isClient) { |
|
my $obs; |
|
my $rights; |
my ($cmd,$ulsdir)=split(/:/,$userinput); |
my ($cmd,$ulsdir)=split(/:/,$userinput); |
my $ulsout=''; |
my $ulsout=''; |
my $ulsfn; |
my $ulsfn; |
Line 2489 sub make_new_child {
|
Line 2759 sub make_new_child {
|
if(-d $ulsdir) { |
if(-d $ulsdir) { |
if (opendir(LSDIR,$ulsdir)) { |
if (opendir(LSDIR,$ulsdir)) { |
while ($ulsfn=readdir(LSDIR)) { |
while ($ulsfn=readdir(LSDIR)) { |
|
undef $obs, $rights; |
my @ulsstats=stat($ulsdir.'/'.$ulsfn); |
my @ulsstats=stat($ulsdir.'/'.$ulsfn); |
$ulsout.=$ulsfn.'&'. |
#We do some obsolete checking here |
join('&',@ulsstats).':'; |
if(-e $ulsdir.'/'.$ulsfn.".meta") { |
|
open(FILE, $ulsdir.'/'.$ulsfn.".meta"); |
|
my @obsolete=<FILE>; |
|
foreach my $obsolete (@obsolete) { |
|
if($obsolete =~ m|(<obsolete>)(on)|) { $obs = 1; } |
|
if($obsolete =~ m|(<copyright>)(default)|) { $rights = 1; } |
|
} |
|
} |
|
$ulsout.=$ulsfn.'&'.join('&',@ulsstats); |
|
if($obs eq '1') { $ulsout.="&1"; } |
|
else { $ulsout.="&0"; } |
|
if($rights eq '1') { $ulsout.="&1:"; } |
|
else { $ulsout.="&0:"; } |
} |
} |
closedir(LSDIR); |
closedir(LSDIR); |
} |
} |
Line 2602 sub ManagePermissions
|
Line 2885 sub ManagePermissions
|
my $authtype= shift; |
my $authtype= shift; |
|
|
# See if the request is of the form /$domain/_au |
# See if the request is of the form /$domain/_au |
&logthis("ruequest is $request"); |
|
if($request =~ /^(\/$domain\/_au)$/) { # It's an author rolesput... |
if($request =~ /^(\/$domain\/_au)$/) { # It's an author rolesput... |
my $execdir = $perlvar{'lonDaemons'}; |
my $execdir = $perlvar{'lonDaemons'}; |
my $userhome= "/home/$user" ; |
my $userhome= "/home/$user" ; |