[OpenAFS-devel] Soft mounts for /afs root

Garance A Drosihn drosih@rpi.edu
Thu, 14 Jun 2001 19:19:31 -0400


At 4:41 PM -0700 6/13/01, Jeff Riegel wrote:
>We have been looking into modifying the AFS client to support
>"soft mounts" for the AFS root directory.  Currently, /afs is
>mounted from your home cell's root.afs volume and provides
>mount points to each cell's root.cell volume.  The modification
>would allow the client to see a virtual /afs with cell mount
>points determined from a client-specific database or
>configuration file.  A mechanism could be provided to allow the
>user to add a particular cell to his AFS root, similar to a Web
>bookmark.

I used to run AFS on NeXTSTEP, and now run OpenAFS on MacOS 10,
both of which have the "feature" that they'll stat() every
directory in /afs if their GUI browser ever sees /afs.  This,
of course, gets painful.

I use a strategy which has a similar effect to what you're
proposing.  I just take my client's CellServDB file and
remove the entries for all cells that I personally do not
care about.  Since openafs on my client does not know how
to contact those cells, it may see the mount points (which
can be convenient), and yet it won't contact anything for
cells which are not in my local CellServDB.

I then have a perl script I can run to add a cell, eg:

     sudo reset_afs_cells -doit -add bu.edu

[the '-doit' parameter is just because it defaults to
printing out the 'fs newcell' commands instead of actually
doing them...]

>This approach should allow a client to have access to the AFS
>space without needing to be a member of a home cell.  We are
>therefore considering calling this project the "Freelance AFS
>client".

Doesn't an AFS client need to have some home cell?  How does
it know which cell 'klog' will authenticate to, for instance?

>We'd like to get feedback from the AFS user community regarding
>this project.  Does it seem like a useful function?  Any specific
>features that people feel we should include?

I suspect it's a useful project, it's just that I'm so used to
doing what I already do that I'm pretty comfortable with what
I have...    :-)

I have mentioned my "add a cell" script before (mainly on the
openafs-darwin mailing list), so this time I thought I should
dust it off, clean it up, and present it for others to look at.
So, here it is.  Sometimes I screw up when I copy&paste a file
into a message, so I also have a copy of this available at:

ftp://freefour.acs.rpi.edu/pub/openafs/reset_afs_cells

Note that while it's certainly a lot prettier than it was a
few hours ago, it could probably use a bit more work.  The
ORIGINAL purpose of this script was to read in two cellservDB's,
and create the "trimmed down" client CellServDB from the standard
one in /afs/<my_cell> space.  I never got around to that part,
but that's why it's structured the way it is.

So, here it is, in "pasted" form:

#!/usr/bin/perl

# 
-------+---------+---------+---------+---------+---------+---------+---------+
# Copyright (c) 2001  - Garance Alistair Drosehn <gad@FreeBSD.org>.
#
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
# 
-------+---------+---------+---------+---------+---------+---------+---------+

#    Script which can update AFS cell information on a running system
#    based on entries in CellServDB.

# 
-------+---------+---------+---------+---------+---------+---------+---------+

use strict;
package main;

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Two arrays indicate what domains we are most interested in
#
#     Substring match, which will require more tedious logic
#     to check.
@main::isin_domain = ("rpi.edu",   "transarc.ibm.com");
#
#     Exact match, so use associative array.  Note that you
#     can also use this to explicitly NOT care about a domain
#     by defining it to zero instead of 1.  A value of '2' is
#     used for entries added by command-line options.
%main::is_domain   = (
	"msc.cornell.edu", 1, "theory.cornell.edu", 1,
	"athena.mit.edu",  1,
	"umich.edu", 1,       "citi.umich.edu", 1,
);

BEGIN {
	$main::do_cmds = 0;		# still kinda in "testing" mode...
	$main::echo_cmds = -1;
	$main::FS_pgm = "";
	$main::use_alt_DB = 0;		# use "alternate" db file.
}

