Getting Started with AMD

Introduction

An automounter allows the binding of a directory name to a filesystm to be delayed until the name is referenced. This can be advantageous merely to reduce the number of simultaneous mounts, but it can improve system reliability, simplify administration and provide transparent redundancy as well. Examples of automouters are autofs (supplied with Linux) and automountd (supplied with SUNOS) Amd is an advanced automounter, with great flexibility. It is the default automounter pre-installed in FreeBSD and is currently maintained for over 100 operating systems by Erez Zadok. As of the fall of 2000, it is at version 6.04.

The Command Line

As root in FreeBSD, you can start Amd with just:

amd /net /etc/disk.map

where Amd will wait for requests to use directories in the automount point (/net) and mount the appropriate filesystems described in the mount map (/etc/disk.map). That is, once Amd is running, a file open requesting /net/foo will result in Amd searching /etc/disk.map for the foo entry, and following the instructions there for mounting a (possibly remote) filesystem in /net/foo to satisfy the request.

The remote filesystem is not actually mounted in the /net directory, rather it is mounted in a temporary mount directory (/a by default) and symbolic link is created that points from the net directory to the temporary mount point. Think of /net as the logical mount point and /a as the real mount point. [Question: Why is /net not specified in the map file? How do you spread mounts across several watched directories?] The enormous power of Amd comes from the embedded macro language supported in the map file, which allows the mount specification to be created from a list of possible matches according to the properties of the request. This language is discussed below in a series of examples that are not intended to be realistic, but will show the flexibility of the mapping translation that can be obtained. The reference manual has more realistic (and more complicated) examples.

In a real installation, the startup script might include a large number of options and other parameters. Such a script wants to know the process id (-p) and set the logfile location (-l). It may want to change the length of time that a filesystem remains mounted after last use (-c and -w) or the location of the real mount points (-a). If there are many options, you may wish to include them in an amd.conf file (-F), but that is beyond the scope of this introduction and you are referred to the man page for details.

While experimenting with Amd at a console, a convenient command line is:

amd -l /dev/stderr -x all /net /etc/disk.map

which will log all actions to the console as they occur. You probably want to open up another console for testing.

The map file

The simplest map file consists a list of key (logical directories) and mount instruction pairs. That is: key value ...

where key is the name of a subdirectory in the automount point (/net in our example) and value is a space delimited list of possible locations. Here is an example with two keys and a single location for each:

joe type:=nfs;rhost:=fileserver;rfs:=/joe may type:=nfs;rhost:=fileserver;rfs:=/may

With this map Amd will mount fileserver:/joe or fileserver:/may as required when either directory in /net is opened. The value of rfs is the left hand side of an NFS export specification on the machine with the canonical name given by the value of rhost. It is possible to allow CNAMEs in the specification via an option.

Of course, on a large system, there will be many users, and the map file would be both large and a nuisance to maintain. Amd allows common elements to be specified in a defaults line:

/defaults type:=nfs;rhost:=fileserver joe rfs:=/joe may rfs:=/may

Probably Joe and May share a single filesystem on the fileserver, perhaps subdirectories of the /home exported filesystem. In which case the map would be:

/defaults type:=nfs;rhost:=fileserver; joe rfs:=/home;sublink:=joe may rfs:=/home;sublink:=may

The sublink shows where in the remote filesystem the logical link in /net should point. Somewhat more surprisingly, the key can be a regular expression:

/defaults type:=nfs;rhost:=fileserver * rfs:=/home/;sublink:=${key}

will match every possible key and map it to the appropriate directory on the same remote fileserver. That is /net/joe becomes a link to /a/fileserver/home/joe, and Amd arranges to mount the remote filesystem as required. Note how ${key} expands to the actual value of the key at time /net/key is requested. If there is no appropriately named export from fileserver then the user will get a permission denied message. Or try:

/defaults type:=nfs;rfs:=/home [a-m]* type:=nfs;rhost:=amserver;sublink:=${key} [n-z]* type:=nfs;rhost:=nzserver;sublink:=${key}

