--- loncom/lond 2006/01/31 04:40:12 1.310 +++ loncom/lond 2006/01/31 16:12:12 1.313 @@ -2,7 +2,7 @@ # The LearningOnline Network # lond "LON Daemon" Server (port "LOND" 5663) # -# $Id: lond,v 1.310 2006/01/31 04:40:12 albertel Exp $ +# $Id: lond,v 1.313 2006/01/31 16:12:12 albertel Exp $ # # Copyright Michigan State University Board of Trustees # @@ -53,13 +53,15 @@ use LONCAPA::ConfigFileEdit; use LONCAPA::lonlocal; use LONCAPA::lonssl; use Fcntl qw(:flock); +use Symbol; my $DEBUG = 0; # Non zero to enable debug log entries. my $status=''; my $lastlog=''; +my $lond_max_wait_time = 13; -my $VERSION='$Revision: 1.310 $'; #' stupid emacs +my $VERSION='$Revision: 1.313 $'; #' stupid emacs my $remoteVERSION; my $currenthostid="default"; my $currentdomainid; @@ -971,23 +973,13 @@ sub tie_domain_hash { my $user_top_dir = $perlvar{'lonUsersDir'}; my $domain_dir = $user_top_dir."/$domain"; - my $resource_file = $domain_dir."/$namespace.db"; - my %hash; - if(tie(%hash, 'GDBM_File', $resource_file, $how, 0640)) { - if (defined($loghead)) { # Need to log the operation. - my $logFh = IO::File->new(">>$domain_dir/$namespace.hist"); - if($logFh) { - my $timestamp = time; - print $logFh "$loghead:$timestamp:$logtail\n"; - } - $logFh->close; - } - return \%hash; # Return the tied hash. - } else { - return undef; # Tie failed. - } + my $resource_file = $domain_dir."/$namespace"; + return &_locking_hash_tie($resource_file,$namespace,$how,$loghead,$logtail); } +sub untie_domain_hash { + return &_locking_hash_untie(@_); +} # # Ties a user's resource file to a hash. # If necessary, an appropriate history @@ -1013,18 +1005,27 @@ sub tie_user_hash { $namespace=~s/\//\_/g; # / -> _ $namespace=~s/\W//g; # whitespace eliminated. my $proname = propath($domain, $user); - - # Tie the database. - + + my $file_prefix="$proname/$namespace"; + return &_locking_hash_tie($file_prefix,$namespace,$how,$loghead,$what); +} + +sub untie_user_hash { + return &_locking_hash_untie(@_); +} + +# internal routines that handle the actual tieing and untieing process + +sub _do_hash_tie { + my ($file_prefix,$namespace,$how,$loghead,$what) = @_; my %hash; - if(tie(%hash, 'GDBM_File', "$proname/$namespace.db", - $how, 0640)) { + if(tie(%hash, 'GDBM_File', "$file_prefix.db", $how, 0640)) { # If this is a namespace for which a history is kept, # make the history log entry: if (($namespace !~/^nohist\_/) && (defined($loghead))) { my $args = scalar @_; - Debug(" Opening history: $namespace $args"); - my $hfh = IO::File->new(">>$proname/$namespace.hist"); + Debug(" Opening history: $file_prefix $args"); + my $hfh = IO::File->new(">>$file_prefix.hist"); if($hfh) { my $now = time; print $hfh "$loghead:$now:$what\n"; @@ -1035,7 +1036,76 @@ sub tie_user_hash { } else { return undef; } +} + +sub _do_hash_untie { + my ($hashref) = @_; + my $result = untie(%$hashref); + return $result; +} + +{ + my $sym; + + sub _locking_hash_tie { + my ($file_prefix,$namespace,$how,$loghead,$what) = @_; + + my ($lock); + if ($how eq &GDBM_READER()) { + $lock=LOCK_SH; + $how=$how|&GDBM_NOLOCK(); + #if the db doesn't exist we can't read from it + if (! -e "$file_prefix.db") { + $! = 2; + return undef; + } + } elsif ($how eq &GDBM_WRCREAT()) { + $lock=LOCK_EX; + $how=$how|&GDBM_NOLOCK(); + if (! -e "$file_prefix.db") { + # doesn't exist but we need it to in order to successfully + # lock it so bring it into existance + open(TOUCH,">>$file_prefix.db"); + close(TOUCH); + } + } else { + &logthis("Unknown method $how for $file_prefix"); + die(); + } + + &logthis("$$ for $namespace"); + $sym=&Symbol::gensym(); + open($sym,"$file_prefix.db"); + &logthis("$$ for $namespace attempt lock"); + my $failed=0; + eval { + local $SIG{__DIE__}='DEFAULT'; + local $SIG{ALRM}=sub { + $failed=1; + die("failed lock"); + }; + alarm($lond_max_wait_time); + flock($sym,$lock); + alarm(0); + }; + if ($failed) { + &logthis("$$ for $namespace got failed lock"); + $! = 100; # throwing error # 100 + return undef; + } + &logthis("$$ for $file_prefix.db got lock"); + return &_do_hash_tie($file_prefix,$namespace,$how,$loghead,$what); + } + + sub _locking_hash_untie { + my ($hashref) = @_; + my $result = untie(%$hashref); + flock($sym,LOCK_UN); + close($sym); + undef($sym); + return $result; + } } # read_profile @@ -1068,7 +1138,7 @@ sub read_profile { $qresult.="$hashref->{$queries[$i]}&"; # Presumably failure gives empty string. } $qresult=~s/\&$//; # Remove trailing & from last lookup. - if (untie %$hashref) { + if (&untie_user_hash($hashref)) { return $qresult; } else { return "error: ".($!+0)." untie (GDBM) Failed"; @@ -2377,7 +2447,7 @@ sub put_user_profile_entry { my ($key,$value)=split(/=/,$pair); $hashref->{$key}=$value; } - if (untie(%$hashref)) { + if (&untie_user_hash($hashref)) { &Reply( $client, "ok\n", $userinput); } else { &Failure($client, "error: ".($!+0)." untie(GDBM) failed ". @@ -2440,7 +2510,7 @@ sub newput_user_profile_entry { $hashref->{$key}=$value; } - if (untie(%$hashref)) { + if (&untie_user_hash($hashref)) { &Reply( $client, "ok\n", $userinput); } else { &Failure($client, "error: ".($!+0)." untie(GDBM) failed ". @@ -2493,7 +2563,7 @@ sub increment_user_value_handler { } } } - if (untie(%$hashref)) { + if (&untie_user_hash($hashref)) { &Reply( $client, "ok\n", $userinput); } else { &Failure($client, "error: ".($!+0)." untie(GDBM) failed ". @@ -2560,7 +2630,7 @@ sub roles_put_handler { $auth_type); $hashref->{$key}=$value; } - if (untie($hashref)) { + if (&untie_user_hash($hashref)) { &Reply($client, "ok\n", $userinput); } else { &Failure( $client, "error: ".($!+0)." untie(GDBM) Failed ". @@ -2611,7 +2681,7 @@ sub roles_delete_handler { foreach my $key (@rolekeys) { delete $hashref->{$key}; } - if (untie(%$hashref)) { + if (&untie_user_hash(%$hashref)) { &Reply($client, "ok\n", $userinput); } else { &Failure( $client, "error: ".($!+0)." untie(GDBM) Failed ". @@ -2752,7 +2822,7 @@ sub delete_profile_entry { foreach my $key (@keys) { delete($hashref->{$key}); } - if (untie(%$hashref)) { + if (&untie_user_hash(%$hashref)) { &Reply($client, "ok\n", $userinput); } else { &Failure($client, "error: ".($!+0)." untie(GDBM) Failed ". @@ -2794,7 +2864,7 @@ sub get_profile_keys { foreach my $key (keys %$hashref) { $qresult.="$key&"; } - if (untie(%$hashref)) { + if (&untie_user_hash(%$hashref)) { $qresult=~s/\&$//; &Reply($client, "$qresult\n", $userinput); } else { @@ -2855,7 +2925,7 @@ sub dump_profile_database { $data{$symb}->{$param}=$value; $data{$symb}->{'v.'.$param}=$v; } - if (untie(%$hashref)) { + if (&untie_user_hash($hashref)) { while (my ($symb,$param_hash) = each(%data)) { while(my ($param,$value) = each (%$param_hash)){ next if ($param =~ /^v\./); # Ignore versions... @@ -2947,7 +3017,7 @@ sub dump_with_regexp { } } } - if (untie(%$hashref)) { + if (&untie_user_hash($hashref)) { chop($qresult); &Reply($client, "$qresult\n", $userinput); } else { @@ -3009,7 +3079,7 @@ sub store_handler { $hashref->{"$version:$rid:timestamp"}=$now; $allkeys.='timestamp'; $hashref->{"$version:keys:$rid"}=$allkeys; - if (untie($hashref)) { + if (&untie_user_hash($hashref)) { &Reply($client, "ok\n", $userinput); } else { &Failure($client, "error: ".($!+0)." untie(GDBM) Failed ". @@ -3076,7 +3146,7 @@ sub restore_handler { $qresult.="$scope:$key=".$hashref->{"$scope:$rid:$key"}."&"; } } - if (untie(%$hashref)) { + if (&untie_user_hash($hashref)) { $qresult=~s/\&$//; &Reply( $client, "$qresult\n", $userinput); } else { @@ -3309,7 +3379,7 @@ sub put_course_id_handler { } $hashref->{$key}=$courseinfo.':'.$now; } - if (untie(%$hashref)) { + if (&untie_domain_hash($hashref)) { &Reply( $client, "ok\n", $userinput); } else { &Failure($client, "error: ".($!+0) @@ -3425,7 +3495,7 @@ sub dump_course_id_handler { $qresult.=$key.'='.$descr.':'.$inst_code.':'.$owner.'&'; } } - if (untie(%$hashref)) { + if (&untie_domain_hash($hashref)) { chop($qresult); &Reply($client, "$qresult\n", $userinput); } else { @@ -3474,7 +3544,7 @@ sub put_id_handler { my ($key,$value)=split(/=/,$pair); $hashref->{$key}=$value; } - if (untie(%$hashref)) { + if (&untie_domain_hash($hashref)) { &Reply($client, "ok\n", $userinput); } else { &Failure($client, "error: ".($!+0)." untie(GDBM) Failed ". @@ -3523,7 +3593,7 @@ sub get_id_handler { for (my $i=0;$i<=$#queries;$i++) { $qresult.="$hashref->{$queries[$i]}&"; } - if (untie(%$hashref)) { + if (&untie_domain_hash($hashref)) { $qresult=~s/\&$//; &Reply($client, "$qresult\n", $userinput); } else { @@ -3567,7 +3637,7 @@ sub put_dcmail_handler { my ($key,$value)=split(/=/,$what); $hashref->{$key}=$value; } - if (untie(%$hashref)) { + if (&untie_domain_hash($hashref)) { &Reply($client, "ok\n", $userinput); } else { &Failure($client, "error: ".($!+0)." untie(GDBM) Failed ". @@ -3647,7 +3717,7 @@ sub dump_dcmail_handler { $qresult.=$key.'='.$value.'&'; } } - if (untie(%$hashref)) { + if (&untie_domain_hash($hashref)) { chop($qresult); &Reply($client, "$qresult\n", $userinput); } else { @@ -3694,7 +3764,7 @@ sub put_domainroles_handler { my ($key,$value)=split(/=/,$pair); $hashref->{$key}=$value; } - if (untie(%$hashref)) { + if (&untie_domain_hash($hashref)) { &Reply($client, "ok\n", $userinput); } else { &Failure($client, "error: ".($!+0)." untie(GDBM) Failed ". @@ -3775,7 +3845,7 @@ sub dump_domainroles_handler { $qresult.=$key.'='.$value.'&'; } } - if (untie(%$hashref)) { + if (&untie_domain_hash($hashref)) { chop($qresult); &Reply($client, "$qresult\n", $userinput); } else { @@ -5629,7 +5699,7 @@ sub get_chat { &GDBM_READER()); if ($hashref) { @entries=map { $_.':'.$hashref->{$_} } sort(keys(%$hashref)); - untie(%$hashref); + &untie_user_hash($hashref); } my @participants=(); my $cutoff=time-60; @@ -5642,7 +5712,7 @@ sub get_chat { push(@participants, 'active_participant:'.$user); } } - untie(%$hashref); + &untie_user_hash($hashref); } return (@participants,@entries); } @@ -5679,7 +5749,7 @@ sub chat_add { } close(CHATLOG); } - untie(%$hashref); + &untie_user_hash($hashref); } }