MAIN:{
	my($DB_File, $my_cell);

	# - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	# Determine likely values of the few key files we care about.
	#
	$DB_File = "/usr/vice/etc/CellServDB";
	if (not -r $DB_File) {
		# try for openafs version on darwin/MacOS-10
		$DB_File = "/var/db/openafs/etc/CellServDB";
	}
	die "* Unable to find CellServDB file for this client"
	    unless -r $DB_File;

	$main::FS_pgm = "/usr/vice/etc/fs";
	if (not -x $main::FS_pgm) {
		# try for openafs version on darwin/MacOS-10
		$main::FS_pgm = "/usr/bin/fs";
	}
	die "* Unable to find 'fs' program on this client"
	    unless -x $main::FS_pgm;

	# - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	# Parse the command options (except for input filenames)
	#
	exit unless &parse_ARGV();
	if ($main::use_alt_DB) {
	    $DB_File .= ".orig";
	    die "* Unable to find alternate CellServDB file, $DB_File"
		unless -r $DB_File;
	}

	# - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	# A few more checks, and tell the user what we'll be working on.
	#
	$my_cell = `$main::FS_pgm wscell`;
	chomp $my_cell;
	if ($my_cell =~ s/^[^']+'([^\s']+)'.*$/$1/ ) {
		print STDERR "* Assuming AFS cell = $my_cell\n";
		print STDERR "* Reading cell info from $DB_File\n";
		#    Always reset our own cell.
		$main::is_domain{$my_cell} = 2;
	} else {
		print STDERR "* Can not determine AFS cell of current 
machine!\n";
		$my_cell = "unknown!";
	}
	my $do_linked = 0;
	my $linked = "";
	if ( -d "/..." ) {              # does this machine have DFS too?
		# (probably should drop this DFS-related cruft)
		print STDERR "* Assuming we should do linked AFS/DFS cells\n";
		$do_linked = 1;
	}

	# - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	# Initial checks done, Real processing starts here...
	#
	my %new_info = &read_db($DB_File);

	my ($cellname, $cmd, $cur_key, $key_cell, $reset_count, $want_it);
	$reset_count = 0;
	foreach $cur_key (sort domainnamesort keys(%new_info)) {
		$cellname = $cur_key;
		$linked = "";
		$cmd = $main::FS_pgm . " newcell";
		if ($cur_key =~ m/^(\S+)\s+(\S+)$/) {
			next unless $do_linked;		# skip DFS-linked cells
			($cellname, $linked) = ($1, $2);
			$cmd .= " -name $cellname -linkedcell $linked";
		} else {
			$cmd .= " -name $cur_key";
		}
		$cmd .= " -servers " . $new_info{$cur_key};

		$want_it = 0;
		foreach $key_cell (@main::isin_domain) {
			if ($cellname =~ m/$key_cell$/) {
				$want_it = 1;
				last;
			}
		}
		if (defined($main::is_domain{$cellname})) {
			$want_it = $main::is_domain{$cellname};
		}
		next unless $want_it;

		$reset_count++;
		&exec_cmd($cmd);
		if (($cellname eq $my_cell) or ($linked eq $my_cell)) {
			$cmd = $main::FS_pgm . " setcell -cell 
$cellname -suid";
			&exec_cmd($cmd);
		}
	}
	if ($main::do_cmds) {
		print STDERR "* Reset $reset_count cells.\n";
	} else {
		print STDERR "* Would have reset $reset_count cells 
if '-doit' had been specified.\n";
	}

	# - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	# Make sure that we saw all of the specific-domains that
	# had been expected.
	#
	my $did_header = 0;
	foreach $cur_key (sort domainnamesort keys(%main::is_domain)) {
		$cellname = $cur_key;
		if (not defined($new_info{$cellname})) {
			if (not $did_header) {
				print STDERR "#\n# WARNING: Not found 
in $DB_File:\n";
				$did_header = 1;
			}
			print STDERR "#          cell '$cellname'\n";
		}
	}

	exit 0;
}

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
#  Parse the options specified in @ARGV
#
sub parse_ARGV {
	my($opt_error, $want_help, $in_add);

	$opt_error = 0;
	$want_help = 0;
	$in_add = 0;
	while ($_ = shift @ARGV) {
		if ( $_ =~ m/^-/ ) {
			$in_add = 0;
		}
		if ($_ eq "-doit") {
			$main::do_cmds = 1;
		} elsif ($_ eq "-add") {
			$in_add = 1;
			$main::use_alt_DB = 1;
		} elsif ($_ eq "-echo") {
			$main::echo_cmds = 1;
		} elsif ($_ eq "-noecho") {
			$main::echo_cmds = 0;
		} elsif (($_ eq "-h") or ($_ eq "-help")) {
			$want_help = 1;
		} elsif (m/^-/) {
			print STDERR "#* Unexpected option $_\n";
			$opt_error = 1;

		} elsif ($in_add) {
			$main::is_domain{$_} = 2;
		} else {
			print STDERR "#* Unexpected parameter $_\n";
			$opt_error = 1;
		}
	}

	if ($want_help) {
		print STDERR "#   This script can be used to reset 
AFS cell-information on the current\n";
		print STDERR "#   machine, and is usually run after 
some other process has changed\n";
		print STDERR "#   CellServDB.  It can also be used to 
add cells to the current client\n";
		print STDERR "#   based on entries in 'CellServDB.orig'\n";
		print STDERR "\n";
		$opt_error = 1;		# fake error, to get the rest 
of the info
	}
	if ($opt_error) {
		print STDERR "Usage:  reset_afs_cells [-help] [-echo 
| -noecho] [-add <cell> ...]\n";
		print STDERR "where:  -help    - prints some brief 
help information.\n";
		print STDERR "        -add     - adds one or more cells.\n";
		print STDERR "        -echo    - echo 'fs' commands 
before executing them.\n";
		print STDERR "        -noecho  - turn off echoing of 
commands.\n";
		return 0;		# error return
	}

	# Minor adjustments of options
	if ((not $main::do_cmds) and ($main::echo_cmds < 0)) {
		#  If we're not going to do the commands, we should echo them.
		$main::echo_cmds = 1;
	}

	return 1;		# successful return
}

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
# Read a CellServDB file into an associative array.
#
sub read_db () {
	my($cservdb) = @_;
	my(%cell_info);
	my($cur_cell, $cur_IPs, $warned_fname);
	my($total_cells, $total_IPs);

	if (not open(CELLSERVDB, $cservdb)) {
		print STDERR "Unable to open $cservdb, exitting...\n";
		exit 4;
	}

	$cur_cell = "";
	$cur_IPs = "";
	$total_cells = 0;
	$total_IPs = 0;
	$warned_fname = 0;
	while(<CELLSERVDB>) {
		chomp;
		s/\s*#.*$//;
		next if $_ eq "";
		if ( s/^>// ) {
			if ($cur_cell ne "") {
			  #	print "Found cell $cur_cell = $cur_IPs\n";
				$cell_info{$cur_cell} = $cur_IPs;
				$total_cells++;
			}
			$cur_cell = $_;
			$cur_IPs = "";
			next;
		} elsif ( m/^\d+\.\d+\.\d+\.\d+$/ ) {
			if (length($cur_IPs) == 0) {
				$cur_IPs = $_;
			} else {
				$cur_IPs .= " " . $_;
			}
			$total_IPs++;
			next;
		} else {
		    if (not $warned_fname) {
			    print STDERR "* Error(s) in file $cservdb:\n";
			    $warned_fname = 1;
		    }
		    print STDERR "* Unrecognized text at line $.: $_\n";
		}
	}
    
	close(CELLSERVDB);
	print STDERR "* Found $total_cells cells, and $total_IPs servers.\n";
	return %cell_info;
}

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
# Sort by domain names  (all edu's together, all com's together, etc)
#
sub domainnamesort {
	my($a_value, $b_value);
	$a_value = join(".", reverse( split("[.]", $a) ));
	$b_value = join(".", reverse( split("[.]", $b) ));

	my $res = ($a_value cmp $b_value);
	return $res;
}

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
# Execute a command (allows one to nullify all commands by setting
#                    command-line options...)
#
sub exec_cmd {
	my($given_cmd) = @_ ;

	if ($main::echo_cmds) {
		# This looks prettier without the full pathname of 'fs'
		my $trunc = $given_cmd;
		$trunc =~ s/$main::FS_pgm/fs/o;
		print "+ " , $trunc, "\n";
	}

	if ($main::do_cmds) {
		system($given_cmd);
	}
}

-- 
Garance Alistair Drosehn            =   gad@eclipse.acs.rpi.edu
Senior Systems Programmer           or  gad@freebsd.org
Rensselaer Polytechnic Institute    or  drosih@rpi.edu