--- loncom/lond	2005/07/12 21:29:58	1.291
+++ loncom/lond	2006/01/31 21:54:34	1.315
@@ -2,7 +2,7 @@
 # The LearningOnline Network
 # lond "LON Daemon" Server (port "LOND" 5663)
 #
-# $Id: lond,v 1.291 2005/07/12 21:29:58 albertel Exp $
+# $Id: lond,v 1.315 2006/01/31 21:54:34 albertel Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -48,17 +48,20 @@ use localauth;
 use localenroll;
 use localstudentphoto;
 use File::Copy;
+use File::Find;
 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.291 $'; #' stupid emacs
+my $VERSION='$Revision: 1.315 $'; #' stupid emacs
 my $remoteVERSION;
 my $currenthostid="default";
 my $currentdomainid;
@@ -86,6 +89,7 @@ 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.
 
@@ -969,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
@@ -1011,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";
@@ -1033,7 +1036,72 @@ 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();
+	}
+    
+	$sym=&Symbol::gensym();
+	open($sym,"$file_prefix.db");
+	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) {
+	    $! = 100; # throwing error # 100
+	    return undef;
+	}
+	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
@@ -1066,7 +1134,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";
@@ -1363,18 +1431,18 @@ sub du_handler {
     #  etc.
     #
     if (-d $ududir) {
-	#  And as Shakespeare would say to make
-	#  assurance double sure, 
-	# use execute_command to ensure that the command is not executed in
-	# a shell that can screw us up.
-        my $file_list =  execute_command("find $ududir -not -regex '.*\.[0-9]+\.[^\.]+' -not -name '*.meta' -print");
-	my $duout = execute_command("du -ks $file_list");
-	$duout=~s/[^\d]//g; #preserve only the numbers
-	&Reply($client,"$duout\n","$cmd:$ududir");
+	my $total_size=0;
+	my $code=sub { 
+	    if ($_=~/\.\d+\./) { return;} 
+	    if ($_=~/\.meta$/) { return;}
+	    $total_size+=(stat($_))[7];
+	};
+	chdir($ududir);
+	find($code,$ududir);
+	$total_size=int($total_size/1024);
+	&Reply($client,"$total_size\n","$cmd:$ududir");
     } else {
-
 	&Failure($client, "bad_directory:$ududir\n","$cmd:$ududir"); 
-
     }
     return 1;
 }
@@ -1421,7 +1489,7 @@ sub ls_handler {
 			open(FILE, $ulsdir.'/'.$ulsfn.".meta");
 			my @obsolete=<FILE>;
 			foreach my $obsolete (@obsolete) {
-			    if($obsolete =~ m|(<obsolete>)(on)|) { $obs = 1; } 
+			    if($obsolete =~ m/(<obsolete>)(on|1)/) { $obs = 1; } 
 			    if($obsolete =~ m|(<copyright>)(default)|) { $rights = 1; }
 			}
 		    }