if you need to spread the home directories over two fileservers according to the first letter of the login name. [Note: this sort of regular expression doesn't seem to be recognized in our case.] This works to reduce the size of maps on clients systems, but another problem is the proliferation of slightly different maps for different systems. For example, if you tried to use the above map on fileserver itself, Amd would try to nfs mount its own export, and that is disallowed. So Amd allows selectors to provide alternative definitions so that a single map file may server many machines. For example:

joe host!=fileserver;type:=nfs;rhost:=fileserver;rfs:=/home/joe \ host==fileserver;type:=link;fs:=/home/joe

uses the first location on client machines (host!=fileserver) and the second when executed on the fileserver itself. The alternate locations may be local or remote, and Amd will pick the first that has a valid selector. Here fs points to the physical data when type:=link, while for other link-types, fs designates the place for Amd to mount remote filesystems. [Question: What does || do?]There are several mount types, including ufs, allowing Amd to connect the key to any possible directory in the network.

Note the syntax: the backslash keeps the key-value pair on a single logical line, while spaces delimit the alternative locations and the key.

The selection can be based on any of a large number of criteria, including OS name, netgroup, big or little endedness, etc. These are host machine characteristics, and their values can always (and only) obtained by running:

amd -v

Alternate NFS and Link specifications are so common that Amd has a special combination type given by nfsl:

joe type=nfsl;fs:=/net/joe;rhost:=fileserver;rfs:=/home/joe Here fs specifies the location when the filesystem is on the local machine and rfs specifies the location when the filesystem is mounted from rhost. [Question Is this right? The FM doesn't describe it quite like this.]

Client dependent mounts

Another important use of Amd is the remote mounting of program directories such as /usr/bin/utils. On a site with multiple architectures, there may be several different /usr/bin/utils directories, depending on the client hardware and OS. Here is a one-line example showing how the same path on the clients can connect to different paths on the server, depending on the client OS: bin type:=nfs;opts:=ro;rhost:=fileserver;rfs:=/utils/${os}/bin

Regardless of the OS, the client map is the same, but it depends on the call to Amd to supply the value of ${os} . On our machine ${os} evaluates to freebsd3, so to use this feature we would need to create a directory /utils/freebsd3 on fileserver. Again, the exact result of expanding any of the macro variables can be learned from the -v option on the amd command. When we add a Linux version, we will need to add a directory /utils/linux2 on the same fileserver. The real directories are likely to be on different systems, in which case the map could be written as:

bin ${os}==freebsd3;opts:=ro;rhost:=freebsdserver;rfs:=/utils \ ${os}==linux2;opts:=ro;rhost:=linuxserver;rfs:=/utils This would allow some freedom to determine both the server and directory where the different /bin/utils directories were kept.

Most of the macro values can be overridden on the Amd command line, if the supplied values are insufficiently specific for your application. Again, see the man page for details.

The selection of opts that can be specified in any location is much the same as for the mount command, with a few extra noted in the reference manual. [Question: What does - do?]

Status Information and Control

Watch the log file carefully if Amd doesn't seem to be doing what you expect. It will report what it tried to mount where, and that may suggest what you may have done wrong. Also watch /a for mounted remote filesystems. The Amq program will return the status of automounted filesystems. Try:

amq amq -m amq /net/joe

for mount status listed by automount points, by physical mount points, or print statistics for a named automount point. Amq can also control the daemon:

amq -u /net/joe

will cause Amd to reread the map files or cause an unmount of the logical directory named.[Note: On our system this has has no apparent effect.] You can also cause Amd to reread the map files with:

kill -HUP `amq -p`

Advanced topics

In the reference manual you can learn how to use NIS, ndbm, LDAP and other techniques to distribute the maps, reference replicated filesystems for redundancy, distribute mail to user's home directories, automount other types of filesystems such as floppies and CD-ROMs, match complex keys with automounted directories in other automounted directories, use the many other macro variables and even specify the program and arguments Amd should execute when requests to open (or close) directories occur.

Amd uses a different map format than Autofs, so you can't share maps (via NIS, for example) with other automounters which use the traditional SUN format, unless you are as clever as the author of the letter attached at the end of this note.

Sources

The Am-Utils home page is located http://www.am-utils.org. There you will find complete source code, many binaries and a 122 page reference manual. But don't go looking for the values of the macro variables - you get them by running the program.

Since this tutorial was first written, Erez Zadok has written a book Linux NFS and Automounter Administration which can serve as both a reference manual and a comprehensive introduction to Amd, even for FreeBSD.


Daniel Feenberg
Mohan Ramanajan
Last Modified 1 March 2002
Current version kept at: http://www.nber.org/amd.html

Appendix

A potential Amd user posted a note asking where to find out about it. This was one response.

From hag@linnaean.org Mon Nov 20 12:54:26 2000
Date: Mon, 20 Nov 2000 12:32:01 -0500
From: Daniel Hagerty 
To: Daniel Feenberg 
Cc: Doug Mildram , bblisa@bblisa.org
Subject: Re: BBLISA: "amd" on freeBSD for automount with NIS ?

 > In short - Amd uses a different map sytax from autofs, and using it on an
 > NIS client to a Solaris server is an advanced application. We would be
 > interested in hearing from anyone who is doing that.

    I know a place that does this; automagically no less.  The maps
are maintained in solaris automount format, and there's an awk script
that generates amd format maps from these.

    The (somewhat illegible) conversion script is appended.  YMMV,
depending on the complexity of your solaris automount maps.  But this
is known to work in one place...

    The typical amd startup is something like
amd `ypcat -k amd.master` .

#
#  translate simple automount map into equivalent amd map.
#
BEGIN {
 print "#";
 print "# PLEASE!  Don't edit this file directly!";
 print "# Canonical version of this map is in " ARGV[1];
 print "# Please edit that file to make changes, then:";
 print "# cd /var/yp ; make";
 print "#";
 print "/defaults\topts:=rw,grpid,intr,rsize=8192,wsize=8192,resvport;type:=nfs";
}
{
  key = $1;

  if (NF == 0) next;

  if (NF > 2) {
    gsub(/^-/, "", $2);
    opts = $2;
  }
  else
    opts = "";

  FS = ":";
  n = split($NF, parts);

  if (key == "*") {
    print key " host==${key};type:=link;fs:=/fs/${key};host!=${key};type:=nfs;rhost:=${key};rfs:=/fs/${key}";
  }
  else {
    if (NF > 2)
      print key " host==" parts[1] ";type:=link;fs:=" parts[2] " host!=" parts[1] ";type:=nfs;rhost:=" parts[1] ";rfs:=" parts[2] ";opts:=" opts;
    else
      print key " host==" parts[1] ";type:=link;fs:=" parts[2] " host!=" parts[1] ";os!=netbsd1;type:=nfs;rhost:=" parts[1] ";rfs:=" parts[2] " host!=" parts[1] ";os==netbsd1;type:=nfs;opts:=rw,grpid,intr,resvport,proto=\"udp\",vers=2;rhost:=" parts[1] ";
  }

  FS = " ";
}