--- loncom/lond 2007/03/12 22:24:58 1.362 +++ loncom/lond 2007/09/29 04:03:39 1.382 @@ -2,7 +2,7 @@ # The LearningOnline Network # lond "LON Daemon" Server (port "LOND" 5663) # -# $Id: lond,v 1.362 2007/03/12 22:24:58 albertel Exp $ +# $Id: lond,v 1.382 2007/09/29 04:03:39 albertel Exp $ # # Copyright Michigan State University Board of Trustees # @@ -33,6 +33,7 @@ use strict; use lib '/home/httpd/lib/perl/'; use LONCAPA; use LONCAPA::Configuration; +use Apache::lonnet; use IO::Socket; use IO::File; @@ -49,7 +50,6 @@ use localenroll; use localstudentphoto; use File::Copy; use File::Find; -use LONCAPA::ConfigFileEdit; use LONCAPA::lonlocal; use LONCAPA::lonssl; use Fcntl qw(:flock); @@ -59,7 +59,7 @@ my $DEBUG = 0; # Non zero to ena my $status=''; my $lastlog=''; -my $VERSION='$Revision: 1.362 $'; #' stupid emacs +my $VERSION='$Revision: 1.382 $'; #' stupid emacs my $remoteVERSION; my $currenthostid="default"; my $currentdomainid; @@ -69,7 +69,6 @@ my $clientip; # IP address of client. my $clientname; # LonCAPA name of client. my $server; -my $thisserver; # DNS of us. my $keymode; @@ -85,12 +84,6 @@ my $tmpsnum = 0; # Id of tmpputs. my $ConnectionType; -my %hostid; # ID's for hosts in cluster by ip. -my %hostdom; # LonCAPA domain for hosts in cluster. -my %hostname; # DNSname -> ID's mapping. -my %hostip; # IPs for hosts in cluster. -my %hostdns; # ID's of hosts looked up by DNS name. - my %managers; # Ip -> manager names my %perlvar; # Will have the apache conf defined perl vars. @@ -142,7 +135,7 @@ my @adderrors = ("ok", "lcuseradd Unable to make www member of users's group", "lcuseradd Unable to su to root", "lcuseradd Unable to set password", - "lcuseradd Usrname has invalid characters", + "lcuseradd Username has invalid characters", "lcuseradd Password has an invalid character", "lcuseradd User already exists", "lcuseradd Could not add user.", @@ -178,19 +171,16 @@ sub ResetStatistics { # $Socket - Socket open on client. # $initcmd - The full text of the init command. # -# Implicit inputs: -# $thisserver - Our DNS name. -# # Returns: # IDEA session key on success. # undef on failure. # sub LocalConnection { my ($Socket, $initcmd) = @_; - Debug("Attempting local connection: $initcmd client: $clientip me: $thisserver"); + Debug("Attempting local connection: $initcmd client: $clientip"); if($clientip ne "127.0.0.1") { &logthis(' LocalConnection rejecting non local: ' - ."$clientip ne $thisserver "); + ."$clientip ne 127.0.0.1 "); close $Socket; return undef; } else { @@ -424,7 +414,7 @@ sub ReadManagerTable { if ($host =~ "^#") { # Comment line. next; } - if (!defined $hostip{$host}) { # This is a non cluster member + if (!defined &Apache::lonnet::get_host_ip($host)) { # This is a non cluster member # The entry is of the form: # cluname:hostname # cluname - A 'cluster hostname' is needed in order to negotiate @@ -442,7 +432,7 @@ sub ReadManagerTable { } } else { logthis(' existing host'." $host\n"); - $managers{$hostip{$host}} = $host; # Use info from cluster tab if clumemeber + $managers{&Apache::lonnet::get_host_ip($host)} = $host; # Use info from cluster tab if clumemeber } } } @@ -1032,7 +1022,7 @@ sub ping_handler { sub pong_handler { my ($cmd, $tail, $replyfd) = @_; - my $reply=&reply("ping",$clientname); + my $reply=&Apache::lonnet::reply("ping",$clientname); &Reply( $replyfd, "$currenthostid:$reply\n", "$cmd:$tail"); return 1; } @@ -1142,7 +1132,7 @@ sub load_handler { sub user_load_handler { my ($cmd, $tail, $replyfd) = @_; - my $userloadpercent=&userload(); + my $userloadpercent=&Apache::lonnet::userload(); &Reply($replyfd, "$userloadpercent\n", "$cmd:$tail"); return 1; @@ -1850,13 +1840,13 @@ sub update_resource_handler { my $now=time; my $since=$now-$atime; if ($since>$perlvar{'lonExpire'}) { - my $reply=&reply("unsub:$fname","$clientname"); + my $reply=&Apache::lonnet::reply("unsub:$fname","$clientname"); &devalidate_meta_cache($fname); unlink("$fname"); unlink("$fname.meta"); } else { my $transname="$fname.in.transfer"; - my $remoteurl=&reply("sub:$fname","$clientname"); + my $remoteurl=&Apache::lonnet::reply("sub:$fname","$clientname"); my $response; alarm(120); { @@ -1901,22 +1891,12 @@ sub devalidate_meta_cache { my ($url) = @_; use Cache::Memcached; my $memcache = new Cache::Memcached({'servers'=>['127.0.0.1:11211']}); - $url = &declutter($url); + $url = &Apache::lonnet::declutter($url); $url =~ s-\.meta$--; my $id = &escape('meta:'.$url); $memcache->delete($id); } -sub declutter { - my $thisfn=shift; - $thisfn=~s/^\Q$perlvar{'lonDocRoot'}\E//; - $thisfn=~s/^\///; - $thisfn=~s|^adm/wrapper/||; - $thisfn=~s|^adm/coursedocs/showdoc/||; - $thisfn=~s/^res\///; - $thisfn=~s/\?.+$//; - return $thisfn; -} # # Fetch a user file from a remote server to the user's home directory # userfiles subdir. @@ -2115,6 +2095,37 @@ sub rename_user_file_handler { ®ister_handler("renameuserfile", \&rename_user_file_handler, 0,1,0); # +# Checks if the specified user has an active session on the server +# return ok if so, not_found if not +# +# Parameters: +# cmd - The request keyword that dispatched to tus. +# tail - The tail of the request (colon separated parameters). +# client - Filehandle open on the client. +# Return: +# 1. +sub user_has_session_handler { + my ($cmd, $tail, $client) = @_; + + my ($udom, $uname) = map { &unescape($_) } (split(/:/, $tail)); + + &logthis("Looking for $udom $uname"); + opendir(DIR,$perlvar{'lonIDsDir'}); + my $filename; + while ($filename=readdir(DIR)) { + last if ($filename=~/^\Q$uname\E_\d+_\Q$udom\E_/); + } + if ($filename) { + &Reply($client, "ok\n", "$cmd:$tail"); + } else { + &Failure($client, "not_found\n", "$cmd:$tail"); + } + return 1; + +} +®ister_handler("userhassession", \&user_has_session_handler, 0,1,0); + +# # Authenticate access to a user file by checking that the token the user's # passed also exists in their session file # @@ -2207,13 +2218,13 @@ sub subscribe_handler { ®ister_handler("sub", \&subscribe_handler, 0, 1, 0); # -# Determine the version of a resource (?) Or is it return -# the top version of the resource? Not yet clear from the -# code in currentversion. +# Determine the latest version of a resource (it looks for the highest +# past version and then returns that +1) # # Parameters: # $cmd - The command that got us here. # $tail - Tail of the command (remaining parameters). +# (Should consist of an absolute path to a file) # $client - File descriptor connected to client. # Returns # 0 - Requested to exit, caller should shut down. @@ -4452,10 +4463,10 @@ sub get_institutional_code_format_handle \%cat_titles, \%cat_order); if ($formatreply eq 'ok') { - my $codes_str = &hash2str(%codes); - my $codetitles_str = &array2str(@codetitles); - my $cat_titles_str = &hash2str(%cat_titles); - my $cat_order_str = &hash2str(%cat_order); + my $codes_str = &Apache::lonnet::hash2str(%codes); + my $codetitles_str = &Apache::lonnet::array2str(@codetitles); + my $cat_titles_str = &Apache::lonnet::hash2str(%cat_titles); + my $cat_order_str = &Apache::lonnet::hash2str(%cat_order); &Reply($client, $codes_str.':'.$codetitles_str.':'.$cat_titles_str.':' .$cat_order_str."\n", @@ -4503,6 +4514,70 @@ sub get_institutional_defaults_handler { ®ister_handler("autoinstcodedefaults", \&get_institutional_defaults_handler,0,1,0); +sub get_institutional_user_rules { + my ($cmd, $tail, $client) = @_; + my $userinput = "$cmd:$tail"; + my $dom = &unescape($tail); + my (%rules_hash,@rules_order); + my $outcome; + eval { + local($SIG{__DIE__})='DEFAULT'; + $outcome = &localenroll::username_rules($dom,\%rules_hash,\@rules_order); + }; + if (!$@) { + if ($outcome eq 'ok') { + my $result; + foreach my $key (keys(%rules_hash)) { + $result .= &escape($key).'='.&Apache::lonnet::freeze_escape($rules_hash{$key}).'&'; + } + $result =~ s/\&$//; + $result .= ':'; + if (@rules_order > 0) { + foreach my $item (@rules_order) { + $result .= &escape($item).'&'; + } + } + $result =~ s/\&$//; + &Reply($client,$result."\n",$userinput); + } else { + &Reply($client,"error\n", $userinput); + } + } else { + &Failure($client,"unknown_cmd\n",$userinput); + } +} +®ister_handler("instuserrules",\&get_institutional_user_rules,0,1,0); + + +sub institutional_username_check { + my ($cmd, $tail, $client) = @_; + my $userinput = "$cmd:$tail"; + my %rulecheck; + my $outcome; + my ($udom,$uname,@rules) = split(/:/,$tail); + $udom = &unescape($udom); + $uname = &unescape($uname); + @rules = map {&unescape($_);} (@rules); + eval { + local($SIG{__DIE__})='DEFAULT'; + $outcome = &localenroll::username_check($udom,$uname,\@rules,\%rulecheck); + }; + if (!$@) { + if ($outcome eq 'ok') { + my $result=''; + foreach my $key (keys(%rulecheck)) { + $result.=&escape($key).'='.&Apache::lonnet::freeze_escape($rulecheck{$key}).'&'; + } + &Reply($client,$result."\n",$userinput); + } else { + &Reply($client,"error\n", $userinput); + } + } else { + &Failure($client,"unknown_cmd\n",$userinput); + } +} +®ister_handler("instrulecheck",\&institutional_username_check,0,1,0); + # Get domain specific conditions for import of student photographs to a course # @@ -4635,8 +4710,12 @@ sub inst_usertypes_handler { my ($cmd, $domain, $client) = @_; my $res; my $userinput = $cmd.":".$domain; # For logging purposes. - my (%typeshash,@order); - if (&localenroll::inst_usertypes($domain,\%typeshash,\@order) eq 'ok') { + my (%typeshash,@order,$result); + eval { + local($SIG{__DIE__})='DEFAULT'; + $result=&localenroll::inst_usertypes($domain,\%typeshash,\@order); + }; + if ($result eq 'ok') { if (keys(%typeshash) > 0) { foreach my $key (keys(%typeshash)) { $res.=&escape($key).'='.&escape($typeshash{$key}).'&'; @@ -4919,7 +4998,7 @@ sub catchexception { $SIG{__DIE__}='DEFAULT'; &status("Catching exception"); &logthis("CRITICAL: " - ."ABNORMAL EXIT. Child $$ for server $thisserver died through " + ."ABNORMAL EXIT. Child $$ for server ".$perlvar{'lonHostID'}." died through " ."a crash with this error msg->[$error]"); &logthis('Famous last words: '.$status.' - '.$lastlog); if ($client) { print $client "error: $error\n"; } @@ -5030,67 +5109,6 @@ sub HUPSMAN { # sig } # -# Kill off hashes that describe the host table prior to re-reading it. -# Hashes affected are: -# %hostid, %hostdom %hostip %hostdns. -# -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}; - } - foreach my $key (keys %hostdns) { - delete $hostdns{$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"; - my $myloncapaname = $perlvar{'lonHostID'}; - Debug("My loncapa name is : $myloncapaname"); - my %name_to_ip; - while (my $configline=) { - if ($configline !~ /^\s*\#/ && $configline !~ /^\s*$/ ) { - my ($id,$domain,$role,$name)=split(/:/,$configline); - $name=~s/\s//g; - my $ip; - if (!exists($name_to_ip{$name})) { - $ip = gethostbyname($name); - if (!$ip || length($ip) ne 4) { - &logthis("Skipping host $id name $name no IP found\n"); - next; - } - $ip=inet_ntoa($ip); - $name_to_ip{$name} = $ip; - } else { - $ip = $name_to_ip{$name}; - } - $hostid{$ip}=$id; # LonCAPA name of host by IP. - $hostdom{$id}=$domain; # LonCAPA domain name of host. - $hostname{$id}=$name; # LonCAPA name -> DNS name - $hostip{$id}=$ip; # IP address of host. - $hostdns{$name} = $id; # LonCAPA name of host by DNS. - - if ($id eq $perlvar{'lonHostID'}) { - Debug("Found me in the host table: $name"); - $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. @@ -5121,13 +5139,12 @@ sub UpdateHosts { # 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. + &Apache::lonnet::reset_hosts_info(); - KillHostHashes; - ReadHostTable; - - foreach my $child (keys %children) { + foreach my $child (keys(%children)) { my $childip = $children{$child}; - if(!$hostid{$childip}) { + if ($childip ne '127.0.0.1' + && !defined(&Apache::lonnet::get_hosts_from_ip($childip))) { logthis(' UpdateHosts killing child ' ." $child for ip $childip "); kill('INT', $child); @@ -5287,63 +5304,6 @@ sub status { $0='lond: '.$what.' '.$local; } -# ----------------------------------------------------------- Send USR1 to lonc - -sub reconlonc { - my $peerfile=shift; - &logthis("Trying to reconnect for $peerfile"); - my $loncfile="$perlvar{'lonDaemons'}/logs/lonc.pid"; - if (my $fh=IO::File->new("$loncfile")) { - my $loncpid=<$fh>; - chomp($loncpid); - if (kill 0 => $loncpid) { - &logthis("lonc at pid $loncpid responding, sending USR1"); - kill USR1 => $loncpid; - } else { - &logthis( - "CRITICAL: " - ."lonc at pid $loncpid not responding, giving up"); - } - } else { - &logthis('CRITICAL: lonc not running, giving up'); - } -} - -# -------------------------------------------------- Non-critical communication - -sub subreply { - my ($cmd,$server)=@_; - my $peerfile="$perlvar{'lonSockDir'}/".$hostname{$server}; - my $sclient=IO::Socket::UNIX->new(Peer =>"$peerfile", - Type => SOCK_STREAM, - Timeout => 10) - or return "con_lost"; - print $sclient "sethost:$server:$cmd\n"; - my $answer=<$sclient>; - chomp($answer); - if (!$answer) { $answer="con_lost"; } - return $answer; -} - -sub reply { - my ($cmd,$server)=@_; - my $answer; - if ($server ne $currenthostid) { - $answer=subreply($cmd,$server); - if ($answer eq 'con_lost') { - $answer=subreply("ping",$server); - if ($answer ne $server) { - &logthis("sub reply: answer != server answer is $answer, server is $server"); - &reconlonc("$perlvar{'lonSockDir'}/".$hostname{$server}); - } - $answer=subreply($cmd,$server); - } - } else { - $answer='self_reply'; - } - return $answer; -} - # -------------------------------------------------------------- Talk to lonsql sub sql_reply { @@ -5412,8 +5372,7 @@ $SIG{USR1} = \&checkchildren; $SIG{USR2} = \&UpdateHosts; # Read the host hashes: - -ReadHostTable; +&Apache::lonnet::load_hosts_tab(); my $dist=`$perlvar{'lonDaemons'}/distprobe`; @@ -5503,19 +5462,17 @@ sub make_new_child { # ----------------------------------------------------------------------------- # see if we know client and 'check' for spoof IP by ineffective challenge - ReadManagerTable; # May also be a manager!! - my $outsideip=$clientip; if ($clientip eq '127.0.0.1') { - $outsideip=$hostip{$perlvar{'lonHostID'}}; + $outsideip=&Apache::lonnet::get_host_ip($perlvar{'lonHostID'}); } - my $clientrec=($hostid{$outsideip} ne undef); + my $clientrec=defined(&Apache::lonnet::get_hosts_from_ip($outsideip)); my $ismanager=($managers{$outsideip} ne undef); $clientname = "[unknonwn]"; if($clientrec) { # Establish client type. $ConnectionType = "client"; - $clientname = $hostid{$outsideip}; + $clientname = (&Apache::lonnet::get_hosts_from_ip($outsideip))[-1]; if($ismanager) { $ConnectionType = "both"; } @@ -5622,14 +5579,9 @@ sub make_new_child { if ($clientok) { # ---------------- New known client connecting, could mean machine online again - - foreach my $id (keys(%hostip)) { - if ($hostip{$id} ne $clientip || - $hostip{$currenthostid} eq $clientip) { - # no need to try to do recon's to myself - next; - } - &reconlonc("$perlvar{'lonSockDir'}/".$hostname{$id}); + if (&Apache::lonnet::get_host_ip($currenthostid) ne $clientip + && $clientip ne '127.0.0.1') { + &Apache::lonnet::reconlonc($clientname); } &logthis("Established connection: $clientname"); &status('Will listen to '.$clientname); @@ -5913,6 +5865,10 @@ sub validate_user { $password, $credentials); $validated = ($krbreturn == 1); + if (!$validated) { + &logthis('krb5: '.$user.', '.$contentpwd.', '. + &Authen::Krb5::error()); + } } else { $validated = 0; } @@ -6158,7 +6114,7 @@ sub subscribe { # the metadata unless ($fname=~/\.meta$/) { &unsub("$fname.meta",$clientip); } $fname=~s/\/home\/httpd\/html\/res/raw/; - $fname="http://$thisserver/".$fname; + $fname="http://".&Apache::lonnet::hostname($perlvar{'lonHostID'})."/".$fname; $result="$fname\n"; } } else { @@ -6312,9 +6268,10 @@ sub sethost { } if (!defined($hostid)) { $hostid=$perlvar{'lonHostID'}; } - if ($hostip{$perlvar{'lonHostID'}} eq $hostip{$hostid}) { + if (&Apache::lonnet::get_host_ip($perlvar{'lonHostID'}) + eq &Apache::lonnet::get_host_ip($hostid)) { $currenthostid =$hostid; - $currentdomainid=$hostdom{$hostid}; + $currentdomainid=&Apache::lonnet::host_domain($hostid); &logthis("Setting hostid to $hostid, and domain to $currentdomainid"); } else { &logthis("Requested host id $hostid not an alias of ". @@ -6330,96 +6287,6 @@ sub version { return "version:$VERSION"; } -#There is a copy of this in lonnet.pm -sub userload { - my $numusers=0; - { - opendir(LONIDS,$perlvar{'lonIDsDir'}); - my $filename; - my $curtime=time; - while ($filename=readdir(LONIDS)) { - if ($filename eq '.' || $filename eq '..') {next;} - my ($mtime)=(stat($perlvar{'lonIDsDir'}.'/'.$filename))[9]; - if ($curtime-$mtime < 1800) { $numusers++; } - } - closedir(LONIDS); - } - my $userloadpercent=0; - my $maxuserload=$perlvar{'lonUserLoadLim'}; - if ($maxuserload) { - $userloadpercent=100*$numusers/$maxuserload; - } - $userloadpercent=sprintf("%.2f",$userloadpercent); - return $userloadpercent; -} - -# Routines for serializing arrays and hashes (copies from lonnet) - -sub array2str { - my (@array) = @_; - my $result=&arrayref2str(\@array); - $result=~s/^__ARRAY_REF__//; - $result=~s/__END_ARRAY_REF__$//; - return $result; -} - -sub arrayref2str { - my ($arrayref) = @_; - my $result='__ARRAY_REF__'; - foreach my $elem (@$arrayref) { - if(ref($elem) eq 'ARRAY') { - $result.=&arrayref2str($elem).'&'; - } elsif(ref($elem) eq 'HASH') { - $result.=&hashref2str($elem).'&'; - } elsif(ref($elem)) { - #print("Got a ref of ".(ref($elem))." skipping."); - } else { - $result.=&escape($elem).'&'; - } - } - $result=~s/\&$//; - $result .= '__END_ARRAY_REF__'; - return $result; -} - -sub hash2str { - my (%hash) = @_; - my $result=&hashref2str(\%hash); - $result=~s/^__HASH_REF__//; - $result=~s/__END_HASH_REF__$//; - return $result; -} - -sub hashref2str { - my ($hashref)=@_; - my $result='__HASH_REF__'; - foreach (sort(keys(%$hashref))) { - if (ref($_) eq 'ARRAY') { - $result.=&arrayref2str($_).'='; - } elsif (ref($_) eq 'HASH') { - $result.=&hashref2str($_).'='; - } elsif (ref($_)) { - $result.='='; - #print("Got a ref of ".(ref($_))." skipping."); - } else { - if ($_) {$result.=&escape($_).'=';} else { last; } - } - - if(ref($hashref->{$_}) eq 'ARRAY') { - $result.=&arrayref2str($hashref->{$_}).'&'; - } elsif(ref($hashref->{$_}) eq 'HASH') { - $result.=&hashref2str($hashref->{$_}).'&'; - } elsif(ref($hashref->{$_})) { - $result.='&'; - #print("Got a ref of ".(ref($hashref->{$_}))." skipping."); - } else { - $result.=&escape($hashref->{$_}).'&'; - } - } - $result=~s/\&$//; - $result .= '__END_HASH_REF__'; - return $result; -} # ----------------------------------- POD (plain old documentation, CPAN style)