@@ -1489,7 +1557,7 @@ sub ls2_handler {
                         open(FILE, $ulsdir.'/'.$ulsfn.".meta");
                         my @obsolete=<FILE>;
                         foreach my $obsolete (@obsolete) {
-                            if($obsolete =~ m|(<obsolete>)(on)|) { $obs = 1; } 
+                            if($obsolete =~ m/(<obsolete>)(on|1)/) { $obs = 1; } 
                             if($obsolete =~ m|(<copyright>)(default)|) {
                                 $rights = 1;
                             }
@@ -1942,6 +2010,7 @@ sub update_resource_handler {
 	    my $since=$now-$atime;
 	    if ($since>$perlvar{'lonExpire'}) {
 		my $reply=&reply("unsub:$fname","$clientname");
+		&devalidate_meta_cache($fname);
 		unlink("$fname");
 	    } else {
 		my $transname="$fname.in.transfer";
@@ -1972,6 +2041,7 @@ sub update_resource_handler {
 			alarm(0);
 		    }
 		    rename($transname,$fname);
+		    &devalidate_meta_cache($fname);
 		}
 	    }
 	    &Reply( $client, "ok\n", $userinput);
@@ -1985,6 +2055,26 @@ sub update_resource_handler {
 }
 &register_handler("update", \&update_resource_handler, 0 ,1, 0);
 
+sub devalidate_meta_cache {
+    my ($url) = @_;
+    use Cache::Memcached;
+    my $memcache = new Cache::Memcached({'servers'=>['127.0.0.1:11211']});
+    $url = &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.
@@ -2353,7 +2443,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 ".
@@ -2416,7 +2506,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 ".
@@ -2469,7 +2559,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 ".
@@ -2536,7 +2626,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 ".
@@ -2587,7 +2677,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 ".
@@ -2728,7 +2818,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 ".
@@ -2770,7 +2860,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 {
@@ -2831,7 +2921,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...
@@ -2886,27 +2976,44 @@ sub dump_with_regexp {
 
     my $userinput = "$cmd:$tail";
 
-    my ($udom,$uname,$namespace,$regexp)=split(/:/,$tail);
+    my ($udom,$uname,$namespace,$regexp,$range)=split(/:/,$tail);
     if (defined($regexp)) {
 	$regexp=&unescape($regexp);
     } else {
 	$regexp='.';
     }
+    my ($start,$end);
+    if (defined($range)) {
+	if ($range =~/^(\d+)\-(\d+)$/) {
+	    ($start,$end) = ($1,$2);
+	} elsif ($range =~/^(\d+)$/) {
+	    ($start,$end) = (0,$1);
+	} else {
+	    undef($range);
+	}
+    }
     my $hashref = &tie_user_hash($udom, $uname, $namespace,
 				 &GDBM_READER());
     if ($hashref) {
         my $qresult='';
+	my $count=0;
 	while (my ($key,$value) = each(%$hashref)) {
 	    if ($regexp eq '.') {
+		$count++;
+		if (defined($range) && $count >= $end)   { last; }
+		if (defined($range) && $count <  $start) { next; }
 		$qresult.=$key.'='.$value.'&';
 	    } else {
 		my $unescapeKey = &unescape($key);
 		if (eval('$unescapeKey=~/$regexp/')) {
+		    $count++;
+		    if (defined($range) && $count >= $end)   { last; }
+		    if (defined($range) && $count <  $start) { next; }
 		    $qresult.="$key=$value&";
 		}
 	    }
 	}
-	if (untie(%$hashref)) {
+	if (&untie_user_hash($hashref)) {
 	    chop($qresult);
 	    &Reply($client, "$qresult\n", $userinput);
 	} else {
@@ -2968,7 +3075,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 ".
@@ -3020,24 +3127,22 @@ sub restore_handler {
     $namespace=~s/\//\_/g;
     $namespace=~s/\W//g;
     chomp($rid);
-    my $proname=&propath($udom,$uname);
     my $qresult='';
-    my %hash;
-    if (tie(%hash,'GDBM_File',"$proname/$namespace.db",
-	    &GDBM_READER(),0640)) {
-	my $version=$hash{"version:$rid"};
+    my $hashref = &tie_user_hash($udom, $uname, $namespace, &GDBM_READER());
+    if ($hashref) {
+	my $version=$hashref->{"version:$rid"};
 	$qresult.="version=$version&";
 	my $scope;
 	for ($scope=1;$scope<=$version;$scope++) {
-	    my $vkeys=$hash{"$scope:keys:$rid"};
+	    my $vkeys=$hashref->{"$scope:keys:$rid"};
 	    my @keys=split(/:/,$vkeys);
 	    my $key;
 	    $qresult.="$scope:keys=$vkeys&";
 	    foreach $key (@keys) {
-		$qresult.="$scope:$key=".$hash{"$scope:$rid:$key"}."&";
+		$qresult.="$scope:$key=".$hashref->{"$scope:$rid:$key"}."&";
 	    }                                  
 	}
-	if (untie(%hash)) {
+	if (&untie_user_hash($hashref)) {
 	    $qresult=~s/\&$//;
 	    &Reply( $client, "$qresult\n", $userinput);
 	} else {
@@ -3270,7 +3375,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)
@@ -3386,7 +3491,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 {
@@ -3435,7 +3540,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 ".
@@ -3484,7 +3589,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 {
@@ -3501,6 +3606,258 @@ sub get_id_handler {
 &register_handler("idget", \&get_id_handler, 0, 1, 0);
 
 #
+# Puts broadcast e-mail sent by Domain Coordinator in nohist_dcmail database 
+#
+# Parameters
+#   $cmd       - Command keyword that caused us to be dispatched.
+#   $tail      - Tail of the command.  Consists of a colon separated:
+#               domain - the domain whose dcmail we are recording
+#               email    Consists of key=value pair 
+#                        where key is unique msgid
+#                        and value is message (in XML)
+#   $client    - Socket open on the client.
+#
+# Returns:
+#    1 - indicating processing should continue.
+# Side effects
+#     reply is written to $client.
+#
+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());
+    if ($hashref) {
+        my ($key,$value)=split(/=/,$what);
+        $hashref->{$key}=$value;
+    }
+    if (&untie_domain_hash($hashref)) {
+        &Reply($client, "ok\n", $userinput);
+    } else {
+        &Failure($client, "error: ".($!+0)." untie(GDBM) Failed ".
+                 "while attempting dcmailput\n", $userinput);
+    }
+    return 1;
+}
+&register_handler("dcmailput", \&put_dcmail_handler, 0, 1, 0);
+
+#
+# Retrieves broadcast e-mail from nohist_dcmail database
+# Returns to client an & separated list of key=value pairs,
+# where key is msgid and value is message information.
+#
+# Parameters
+#   $cmd       - Command keyword that caused us to be dispatched.
+#   $tail      - Tail of the command.  Consists of a colon separated:
+#               domain - the domain whose dcmail table we dump
+#               startfilter - beginning of time window 
+#               endfilter - end of time window
+#               sendersfilter - & separated list of username:domain 
+#                 for senders to search for.
+#   $client    - Socket open on the client.
+#
+# Returns:
+#    1 - indicating processing should continue.
+# Side effects
+#     reply (& separated list of msgid=messageinfo pairs) is 
+#     written to $client.
+#
+sub dump_dcmail_handler {
+    my ($cmd, $tail, $client) = @_;
+                                                                                
+    my $userinput = "$cmd:$tail";
+    my ($udom,$startfilter,$endfilter,$sendersfilter) = split(/:/,$tail);
+    chomp($sendersfilter);
+    my @senders = ();
+    if (defined($startfilter)) {
+        $startfilter=&unescape($startfilter);
+    } else {
+        $startfilter='.';
+    }
+    if (defined($endfilter)) {
+        $endfilter=&unescape($endfilter);
+    } else {
+        $endfilter='.';
+    }
+    if (defined($sendersfilter)) {
+        $sendersfilter=&unescape($sendersfilter);
+	@senders = map { &unescape($_) } split(/\&/,$sendersfilter);
+    }
+
+    my $qresult='';
+    my $hashref = &tie_domain_hash($udom, "nohist_dcmail", &GDBM_WRCREAT());
+    if ($hashref) {
+        while (my ($key,$value) = each(%$hashref)) {
+            my $match = 1;
+            my ($timestamp,$subj,$uname,$udom) = 
+		split(/:/,&unescape(&unescape($key)),5); # yes, twice really
+            $subj = &unescape($subj);
+            unless ($startfilter eq '.' || !defined($startfilter)) {
+                if ($timestamp < $startfilter) {
+                    $match = 0;
+                }
+            }
+            unless ($endfilter eq '.' || !defined($endfilter)) {
+                if ($timestamp > $endfilter) {
+                    $match = 0;
+                }
+            }
+            unless (@senders < 1) {
+                unless (grep/^$uname:$udom$/,@senders) {
+                    $match = 0;
+                }
+            }
+            if ($match == 1) {
+                $qresult.=$key.'='.$value.'&';
+            }
+        }
+        if (&untie_domain_hash($hashref)) {
+            chop($qresult);
+            &Reply($client, "$qresult\n", $userinput);
+        } else {
+            &Failure($client, "error: ".($!+0)." untie(GDBM) Failed ".
+                    "while attempting dcmaildump\n", $userinput);
+        }
+    } else {
+        &Failure($client, "error: ".($!+0)." tie(GDBM) Failed ".
+                "while attempting dcmaildump\n", $userinput);
+    }
+    return 1;
+}
+
+&register_handler("dcmaildump", \&dump_dcmail_handler, 0, 1, 0);
+
+#
+# Puts domain roles in nohist_domainroles database
+#
+# Parameters
+#   $cmd       - Command keyword that caused us to be dispatched.
+#   $tail      - Tail of the command.  Consists of a colon separated:
+#               domain - the domain whose roles we are recording  
+#               role -   Consists of key=value pair
+#                        where key is unique role
+#                        and value is start/end date information
+#   $client    - Socket open on the client.
+#
+# Returns:
+#    1 - indicating processing should continue.
+# Side effects
+#     reply is written to $client.
+#
+
+sub put_domainroles_handler {
+    my ($cmd,$tail,$client) = @_;
+
+    my $userinput = "$cmd:$tail";
+    my ($udom,$what)=split(/:/,$tail);
+    chomp($what);
+    my @pairs=split(/\&/,$what);
+    my $hashref = &tie_domain_hash($udom, "nohist_domainroles", &GDBM_WRCREAT());
+    if ($hashref) {
+        foreach my $pair (@pairs) {
+            my ($key,$value)=split(/=/,$pair);
+            $hashref->{$key}=$value;
+        }
+        if (&untie_domain_hash($hashref)) {
+            &Reply($client, "ok\n", $userinput);
+        } else {
+            &Failure($client, "error: ".($!+0)." untie(GDBM) Failed ".
+                     "while attempting domroleput\n", $userinput);
+        }
+    } else {
+        &Failure( $client, "error: ".($!+0)." tie(GDBM) Failed ".
+                  "while attempting domroleput\n", $userinput);
+    }
+                                                                                  
+    return 1;
+}
+
+&register_handler("domroleput", \&put_domainroles_handler, 0, 1, 0);
+
+#
+# Retrieves domain roles from nohist_domainroles database
+# Returns to client an & separated list of key=value pairs,
+# where key is role and value is start and end date information.
+#
+# Parameters
+#   $cmd       - Command keyword that caused us to be dispatched.
+#   $tail      - Tail of the command.  Consists of a colon separated:
+#               domain - the domain whose domain roles table we dump
+#   $client    - Socket open on the client.
+#
+# Returns:
+#    1 - indicating processing should continue.
+# Side effects
+#     reply (& separated list of role=start/end info pairs) is
+#     written to $client.
+#
+sub dump_domainroles_handler {
+    my ($cmd, $tail, $client) = @_;
+                                                                                           
+    my $userinput = "$cmd:$tail";
+    my ($udom,$startfilter,$endfilter,$rolesfilter) = split(/:/,$tail);
+    chomp($rolesfilter);
+    my @roles = ();
+    if (defined($startfilter)) {
+        $startfilter=&unescape($startfilter);
+    } else {
+        $startfilter='.';
+    }
+    if (defined($endfilter)) {
+        $endfilter=&unescape($endfilter);
+    } else {
+        $endfilter='.';
+    }
+    if (defined($rolesfilter)) {
+        $rolesfilter=&unescape($rolesfilter);
+	@roles = split(/\&/,$rolesfilter);
+    }
+                                                                                           
+    my $hashref = &tie_domain_hash($udom, "nohist_domainroles", &GDBM_WRCREAT());
+    if ($hashref) {
+        my $qresult = '';
+        while (my ($key,$value) = each(%$hashref)) {
+            my $match = 1;
+            my ($start,$end) = split(/:/,&unescape($value));
+            my ($trole,$uname,$udom,$runame,$rudom,$rsec) = split(/:/,&unescape($key));
+            unless ($startfilter eq '.' || !defined($startfilter)) {
+                if ($start >= $startfilter) {
+                    $match = 0;
+                }
+            }
+            unless ($endfilter eq '.' || !defined($endfilter)) {
+                if ($end <= $endfilter) {
+                    $match = 0;
+                }
+            }
+            unless (@roles < 1) {
+                unless (grep/^$trole$/,@roles) {
+                    $match = 0;
+                }
+            }
+            if ($match == 1) {
+                $qresult.=$key.'='.$value.'&';
+            }
+        }
+        if (&untie_domain_hash($hashref)) {
+            chop($qresult);
+            &Reply($client, "$qresult\n", $userinput);
+        } else {
+            &Failure($client, "error: ".($!+0)." untie(GDBM) Failed ".
+                    "while attempting domrolesdump\n", $userinput);
+        }
+    } else {
+        &Failure($client, "error: ".($!+0)." tie(GDBM) Failed ".
+                "while attempting domrolesdump\n", $userinput);
+    }
+    return 1;
+}
+
+&register_handler("domrolesdump", \&dump_domainroles_handler, 0, 1, 0);
+
+
 #  Process the tmpput command I'm not sure what this does.. Seems to
 #  create a file in the lonDaemons/tmp directory of the form $id.tmp
 # where Id is the client's ip concatenated with a sequence number.
@@ -4404,18 +4761,26 @@ 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=<CONFIG>) {
 	if ($configline !~ /^\s*\#/ && $configline !~ /^\s*$/ ) {
 	    my ($id,$domain,$role,$name)=split(/:/,$configline);
 	    $name=~s/\s//g;
-	    my $ip = gethostbyname($name);
-	    if (length($ip) ne 4) {
-		&logthis("Skipping host $id name $name no IP $ip found\n");
-		next;
+	    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};
 	    }
-	    $ip=inet_ntoa($ip);
 	    $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.
 
@@ -4666,12 +5031,12 @@ sub reconlonc {
 
 sub subreply {
     my ($cmd,$server)=@_;
-    my $peerfile="$perlvar{'lonSockDir'}/$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 "$cmd\n";
+    print $sclient "sethost:$server:$cmd\n";
     my $answer=<$sclient>;
     chomp($answer);
     if (!$answer) { $answer="con_lost"; }
@@ -4687,7 +5052,7 @@ sub reply {
 	$answer=subreply("ping",$server);
         if ($answer ne $server) {
 	    &logthis("sub reply: answer != server answer is $answer, server is $server");
-           &reconlonc("$perlvar{'lonSockDir'}/$server");
+           &reconlonc("$perlvar{'lonSockDir'}/".$hostname{$server});
         }
         $answer=subreply($cmd,$server);
     }
@@ -4857,7 +5222,7 @@ sub make_new_child {
 #        my $tmpsnum=0;            # Now global
 #---------------------------------------------------- kerberos 5 initialization
         &Authen::Krb5::init_context();
-	if ($dist ne 'fedora4') {
+	unless (($dist eq 'fedora4') || ($dist eq 'suse9.3')) {
 	    &Authen::Krb5::init_ets();
 	}
 
@@ -4993,7 +5358,7 @@ sub make_new_child {
 		    # no need to try to do recon's to myself
 		    next;
 		}
-		&reconlonc("$perlvar{'lonSockDir'}/$id");
+		&reconlonc("$perlvar{'lonSockDir'}/".$hostname{$id});
 	    }
 	    &logthis("<font color='green'>Established connection: $clientname</font>");
 	    &status('Will listen to '.$clientname);
@@ -5324,38 +5689,38 @@ sub addline {
 
 sub get_chat {
     my ($cdom,$cname,$udom,$uname)=@_;
-    my %hash;
-    my $proname=&propath($cdom,$cname);
+
     my @entries=();
-    if (tie(%hash,'GDBM_File',"$proname/nohist_chatroom.db",
-	    &GDBM_READER(),0640)) {
-	@entries=map { $_.':'.$hash{$_} } sort keys %hash;
-	untie %hash;
+    my $hashref = &tie_user_hash($cdom, $cname, 'nohist_chatroom',
+				 &GDBM_READER());
+    if ($hashref) {
+	@entries=map { $_.':'.$hashref->{$_} } sort(keys(%$hashref));
+	&untie_user_hash($hashref);
     }
     my @participants=();
     my $cutoff=time-60;
-    if (tie(%hash,'GDBM_File',"$proname/nohist_inchatroom.db",
-	    &GDBM_WRCREAT(),0640)) {
-        $hash{$uname.':'.$udom}=time;
-        foreach (sort keys %hash) {
-	    if ($hash{$_}>$cutoff) {
-		$participants[$#participants+1]='active_participant:'.$_;
+    $hashref = &tie_user_hash($cdom, $cname, 'nohist_inchatroom',
+			      &GDBM_WRCREAT());
+    if ($hashref) {
+        $hashref->{$uname.':'.$udom}=time;
+        foreach my $user (sort(keys(%$hashref))) {
+	    if ($hashref->{$user}>$cutoff) {
+		push(@participants, 'active_participant:'.$user);
             }
         }
-        untie %hash;
+        &untie_user_hash($hashref);
     }
     return (@participants,@entries);
 }
 
 sub chat_add {
     my ($cdom,$cname,$newchat)=@_;
-    my %hash;
-    my $proname=&propath($cdom,$cname);
     my @entries=();
     my $time=time;
-    if (tie(%hash,'GDBM_File',"$proname/nohist_chatroom.db",
-	    &GDBM_WRCREAT(),0640)) {
-	@entries=map { $_.':'.$hash{$_} } sort keys %hash;
+    my $hashref = &tie_user_hash($cdom, $cname, 'nohist_chatroom',
+				 &GDBM_WRCREAT());
+    if ($hashref) {
+	@entries=map { $_.':'.$hashref->{$_} } sort(keys(%$hashref));
 	my ($lastid)=($entries[$#entries]=~/^(\w+)\:/);
 	my ($thentime,$idnum)=split(/\_/,$lastid);
 	my $newid=$time.'_000000';
@@ -5365,21 +5730,22 @@ sub chat_add {
 	    $idnum=substr('000000'.$idnum,-6,6);
 	    $newid=$time.'_'.$idnum;
 	}
-	$hash{$newid}=$newchat;
+	$hashref->{$newid}=$newchat;
 	my $expired=$time-3600;
-	foreach (keys %hash) {
-	    my ($thistime)=($_=~/(\d+)\_/);
+	foreach my $comment (keys(%$hashref)) {
+	    my ($thistime) = ($comment=~/(\d+)\_/);
 	    if ($thistime<$expired) {
-		delete $hash{$_};
+		delete $hashref->{$comment};
 	    }
 	}
-	untie %hash;
-    }
-    {
-	my $hfh;
-	if ($hfh=IO::File->new(">>$proname/chatroom.log")) { 
-	    print $hfh "$time:".&unescape($newchat)."\n";
+	{
+	    my $proname=&propath($cdom,$cname);
+	    if (open(CHATLOG,">>$proname/chatroom.log")) { 
+		print CHATLOG ("$time:".&unescape($newchat)."\n");
+	    }
+	    close(CHATLOG);
 	}
+	&untie_user_hash($hashref);
     }
 }
 
@@ -5468,7 +5834,7 @@ sub thisversion {
 sub subscribe {
     my ($userinput,$clientip)=@_;
     my $result;
-    my ($cmd,$fname)=split(/:/,$userinput);
+    my ($cmd,$fname)=split(/:/,$userinput,2);
     my $ownership=&ishome($fname);
     if ($ownership eq 'owner') {
 # explitly asking for the current version?