--- loncom/lond 2008/10/07 10:08:06 1.409 +++ loncom/lond 2009/07/29 22:32:39 1.418 @@ -2,7 +2,7 @@ # The LearningOnline Network # lond "LON Daemon" Server (port "LOND" 5663) # -# $Id: lond,v 1.409 2008/10/07 10:08:06 foxr Exp $ +# $Id: lond,v 1.418 2009/07/29 22:32:39 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -59,7 +59,7 @@ my $DEBUG = 0; # Non zero to ena my $status=''; my $lastlog=''; -my $VERSION='$Revision: 1.409 $'; #' stupid emacs +my $VERSION='$Revision: 1.418 $'; #' stupid emacs my $remoteVERSION; my $currenthostid="default"; my $currentdomainid; @@ -142,6 +142,16 @@ my @adderrors = ("ok", "lcuseradd Password mismatch"); +# This array are the errors from lcinstallfile: + +my @installerrors = ("ok", + "Initial user id of client not that of www", + "Usage error, not enough command line arguments", + "Source file name does not exist", + "Destination file name does not exist", + "Some file operation failed", + "Invalid table filename." + ); # # Statistics that are maintained and dislayed in the status line. @@ -398,6 +408,7 @@ sub isClient { # sub ReadManagerTable { + &Debug("Reading manager table"); # Clean out the old table first.. foreach my $key (keys %managers) { @@ -520,11 +531,9 @@ sub AdjustHostContents { } # # 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. +# - The file is created int a temp directory called .tmp +# - lcinstall file is called to install the file. +# since the web app has no direct write access to the table directory # # Parameters: # Name of the file @@ -532,11 +541,16 @@ sub AdjustHostContents { # Return: # nonzero - success. # 0 - failure and $! has an errno. +# Assumptions: +# File installtion is a relatively infrequent # sub InstallFile { my ($Filename, $Contents) = @_; - my $TempFile = $Filename.".tmp"; +# my $TempFile = $Filename.".tmp"; + my $exedir = $perlvar{'lonDaemons'}; + my $tmpdir = $exedir.'/tmp/'; + my $TempFile = $tmpdir."TempTableFile.tmp"; # Open the file for write: @@ -550,11 +564,27 @@ sub InstallFile { print $fh ($Contents); $fh->close; # In case we ever have a filesystem w. locking - chmod(0660, $TempFile); + chmod(0664, $TempFile); # Everyone can write it. - # Now we can move install the file in position. - - move($TempFile, $Filename); + # Use lcinstall file to put the file in the table directory... + + &Debug("Opening pipe to $exedir/lcinstallfile $TempFile $Filename"); + my $pf = IO::File->new("| $exedir/lcinstallfile $TempFile $Filename > $exedir/logs/lcinstallfile.log"); + close $pf; + my $err = $?; + &Debug("Status is $err"); + if ($err != 0) { + my $msg = $err; + if ($err < @installerrors) { + $msg = $installerrors[$err]; + } + &logthis("Install failed for table file $Filename : $msg"); + return 0; + } + + # Remove the temp file: + + unlink($TempFile); return 1; } @@ -562,8 +592,13 @@ sub InstallFile { # # ConfigFileFromSelector: converts a configuration file selector -# (one of host or domain at this point) into a -# configuration file pathname. +# into a configuration file pathname. +# It's probably no longer necessary to preserve +# special handling of hosts or domain as those +# files have been superceded by dns_hosts, dns_domain. +# The default action is just to prepend the directory +# and append .tab +# # # Parameters: # selector - Configuration file selector. @@ -580,7 +615,7 @@ sub ConfigFileFromSelector { } elsif ($selector eq "domain") { $tablefile = $tabledir."domain.tab"; } else { - return undef; + $tablefile = $tabledir.$selector.'.tab'; } return $tablefile; @@ -603,6 +638,7 @@ sub ConfigFileFromSelector { sub PushFile { my $request = shift; my ($command, $filename, $contents) = split(":", $request, 3); + &Debug("PushFile"); # At this point in time, pushes for only the following tables are # supported: @@ -619,20 +655,7 @@ sub PushFile { if(! (defined $tablefile)) { 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"); - + # If the file being pushed is the host file, we adjust the entry for ourself so that the # IP will be our current IP as looked up in dns. Note this is only 99% good as it's possible # to conceive of conditions where we don't have a DNS entry locally. This is possible in a @@ -645,6 +668,7 @@ sub PushFile { # Install the new file: + &logthis("Installing new $tablefile contents:\n$contents"); if(!InstallFile($tablefile, $contents)) { &logthis(' Pushfile: unable to install ' .$tablefile." $! "); @@ -1198,7 +1222,7 @@ sub user_authorization_type { # a reply is written to the client. sub push_file_handler { my ($cmd, $tail, $client) = @_; - + &Debug("In push file handler"); my $userinput = "$cmd:$tail"; # At this time we only know that the IP of our partner is a valid manager @@ -1206,7 +1230,8 @@ sub push_file_handler { # spoofing). my $cert = &GetCertificate($userinput); - if(&ValidManager($cert)) { + if(&ValidManager($cert)) { + &Debug("Valid manager: $client"); # Now presumably we have the bona fides of both the peer host and the # process making the request. @@ -1215,6 +1240,7 @@ sub push_file_handler { &Reply($client, \$reply, $userinput); } else { + &logthis("push_file_handler $client is not valid"); &Failure( $client, "refused\n", $userinput); } return 1; @@ -1587,6 +1613,46 @@ sub ls3_handler { } ®ister_handler("ls3", \&ls3_handler, 0, 1, 0); +sub server_timezone_handler { + my ($cmd,$tail,$client) = @_; + my $userinput = "$cmd:$tail"; + my $timezone; + my $clockfile = '/etc/sysconfig/clock'; # Fedora/CentOS/SuSE + my $tzfile = '/etc/timezone'; # Debian/Ubuntu + if (-e $clockfile) { + if (open(my $fh,"<$clockfile")) { + while (<$fh>) { + next if (/^[\#\s]/); + if (/^(?:TIME)?ZONE\s*=\s*['"]?\s*([\w\/]+)/) { + $timezone = $1; + last; + } + } + close($fh); + } + } elsif (-e $tzfile) { + if (open(my $fh,"<$tzfile")) { + $timezone = <$fh>; + close($fh); + chomp($timezone); + if ($timezone =~ m{^Etc/(\w+)$}) { + $timezone = $1; + } + } + } + &Reply($client,\$timezone,$userinput); # This supports debug logging. + return 1; +} +®ister_handler("servertimezone", \&server_timezone_handler, 0, 1, 0); + +sub server_loncaparev_handler { + my ($cmd,$tail,$client) = @_; + my $userinput = "$cmd:$tail"; + &Reply($client,\$perlvar{'lonVersion'},$userinput); + return 1; +} +®ister_handler("serverloncaparev", \&server_loncaparev_handler, 0, 1, 0); + # Process a reinit request. Reinit requests that either # lonc or lond be reinitialized so that an updated # host.tab or domain.tab can be processed. @@ -3608,8 +3674,10 @@ sub put_course_id_hash_handler { # will be returned. Pre-2.2.0 legacy entries from # nohist_courseiddump will only contain usernames. # type - optional parameter for selection -# regexp_ok - if true, allow the supplied institutional code -# filter to behave as a regular expression. +# regexp_ok - if 1 or -1 allow the supplied institutional code +# filter to behave as a regular expression: +# 1 will not exclude the course if the instcode matches the RE +# -1 will exclude the course if the instcode matches the RE # rtn_as_hash - whether to return the information available for # each matched item as a frozen hash of all # key, value pairs in the item's hash, or as a @@ -3763,10 +3831,14 @@ sub dump_course_id_handler { if (!$is_hash) { $unesc_val{'inst_code'} = &unescape($val{'inst_code'}); } - if ($regexp_ok) { + if ($regexp_ok == 1) { if (eval{$unesc_val{'inst_code'} !~ /$instcodefilter/}) { $match = 0; } + } elsif ($regexp_ok == -1) { + if (eval{$unesc_val{'inst_code'} =~ /$instcodefilter/}) { + $match = 0; + } } else { if (eval{$unesc_val{'inst_code'} !~ /\Q$instcodefilter\E/i}) { $match = 0; @@ -4277,12 +4349,12 @@ sub dump_domainroles_handler { my ($start,$end) = split(/:/,&unescape($value)); my ($trole,$uname,$udom,$runame,$rudom,$rsec) = split(/:/,&unescape($key)); unless ($startfilter eq '.' || !defined($startfilter)) { - if ($start >= $startfilter) { + if ((defined($start)) && ($start >= $startfilter)) { $match = 0; } } unless ($endfilter eq '.' || !defined($endfilter)) { - if ($end <= $endfilter) { + if ((defined($end)) && ($end <= $endfilter)) { $match = 0; } } @@ -4575,6 +4647,33 @@ sub enrollment_enabled_handler { } ®ister_handler("autorun", \&enrollment_enabled_handler, 0, 1, 0); +# +# Validate an institutional code use for a LON-CAPA course. +# +# Formal Parameters: +# $cmd - The command request that got us dispatched. +# $tail - The tail of the command. In this case, +# this is a colon separated set of words that will be split +# into: +# $inst_course_id - The institutional cod3 from the +# institutions point of view. +# $cdom - The domain from the institutions +# point of view. +# $client - Socket open on the client. +# Returns: +# 1 - Indicating processing should continue. +# +sub validate_instcode_handler { + my ($cmd, $tail, $client) = @_; + my $userinput = "$cmd:$tail"; + my ($dom,$instcode,$owner) = split(/:/, $tail); + my $outcome=&localenroll::validate_instcode($dom,$instcode,$owner); + &Reply($client, \$outcome, $userinput); + + return 1; +} +®ister_handler("autovalidateinstcode", \&validate_instcode_handler, 0, 1, 0); + # Get the official sections for which auto-enrollment is possible. # Since the admin people won't know about 'unofficial sections' # we cannot auto-enroll on them. @@ -4862,6 +4961,39 @@ sub get_institutional_defaults_handler { ®ister_handler("autoinstcodedefaults", \&get_institutional_defaults_handler,0,1,0); +sub get_possible_instcodes_handler { + my ($cmd, $tail, $client) = @_; + my $userinput = "$cmd:$tail"; + + my $reply; + my $cdom = $tail; + my (@codetitles,%cat_titles,%cat_order,@code_order); + my $formatreply = &localenroll::possible_instcodes($cdom, + \@codetitles, + \%cat_titles, + \%cat_order, + \@code_order); + if ($formatreply eq 'ok') { + my $result = join('&',map {&escape($_);} (@codetitles)).':'; + $result .= join('&',map {&escape($_);} (@code_order)).':'; + foreach my $key (keys(%cat_titles)) { + $result .= &escape($key).'='.&Apache::lonnet::freeze_escape($cat_titles{$key}).'&'; + } + $result =~ s/\&$//; + $result .= ':'; + foreach my $key (keys(%cat_order)) { + $result .= &escape($key).'='.&Apache::lonnet::freeze_escape($cat_order{$key}).'&'; + } + $result =~ s/\&$//; + &Reply($client,\$result,$userinput); + } else { + &Reply($client, "format_error\n", $userinput); + } + return 1; +} +®ister_handler("autopossibleinstcodes", + \&get_possible_instcodes_handler,0,1,0); + sub get_institutional_user_rules { my ($cmd, $tail, $client) = @_; my $userinput = "$cmd:$tail"; @@ -5944,7 +6076,7 @@ sub make_new_child { if ($clientip eq '127.0.0.1') { $outsideip=&Apache::lonnet::get_host_ip($perlvar{'lonHostID'}); } - + &ReadManagerTable(); my $clientrec=defined(&Apache::lonnet::get_hosts_from_ip($outsideip)); my $ismanager=($managers{$outsideip} ne undef); $clientname = "[unknonwn]";