--- loncom/lond 2010/07/22 22:08:06 1.449
+++ loncom/lond 2012/02/28 15:54:07 1.486
@@ -2,7 +2,7 @@
# The LearningOnline Network
# lond "LON Daemon" Server (port "LOND" 5663)
#
-# $Id: lond,v 1.449 2010/07/22 22:08:06 raeburn Exp $
+# $Id: lond,v 1.486 2012/02/28 15:54:07 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -15,6 +15,7 @@
#
# 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.
#
@@ -52,13 +53,14 @@ use LONCAPA::lonlocal;
use LONCAPA::lonssl;
use Fcntl qw(:flock);
use Apache::lonnet;
+use Mail::Send;
my $DEBUG = 0; # Non zero to enable debug log entries.
my $status='';
my $lastlog='';
-my $VERSION='$Revision: 1.449 $'; #' stupid emacs
+my $VERSION='$Revision: 1.486 $'; #' stupid emacs
my $remoteVERSION;
my $currenthostid="default";
my $currentdomainid;
@@ -90,6 +92,8 @@ my %managers; # Ip -> manager names
my %perlvar; # Will have the apache conf defined perl vars.
+my $dist;
+
#
# The hash below is used for command dispatching, and is therefore keyed on the request keyword.
# Each element of the hash contains a reference to an array that contains:
@@ -419,8 +423,11 @@ sub ReadManagerTable {
my $tablename = $perlvar{'lonTabDir'}."/managers.tab";
if (!open (MANAGERS, $tablename)) {
- logthis('No manager table. Nobody can manage!!');
- return;
+ my $hostname = &Apache::lonnet::hostname($perlvar{'lonHostID'});
+ if (&Apache::lonnet::is_LC_dns($hostname)) {
+ &logthis('No manager table. Nobody can manage!!');
+ }
+ return;
}
while(my $host = ) {
chomp($host);
@@ -445,7 +452,7 @@ sub ReadManagerTable {
}
} else {
logthis(' existing host'." $host\n");
- $managers{&Apache::lonnet::get_host_ip($host)} = $host; # Use info from cluster tab if clumemeber
+ $managers{&Apache::lonnet::get_host_ip($host)} = $host; # Use info from cluster tab if cluster memeber
}
}
}
@@ -507,7 +514,8 @@ sub AdjustHostContents {
my $me = $perlvar{'lonHostID'};
foreach my $line (split(/\n/,$contents)) {
- if(!(($line eq "") || ($line =~ /^ *\#/) || ($line =~ /^ *$/))) {
+ if(!(($line eq "") || ($line =~ /^ *\#/) || ($line =~ /^ *$/) ||
+ ($line =~ /^\s*\^/))) {
chomp($line);
my ($id,$domain,$role,$name,$ip,$maxcon,$idleto,$mincon)=split(/:/,$line);
if ($id eq $me) {
@@ -595,11 +603,8 @@ sub InstallFile {
#
# ConfigFileFromSelector: converts a configuration file selector
# 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
+# Supports the following file selectors:
+# hosts, domain, dns_hosts, dns_domain
#
#
# Parameters:
@@ -612,15 +617,11 @@ sub ConfigFileFromSelector {
my $tablefile;
my $tabledir = $perlvar{'lonTabDir'}.'/';
- if ($selector eq "hosts") {
- $tablefile = $tabledir."hosts.tab";
- } elsif ($selector eq "domain") {
- $tablefile = $tabledir."domain.tab";
- } else {
+ if (($selector eq "hosts") || ($selector eq "domain") ||
+ ($selector eq "dns_hosts") || ($selector eq "dns_domain")) {
$tablefile = $tabledir.$selector.'.tab';
}
return $tablefile;
-
}
#
# PushFile: Called to do an administrative push of a file.
@@ -646,6 +647,8 @@ sub PushFile {
# supported:
# hosts.tab ($filename eq host).
# domain.tab ($filename eq domain).
+ # dns_hosts.tab ($filename eq dns_host).
+ # dns_domain.tab ($filename eq dns_domain).
# Construct the destination filename or reject the request.
#
# lonManage is supposed to ensure this, however this session could be
@@ -677,11 +680,31 @@ sub PushFile {
return "error:$!";
} else {
&logthis(' Installed new '.$tablefile
- ."");
-
+ ." - transaction by: $clientname ($clientip)");
+ my $adminmail = $perlvar{'lonAdmEMail'};
+ my $admindom = &Apache::lonnet::host_domain($perlvar{'lonHostID'});
+ if ($admindom ne '') {
+ my %domconfig =
+ &Apache::lonnet::get_dom('configuration',['contacts'],$admindom);
+ if (ref($domconfig{'contacts'}) eq 'HASH') {
+ if ($domconfig{'contacts'}{'adminemail'} ne '') {
+ $adminmail = $domconfig{'contacts'}{'adminemail'};
+ }
+ }
+ }
+ if ($adminmail =~ /^[^\@]+\@[^\@]+$/) {
+ my $msg = new Mail::Send;
+ $msg->to($adminmail);
+ $msg->subject('LON-CAPA DNS update on '.$perlvar{'lonHostID'});
+ $msg->add('Content-type','text/plain; charset=UTF-8');
+ if (my $fh = $msg->open()) {
+ print $fh 'Update to '.$tablefile.' from Cluster Manager '.
+ "$clientname ($clientip)\n";
+ $fh->close;
+ }
+ }
}
-
# Indicate success:
return "ok";
@@ -1121,6 +1144,8 @@ sub establish_key_handler {
sub load_handler {
my ($cmd, $tail, $replyfd) = @_;
+
+
# Get the load average from /proc/loadavg and calculate it as a percentage of
# the allowed load limit as set by the perl global variable lonLoadLim
@@ -1618,6 +1643,74 @@ sub ls3_handler {
}
®ister_handler("ls3", \&ls3_handler, 0, 1, 0);
+sub read_lonnet_global {
+ my ($cmd,$tail,$client) = @_;
+ my $userinput = "$cmd:$tail";
+ my $requested = &Apache::lonnet::thaw_unescape($tail);
+ my $result;
+ my %packagevars = (
+ spareid => \%Apache::lonnet::spareid,
+ perlvar => \%Apache::lonnet::perlvar,
+ );
+ my %limit_to = (
+ perlvar => {
+ lonOtherAuthen => 1,
+ lonBalancer => 1,
+ lonVersion => 1,
+ lonSysEMail => 1,
+ lonHostID => 1,
+ lonRole => 1,
+ lonDefDomain => 1,
+ lonLoadLim => 1,
+ lonUserLoadLim => 1,
+ }
+ );
+ if (ref($requested) eq 'HASH') {
+ foreach my $what (keys(%{$requested})) {
+ my $response;
+ my $items = {};
+ if (exists($packagevars{$what})) {
+ if (ref($limit_to{$what}) eq 'HASH') {
+ foreach my $varname (keys(%{$packagevars{$what}})) {
+ if ($limit_to{$what}{$varname}) {
+ $items->{$varname} = $packagevars{$what}{$varname};
+ }
+ }
+ } else {
+ $items = $packagevars{$what};
+ }
+ if ($what eq 'perlvar') {
+ if (!exists($packagevars{$what}{'lonBalancer'})) {
+ if ($dist =~ /^(centos|rhes|fedora|scientific)/) {
+ my $othervarref=LONCAPA::Configuration::read_conf('httpd.conf');
+ if (ref($othervarref) eq 'HASH') {
+ $items->{'lonBalancer'} = $othervarref->{'lonBalancer'};
+ }
+ }
+ }
+ }
+ $response = &Apache::lonnet::freeze_escape($items);
+ }
+ $result .= &escape($what).'='.$response.'&';
+ }
+ }
+ $result =~ s/\&$//;
+ &Reply($client,\$result,$userinput);
+ return 1;
+}
+®ister_handler("readlonnetglobal", \&read_lonnet_global, 0, 1, 0);
+
+sub server_devalidatecache_handler {
+ my ($cmd,$tail,$client) = @_;
+ my $userinput = "$cmd:$tail";
+ my ($name,$id) = map { &unescape($_); } split(/:/,$tail);
+ &Apache::lonnet::devalidate_cache_new($name,$id);
+ my $result = 'ok';
+ &Reply($client,\$result,$userinput);
+ return 1;
+}
+®ister_handler("devalidatecache", \&server_devalidatecache_handler, 0, 1, 0);
+
sub server_timezone_handler {
my ($cmd,$tail,$client) = @_;
my $userinput = "$cmd:$tail";
@@ -1666,6 +1759,15 @@ sub server_homeID_handler {
}
®ister_handler("serverhomeID", \&server_homeID_handler, 0, 1, 0);
+sub server_distarch_handler {
+ my ($cmd,$tail,$client) = @_;
+ my $userinput = "$cmd:$tail";
+ my $reply = &distro_and_arch();
+ &Reply($client,\$reply,$userinput);
+ return 1;
+}
+®ister_handler("serverdistarch", \&server_distarch_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.
@@ -1789,7 +1891,11 @@ sub authenticate_handler {
unless ($clientcancheckhost) {
my $uprimary_id = &Apache::lonnet::domain($udom,'primary');
my $uint_dom = &Apache::lonnet::internet_dom($uprimary_id);
- my @intdoms = &Apache::lonnet::get_internet_names($clientname);
+ my @intdoms;
+ my $internet_names = &Apache::lonnet::get_internet_names($clientname);
+ if (ref($internet_names) eq 'ARRAY') {
+ @intdoms = @{$internet_names};
+ }
unless ($uint_dom ne '' && grep(/^\Q$uint_dom\E$/,@intdoms)) {
my ($remote,$hosted);
my $remotesession = &get_usersession_config($udom,'remotesession');
@@ -1967,7 +2073,7 @@ sub add_user_handler {
."makeuser";
}
unless ($fperror) {
- my $result=&make_passwd_file($uname, $umode,$npass, $passfilename);
+ my $result=&make_passwd_file($uname,$udom,$umode,$npass, $passfilename);
&Reply($client,\$result, $userinput); #BUGBUG - could be fail
} else {
&Failure($client, \$fperror, $userinput);
@@ -2042,7 +2148,7 @@ sub change_authentication_handler {
&Failure($client, \$result);
}
} else {
- my $result=&make_passwd_file($uname, $umode,$npass,$passfilename);
+ my $result=&make_passwd_file($uname,$udom,$umode,$npass,$passfilename);
#
# If the current auth mode is internal, and the old auth mode was
# unix, or krb*, and the user is an author for this domain,
@@ -2151,7 +2257,10 @@ sub update_resource_handler {
my $transname="$fname.in.transfer";
my $remoteurl=&Apache::lonnet::reply("sub:$fname","$clientname");
my $response;
- alarm(120);
+# FIXME: cannot replicate files that take more than two minutes to transfer?
+# alarm(120);
+# FIXME: this should use the LWP mechanism, not internal alarms.
+ alarm(1200);
{
my $ua=new LWP::UserAgent;
my $request=new HTTP::Request('GET',"$remoteurl");
@@ -2159,11 +2268,13 @@ sub update_resource_handler {
}
alarm(0);
if ($response->is_error()) {
+# FIXME: we should probably clean up here instead of just whine
unlink($transname);
my $message=$response->status_line;
&logthis("LWP GET: $message for $fname ($remoteurl)");
} else {
if ($remoteurl!~/\.meta$/) {
+# FIXME: isn't there an internal LWP mechanism for this?
alarm(120);
{
my $ua=new LWP::UserAgent;
@@ -2237,7 +2348,10 @@ sub fetch_user_file_handler {
my $destname=$udir.'/'.$ufile;
my $transname=$udir.'/'.$ufile.'.in.transit';
- my $remoteurl='http://'.$clientip.'/userfiles/'.$fname;
+ my $clientprotocol=$Apache::lonnet::protocol{$clientname};
+ $clientprotocol = 'http' if ($clientprotocol ne 'https');
+ my $clienthost = &Apache::lonnet::hostname($clientname);
+ my $remoteurl=$clientprotocol.'://'.$clienthost.'/userfiles/'.$fname;
my $response;
Debug("Remote URL : $remoteurl Transfername $transname Destname: $destname");
alarm(120);
@@ -2413,7 +2527,6 @@ sub user_has_session_handler {
my ($udom, $uname) = map { &unescape($_) } (split(/:/, $tail));
- &logthis("Looking for $udom $uname");
opendir(DIR,$perlvar{'lonIDsDir'});
my $filename;
while ($filename=readdir(DIR)) {
@@ -3144,7 +3257,7 @@ sub dump_with_regexp {
my $userinput = "$cmd:$tail";
- my ($udom,$uname,$namespace,$regexp,$range)=split(/:/,$tail);
+ my ($udom,$uname,$namespace,$regexp,$range,$extra)=split(/:/,$tail);
if (defined($regexp)) {
$regexp=&unescape($regexp);
} else {
@@ -3162,21 +3275,38 @@ sub dump_with_regexp {
}
my $hashref = &tie_user_hash($udom, $uname, $namespace,
&GDBM_READER());
+ my $skipcheck;
if ($hashref) {
my $qresult='';
my $count=0;
+ if ($extra ne '') {
+ $extra = &Apache::lonnet::thaw_unescape($extra);
+ $skipcheck = $extra->{'skipcheck'};
+ }
+ my @ids = &Apache::lonnet::current_machine_ids();
+ my (%homecourses,$major,$minor,$now);
+ if (($namespace eq 'roles') && (!$skipcheck)) {
+ my $loncaparev = $clientversion;
+ if ($loncaparev eq '') {
+ $loncaparev = $Apache::lonnet::loncaparevs{$clientname};
+ }
+ if ($loncaparev =~ /^\'?(\d+)\.(\d+)\.[\w.\-]+\'?/) {
+ $major = $1;
+ $minor = $2;
+ }
+ $now = time;
+ }
while (my ($key,$value) = each(%$hashref)) {
if ($namespace eq 'roles') {
- if ($key =~ m{^/($LONCAPA::match_domain)/($LONCAPA::match_community)_(cc|co|in|ta|ep|ad|st|cr)}) {
+ if ($key =~ m{^/($LONCAPA::match_domain)/($LONCAPA::match_courseid)(/?[^_]*)_(cc|co|in|ta|ep|ad|st|cr)$}) {
my $cdom = $1;
my $cnum = $2;
- if ($clientversion =~ /^\'?(\d+)\.(\d+)/) {
- my $major = $1;
- my $minor = $2;
- next if (($major < 2) || (($major == 2) && ($minor < 9)));
- } else {
- my $homeserver = &Apache::lonnet::homeserver($cnum,$cdom);
- next unless ($currenthostid eq $homeserver);
+ unless ($skipcheck) {
+ my ($role,$end,$start) = split(/\_/,$value);
+ if (!$end || $end > $now) {
+ next unless (&releasereqd_check($cnum,$cdom,$key,$value,$major,
+ $minor,\%homecourses,\@ids));
+ }
}
}
}
@@ -3196,6 +3326,12 @@ sub dump_with_regexp {
}
}
if (&untie_user_hash($hashref)) {
+ if (($namespace eq 'roles') && (!$skipcheck)) {
+ if (keys(%homecourses) > 0) {
+ $qresult .= &check_homecourses(\%homecourses,$udom,$regexp,$count,
+ $range,$start,$end,$major,$minor);
+ }
+ }
chop($qresult);
&Reply($client, \$qresult, $userinput);
} else {
@@ -3862,7 +3998,7 @@ sub dump_course_id_handler {
$creationcontext = '.';
}
my $unpack = 1;
- if ($description eq '.' && $instcodefilter eq '.' && $coursefilter eq '.' &&
+ if ($description eq '.' && $instcodefilter eq '.' && $ownerfilter eq '.' &&
$typefilter eq '.') {
$unpack = 0;
}
@@ -4246,6 +4382,7 @@ sub put_domain_handler {
sub get_domain_handler {
my ($cmd, $tail, $client) = @_;
+
my $userinput = "$client:$tail";
my ($udom,$namespace,$what)=split(/:/,$tail,3);
@@ -4390,7 +4527,8 @@ sub get_id_handler {
sub put_dcmail_handler {
my ($cmd,$tail,$client) = @_;
my $userinput = "$cmd:$tail";
-
+
+
my ($udom,$what)=split(/:/,$tail);
chomp($what);
my $hashref = &tie_domain_hash($udom, "nohist_dcmail", &GDBM_WRCREAT());
@@ -4654,7 +4792,7 @@ sub tmp_put_handler {
}
my ($id,$store);
$tmpsnum++;
- if ($context eq 'resetpw') {
+ if (($context eq 'resetpw') || ($context eq 'createaccount')) {
$id = &md5_hex(&md5_hex(time.{}.rand().$$));
} else {
$id = $$.'_'.$clientip.'_'.$tmpsnum;
@@ -4972,10 +5110,11 @@ sub get_sections_handler {
sub validate_course_owner_handler {
my ($cmd, $tail, $client) = @_;
my $userinput = "$cmd:$tail";
- my ($inst_course_id, $owner, $cdom) = split(/:/, $tail);
-
+ my ($inst_course_id, $owner, $cdom, $coowners) = split(/:/, $tail);
+
$owner = &unescape($owner);
- my $outcome = &localenroll::new_course($inst_course_id,$owner,$cdom);
+ $coowners = &unescape($coowners);
+ my $outcome = &localenroll::new_course($inst_course_id,$owner,$cdom,$coowners);
&Reply($client, \$outcome, $userinput);
@@ -5963,7 +6102,7 @@ if (-e $pidfile) {
$server = IO::Socket::INET->new(LocalPort => $perlvar{'londPort'},
Type => SOCK_STREAM,
Proto => 'tcp',
- Reuse => 1,
+ ReuseAddr => 1,
Listen => 10 )
or die "making socket: $@\n";
@@ -6026,9 +6165,13 @@ sub HUPSMAN { # sig
# 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);
+# --------------------------- Handle case of another apachereload process (locking)
+ if (&LONCAPA::try_to_lock('/tmp/lock_apachereload')) {
+ my $execdir = $perlvar{'lonDaemons'};
+ my $script = $execdir."/apachereload";
+ system($script);
+ unlink('/tmp/lock_apachereload'); # Remove the lock file.
+ }
}
#
@@ -6201,7 +6344,7 @@ sub logstatus {
sub initnewstatus {
my $docdir=$perlvar{'lonDocRoot'};
my $fh=IO::File->new(">$docdir/lon-status/londstatus.txt");
- my $now=time;
+ my $now=time();
my $local=localtime($now);
print $fh "LOND status $local - parent $$\n\n";
opendir(DIR,"$docdir/lon-status/londchld");
@@ -6292,7 +6435,14 @@ $SIG{USR2} = \&UpdateHosts;
&Apache::lonnet::load_hosts_tab();
my %iphost = &Apache::lonnet::get_iphost(1);
-my $dist=`$perlvar{'lonDaemons'}/distprobe`;
+$dist=`$perlvar{'lonDaemons'}/distprobe`;
+
+my $arch = `uname -i`;
+chomp($arch);
+if ($arch eq 'unknown') {
+ $arch = `uname -m`;
+ chomp($arch);
+}
# --------------------------------------------------------------
# Accept connections. When a connection comes in, it is validated
@@ -6351,6 +6501,7 @@ sub make_new_child {
or die "Can't unblock SIGINT for fork: $!\n";
$children{$pid} = $clientip;
&status('Started child '.$pid);
+ close($client);
return;
} else {
# Child can *not* return from this subroutine.
@@ -6359,6 +6510,13 @@ sub make_new_child {
#don't get intercepted
$SIG{USR1}= \&logstatus;
$SIG{ALRM}= \&timeout;
+ #
+ # Block sigpipe as it gets thrownon socket disconnect and we want to
+ # deal with that as a read faiure instead.
+ #
+ my $blockset = POSIX::SigSet->new(SIGPIPE);
+ sigprocmask(SIG_BLOCK, $blockset);
+
$lastlog='Forked ';
$status='Forked';
@@ -6573,8 +6731,7 @@ sub is_author {
}
#
# Checks to see if the input roleput request was to set
-# an author role. If so, invokes the lchtmldir script to set
-# up a correct public_html
+# an author role. If so, creates construction space
# Parameters:
# request - The request sent to the rolesput subchunk.
# We're looking for /domain/_au
@@ -6584,16 +6741,15 @@ sub is_author {
#
sub manage_permissions {
my ($request, $domain, $user, $authtype) = @_;
-
- &Debug("manage_permissions: $request $domain $user $authtype");
-
# See if the request is of the form /$domain/_au
if($request =~ /^(\/\Q$domain\E\/_au)$/) { # It's an author rolesput...
- my $execdir = $perlvar{'lonDaemons'};
- my $userhome= "/home/$user" ;
- &logthis("system $execdir/lchtmldir $userhome $user $authtype");
- &Debug("Setting homedir permissions for $userhome");
- system("$execdir/lchtmldir $userhome $user $authtype");
+ my $path=$perlvar{'lonDocRoot'}."/priv/$domain";
+ unless (-e $path) {
+ mkdir($path);
+ }
+ unless (-e $path.'/'.$user) {
+ mkdir($path.'/'.$user);
+ }
}
}
@@ -7082,7 +7238,9 @@ sub subscribe {
# the metadata
unless ($fname=~/\.meta$/) { &unsub("$fname.meta",$clientip); }
$fname=~s/\/home\/httpd\/html\/res/raw/;
- $fname="http://".&Apache::lonnet::hostname($perlvar{'lonHostID'})."/".$fname;
+ my $protocol = $Apache::lonnet::protocol{$perlvar{'lonHostID'}};
+ $protocol = 'http' if ($protocol ne 'https');
+ $fname=$protocol.'://'.&Apache::lonnet::hostname($perlvar{'lonHostID'})."/".$fname;
$result="$fname\n";
}
} else {
@@ -7124,7 +7282,7 @@ sub change_unix_password {
sub make_passwd_file {
- my ($uname, $umode,$npass,$passfilename)=@_;
+ my ($uname,$udom,$umode,$npass,$passfilename)=@_;
my $result="ok";
if ($umode eq 'krb4' or $umode eq 'krb5') {
{
@@ -7165,7 +7323,7 @@ sub make_passwd_file {
#
my $uid = getpwnam($uname);
if((defined $uid) && ($uid == 0)) {
- &logthis(">>>Attempted to create privilged account blocked");
+ &logthis(">>>Attempt to create privileged account blocked");
return "no_priv_account_error\n";
}
@@ -7177,6 +7335,7 @@ sub make_passwd_file {
&Debug("user = ".$uname.", Password =". $npass);
my $se = IO::File->new("|$execpath > $perlvar{'lonDaemons'}/logs/lcuseradd.log");
print $se "$uname\n";
+ print $se "$udom\n";
print $se "$npass\n";
print $se "$npass\n";
print $se "$lc_error_file\n"; # Status -> unique file.
@@ -7270,6 +7429,172 @@ sub get_usersession_config {
return;
}
+sub releasereqd_check {
+ my ($cnum,$cdom,$key,$value,$major,$minor,$homecourses,$ids) = @_;
+ my $home = &Apache::lonnet::homeserver($cnum,$cdom);
+ return if ($home eq 'no_host');
+ my ($reqdmajor,$reqdminor,$displayrole);
+ if ($cnum =~ /$LONCAPA::match_community/) {
+ if ($major eq '' && $minor eq '') {
+ return unless ((ref($ids) eq 'ARRAY') &&
+ (grep(/^\Q$home\E$/,@{$ids})));
+ } else {
+ $reqdmajor = 2;
+ $reqdminor = 9;
+ return unless (&useable_role($reqdmajor,$reqdminor,$major,$minor));
+ }
+ }
+ my $hashid = $cdom.':'.$cnum;
+ my ($courseinfo,$cached) =
+ &Apache::lonnet::is_cached_new('courseinfo',$hashid);
+ if (defined($cached)) {
+ if (ref($courseinfo) eq 'HASH') {
+ if (exists($courseinfo->{'releaserequired'})) {
+ my ($reqdmajor,$reqdminor) = split(/\./,$courseinfo->{'releaserequired'});
+ return unless (&useable_role($reqdmajor,$reqdminor,$major,$minor));
+ }
+ }
+ } else {
+ if (ref($ids) eq 'ARRAY') {
+ if (grep(/^\Q$home\E$/,@{$ids})) {
+ if (ref($homecourses) eq 'HASH') {
+ if (ref($homecourses->{$hashid}) eq 'ARRAY') {
+ push(@{$homecourses->{$hashid}},{$key=>$value});
+ } else {
+ $homecourses->{$hashid} = [{$key=>$value}];
+ }
+ }
+ return;
+ }
+ }
+ my $courseinfo = &get_courseinfo_hash($cnum,$cdom,$home);
+ if (ref($courseinfo) eq 'HASH') {
+ if (exists($courseinfo->{'releaserequired'})) {
+ my ($reqdmajor,$reqdminor) = split(/\./,$courseinfo->{'releaserequired'});
+ return unless (&useable_role($reqdmajor,$reqdminor,$major,$minor));
+ }
+ } else {
+ return;
+ }
+ }
+ return 1;
+}
+
+sub get_courseinfo_hash {
+ my ($cnum,$cdom,$home) = @_;
+ my %info;
+ eval {
+ local($SIG{ALRM}) = sub { die "timeout\n"; };
+ local($SIG{__DIE__})='DEFAULT';
+ alarm(3);
+ %info = &Apache::lonnet::courseiddump($cdom,'.',1,'.','.',$cnum,1,[$home],'.');
+ alarm(0);
+ };
+ if ($@) {
+ if ($@ eq "timeout\n") {
+ &logthis("WARNING courseiddump for $cnum:$cdom from $home timedout");
+ } else {
+ &logthis("WARNING unexpected error during eval of call for courseiddump from $home");
+ }
+ } else {
+ if (ref($info{$cdom.'_'.$cnum}) eq 'HASH') {
+ my $hashid = $cdom.':'.$cnum;
+ return &Apache::lonnet::do_cache_new('courseinfo',$hashid,$info{$cdom.'_'.$cnum},600);
+ }
+ }
+ return;
+}
+
+sub check_homecourses {
+ my ($homecourses,$udom,$regexp,$count,$range,$start,$end,$major,$minor) = @_;
+ my ($result,%addtocache);
+ my $yesterday = time - 24*3600;
+ if (ref($homecourses) eq 'HASH') {
+ my (%okcourses,%courseinfo,%recent);
+ my $hashref = &tie_domain_hash($udom, "nohist_courseids", &GDBM_WRCREAT());
+ if ($hashref) {
+ while (my ($key,$value) = each(%$hashref)) {
+ my $unesc_key = &unescape($key);
+ if ($unesc_key =~ /^lasttime:(\w+)$/) {
+ my $cid = $1;
+ $cid =~ s/_/:/;
+ if ($value > $yesterday ) {
+ $recent{$cid} = 1;
+ }
+ next;
+ }
+ my $items = &Apache::lonnet::thaw_unescape($value);
+ if (ref($items) eq 'HASH') {
+ my $hashid = $unesc_key;
+ $hashid =~ s/_/:/;
+ $courseinfo{$hashid} = $items;
+ if (ref($homecourses->{$hashid}) eq 'ARRAY') {
+ my ($reqdmajor,$reqdminor) = split(/\./,$items->{'releaserequired'});
+ if (&useable_role($reqdmajor,$reqdminor,$major,$minor)) {
+ $okcourses{$hashid} = 1;
+ }
+ }
+ }
+ }
+ unless (&untie_domain_hash($hashref)) {
+ &logthis('Failed to untie tied hash for nohist_courseids.db');
+ }
+ } else {
+ &logthis('Failed to tie hash for nohist_courseids.db');
+ return;
+ }
+ foreach my $hashid (keys(%recent)) {
+ my ($result,$cached)=&Apache::lonnet::is_cached_new('courseinfo',$hashid);
+ unless ($cached) {
+ &Apache::lonnet::do_cache_new('courseinfo',$hashid,$courseinfo{$hashid},600);
+ }
+ }
+ foreach my $hashid (keys(%{$homecourses})) {
+ next if ($recent{$hashid});
+ &Apache::lonnet::do_cache_new('courseinfo',$hashid,$courseinfo{$hashid},600);
+ }
+ foreach my $hashid (keys(%okcourses)) {
+ if (ref($homecourses->{$hashid}) eq 'ARRAY') {
+ foreach my $role (@{$homecourses->{$hashid}}) {
+ if (ref($role) eq 'HASH') {
+ while (my ($key,$value) = each(%{$role})) {
+ if ($regexp eq '.') {
+ $count++;
+ if (defined($range) && $count >= $end) { last; }
+ if (defined($range) && $count < $start) { next; }
+ $result.=$key.'='.$value.'&';
+ } else {
+ my $unescapeKey = &unescape($key);
+ if (eval('$unescapeKey=~/$regexp/')) {
+ $count++;
+ if (defined($range) && $count >= $end) { last; }
+ if (defined($range) && $count < $start) { next; }
+ $result.="$key=$value&";
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return $result;
+}
+
+sub useable_role {
+ my ($reqdmajor,$reqdminor,$major,$minor) = @_;
+ if ($reqdmajor ne '' && $reqdminor ne '') {
+ return if (($major eq '' && $minor eq '') ||
+ ($major < $reqdmajor) ||
+ (($major == $reqdmajor) && ($minor < $reqdminor)));
+ }
+ return 1;
+}
+
+sub distro_and_arch {
+ return $dist.':'.$arch;
+}
+
# ----------------------------------- POD (plain old documentation, CPAN style)
=head1 NAME