aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--contrib/amd/AUTHORS190
-rw-r--r--contrib/amd/BUGS73
-rw-r--r--contrib/amd/COPYING37
-rw-r--r--contrib/amd/ChangeLog3039
-rw-r--r--contrib/amd/INSTALL113
-rw-r--r--contrib/amd/MIRRORS43
-rw-r--r--contrib/amd/NEWS628
-rw-r--r--contrib/amd/README105
-rw-r--r--contrib/amd/TODO177
-rw-r--r--contrib/amd/amd/am_ops.c441
-rw-r--r--contrib/amd/amd/amd.8352
-rw-r--r--contrib/amd/amd/amd.c535
-rw-r--r--contrib/amd/amd/amd.h303
-rw-r--r--contrib/amd/amd/amfs_auto.c1595
-rw-r--r--contrib/amd/amd/amfs_direct.c106
-rw-r--r--contrib/amd/amd/amfs_error.c150
-rw-r--r--contrib/amd/amd/amfs_host.c686
-rw-r--r--contrib/amd/amd/amfs_inherit.c200
-rw-r--r--contrib/amd/amd/amfs_link.c141
-rw-r--r--contrib/amd/amd/amfs_linkx.c104
-rw-r--r--contrib/amd/amd/amfs_nfsl.c237
-rw-r--r--contrib/amd/amd/amfs_nfsx.c532
-rw-r--r--contrib/amd/amd/amfs_program.c191
-rw-r--r--contrib/amd/amd/amfs_root.c99
-rw-r--r--contrib/amd/amd/amfs_toplvl.c355
-rw-r--r--contrib/amd/amd/amfs_union.c124
-rw-r--r--contrib/amd/amd/amq_subr.c501
-rw-r--r--contrib/amd/amd/amq_svc.c157
-rw-r--r--contrib/amd/amd/autil.c418
-rw-r--r--contrib/amd/amd/clock.c247
-rw-r--r--contrib/amd/amd/conf.c939
-rw-r--r--contrib/amd/amd/conf_parse.y159
-rw-r--r--contrib/amd/amd/conf_tok.l186
-rw-r--r--contrib/amd/amd/get_args.c389
-rw-r--r--contrib/amd/amd/info_file.c265
-rw-r--r--contrib/amd/amd/info_hesiod.c163
-rw-r--r--contrib/amd/amd/info_ldap.c465
-rw-r--r--contrib/amd/amd/info_ndbm.c141
-rw-r--r--contrib/amd/amd/info_nis.c401
-rw-r--r--contrib/amd/amd/info_nisplus.c321
-rw-r--r--contrib/amd/amd/info_passwd.c195
-rw-r--r--contrib/amd/amd/info_union.c149
-rw-r--r--contrib/amd/amd/map.c1112
-rw-r--r--contrib/amd/amd/mapc.c1205
-rw-r--r--contrib/amd/amd/mntfs.c335
-rw-r--r--contrib/amd/amd/nfs_prot_svc.c250
-rw-r--r--contrib/amd/amd/nfs_start.c472
-rw-r--r--contrib/amd/amd/nfs_subr.c610
-rw-r--r--contrib/amd/amd/ops_TEMPLATE.c293
-rw-r--r--contrib/amd/amd/ops_autofs.c1275
-rw-r--r--contrib/amd/amd/ops_cachefs.c247
-rw-r--r--contrib/amd/amd/ops_cdfs.c206
-rw-r--r--contrib/amd/amd/ops_efs.c164
-rw-r--r--contrib/amd/amd/ops_lofs.c154
-rw-r--r--contrib/amd/amd/ops_mfs.c55
-rw-r--r--contrib/amd/amd/ops_nfs.c799
-rw-r--r--contrib/amd/amd/ops_nfs3.c55
-rw-r--r--contrib/amd/amd/ops_nullfs.c55
-rw-r--r--contrib/amd/amd/ops_pcfs.c179
-rw-r--r--contrib/amd/amd/ops_tfs.c55
-rw-r--r--contrib/amd/amd/ops_tmpfs.c55
-rw-r--r--contrib/amd/amd/ops_ufs.c173
-rw-r--r--contrib/amd/amd/ops_umapfs.c55
-rw-r--r--contrib/amd/amd/ops_unionfs.c55
-rw-r--r--contrib/amd/amd/ops_xfs.c164
-rw-r--r--contrib/amd/amd/opts.c1304
-rw-r--r--contrib/amd/amd/restart.c208
-rw-r--r--contrib/amd/amd/rpc_fwd.c476
-rw-r--r--contrib/amd/amd/sched.c300
-rw-r--r--contrib/amd/amd/srvr_amfs_auto.c214
-rw-r--r--contrib/amd/amd/srvr_nfs.c851
-rw-r--r--contrib/amd/amq/amq.8214
-rw-r--r--contrib/amd/amq/amq.c938
-rw-r--r--contrib/amd/amq/amq.h63
-rw-r--r--contrib/amd/amq/amq_clnt.c208
-rw-r--r--contrib/amd/amq/amq_xdr.c259
-rw-r--r--contrib/amd/amq/pawd.172
-rw-r--r--contrib/amd/amq/pawd.c296
-rw-r--r--contrib/amd/conf/checkmount/checkmount_bsd44.c78
-rw-r--r--contrib/amd/conf/fh_dref/fh_dref_freebsd22.h2
-rw-r--r--contrib/amd/conf/hn_dref/hn_dref_default.h2
-rw-r--r--contrib/amd/conf/mount/mount_freebsd3.c68
-rw-r--r--contrib/amd/conf/mtab/mtab_bsd.c145
-rw-r--r--contrib/amd/conf/nfs_prot/nfs_prot_freebsd2.h146
-rw-r--r--contrib/amd/conf/nfs_prot/nfs_prot_freebsd3.h211
-rw-r--r--contrib/amd/conf/sa_dref/sa_dref_bsd44.h5
-rw-r--r--contrib/amd/conf/transp/transp_sockets.c399
-rw-r--r--contrib/amd/conf/trap/trap_default.h2
-rw-r--r--contrib/amd/conf/trap/trap_freebsd3.h3
-rw-r--r--contrib/amd/conf/umount/umount_bsd44.c89
-rw-r--r--contrib/amd/doc/am-utils.texi7816
-rw-r--r--contrib/amd/doc/stamp-vti3
-rw-r--r--contrib/amd/doc/texinfo.tex4935
-rw-r--r--contrib/amd/doc/version.texi3
-rw-r--r--contrib/amd/fixmount/fixmount.8159
-rw-r--r--contrib/amd/fixmount/fixmount.c612
-rw-r--r--contrib/amd/fsinfo/fsi_analyze.c670
-rw-r--r--contrib/amd/fsinfo/fsi_data.h236
-rw-r--r--contrib/amd/fsinfo/fsi_dict.c138
-rw-r--r--contrib/amd/fsinfo/fsi_gram.y419
-rw-r--r--contrib/amd/fsinfo/fsi_lex.l270
-rw-r--r--contrib/amd/fsinfo/fsi_util.c693
-rw-r--r--contrib/amd/fsinfo/fsinfo.8101
-rw-r--r--contrib/amd/fsinfo/fsinfo.c293
-rw-r--r--contrib/amd/fsinfo/fsinfo.h131
-rw-r--r--contrib/amd/fsinfo/wr_atab.c334
-rw-r--r--contrib/amd/fsinfo/wr_bparam.c109
-rw-r--r--contrib/amd/fsinfo/wr_dumpset.c96
-rw-r--r--contrib/amd/fsinfo/wr_exportfs.c108
-rw-r--r--contrib/amd/fsinfo/wr_fstab.c342
-rw-r--r--contrib/amd/hlfsd/hlfsd.8310
-rw-r--r--contrib/amd/hlfsd/hlfsd.c953
-rw-r--r--contrib/amd/hlfsd/hlfsd.h171
-rw-r--r--contrib/amd/hlfsd/homedir.c799
-rw-r--r--contrib/amd/hlfsd/nfs_prot_svc.c250
-rw-r--r--contrib/amd/hlfsd/stubs.c530
-rw-r--r--contrib/amd/include/am_compat.h260
-rw-r--r--contrib/amd/include/am_defs.h1320
-rw-r--r--contrib/amd/include/am_utils.h956
-rw-r--r--contrib/amd/include/am_xdr_func.h203
-rw-r--r--contrib/amd/include/amq_defs.h157
-rw-r--r--contrib/amd/libamu/amu.h78
-rw-r--r--contrib/amd/libamu/hasmntopt.c119
-rw-r--r--contrib/amd/libamu/misc_rpc.c168
-rw-r--r--contrib/amd/libamu/mount_fs.c892
-rw-r--r--contrib/amd/libamu/mtab.c121
-rw-r--r--contrib/amd/libamu/nfs_prot_xdr.c59
-rw-r--r--contrib/amd/libamu/util.c176
-rw-r--r--contrib/amd/libamu/wire.c404
-rw-r--r--contrib/amd/libamu/xdr_func.c1155
-rw-r--r--contrib/amd/libamu/xutil.c825
-rw-r--r--contrib/amd/mk-amd-map/mk-amd-map.862
-rw-r--r--contrib/amd/mk-amd-map/mk-amd-map.c356
-rw-r--r--contrib/amd/scripts/Makefile.am49
-rw-r--r--contrib/amd/scripts/Makefile.in379
-rw-r--r--contrib/amd/scripts/am-eject.in52
-rw-r--r--contrib/amd/scripts/amd.conf-sample94
-rw-r--r--contrib/amd/scripts/amd.conf.5539
-rwxr-xr-xcontrib/amd/scripts/amd2ldif.in58
-rwxr-xr-xcontrib/amd/scripts/amd2sun.in51
-rwxr-xr-xcontrib/amd/scripts/ctl-amd.in113
-rwxr-xr-xcontrib/amd/scripts/ctl-hlfsd.in101
-rw-r--r--contrib/amd/scripts/expn.11370
-rwxr-xr-xcontrib/amd/scripts/expn.in1370
-rwxr-xr-xcontrib/amd/scripts/fix-amd-map.in52
-rwxr-xr-xcontrib/amd/scripts/fixrmtab24
-rw-r--r--contrib/amd/scripts/lostaltmail.conf-sample84
-rwxr-xr-xcontrib/amd/scripts/lostaltmail.in648
-rwxr-xr-xcontrib/amd/scripts/wait4amd.in45
-rwxr-xr-xcontrib/amd/scripts/wait4amd2die.in49
-rw-r--r--contrib/amd/tasks64
-rw-r--r--contrib/amd/wire-test/wire-test.870
-rw-r--r--contrib/amd/wire-test/wire-test.c133
153 files changed, 63365 insertions, 0 deletions
diff --git a/contrib/amd/AUTHORS b/contrib/amd/AUTHORS
new file mode 100644
index 000000000000..e555a7426ed9
--- /dev/null
+++ b/contrib/amd/AUTHORS
@@ -0,0 +1,190 @@
+# -*- text -*-
+PRIMARY AUTHORS AND MAJOR CONTRIBUTORS TO AM_UTILS:
+
+Original authors of amd were the Berkeley team and especially Jan-Simon
+Pendry. Since then many people have contributed patches.
+
+This file lists the ones who contributed major code changes, in no
+particular order, and I thank them all. This is of course not to diminish
+the smaller contributes of the many others. Thank you all.
+
+* Erez Zadok <ezk@cs.columbia.edu>
+
+The most significant changes were made by Erez Zadok in terms of bug fixes,
+ports, and new features added. Erez Zadok is the current maintainer of
+am-utils, as of January 1997.
+
+There is a mailing list dedicated to developers of am-utils. To subscribe
+to it, send mail to majordomo@majordomo.cs.columbia.edu, with the body of
+the message having the single line "subscribe amd-dev".
+
+* Randall S. Winchester <rsw@glue.umd.edu>
+
+May 7, 1997: contributed a special version of upl102 that included NFS V.3
+support. Some of the code was contributed by Christos Zoulas
+<christos@deshaw.com>. I (Erez) ported these changes to am-utils.
+
+September 12, 1997: lots of small prototype cleanups and fixes to numerous
+files.
+
+January 27, 1998: support pid files in the amd.conf file. Provide base name
+for hesiod zone files. Always use /etc/amd.conf if exists.
+
+* Hannes Reinecke <hare@MathI.UNI-Heidelberg.DE>
+
+Back in 1995, contributed code for linux. A new parser for file system
+specific options that only exist under linux.
+
+* Leif Johansson <leifj@matematik.su.se>
+
+June 22, 1997: minor patch to ensure that systems without an RE library work.
+
+June 23, 1997: mount options should be properly comma limited.
+
+July 10, 1997: info_ldap.c and prototype changes to all map _init and _mtime
+functions. Contributed scripts/amd2ldif.pl.
+
+August 4, 1997: info_ldap.c fixes and adding two new amd.conf ldap
+variables: ldap_cache_seconds and ldap_cache_maxmem.
+
+* Andreas Stolcke <stolcke@speech.sri.com>
+
+June 22, 1997: patches to ensure that proto= and vers= options work
+properly in mount tables and can be overridden. Later on, more code
+contribued to optimize the best combination of proto/vers.
+
+July 4, 1997: patches to get NFS V.3 working under irix5.
+
+September 9, 1997: initialize all fields of mntent_t structures to 0.
+
+October 2, 1997: don't log an RPC timeout as an error but only as an info
+message.
+
+December 19, 1997: detected an FMR (Free Memory Read) in amd/mntfs.c,
+uninit_mntfs().
+
+* Danny Braniss <danny@cs.huji.ac.il>
+
+July, 6 1997: contributed patches to hesiod on bsdi3.
+
+* Tom Schmidt <tschmidt@micron.com>
+
+July 10, 1997: Recommdation to include libgdbm if libc has no dbm_open.
+Patches for netgrp(host) command. Mods to aux/config.guess to recognize
+sun3.
+
+January 19, 1998: print correct -l option depending if system supports
+syslog and/or syslog facilities.
+
+January 29, 1998: fix for 0.0.0.0 loopback on SunOS 3.X which defines
+IFF_ROUTE instead of IFF_LOOPBACK.
+
+* Daniel S. Riley <dsr@mail.lns.cornell.edu>
+
+July 11, 1997: fixes to DU-4.0 to support string POSIX.1 signals, and struct
+sockaddr with sa_len field.
+
+July 13, 1997: Move amd.conf parsing to before switch_option() on log/debug
+options. Minor type wrt "ro" option in libamu/mount_fs.c. Added more
+fillers of mnttab options, for acdirmax, acdirmin, acregmax, acregmin, noac,
+grpid, nosuid, and actimo.
+
+* Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
+
+July 23, 1997: Got lots of patches from the Debian Linux folks, who fixed
+several generic bugs, and one serious one for Linux. The latter involved
+using connected sockets for NFS mounts on kernels 1.3.10 and older. Roman's
+work is baed on amd-upl102, and work from Ian Murdock <imurdock@debian.org>
+and Dominik Kubla <dominik@debian.org>.
+
+* Rainer Orth <ro@TechFak.Uni-Bielefeld.DE>
+
+August 6, 1997: assorted fixes to support hesiod-1.3, solaris 2.4 (which I
+already fixed but did not release yet), and support for $LDFLAGS at
+configure/compile time.
+
+February 24, 1998: lots of patches for ultrix 4.3 port.
+
+February 28, 1998: lots of documentation fixes!
+
+* Jason Thorpe <thorpej@nas.nasa.gov>
+
+August 25, 1997: make amd work when talking to NIS+ servers in NIS
+compatibility mode. Fix originally came from Matthieu Herrb
+<matthieu@laas.fr>.
+
+* Chris Metcalf <metcalf@catfish.lcs.mit.edu>
+
+August 29, 1997: patch to make amd use FQHN for NFS/RPC authentication,
+useful esp. for cross-domain NFS mounts.
+September 2, 1997: if plock() succeeded, don't display errno string.
+
+* Enami Tsugutomo <enami@cv.sony.co.jp>
+
+September 4, 1997: don't ping remote servers with NFS V.3 always, but V.2,
+regardless of client's NFS version. (conf/transp/transp_sockets.c)
+
+* Dan Riley <dsr@mail.lns.cornell.edu>
+
+September 19, 1997: make sure that amd works with more secure portmappers
+that do not allow forwarding of RPC messages to other services.
+
+* Wolfgang Rupprecht <wolfgang@wsrcc.com>
+
+August 10, 1997: netbsd and other bsd systems have a mask flag for
+pcfs_args (msdos mount).
+
+* Christos Zoulas <christos@deshaw.com>
+
+September 25, 1997: fix to initialize uid/gid fields of pcfs_args_t on
+netbsd.
+
+October 10, 1997: compile time cleanups of printf()s in hlfsd code. If nfs
+server is down or does not support a portmapper call, then mark it down as
+version 2, and try again later.
+
+* Bill Paul <wpaul@ctr.columbia.edu>
+
+November 5, 1997: NFS v.3 support for AIX 4.2.1, which does *not* include
+headers for this. Bill had to guess at the right structures, field names,
+sizes, alignment, etc.
+
+* Stefan Vogel <vogel@physik-rzu.unizh.ch>
+
+November 14, 1997: typo in the subscription instructions to amd-dev.
+
+* Guntram Wolski <gwolsk@sei.com>
+
+November 15, 1997: pointed out mismatching documentation for the -o option.
+
+* Michael Hucka <hucka@eecs.umich.edu>
+
+January 11, 1997: pointed out reversed definition of NFS mount options vers
+and proto.
+
+* Albert Chin <china@pprd.abbott.com>
+
+January 12, 1998: minor bug in output of amd -H.
+
+* Thomas Richter <richter@chemie.fu-berlin.de>
+
+January 13, 1998: use case insensitive comparisons for variables that need
+it (such as all hostname related ones, and more).
+
+* Fred Korz <korz@smarts.com>
+
+January 30, 1998: minor typo fixed to tftp example in am-utils.texi.
+
+* Donald Buczek <buczek@MPIMG-Berlin-Dahlem.MPG.DE>
+
+March 6, 1998: correctly inherit existing NFS V.3 mounts upon restart.
+
+March 17, 1998: compare log file name and syslog string with correct length.
+
+March 20, 1998: do not close stdout in case it gets reused elsewhere and to
+allow startup script to redirect it. Set a temporary secure umask(0022)
+before writing log file and restore it afterwards.
+
+* Matthew Crosby <mcrosby@ms.com>
+April 20, 1998: allow arbitrary number of interfaces in wire listing.
+
diff --git a/contrib/amd/BUGS b/contrib/amd/BUGS
new file mode 100644
index 000000000000..125714dbaa41
--- /dev/null
+++ b/contrib/amd/BUGS
@@ -0,0 +1,73 @@
+# -*- text -*-
+
+ LIST OF KNOWN BUGS IN AM-UTILS OR OPERATING SYSTEMS
+
+
+(1) mips-sgi-irix*
+
+[1A] known to have flakey NFS V.3 and TCP. Amd tends to hang or spin
+infinitely after a few hours or days of use. Users must install recommended
+patches from vendor. Patches help, but not all the time. Otherwise avoid
+using NFS V.3 and TCP on these systems, by setting
+
+ /defaults opts:=vers=2,proto=udp
+
+[1B] yp_all() leaks a file descriptor. Eventually amd runs out of file
+descriptors and hangs. Am-utils circumvents this by using its own version
+of yp_all which uses udp and iterats over NIS maps. The latter isn't as
+reliable as yp_all() which uses TCP, but it is better than hanging.
+
+
+(2) alpha-unknown-linux-gnu (RedHat Linux 4.2)
+
+hasmntopt(mnt, opt) can goes into an infinite loop if opt is any substring
+of mnt->mnt_opts. Redhat 5.0 does not have this libc bug. Here is an
+example program:
+
+#include <stdio.h>
+#include <mntent.h>
+main()
+{
+ struct mntent mnt;
+ char *cp;
+ mnt.mnt_opts = "intr,rw,port=1023,timeo=8,foo=br,retrans=110,indirect,map=/usr/local/AMD/etc/amd.proj,boo";
+ cp = hasmntopt(&mnt, "ro");
+ printf("cp = %s\n", cp);
+ exit(0);
+}
+
+
+(3) mips-dec-ultrix4.3
+
+Rainer Orth <ro@TechFak.Uni-Bielefeld.DE> reports
+
+[3A] At least the gcc 2.7.0 fixincludes-mangled <sys/utsname.h> needs a
+forward declaration of struct utsname to avoid lots of gcc warnings:
+
+RCS file: RCS/utsname.h,v
+retrieving revision 1.1
+diff -u -r1.1 utsname.h
+--- utsname.h 1995/06/19 13:07:01 1.1
++++ utsname.h 1998/01/27 12:34:26
+@@ -59,6 +59,7 @@
+ #ifdef KERNEL
+ #include "../h/limits.h"
+ #else /* user mode */
++struct utsname;
+ extern int uname _PARAMS((struct utsname *));
+ #endif
+ #define __SYS_NMLN 32
+
+[3B] It autoconfigures and compiles cleanly, but currently hangs after a
+couple of hours without leaving any traces in the syslog output.
+
+
+(4) powerpc-ibm-aix4.2.1.0
+
+[4A] "Randall S. Winchester" <rsw@Glue.umd.edu> reports that for amd to
+start, you need to kill and restart rpc.mountd and possibly also make sure
+that nfsd is running. Normally these are not required.
+
+[4B] "Stefan Vogel" <vogel@physik.unizh.ch> reports that if your amq
+executable dump core unexpectedly, then it may be a bug in gcc 2.7.x.
+Upgrade to gcc 2.8.x or use IBM's xlC compiler.
diff --git a/contrib/amd/COPYING b/contrib/amd/COPYING
new file mode 100644
index 000000000000..49783ecfbe93
--- /dev/null
+++ b/contrib/amd/COPYING
@@ -0,0 +1,37 @@
+Copyright (c) 1997-1998 Erez Zadok
+Copyright (c) 1989 Jan-Simon Pendry
+Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+Copyright (c) 1989 The Regents of the University of California.
+All rights reserved.
+
+This code is derived from software contributed to Berkeley by
+Jan-Simon Pendry at Imperial College, London.
+
+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.
+3. All advertising materials mentioning features or use of this software
+ must display the following acknowledgment:
+ This product includes software developed by the University of
+ California, Berkeley and its contributors, as well as the Trustees of
+ Columbia University.
+4. Neither the name of the University nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
diff --git a/contrib/amd/ChangeLog b/contrib/amd/ChangeLog
new file mode 100644
index 000000000000..3a059280ed47
--- /dev/null
+++ b/contrib/amd/ChangeLog
@@ -0,0 +1,3039 @@
+Thu Apr 23 00:22:17 1998 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ *******************************************************************
+ *** Released am-utils-6.0a16 ***
+ *******************************************************************
+
+Wed Apr 22 01:20:39 1998 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * aux/macros/check_mount_style.m4: define freebsd3 mount style.
+
+ * aux/macros/check_mount_trap.m4: new mount trap for freebsd3.
+
+ * aux/macros/check_mtype_printf_type.m4,
+ aux/macros/check_mtype_type.m4 (ac_cv_mtype_type,): freebsd3 (as
+ of snapshot 3.0-980311-SNAP) uses char * types for mount(2), not
+ integers, but I'll keep them as integers and do the mapping in
+ conf/mount/mount_freebsd3.c
+
+ * minor new port to i386-pc-bsdi3.1.
+
+ * minor new port to i386-unknown-netbsd1.3.1.
+
+ * amd/opts.c: new option addopts:=ARG, which smartly merges ARG
+ options with whatever the /default ones for a key are. This
+ allows adding or overriding /default options individual keys.
+
+ * amd/am_ops.c (ops_match): strdup/malloc string assigned to
+ opt_opts because it'll get free()'d upon next use.
+ (merge_opts): new function to merge two sets of options.
+ (ops_match): if addopts option exist, append and merge it to the
+ current default options.
+
+Tue Apr 21 12:54:59 1998 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * wire-test/wire-test.c (main): use dynamically allocated returned
+ string with list of interfaces.
+
+ * amd/get_args.c (get_version_string): allocate enough space for
+ header version string and a list of network interfaces of any
+ length.
+
+ * libamu/wire.c (print_wires): return dynamically allocated string
+ containing list of networks. Must be dynamic because some sites
+ had potentially dozens of network interfaces. Patch from Matthew
+ Crosby <mcrosby@ms.com> slightly modified.
+
+Mon Apr 20 00:37:20 1998 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * conf/nfs_prot/nfs_prot_irix5.h: underfine MNTTYPE_XFS because
+ irix 5.3 does not have full header definitions for it.
+
+ * fsinfo/fsi_lex.l (yywrap): define yywrap if needed, and
+ undefined it when not needed (similar to amd/conf_tok.l)
+
+ * hlfsd/hlfsd.h (ROOTID, SLINKID, INVALID): reduced maximum size
+ of these to unsigned short (because uid_t on some linux systems is
+ small).
+
+ * released snapshot am-utils-6.0a16s10
+
+ * doc/am-utils.texi (opts Option): documented resvport mount
+ option.
+
+Sun Apr 19 18:17:03 1998 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * doc/am-utils.texi (-D-Option): document behavior of -D info and
+ especially what it does to hesiod (turn on RES_DEBUG).
+
+ * scripts/amd.conf.5: document info debugging option.
+
+ * libamu/xutil.c (dbg_opt): parse info debugging option.
+
+ * include/am_utils.h (D_INFO): define new trace option.
+
+ * Makefile.am (EXTRA_DIST): include list of official mirrors in
+ distribution.
+
+ * libamu/wire.c (SIZE): in the simple case, just compute
+ sizeof(struct ifreq).
+
+Sun Apr 19 16:30:35 1998 Erez Zadok <ezk@lorien.cs.columbia.edu>
+
+ * amd/amd.c (daemon_mode): set a temporary secure umask(0022)
+ before writing log file and restore it afterwards. Patch from
+ Donald Buczek <buczek@MPIMG-Berlin-Dahlem.MPG.DE>.
+
+ * amd/get_args.c (get_args):
+
+ * doc/am-utils.texi (-F Option), amd/amd.8: mention that amd.conf
+ file specified by -F is always processed last.
+
+ * amd/amd.c (daemon_mode): do not fclose(stdout) so that the fd
+ won't be reused. Allows startup script to redirect stdout. Patch
+ from Donald Buczek <buczek@MPIMG-Berlin-Dahlem.MPG.DE>.
+
+ * libamu/xutil.c (switch_to_logfile): compare logfile name and
+ syslog string with correct length. Patch from Donald Buczek
+ <buczek@MPIMG-Berlin-Dahlem.MPG.DE>.
+
+ * amd/restart.c (restart): correctly inherit existing NFS V.3
+ mounts upon restart. Patch from Donald Buczek
+ <buczek@MPIMG-Berlin-Dahlem.MPG.DE>.
+
+Sat Apr 18 19:01:19 1998 Erez Zadok <ezk@lorien.cs.columbia.edu>
+
+ * using libtool 1.2 and automake 1.3 (with my patches).
+
+ * aux/macros/opt_am_cflags.m4, */Makefile.in: rename AM_CFLAGS to
+ AMU_CFLAGS to avoid conflicts with automake-1.3.
+
+Sun Apr 5 23:09:08 1998 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * converted to using libtool-1.2.
+
+Tue Mar 10 16:52:09 1998 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * fsinfo/fsi_analyze.c (fixup_required_mount_info): replaced silly
+ for loop (ITER) which used to run only to initialize the variable
+ dd once and then break; with a simple assignment.
+
+ * hlfsd/hlfsd.h: cleanup so it compiles with Solaris
+ /opt/SUNWspro/bin/cc, and use gid_t not int.
+
+ * amd/mapc.c (mapc_sync): don't dereference pointer (so it
+ compiles with Solaris /opt/SUNWspro/bin/cc)
+
+Sun Mar 8 15:54:22 1998 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * aux/config.{guess,sub}: used from gcc-2.8.1.
+
+Sat Mar 7 15:33:27 1998 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * released snapshot am-utils-6.0a16s9
+
+ * INSTALL, doc/am-utils.texi (Supported Platforms): updated names
+ based on new output of new config.guess.
+
+ * aux/config.{sub,guess}: used newer versions from gcc-2.8.0.
+
+ * amd/amd.8,doc/am-utils.texi: document correct usage of default
+ /etc/amd.conf file.
+
+ * fixmount/fixmount.c (inetresport,privsock): use some more
+ portable code from amq/amq.c.
+
+ * amd/get_args.c (get_args): use default /etc/amd.conf file only
+ if no arguments had been passed to amd.
+
+ * fixmount/fixmount.c (clnt_create_timeout): use tli/socket code
+ ala amq/amq.c so that fixmount will work on both types of systems.
+
+ * amq/amq.c (main): do no close tcp socket before running udp try,
+ because we're not sure if it was opened at all.
+
+ * mips-dec-ultrix* port merged in and cleaned up.
+
+ * aux/configure.in: up minor shared library revision number, since
+ it has changed enough.
+
+ * hlfsd/hlfsd.c (main): don't check for overlay mount option here,
+ as it is now done in compute_nfs_args(). Remove ultrix specific
+ code also because it was moved to compute_nfs_args().
+
+ * conf/mount/mount_svr4.c: removed DEBUG_MOUNT_SVR4 code, now that
+ we have more generic code in print_nfs_args().
+
+ * libamu/mount_fs.c (print_nfs_args): print maxlen of
+ nfs_args.addr, and also syncaddr (which is mostly NULL). Print
+ struct knetconfig from nfs_args->knconf.
+
+ * aux/macros/mount_headers.m4 (define): do not check for ufs/cdfs
+ headers here, but in try_compile_anyfs.m4.
+
+ * aux/macros/type_ufs_args.m4: move test for struct ufs_specific
+ (for ultrix) here from its own macro.
+
+ * aux/macros/type_cdfs_args.m4: move test for struct iso_specific
+ (for ultrix) here from its own macro.
+
+ * amd/ops_nfs.c (mount_nfs_fh): no need to run code again checking
+ for overlay mount option. Remove code which checks for overlay
+ option, as it is now done in compute_nfs_args().
+
+ * libamu/mount_fs.c (compute_mount_flags): move here code which
+ checks for overlay mount option which was in amd/amfs_toplvl.c:
+ mount_amfs_toplvl().
+
+Wed Feb 7 15:35:51 1998 Rainer Orth <ro@xayide.TechFak.Uni-Bielefeld.DE>
+
+ * The following are Rainer's ChangeLog entries for his ultrix
+ port, added manually. -Erez.
+
+ * merged Ultrix port with am-utils 6.0 a16s5: amd/ops_afs.c
+ (mount_toplvl) -> amd/amfs_toplvl.c (mount_amfs_toplvl)
+
+ * include/am_defs.h: include cdfs specific mount headers avoid
+ duplication definition of gt_names[] in <sys/fs_types.h> (Ultrix)
+
+ * include/am_compat.h: define several mount options corresponding
+ to mount flags: pgthresh, hard (nfs), defperm, nodefperm,
+ noversion, rrip (cdfs), nocache, quota, sync (generic) removed
+ duplicate MNTTAB_OPT_RO definition
+
+ * aux/macros/try_compile_anyfs.m4: include Ultrix specific
+ ufs/cdfs mount headers
+
+ * aux/macros/mount_headers.m4: include several Ultrix/Digital UNIX
+ specific mount headers
+
+ * aux/macros/check_{fs_mntent, mount_type}.m4: check for GT_*
+ mount types in mount headers (Ultrix)
+
+ * aux/configure.in: Ultrix/Digital UNIX specific checks: new
+ headers, mount structures and fields, filesystem and mount types,
+ mount flags
+
+ * aux/acconfig.h: placeholders for new mount options: quota
+ (generic), ro (nfs), defperm, nodefperm, noversion, rrip (cdfs)
+ cdfs, nfs, ufs mount structure fields new ufs, cdfs mount
+ structures
+
+ * amd/ops_ufs.c (mount_ufs): Ultrix stores generic mount flags in
+ ufs_args.ufs_flags and has ufs specific pgthresh option
+
+ * amd/ops_cdfs.c (mount_cdfs): handle Ultrix/Digital UNIX specific
+ CDFS mount flags and options
+
+ * amd/ops_afs.c (mount_toplvl), amd/ops_nfs.c (mount_nfs_fh),
+ hlfsd/hlfsd.c (main): store generic mount flags in
+ nfs_args.gfs_flags, handle separate NFS ro flag
+
+ * aux/macros/{check_mnt2_cdfs_opt, struct_iso_specific,
+ struct_ufs_specific}.m4: new files; check for Ultrix specific
+ mount structures
+
+ * conf/trap/trap_ultrix.h: arg 3 to mount(2) is rwflag
+
+ * aux/macros/type_auth_create_gidlist.m4 (ultrix*):
+ AUTH_CREATE_GIDLIST_TYPE is int (not short == gid_t) from a
+ comment in <rpc/auth.h>
+
+ * include/am_defs.h: #define KERNEL to avoid definition of
+ gt_names[] conf/mtab/mtab_ultrix.c: #include <sys/fs_types> before
+ <config.h> to force single definition here
+
+ * libamu/mount_fs.c (compute_nfs_args): Ultrix support for
+ nfs_args.gfs_flags moved to callers store mount options in
+ nap->optstr
+
+ * include/am_defs.h: fix _am_mntent mnt_{type, opts} field
+ descriptions
+
+ * aux/macros/mount_headers.m4, aux/macros/try_compile_nfs.m4:
+ include <rpc/rpc.h> before AMU_NFS_PROTOCOL_HEADER for svc_req
+ definition in prototypes
+
+ * aux/macros/mount_headers.m4: include <sys/errno.h> before
+ AMU_NFS_PROTOCOL_HEADER: <nfs/nfs.h> needs it for NFSERR_*
+ definitions
+
+ * aux/aclocal: adapt for local perl path and $prefix
+
+ * conf/nfs_prot/nfs_prot_ultrix.h: new file
+
+ * aux/macros/check_nfs_prot_headers.m4: use nfs_prot_ultrix.h
+
+ * aux/macros/mount_headers.m4, aux/macros/try_compile_nfs.m4,
+ aux/macros/try_compile_rpc.m4, include/am_defs.h: avoid multiple
+ inclusion of <rpc/xdr.h>
+
+Sat Mar 7 13:56:05 1998 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * aux/configure.in: save state of config.cache and confdefs.h
+ (as dbgcf.h) at various points of the configure.
+
+ * aux/macros/save_state.m4: new macro to save state of configure,
+ esp. useful in long ones. Saves confdefs.h and write $ac_cv_*
+ cache variables that are known so far.
+
+ * released snapshot am-utils-6.0a16s8
+
+1998-03-06 Erez Zadok <ezk@mercer.psl.cs.columbia.edu>
+
+ * fsinfo/fsi_lex.l,amd/conf_tok.l: define ECHO after undefining
+ it, but only for flex.
+
+Fri Mar 6 17:23:17 1998 Erez Zadok <ezk@chestnut.mcl.cs.columbia.edu>
+
+ * hlfsd/homedir.c (plt_print): change pathname of hlfsd dump file
+ to /usr/tmp/hlfsd.dump.XXXXXX, and use a safe method (if possible)
+ to write the dump file.
+
+ * doc/am-utils.texi,amd/opts.c: rename all references to nomadic
+ functions to boolean functions, which is what they really are.
+
+ * aux/configure.in: don't look for strcasecmp in libucb at all,
+ but rather complete it from libamu/strcasecmp.c as needed.
+
+Fri Mar 6 03:29:20 1998 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * conf/nfs_prot/nfs_prot_osf4.h: avoid conflicts with Irix's EFS.
+
+ * libamu/wire.c (getwire): fix for 0.0.0.0 loopback on SunOS 3.X
+ which defines IFF_ROUTE instead of IFF_LOOPBACK. Patch from Tom
+ Schmidt <tschmidt@micron.com>.
+
+ * released snapshot am-utils-6.0a16s7
+
+ * conf/nfs_prot/nfs_prot_hpux11.h: a first working port of amd to
+ hppa1.0-hp-hpux11.00.tgz.
+
+Thu Mar 5 21:59:03 1998 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * converted all sources to use mntent field names, and map mnttab
+ field names to mntent's.
+
+ * include/am_defs.h (mnt_special): map struct mnttab field names
+ to struct mntent field names.
+
+ * aux/macros/check_mount_trap.m4: hpux11's mount trap style is
+ identical to svr4/solaris2.
+
+ * aux/macros/check_mount_style.m4: hpux11's mount style is
+ identical to svr4/solaris2.
+
+ * aux/macros/check_network_transport_type.m4: hpux11 is a TLI
+ system!
+
+ * aux/macros/check_nfs_sa_dref.m4: hpux11's NFS host address
+ dereferencing style is same as svr4.
+
+ * aux/macros/check_nfs_fh_dref.m4: hpux11's NFS file fh
+ dereferencing style is same as svr4.
+
+ * conf/transp/transp_tli.c (create_nfs_service): if failed to
+ getnetconfigent() of ticlts, then try udp (hpux11).
+
+ * conf/nfs_prot/nfs_prot_hpux11.h: added correct definitions for
+ struct nfs_args, nfs_fh, and NFSMNT_* flags, taken from solaris
+ 2.5.1 (HP used them).
+
+ * amd/rpc_fwd.c (fwd_init): don't use O_NDELAY for t_open()
+ because hpux11 doesn't like it. if t_open failed, print error
+ based on t_errlist, not sys_errlist.
+
+Wed Mar 4 22:01:55 1998 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * doc/am-utils.texi: lots of documentation fixes from Rainer Orth
+ <ro@TechFak.Uni-Bielefeld.DE>.
+
+Sat Feb 28 22:16:35 1998 Erez Zadok <ezk@kosh.cs.columbia.edu>
+
+ * fsinfo/fsi_lex.l: undefine ECHO again, so it doesn't get used
+ later.
+
+ * include/am_defs.h: defined the extern for ualarm() if it isn't
+ found, regardless if the function isn't found in standard
+ libraries, because otherwise libamu will include it.
+
+1998-02-28 Erez Zadok (per Ron Snyder) <ezk@short.cvo.roguewave.com>
+
+ * initial port to hpux-11 completed. Compiles cleanly, but
+ probably does not work, because of missing NFS V.2/3 headers.
+
+ * amd/conf_tok.l: cast yytext to char* when passed to strlen and
+ strdup, for hpux-11's ansi-cc compiler.
+
+ * include/am_utils.h: renamed all xfree() to XFREE() to avoid
+ conflict with hpux-11's system headers. Also move (voidp) cast
+ from sources to inside the macro itself.
+
+Sat Feb 28 13:44:21 1998 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * released snapshot am-utils-6.0a16s6
+
+ * amd/info_nis.c (nis_isup): new function to test if NIS is up
+ without hanging amd. Used to ensure that amd does not clear the
+ maps when the expiration period arrived, if the service is down.
+ Otherwise it would be left with empty maps. It is better to stay
+ with possibly old information than none at all.
+
+ * amd/mapc.c (mapc_sync): check to see if map service is up.
+
+Tue Feb 24 02:19:42 1998 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * aux/macros/check_lib2.m4 (AC_CHECK_LIB2): fix macro so it
+ includes auxiliary library only if needed.
+
+1998-02-22 Erez Zadok <ezk@mercer.psl.cs.columbia.edu>
+
+ * amd/conf_tok.l: undefine ECHO again, so it doesn't get used
+ later.
+
+Sun Feb 22 01:41:08 1998 Erez Zadok <ezk@zen.cs.columbia.edu>
+
+ * conf/nfs_prot/nfs_prot_osf2.h: port to alpha-dec-osf2.1
+ completed.
+
+ * conf/mtab/mtab_osf.c (mnt_dup): not all OSF have NFS3.
+
+Sat Feb 21 19:45:48 1998 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * doc/am-utils.texi (Network Host Filesystem): correct example for
+ type:=host map.
+
+ * aux/macros/os_cflags.m4: only osf4 should compile with
+ -D_SOCKADDR_LEN.
+
+ * aux/macros/check_nfs_prot_headers.m4: distinguish between OSF2
+ and OSF4.
+
+ * BUGS: include a new file listing known bugs.
+
+Sat Feb 21 03:50:48 1998 Erez Zadok <ezk@mercer.psl.cs.columbia.edu>
+
+ * conf/nfs_prot/nfs_prot_linux.h: turn DES off on all linux
+ versions.
+
+ * aux/macros/type_recvfrom_fromlen.m4: linux alpha should use
+ size_t for recvfrom fromlen arg.
+
+Sat Feb 21 03:33:59 1998 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * scripts/ctl-hlfsd.in: turn -D fork so primary process never
+ hangs.
+
+Sat Feb 21 02:45:51 1998 Erez Zadok <ezk@mercer.psl.cs.columbia.edu>
+
+ * include/am_defs.h: don't include <sys/stat.h> and
+ <linux/stat.h>, just remaining definitions in <statbuf.h>
+
+Thu Jan 29 00:44:28 1998 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * released snapshot am-utils-6.0a16s5
+
+ * conf/mount/mount_irix6.c (mount_irix): pass {u,e,x}fs_args to
+ mount(2).
+
+ * check for <sys/fs/xfs_clnt.h> and include it when needed.
+
+ * conf/mount/{mount_irix5.c,mount_irix6.c} (mount_irix): recognize
+ efs and xfs separately from "ufs".
+
+ * amd/{ops_xfs.c,ops_efs.c}: new support for irix xfs/efs.
+
+ * aux/configure.in: look for irix efs and xfs file systems.
+
+ * scripts/amd.conf.5: new unmount_on_exit option documented.
+
+ * doc/am-utils.texi (unmount_on_exit): new option documented.
+
+ * amd/conf.c (gopt_unmount_on_exit): new function to handle new
+ amd.conf option.
+
+ * amd/map.c (umount_exported): if global amd.conf flag
+ unmount_on_exit is on, then try to unmount all mounted (or
+ restartable) file systems.
+
+ * amd/amd.c: if print_pid option is on, then normally print it to
+ stdout. If also pid_file is specified, then print the process ID
+ into that file.
+
+ * amd/get_args.c (get_args): print correct -l option depending if
+ system supports syslog and/or syslog facilities. Patch from
+ Tom Schmidt <tschmidt@micron.com>.
+
+ * doc/am-utils.texi (opts Option): updates for documentation for
+ new cdfs options defperm, nodefperm, noversion, rrip.
+
+ * amd/ops_cdfs.c (mount_cdfs): support cdfs options defperm,
+ nodefperm, noversion, rrip.
+
+ * conf/nfs_prot/nfs_prot_osf.h: need to look at <cdfs/cdfsmount.h>
+ on osf.
+
+ * aux/macros/{expand_cpp_hex.m4, expand_cpp_int.m4,
+ expand_cpp_string.m4}: use #error to make failed compilations
+ fail faster, rather than have the program fail during a run.
+
+ * include/am_compat.h: added a few new cdfs options: rrip,
+ noversion, defperm, and nodefperm (used in OSF).
+
+Wed Jan 28 20:24:09 1998 Erez Zadok <ezk@lorien.cs.columbia.edu>
+
+ * released snapshot am-utils-6.0a16s4
+
+ * doc/am-utils.texi (Global Parameters): update manual for new
+ amd.conf global variable hesiod_base.
+
+ * scripts/amd.conf.5: update man page for new amd.conf global
+ variable hesiod_base.
+
+ * amd/get_args.c (get_args): use /etc/amd.conf file if exists by
+ default, else try command line options. Patch from Randall
+ S. Winchester <rsw@Glue.umd.edu>.
+
+Wed Jan 28 12:20:56 1998 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * amd/info_hesiod.c (hesiod_search), amd/conf.c
+ (gopt_hesiod_base), amd/amd.h, amd/amd.c (init_global_options):
+ included patch from Randall S. Winchester <rsw@Glue.umd.edu>, to
+ support a hesiod base name in the amd.conf file.
+
+ * doc/am-utils.texi (pid_file Parameter): document new global
+ amd.conf parameter.
+
+ * scripts/amd.conf.5: document new global option pid_file.
+
+ * amd/amd.c (daemon_mode): if cannot open pid_file, continue to
+ run, but print error message.
+
+ * amd/conf.c (gopt_pid_file), amd/amd.h, amd/amd.c (daemon_mode,
+ init_global_options): Included patch from Randall S. Winchester
+ <rsw@Glue.umd.edu>, to support pid files in the amd.conf file.
+
+ * amd/get_args.c (get_args): correct usage info on amd -t.
+
+ * amd/*.c: massive file name and symbol name changes. All amd
+ file systems files and symbols are prefix with amfs_*. Now using
+ real name of file system: amfs_auto instead of afs, amfs_inherit
+ instead of ifs, amfs_linkx instead of sfsx, etc. This will enable
+ clear distinction between amd file system and generic ones like
+ nfs/ufs/pcfs/hsfs. Also, now we can implement true afs (Andrew
+ f/s) and DFS (Distributed f/s).
+
+ * amd/amfs_union.c: unionfs for amd is always defined, no need to
+ #ifdef it.
+
+Mon Jan 26 16:51:38 1998 Erez Zadok <ezk@chestnut.mcl.cs.columbia.edu>
+
+ * libamu/mount_fs.c (compute_nfs_args): set 'noconn' NFS option on
+ or off, based on the particular quirks of the OS in question.
+
+ * aux/macros/check_nfs_socket_connection.m4: new macro to set the
+ correct wait of handling un/connected NFS sockets.
+
+ * scripts/wait4amd.in: use rlogin instead of rsh.
+
+ * amd/am_ops.c (ops_showfstypes): when showing F/S types in amd
+ -v, always show "nfs3" for NFS V.3.
+
+1998-01-25 Erez Zadok <ezk@snoopy.cs.columbia.edu>
+
+ * lots of small fixes for solaris 2.6, since gcc 2.8.0 -Wall gets
+ more picky than 2.7.2.3 did.
+
+1998-01-24 Erez Zadok <ezk@snoopy.cs.columbia.edu>
+
+ * include/am_utils.h (dlog): amuDebug macros reduced to shorter
+ forms.
+
+Fri Jan 23 18:38:30 1998 Erez Zadok <ezk@goat.mcl.cs.columbia.edu>
+
+ * released snapshot am-utils-6.0a16s3
+
+ * include/am_defs.h: on Solaris 2.6, <sys/varargs.h> is included
+ in <sys/fs/autofs.h> but since stdargs.h is used, and varargs.h
+ must not, fake a definition for varargs.
+
+Fri Jan 16 10:16:40 1998 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * README: show how to configure in additional support such as
+ hesiod using various --enable-* flags.
+
+Wed Jan 14 15:13:02 1998 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * aux/configure.in: look for strcasecmp(), and replace it if not
+ available.
+
+ * amd/ops_nfsl.c (nfsl_match): host names should be compared using
+ case insensitive.
+
+ * amd/opts.c: for each map variable, define if the comparison
+ needs to be done case insensitive.
+
+ * include/am_utils.h (STRCEQ): a new macro to use strcasecmp().
+ Patch from Thomas Richter <richter@chemie.fu-berlin.de>
+
+1998-01-14 Erez Zadok <ezk@irt.cs.columbia.edu>
+
+ * aux/configure.in: look for "isofs" as a possible mount type for
+ cdfs (RedHat Linux).
+
+Wed Jan 14 02:07:05 1998 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * conf/mount/mount_linux.c (parse_opts): don't look for cdfs mount
+ type if not defined.
+
+ * conf/nfs_prot/nfs_prot_linux.h: don't define __FD_* macros if
+ already defined.
+
+ * aux/configure.in: look for ext2fs before ufs, b/c some linux
+ have both.
+
+Mon Jan 12 15:43:20 1998 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * Makefile.am (snapshot): allow me to install snapshots into the
+ ftp directory.
+
+ * scripts/ctl-hlfsd.in: don't turn on -D mem by default.
+
+ * amd/get_args.c (get_args): minor bug in output of amd -H.
+
+Mon Jan 12 03:05:06 1998 Erez Zadok <ezk@chestnut.mcl.cs.columbia.edu>
+
+ *******************************************************************
+ *** Released am-utils-6.0a15 ***
+ *******************************************************************
+
+Sun Jan 11 15:06:34 1998 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * amd/conf.c (*_browsable_dirs): allow specifying
+ browsable_dirs=full, which will print *all* entries (other than
+ /default), including those with '*' and '/' characters.
+
+ * amd/info_file.c (MAX_LINE_LEN): up maximum readdir chain length
+ to 1500.
+
+ * doc/am-utils.texi: fixed reversed documentation for NFS mount
+ options vers and proto.
+
+ * doc/Makefile.am (install-html): separate target just for
+ installing html files. Another new target "alldocs" is just for
+ installing all files needed for the am-utils home page.
+
+ * scripts/amd.conf-sample: updated example for log_file.
+
+ * scripts/amd.conf.5: updated for new way to declare new syslog
+ facility in the log_file option.
+
+ * amd/amd.8: updated for new way to declare new syslog facility in
+ the -l option.
+
+ * libamu/xutil.c (get_syslog_facility): allow users to specify the
+ syslog facility as an appended string to the log device. Ex. amd
+ -l syslog:local7 will use LOG_LOCAL7, while the older -l syslog
+ will use the default LOG_DAEMON facility.
+
+ * amd/get_args.c (get_args): updated new syntax for amd -l
+ syslog:facility.
+
+Thu Jan 8 04:05:10 1998 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * aux/macros/host_macros.m4: don't confuse sun3 (sparc) with sun3
+ (intel).
+
+ * libamu/wire.c (print_wires): bug fix. Nullify output buffer
+ before appending to it, on systems with two or more network
+ interfaces.
+
+ * conf/nfs_prot/nfs_prot_sunos5_3.h: add missing definition for
+ the mntent for cachefs.
+
+ * include/am_defs.h: external definition for strstr() should use
+ const for both arguments.
+
+ * aux/configure.in: add extern definitions for getwd() if missing.
+
+ * include/am_defs.h: define the extern for strdup() even if the
+ function does not exist, for it will be filled in by
+ libamu/strdup.c
+
+ * amq/pawd.c (cluster_server): don't use getccent() if the extern
+ for it isn't there. On hpux 10.20, the function is in libc, but
+ <cluster.h> and struct cct_entry do not exist.
+
+ * aux/configure.in: check for an extern for getccent() b/c some
+ hpux systems don't have it (hpux 10.20).
+
+Wed Jan 7 00:09:19 1998 Erez Zadok <ezk@lorien.cs.columbia.edu>
+
+ * doc/am-utils.texi (Amq -T option): manual documentation updates
+ for new amq options -T/-U.
+
+ * amq/amq.8: updated man page for new amq options -T/-U.
+
+ * amq/amq.c: two new switches to amq. -U will contact amd using
+ UDP only. -T will use TCP only. Normally amq will try TCP and
+ if that failed, will try UDP.
+
+ * doc/am-utils.texi (pawd): manual documentation for pawd.
+
+ * amq/pawd.1: new man page for pawd.
+
+Tue Jan 6 04:21:59 1998 Erez Zadok <ezk@lorien.cs.columbia.edu>
+
+ * amq/pawd.c: a new program, Print Automounter Working Directory,
+ to print the proper pathname of the cwd or any other pathname,
+ adjusted for automounter paths, while avoiding mount points.
+
+ * aux/macros/localconfig.m4: trivial support for am-utils
+ maintainers to adjust some of the configuration of am-utils after
+ it has been auto-configured by putting whatever definitions you
+ wish in a file called localconfig.h, located in the top build
+ directory (the same one where config.h is created for you).
+
+ * doc/am-utils.texi (Caching Filesystem): updated documents for
+ cachefs file system.
+
+ * amd/ops_cachefs.c: initial cachefs support, for solaris.
+ type:=cachefs, requires cachedir:=/cache/dir/name to be defined
+ and initialized with cfsadmin -c. $rfs is backdir to be cached
+ from, and $fs is the local mount point of the cachefs.
+
+ * conf/mount/mount_svr4.c (mount_svr4): support mounting of
+ cachefs file systems.
+
+ * amd/ops_cdfs.c: cdfs should be named 'cdfs', not whatever the
+ mnttab type is.
+
+Mon Jan 5 23:22:49 1998 Erez Zadok <ezk@lorien.cs.columbia.edu>
+
+ * amd/opts.c: added support for new variable $cachedir.
+
+ * include/am_utils.h: added opt_cachedir field to struct am_opts.
+
+Sat Jan 3 01:43:57 1998 Erez Zadok <ezk@lorien.cs.columbia.edu>
+
+ * amd/ops_nfsl.c (nfsl_ffserver, nfsl_match): also check if the
+ local hostname does not match $rhost, and if so, also assume
+ type:=nfs.
+
+Fri Jan 2 01:00:40 1998 Erez Zadok <ezk@lorien.cs.columbia.edu>
+
+ * updated all source files to add 1998 as a copyright year.
+
+ * amd/ops_nfsl.c: new amd meta file system "type:=nfsl". Behaves
+ like type:=linkx if the pathname denoted by $fs exists, and like
+ type:=nfs if it does not. A convenient shortcut for the most
+ popular amd map entry.
+
+ * amd/amd.c (main): amd should chdir to / before starting, to
+ avoid hanging on other NFS server if started elsewhere.
+
+ * amd/ops_TEMPLATE.c: added an empty template file for developers
+ who wish to write new amd pseudo file systems.
+
+Thu Jan 1 00:27:28 1998 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * hlfsd/homedir.c (plt_init): make function exported, to save on
+ the unnecessary init_homedir() function which was removed.
+ (table_add): don't use xmalloc() when you can use strdup()
+ instead.
+
+Thu Jan 1 00:15:58 1998 Erez Zadok <ezk@chestnut.mcl.cs.columbia.edu>
+
+ * hlfsd/homedir.c (hlfsd_endpwent): Don't actually run this
+ because we will be making more passwd calls afterwards. On
+ Solaris 2.5.1, making getpwent() calls after calling endpwent()
+ results in a memory leak! (and no, even Purify didn't detect
+ it...)
+
+Tue Dec 23 18:23:47 1997 Erez Zadok <ezk@chestnut.mcl.cs.columbia.edu>
+
+ * hlfsd/hlfsd.c (main): Bug fix. Don't try to free() an
+ automatically allocated address.
+
+ * amd/ops_afs.c (mount_toplvl): Bug fix. Don't try to free() an
+ automatically allocated address.
+
+ * ALL SOURCES: change every direct call to free() to xfree(), so
+ it can be mapped to the right debugging function as needed.
+
+ * include/am_utils.h: new free() policy. If debugging memory,
+ call dxfree(), which will print the file name and line number
+ where the free occurred and the pointer address. if only regular
+ debugging, then free() and reset the pointer to NULL so it cannot
+ be used afterwards inadvertently. If not DEBUG at all, then just
+ run free(). Three different #define macros set the right mapping.
+
+ * libamu/xutil.c (dxfree): renamed function to dxfree(), so it can
+ be called only when debugging the memory.
+
+Tue Dec 23 04:24:28 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * wire-test/wire-test.c (main): use xmalloc() not malloc().
+
+ * conf/transp/transp_tli.c (get_knetconfig): use xzalloc() not
+ calloc().
+
+ * conf/mtab/mtab_mach3.c (convert): use xzalloc() not calloc().
+
+ * conf/mount/mount_linux.c (parse_opts): use xmalloc() not
+ malloc().
+
+ * amd/info_ldap.c: use xmalloc() not malloc().
+
+ * libamu/xutil.c (xzalloc): new function to allocate memory and
+ zero its bytes.
+
+ * amq/amq.c: amq does not need its own definition of xfree().
+
+ * aux/macros/opt_debug.m4: if used --enable-debug=mem, then also
+ look for function malloc_verify() in libmapmalloc.a, and function
+ mallinfo() in libmalloc.
+
+ * libamu/xutil.c (checkup_mem): do not use mallinfo field
+ uordbytes, because it does not always exist. Rather, compute it
+ from other fields..
+
+ * include/am_utils.h: add external definition to xfree() function
+ used when debugging memory references.
+
+Mon Dec 22 03:01:30 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * amd/ops_afs.c (afs_readdir_browsable): reduce the number of
+ bytes heuristically computed to be returned to the kernel.
+ Otherwise browsable_dirs fails on OpenBSD 2.2.
+
+ * amd/mntfs.c (uninit_mntfs): bug fix. Moved freeing of
+ mf_private field to AFTER it gets used.
+
+Sat Dec 20 00:51:21 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * amd/ops_host.c (host_umounted): don't use clnt_spcreateerror()
+ on systems that don't support it.
+
+ * include/am_defs.h: add missing extern definition for free().
+
+ * aux/configure.in: check for external definition for free().
+
+ * libamu/Makefile.am: add to comment a mention of strstr as an
+ optional function.
+
+ * libamu/xutil.c (switch_to_logfile): use openlog() options only
+ if they exist.
+
+ * conf/transp/transp_sockets.c (get_nfs_version): don't use
+ clnt_spcreateerror if it does not exist.
+
+ * aux/configure.in: check for missing functions clnt_create and
+ clnt_spcreateerror. Check for missing strstr, and complete
+ its code. Check for missing external definition for strstr.
+
+ * aux/macros/host_macros.m4: normalize sun3* names.
+
+ * Makefile.am: updates for new sunos3 files distributed.
+
+ * conf/nfs_prot/nfs_prot_sunos3.h: new file.
+
+ * conf/fh_dref/fh_dref_sunos3.h: new file.
+
+ * aux/macros/check_nfs_prot_headers.m4: added sunos3 case.
+
+ * aux/macros/check_nfs_fh_dref.m4: added sunos3 case.
+
+ * doc/am-utils.texi (wire Selector Variable): updated manual for
+ adjusted variables wire, network, and netnumber, all using the
+ in_network() function.
+
+Fri Dec 19 04:37:36 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * amd/opts.c: allow options to be processed by executing arbitrary
+ functions. Converted wire, network, and netnumber all to use the
+ nomadic function in_network. So from now on they perform a match
+ against all networks, not just primary.
+
+ * initial port to alpha-unknown-linux-gnu, probably works, but
+ untested yet.
+
+ * conf/nfs_prot/nfs_prot_linux.h: add special definitions for
+ __FD_* macros which for som strange reason do not get included
+ from <selectbits.h> on redhat alpha linux. Also turn off usage of
+ <rpc/des_auth.h> because it is incomplete on the same systems.
+
+ * hlfsd/homedir.c (table_add): cast uid field to int, for
+ comparison.
+
+ * include/am_defs.h: more coflicts with redhat alpha linux
+ "resolved".
+
+ * aux/macros/{mount_headers,try_compile_anyfs,try_compile_nfs}.m4:
+ There's a conflict of definitions on redhat alpha linux between
+ <netinet/in.h> and <linux/fs.h> which must be avoided.
+
+ * aux/configure.in: check for <socketbits.h>, which is in use on
+ redhat alpha linux.
+
+ * doc/am-utils.texi (Selectors): added documentation to describe
+ the new "!" (negated) nomadic functions.
+
+ * amd/opts.c (f_in_network): print debugging info that is correct,
+ rather than saying that any ARG is on a local network.
+ (eval_opts): added support for negating nomadic functions, by
+ prepending "!" to their name. Example, !exists(/foo/bar).
+
+Thu Dec 18 20:57:19 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * include/am_compat.h: ensure that all NFS3 systems have the mount
+ table entries (and amd opts switches) "proto" and "vers".
+
+ * hlfsd/homedir.c (table_add): added some debugging info to try
+ and track down a serious memory leak in hlfsd.
+
+1997-12-18 Erez Zadok <ezk@t-rex.mcl.cs.columbia.edu>
+
+ * libamu/mount_fs.c (compute_nfs_args): OpenBSD 2.2 requires that
+ you do NOT set the noconn option, and use connected sockets
+ always. So I'm now forced not to set it at all, and have the user
+ specify it as "conn" or "noconn" in their /default entry. Argh...
+ Finally, it looks as if OpenBSD 2.2's NFS 3 implementation may be
+ buggy (TCP hangs with "short receive" kernel errors). I'd better
+ wait until they get it working in their version of the automounter
+ first. So I'm putting the "noconn" option back.
+
+Thu Dec 18 02:39:39 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * libamu/mount_fs.c (compute_nfs_args): use maxgrps option and set
+ nfs_args field maxgrouplist accordingly.
+
+ * include/am_compat.h (MNTTAB_OPT_MAXGROUPS): complete definition
+ for mount table entry for maxgroups based on NFS mount option
+ maxgrp.
+
+ * aux/configure.in: put back testing for NFS mount option
+ "maxgrps". Added test for mount table entry "maxgroups".
+
+ * libamu/mount_fs.c (compute_nfs_args): perform more careful tests
+ on nfs_proto, because it could be NULL.
+
+ * doc/am-utils.texi (Selectors): added example of in_network()
+ selector.
+
+ * aux/macros/check_hide_mount_type.m4: all hpux versions,
+ including 9.x, should use "ignore" as the mount type to hide from
+ amd.
+
+Wed Dec 17 13:09:21 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * include/am_utils.h (NSTREQ): use new macro instead of strncmp()
+ every where in the sources.
+
+Wed Dec 17 01:15:01 1997 Erez Zadok <ezk@lorien.cs.columbia.edu>
+
+ * libamu/mount_fs.c (print_nfs_args): if -D trace is on, will
+ print as much info that is given in struct nfs_args as possible.
+ useful for detecting internal flags and options, as well as the
+ file handle used.
+
+ * scripts/ctl-amd.in: look for amd.conf file in ${prefix}/etc
+ after /etc and before /etc/local.
+
+Tue Dec 16 18:51:36 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * aux/configure.in, libamu/Makefile.am: added support for linking
+ with specific versions of libamu. Upped shared library version of
+ libamu to 1 (libamu.so.1.0.0)
+
+ * include/am_defs.h: added external definition for mkstemp() as
+ needed.
+
+ * minor new port to i386-unknown-netbsd1.3 (BETA version of
+ netbsd).
+
+ * aux/config.guess: don't leave a trailing '.' on the system name
+ for NetBSD-1.3_BETA and similarly for OpenBSD.
+
+ * conf/nfs_prot/nfs_prot_netbsd1_3.h: new header for the slight
+ differences in ypall calling conventions.
+
+ * mk-amd-map/mk-amd-map.c (main): use mkstemp() whenever possible
+ in preference over mktemp(), b/c it is more secure.
+
+ * aux/configure.in: check for mkstemp(), a more secure version of
+ mktemp().
+
+Mon Dec 15 02:32:14 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * amd/ops_nfs.c (mount_nfs_fh): Systems that include the mount
+ table "vers" option generally do not set the mnttab entry to
+ "nfs3", but to "nfs" and then they set "vers=3". Setting it to
+ "nfs3" works, but it may break some things like "df -t nfs" and
+ the "quota" program (esp. on Solaris and Irix). So on those
+ systems, set it to "nfs".
+
+Sat Dec 13 01:36:27 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * doc/am-utils.texi (Distrib): updates for new ports, location of
+ am-utils' home page, and URL cross references (now supported by my
+ version of texi2html).
+
+ * aux/macros/check_mtype_type.m4: also use string type for file
+ system mount types under nextstep for mount(2).
+
+ * aux/macros/check_mtype_printf_type.m4}: nextstep can handle both
+ integer and string types for file system mount types in mount(2),
+ but it is better to use string types.
+
+ * conf/nfs_prot/nfs_prot_nextstep.h: set emum nfsstat's NFS_OK to
+ 0, not 1! Otherwise, all NFS calls (esp. mount) return errno 1
+ (EPERM) instead of 0 (OK). Fixes m68k-next-nextstep3 which now
+ works.
+
+ * conf/nfs_prot/nfs_prot_bsdi2.h: set emum nfsstat's NFS_OK to 0,
+ not 1! Otherwise, all NFS calls (esp. mount) return errno 1
+ (EPERM) instead of 0 (OK).
+
+ * libamu/xdr_func.c (xdr_*): add debugging calls that are optioned
+ by -D trace (protocol trace).
+
+1997-12-11 Erez Zadok <ezk@bach.cs.columbia.edu>
+
+ * A.m68k-next-nextstep3/amu_nfs_prot.h: define missing S_ISDIR
+ macro based on existence on others, and include <sys/stat.h>.
+
+Thu Dec 11 14:14:38 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * aux/macros/try_compile_nfs.m4 (AC_TRY_COMPILE): turn off
+ inclusion of <nfs/nfs_mount.h> which only affects nextstep3, on
+ which this header is broken (it tries to include non-existing
+ headers).
+
+Wed Dec 10 16:09:07 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * libamu/mount_fs.c (mnt_flags): support either nfs or generic
+ mount option for grpid.
+
+ * aux/configure.in: turn back on NFS mount option grpid.
+
+Sat Dec 6 04:36:05 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * doc/Makefile.am (install-ps): added target to install postscript
+ file in the am-utils' home page, as well as a few other useful
+ files for the new am-utils Web page.
+
+Mon Nov 17 05:22:56 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ *******************************************************************
+ *** Released am-utils-6.0a14 ***
+ *******************************************************************
+
+Sun Nov 16 21:56:16 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * doc/am-utils.texi (Supported Platforms): updated table of new
+ ports.
+
+Sat Nov 15 06:36:27 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * libamu/mount_fs.c (compute_automounter_nfs_args): pass mnttab
+ options so that they can be printed in logs as needed.
+
+ * doc/am-utils.texi (osver Parameter): updated for the correct
+ usage of the -o/-O options.
+
+ * scripts/amd.conf.5: corrected info for -o/-O options.
+
+ * amd/amd.8: updated man page for new -O op_sys_name option, and
+ corrected the usage of the -o op_sys_ver option
+
+ * amd/get_args.c (get_args): added new amd option -O to override
+ OS name.
+ (get_args): updated usage string. Removed old -m option. Added
+ -o and -O options.
+
+ * conf/nfs_prot/nfs_prot_irix5.h: add definition to FHSIZE in case
+ it is not there. Reportedly, irix 5.2 does not define it.
+
+ * scripts/lostaltmail.in: removed references to unused $hack
+ variable.
+
+ * aux/macros/check_nfs_prot_headers.m4: Patch from Chris Metcalf
+ <metcalf@cag.lcs.mit.edu> to correctly set the nfs_prot headers
+ for solaris2.5.
+
+ * doc/am-utils.texi (Distrib): typo in the subscription
+ instructions to amd-dev. Patch from Stefan Vogel
+ <vogel@physik-rzu.unizh.ch>.
+
+1997-11-15 Erez Zadok <ezk@amtrak.cs.columbia.edu>
+
+ * NFS3 works on powerpc-ibm-aix4.2.1.0.
+
+ * libamu/mount_fs.c (compute_nfs_args): set fh3 variable to
+ static. Some compilers (gcc from egcs on aix 4.2.1) corrupt the
+ stack of an automatic variable when pointer to it is passed around
+ several times.
+
+Fri Nov 14 20:09:28 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * libamu/xutil.c (real_plog): syslog like behavior for normal
+ logging. Will not print repeated strings, only a count "last
+ message repeated N times", but will always print something if
+ message is repeated more than 100 times.
+
+ * scripts/lostaltmail.in: look for lostaltmail.conf files also in
+ /etc/global, /etc/os, and /etc/local (in that order).
+
+Mon Nov 10 03:03:17 1997 Erez Zadok <ezk@lorien.cs.columbia.edu>
+
+ * conf/mount/mount_aix.c (mount_aix3): add support for NFS V.3
+ mounts.
+
+ * aux/macros/struct_nfs_args.m4 (AC_TRY_COMPILE_NFS): test for
+ aix42_nfs_args, specially set in conf/nfs_prot/nfs_prot_aix4_2.h
+
+ * conf/sa_dref/sa_dref_*.h: from now on, the "dst" argument to the
+ NFS_SA_DREF macro is a pointer to the structure that used to be
+ passed to it before. So now I have to dereference the pointer
+ before accessing its values.
+
+ * hlfsd/hlfsd.c (main): use the new and cleaner
+ compute_nfs_args() and compute_automounter_nfs_args() functions.
+
+ * amd/ops_nfs.c (mount_nfs_fh): use the new and cleaner
+ compute_nfs_args() and compute_automounter_nfs_args() functions.
+
+ * amd/ops_afs.c (mount_toplvl): MAJOR CODE REVISION. Use the new
+ and cleaner compute_nfs_args() and compute_automounter_nfs_args()
+ functions.
+
+ * libamu/mount_fs.c (compute_nfs_args): major code cleanup that
+ relates for struct nfs_args. A new function sets the numerous
+ possible flags and fields of nfs_args_t in an orderly fashion.
+ Code cleaned up, organized, and moved from amd/hlfsd into
+ libamu. This saves on overall size of code.
+ (compute_automounter_nfs_args): A variant of compute_nfs_args()
+ which sets special options/flags that need to be used when NFS
+ mounting an automounter's mount point. Used by hlfsd and amd.
+
+ * amq/amq.c (get_secure_amd_client): don't print any more
+ "get_secure_amd_client: using TCP, port 12345". It's more
+ annoying than useful a message, even when assumed to be compiled
+ under DEBUG only.
+
+Thu Oct 30 14:33:38 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * aux/macros/check_mnttab_type.m4: MAJOR BUG fixed for non-nfs
+ mount table entries. The code used to define the /etc/mnttab name
+ for file systems based on physical media (disk, cdrom, floppy) to
+ the generic name, rather than the OS specific name. For example
+ autoconf searches for all these types of mount table entries for a
+ floppy based file system: vfat pcfs pc msdos msdosfs fat. But if
+ it found any, it hard-coded the mnttab name to "pcfs" rather than
+ the actual name found! Same for ufs, cdfs, pcfs, tmpfs, lofs,
+ nullfs, unionfs, and umapfs.
+
+ * aux/configure.in: also test for xfs (for newer Irix) when
+ looking for mount type names, numbers, etc of a disk-based ufs.
+
+Sun Oct 26 19:32:57 1997 Erez Zadok <ezk@defiant.soscorp.com>
+
+ * amq/amq.c (main): use pmap_ping() to test if remote host is up.
+ This works better on bsdi2 and bsdi3, because their portmapper
+ does not appear to like forwarding operations (it is generaly
+ insecure).
+
+Sat Oct 25 04:55:56 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ *******************************************************************
+ *** Released am-utils-6.0a13 ***
+ *******************************************************************
+
+Fri Oct 24 05:04:37 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * conf/nfs_prot/nfs_prot_ncr2.h: added missing definition for
+ struct datum typedef.
+
+ * conf/nfs_prot/nfs_prot_netbsd.h: corrected typedef for
+ ypall_callback_fxn_t for netbsd.
+
+ * aux/macros/type_auth_create_gidlist.m4: hpux10.10 uses int for
+ the 5th arg to authunix_create(), while before and after they used
+ gid_t.
+
+ * conf/transp/transp_tli.c (amu_clnt_create_best_vers): don't use
+ clnt_create_timed() on older Solaris 2.3 systems that did not have
+ it.
+
+ * conf/nfs_prot/nfs_prot_bsdi2.h: <msdosfs/msdosfsmount.h> is for
+ kernel only on bsdi2, so do not include it.
+
+Fri Oct 24 00:29:42 1997 Erez Zadok <ezk@lorien.cs.columbia.edu>
+
+ * hlfsd/stubs.c (nfsproc_readlink_2_svc): avoid logging repeated
+ messages about resolution of mailboxes based on uid/gid.
+
+ * scripts/ctl-hlfsd.in: check for alternate password file in
+ ${prefix}/etc/passwd and use it in hlfsd's startup.
+
+Thu Oct 23 22:48:50 1997 Erez Zadok <ezk@lorien.cs.columbia.edu>
+
+ * hlfsd/homedir.c (hlfsd_getpwent): added support for -P
+ passwdfile option. If hlfsd is started with -P passwdfile, then
+ the passwdfile is read and parsed just as a standard unix
+ passwd(4) file. Only the username, uid, and homedir fields are
+ read and checked. All the rest are ignored. No NIS/netgroup
+ support!
+
+ * hlfsd/hlfsd.c (main): check if hlfsd is run as root after
+ parsing command line options, so that usage() can be printed when
+ -h is invoked.
+
+Wed Oct 22 00:16:39 1997 Erez Zadok <ezk@lorien.cs.columbia.edu>
+
+ * amd/nfs_start.c (mount_automounter): if autofs service listener
+ fails to initialize it is OK if using a test amd.
+
+ * amd/opts.c (f_in_network): new nomadic function in_network(arg),
+ which returns TRUE if 'arg' is any one of this host's networks.
+
+ * libamu/wire.c (getwire): rewritten parts of function, to store
+ all networks names and numbers in the order of the interface (not
+ in reverse).
+
+Mon Oct 20 01:59:25 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * aux/configure.in: don't add ops_ufs.o on AIX twice. My code
+ would have worked had it not been due to AIX's buggy /bin/sh.
+
+ * doc/Makefile.am (EXTRA_DIST): include am-utils.dvi and
+ am-utils.ps in the distribution sources.
+ (html): generate html documentation for
+ http://www.cs.columbia.edu/~ezk/am-utils/
+
+Sun Oct 19 12:35:49 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * conf/nfs_prot/nfs_prot_netbsd.h: override the definition of
+ ypall_callback_fxn_t. The NetBSD team compiles all sources with
+ gcc -Wall -Wmissing-prototypes -Wstrict-prototypes.
+
+ * amd/info_nis.c: allow for override of ypall_callback_fxn_t
+ function typedef.
+
+ * libamu/xutil.c (amu_release_controlling_tty): new function to
+ release the controlling tty in a clean and sane manner. No longer
+ using setpgid() b/c it may not work. Uses setsid(),
+ and ioctl() (in order). This new function is used in amd/amd.c
+ and hlfsd/hlfsd.c. Also avoid setpgrp(), because it works on some
+ systems, but on others it is the same as setpgid().
+
+Sat Oct 18 23:42:40 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * aux/acconfig.h: check for nfs mount option 'fsname'. Code for
+ it was in amd/ops_afs.c, amd/ops_nfs.c, and hlfsd/hlfsd.c, but
+ never used, because the configure test for fsname flag wasn't
+ used. This fixed a bug in HPUX 10, where syncer and manual mounts
+ left blank lines in /etc/mnttab.
+
+ * aux/configure.in: check for nfs mount option 'fsname'.
+
+Fri Oct 17 13:30:27 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * amd/ops_afs.c (mount_toplvl): turn on 'ignore' option in nfs
+ mount flags of toplvl mounts. Important esp. for HPUX 10. Now
+ hpux-10 will mount amd's mounts points as 'ignore' (by df).
+
+ * aux/configure.in: check for NFS mount option 'ignore', useful in
+ HPUX 10.
+
+Thu Oct 16 03:28:33 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * aux/macros/check_hide_mount_type.m4: hpux 10 should use "ignore"
+ mount type and not NFS, for the amd mount points.
+
+ * conf/transp/transp_tli.c (amu_clnt_create_best_vers): don't log
+ an RPC timeout as an error, but just an info message. From
+ Andreas Stolcke <stolcke@speech.sri.com>
+
+ * If the NFS server is down or does not support the portmapper
+ call (such as certain Novell NFS servers) we mark it as version 2
+ and we let the nfs code deal with the case that is down. If when
+ the server comes back up, it can support NFS V.3 and/or TCP, it
+ will use those. From christos@deshaw.com (Christos Zoulas).
+
+ * hlfsd/homedir.c (plt_print): various compile time cleanups to
+ printfs that take a long, but pass an int to print. From
+ christos@deshaw.com (Christos Zoulas).
+
+ * aux/macros/func_bad_yp_all.m4 (ac_cv_func_yp_all_clean,): new
+ macro to determine if the OS has a bad yp_all(), based on the host
+ OS name. So far only irix (all versions) have a bad yp_all, so
+ they will use am_yp_all() instead (which is slower as it
+ enumerates manually all of the map's entries).
+
+Thu Oct 16 03:14:37 1997 Erez Zadok <ezk@subzero.cs.columbia.edu>
+
+ * amd/info_nis.c (am_yp_all): code for a replacement yp_all that
+ avoids using a file-descriptor leaking yp_all() on some systems
+ like irix.
+
+Wed Oct 15 21:52:35 1997 Erez Zadok <ezk>
+
+ * fsinfo/fsinfo.h: avoid redefining yywrap incorrectly on DU-4.x
+ systems using /usr/bin/flex.
+
+ * amd/conf_tok.l (yywrap): avoid redefining yywrap for systems
+ that have a modified GNU flex which does define yywrap (DU-4.x).
+
+Fri Sep 26 14:25:29 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * conf/checkmount/checkmount_bsd44.c: include prototype.
+
+Fri Sep 26 01:26:24 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ *******************************************************************
+ *** Released am-utils-6.0a12 ***
+ *******************************************************************
+
+Fri Sep 26 00:13:48 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * conf/nfs_prot/nfs_prot_irix6.h: completely turn off all autofs
+ code in irix6 until it can be tested correctly
+
+ * conf/transp/transp_tli.c (amu_clnt_create_best_vers): time out
+ on clnt_create for 3 seconds, rather than wait for the much longer
+ default.
+
+ * conf/nfs_prot/nfs_prot_aix4_2.h: port to powerpc-ibm-aix4.2.1.0.
+ Includes NFS3, untested.
+
+Thu Sep 25 11:03:11 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * amd/ops_pcfs.c (mount_pcfs): fill in uid/gid fields of
+ pcfs_args_t if they exist.
+
+ * amd/ops_cdfs.c (mount_cdfs): fill in ssector field of
+ cdfs_args_t if it exists.
+
+ * new minor ports hppa1.1-hp-hpux10.10, hppa1.1-hp-hpux9.05,
+ hppa1.1-hp-hpux9.07, m68k-hp-hpux9.00, and sparc-sun-sunos4.1.4.
+
+Wed Sep 24 00:48:05 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * doc/am-utils.texi (wire-test): updated documentation for the new
+ option.
+
+ * wire-test/wire-test.8: updated man page for new option.
+
+ * wire-test/wire-test.c (main): added a test for various
+ combinations of NFS proto/vers to a remote/local host.
+
+ * aux/macros/try_compile_anyfs.m4 (AC_TRY_COMPILE): include
+ <isofs/cd9660/cd9660_mount.h> and <msdosfs/msdosfsmount.h> so that
+ freebsd will correctly set mount options for pcfs and cdfs.
+
+ * amd/ops_pcfs.c (mount_pcfs): set mask field of pcfs_args to
+ 0777 (if field exists).
+
+ * aux/macros/try_compile_anyfs.m4 (AC_TRY_COMPILE): include
+ <msdosfs/msdosfsmount.h> if available.
+
+ * minor new port: rs6000-ibm-aix4.1.4.0.
+
+ * libamu/mount_fs.c (mount_fs): fixed bug that caused HPUX not to
+ write the "time" field in /etc/mnttab.
+
+ * conf/mtab/mtab_file.c (mnt_dup): fixed typo in macro names for
+ detecting mnt_time field of mntent_t.
+
+ * libamu/mtab.c (mnt_free): fixed typo in macro names for
+ detecting mnt_time field of mntent_t.
+
+Tue Sep 23 15:30:03 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * conf/mtab/mtab_file.c (lock): Use flock() in preference over
+ fcntl() to lock the mtab file.
+
+Mon Sep 22 23:04:58 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * conf/transp/transp_sockets.c (get_nfs_version): define
+ the try_again goto label only for NFS3.
+ (pmap_ping): assume timeout failure of clnt_stat.
+
+ * libamu/xdr_func.c (xdr_groupnode, xdr_exportnode): cast to
+ "groups *".
+
+Mon Sep 22 20:34:33 1997 Erez Zadok <ezk@lorien.cs.columbia.edu>
+
+ * conf/transp/transp_sockets.c (pmap_ping): patch from Dan Riley
+ <dsr@mail.lns.cornell.edu> to make sure that amd works with more
+ secure portmappers that do not allow forwarding of RPC messages to
+ other services.
+
+ * */Makefile.am: all source files should also depend on the new
+ include/am_xdr_func.h header.
+
+ * include/am_xdr_func.h: new file as part of the code cleanup from
+ Christos Zoulas <christos@deshaw.com>.
+
+ * Lots of fixes from Christos Zoulas <christos@deshaw.com> that
+ involved missing prototypes, cleaned up ones, and removal of
+ unused variables.
+
+ * libamu/xdr_func.c (xdr_mountres3): some code cleanup. A switch
+ statement with only one case is unclean.
+
+Mon Sep 22 17:26:38 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * amd/ops_<FOO>fs.c (mount_<FOO>fs): initialize mntent_t fields to
+ 0 so that all fields are initialized to zero at first. This way
+ SunOS 4.x and other systems don't get /etc/mtab entries with
+ random integers for the fsck/freq fields. Patch from Andreas
+ Stolcke <stolcke@speech.sri.com>.
+
+Mon Sep 22 00:34:19 1997 Erez Zadok <ezk@americas.psl.cs.columbia.edu>
+
+ * conf/nfs_prot/nfs_prot_sunos4.h: include <nfs/nfs_clnt.h> so
+ that the important definition for HOSTNAMESZ will be found.
+
+ * amd/ops_afs.c (mount_toplvl): limit hostname size to HOSTNAMESZ
+ (inside the nfs_args structure) if the system uses that macro.
+ Otherwise systems like SunOS refuse to NFS mount long pathnames
+ and result in ENAMETOOLONG errno's.
+
+Wed Sep 17 04:56:11 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * doc/am-utils.texi (Supported Platforms): specify new support for
+ solaris 2.6, and hlfsd running on 2.5.1 too.
+
+Wed Sep 17 03:55:02 1997 Erez Zadok <ezk@amsterdam.psl.cs.columbia.edu>
+
+ * conf/mount/mount_svr4.c (mount_svr4): don't attemt an autofs
+ mount if it is not supported or was turned off.
+
+Wed Sep 17 03:19:36 1997 Erez Zadok <ezk@americas.psl.cs.columbia.edu>
+
+ * conf/nfs_prot/nfs_prot_sunos4.h: fixed so that it will nfs mount
+ again. Apparently at some previous release SunOS 4 supported was
+ broken and all nfs mounts resulted in ESTALE.
+
+Wed Sep 17 00:26:25 1997 Erez Zadok <ezk@amsterdam.psl.cs.columbia.edu>
+
+ * conf/nfs_prot/nfs_prot_sunos5_6.h: nfs protocol headers for
+ solaris 2.6.
+
+ * aux/macros/check_nfs_prot_headers.m4: added solaris 2.6 nfs
+ protocol header selection.
+
+Sat Sep 13 14:31:51 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * conf/transp/transp_tli.c (amu_clnt_create_best_vers): avoid
+ infinite loop.
+
+Sun Sep 7 18:23:23 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ *******************************************************************
+ *** Released am-utils-6.0a11 ***
+ *******************************************************************
+
+Fri Sep 5 11:55:10 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * amd/amd.c (main): if plock() succeeded, don't try to display
+ errno message. Patch from Chris Metcalf
+ <metcalf@catfish.lcs.mit.edu.
+
+Thu Sep 4 19:17:58 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * hlfsd/homedir.c (table_add): make sure duplicate passwd entries
+ are ignored. Only the first entry should ever be used, because
+ that's how lookup in passwd database works.
+
+ * conf/transp/transp_sockets.c (get_nfs_version): when determining
+ if a remote host is up or down, use NFS v.2. Fix suggested by
+ Enami Tsugutomo <enami@cv.sony.co.jp>.
+
+ * conf/transp/transp_tli.c (amu_clnt_create_best_vers): new
+ function to get the best possible NFS version, but also timeout
+ faster than normal defaults, so remote hosts which are down can be
+ detected in a reasonable time.
+ (get_nfs_version): use the new "best" function.
+
+Tue Sep 2 00:41:00 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ *******************************************************************
+ *** Released am-utils-6.0a10 ***
+ *******************************************************************
+
+Mon Sep 1 15:39:51 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * conf/transp/transp_tli.c (get_nfs_version): don't use
+ clnt_tp_create_timed() on systems that don'e have it (Solaris 2.4
+ and earlier).
+
+ * aux/configure.in: test for existence of clnt_tp_create_timed(),
+ since Solaris 2.4 (and possibly earlier) do not have it.
+
+Mon Sep 1 15:23:18 1997 Erez Zadok <ezk@felix.psl.cs.columbia.edu>
+
+ * amd/ops_autofs.c (autofs_unmount_1_svc): only display rdevid
+ field if it exists (solaris 2.4's autofs does not have it).
+
+Mon Sep 1 14:45:09 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * aux/macros/type_auth_create_gidlist.m4: a new test to determine
+ the correct type to the 5th argument of authunix_create().
+
+Mon Sep 1 03:44:32 1997 Erez Zadok <ezk@felix.psl.cs.columbia.edu>
+
+ * libamu/xdr_func.c (xdr_umntrequest): add test for autofs
+ structure umntrequest, if it has the field rdevid. Seems Solaris
+ 2.4 and earlier didn't have it.
+
+Mon Sep 1 01:10:53 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * hlfsd/hlfsd.c (hlfsd_init): moved initialization upwards, so it
+ can be done before primary process forks.
+ (main): bug fix: wait a bit to give child a chance to initialize.
+ Without this, hlfsd got into a busy infinite loop while, never
+ completing the mount.
+
+ * amd/info_nis.c: patch from Jason Thorpe <thorpej@nas.nasa.gov>
+ to make amd works when talking to NIS+ servers in NIS
+ compatibility mode.
+
+Mon Sep 1 00:46:56 1997 Erez Zadok <ezk@lorien.cs.columbia.edu>
+
+ * amd/mapc.c (MAX_CHAIN): increased maximum chain size to 1024.
+
+Sun Aug 31 20:20:40 1997 Erez Zadok <ezk@lorien.cs.columbia.edu>
+
+ * amd/conf.c (gopt_fully_qualified_hosts): a new function to
+ process another new global variable.
+
+ * amd/ops_nfs.c (make_nfs_auth): if a new global variable
+ fully_qualified_hosts is on, use FQHN in RPC/NFS authentications.
+ Patch from Chris Metcalf <metcalf@catfish.lcs.mit.edu>.
+
+ * amd/conf.c (process_last_regular_map): If the amd.conf file only
+ has a [global] section (pretty useless IMHO), do not try to
+ process a map that does not exist.
+
+ * scripts/amd.conf.5: fixed typos (repeated 'as' word).
+
+ * doc/am-utils.texi: MAJOR DOCUMENTATION REVISION COMPLETED!!!
+
+ * amd/conf.c (reset_cf_map): Bug fix. Reset 'tag' field of cfm
+ structure, so it does not carry over from map entry to another.
+
+Sat Aug 30 18:39:21 1997 Erez Zadok <ezk@lorien.cs.columbia.edu>
+
+ * amd/amd.c (main): fixed the meaning of the plock option. A bug
+ caused it to be reversed.
+
+Sat Aug 30 15:13:18 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * hlfsd/stubs.c: don't initialize some statics here. They are
+ better initialized in hlfsd_init_filehandles().
+
+Fri Aug 22 14:47:16 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * amd/conf.c (gopt_show_statfs_entries): new function and a global
+ amd.conf key show_statfs_entries. Defaults to 'no'. If 'yes',
+ then all maps flagged as browsable will also show a count of the
+ number of entries (keys) in that map.
+
+ * amd/nfs_subr.c (count_map_entries): new function to count number
+ of entries in a map. Now used if browsable is turned on in a map,
+ to return a count of available keys in a given map.
+
+ * hlfsd/stubs.c (eq_fh): use correct memcmp(). BUG fixed! hlfsd
+ confused the file handles for ".", "..", and the symlink, causing
+ mess.
+
+ * aux/macros/func_bad_memcmp.m4: new test, based on autoconf 2.12,
+ but also defines HAVE_BAD_MEMCMP, so I avoid linkage conflicts
+ with bad memcmp() in libc's (nextstep3).
+
+Thu Aug 21 17:38:41 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * aux/configure.in: check if autofs_args_t has a field named
+ addr. Solaris has it for specifying the address of the daemon,
+ but Irix does not. So how does irix tell the kernel the daemon's
+ address? I suspect that it is hardcoded to "localhost" using a
+ "private well-known port" (#define AUTOFS_PORT 2048).
+
+ * conf/nfs_prot/nfs_prot_linux.h: added missing autofs
+ definitions.
+
+ * README.autofs: new file listing autofs support notes
+
+ * very preliminary autofs code working. Call it pre-alpha
+ quality.
+
+Sun Aug 17 02:25:09 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * amd/conf.c (ropt_mount_type, gopt_mount_type): don't accept
+ "autofs" if system does not support it.
+
+ * amd/amd.c (main): log am-utils version string (same as amd -v)
+ at start of run.
+
+ * conf/transp/transp_sockets.c (get_nfs_version): in order to tell
+ if a remote host is available, try to contact its portmapper and
+ timeout if no answer is received in 3 seconds.
+
+Thu Aug 14 16:12:04 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * aux/macros/check_extern.m4 (pattern): use a simpler pattern for
+ match for external function definitions. The function could span
+ multiple lines, so only match the opening parenthesis, not the
+ closing one too.
+
+ * aux/macros/os_cpp-flags.m4 (ac_cv_os_cppflags,): do NOT turn on
+ posix mode for nextstep3. It is broken.
+
+ * amd/amd.c (init_global_options): run uname() only if
+ <sys/utsname.h> and uname(2) exist.
+
+ * amd/rpc_fwd.c (fwd_packet): if remote host is down, the
+ forwarding socket is null, so declare this an error.
+
+ * include/am_utils.h (AM_ERRNO_HOST_DOWN): find best errno to set
+ for when a remote host is down. Try, in order, "host down", "host
+ unreachable", "invalid argument".
+
+ * amd/ops_nfs.c (discard_fh): don't dereference a null pointer
+ (happens when remote host is down, and fp->fh_fs is NULL).
+ (prime_nfs_fhandle_cache): ditto, and set fs flags to !FSF_VALID
+ and FSF_DOWN.
+
+ * amd/sched.c: nobody uses "union wait" any more, or so it
+ appears, so clean up that code.
+
+ * conf/transp/transp_tli.c (get_nfs_version): if remote host is
+ down, time it out faster than default (3 seconds).
+ (get_mount_client): free netconfig entry when done with it.
+
+ * conf/nfs_prot/nfs_prot_nextstep.h: initial (and somewhat ugly)
+ port to nextstep3 (m68k-next-nextstep3).
+
+ * fsinfo/Makefile.am (LDADD): needs to link with libamu in case
+ system does not have strdup().
+
+ * include/am_defs.h: complete external definition for getlogin()
+ if needed.
+
+ * conf/transp/transp_tli.c (get_nfs_version): no need to keep
+ static versout variable.
+
+ * amd/rpc_fwd.c (fwd_packet): if fwdto packet is null, remote
+ server is probably down. Don't use sendto() in that case.
+
+ * buildall (default): run gmake if found, before trying plain
+ make. GNU make is always preferable.
+
+Tue Aug 12 21:23:58 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * aux/macros/host_macros.m4: cleanup of os_version and os
+ overrides. Solaris 2.5.1 for example will come up as solaris2 and
+ 2.5.1, rather than sunos5 and 5.5.1. Both can be overridden in
+ the amd.conf file.
+
+Fri Aug 8 14:37:30 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ *******************************************************************
+ *** Released am-utils-6.0a9 ***
+ *******************************************************************
+
+Thu Aug 7 00:52:14 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * conf/nfs_prot/nfs_prot_ncr2.h: new file, for NCR2
+ (i486-ncr-sysv4.3.03) headers. Needed to complete missing stuff
+ from <ndbm.h> and <sys/resource.h>.
+
+ * scripts/amd.conf.5: new map page.
+
+ * amd/info_hesiod.c: define extern for hesiod_resolve, since bsdi3
+ does not.
+
+ * amd/amd.8: updated man page.
+
+ * amd/get_args.c (get_args): removed defunct -h option to amd.
+
+ * amq/amq.8: updated man page.
+
+ * amd/info_nisplus.c (nisplus_search): prototype fixes so Sun
+ SparcCompiler CC won't complain.
+
+ * amd/info_hesiod.c (hesiod_search): small fixes to compile with
+ hesiod-1.3, as per Rainer Orth <ro@TechFak.Uni-Bielefeld.DE>.
+
+ * aux/macros/opt_ldflags.m4: new option added to configure
+ --enable-ldflags, to specify -L option for configuring/compiling.
+ The older one --enable-libs is now to be used only for -l options.
+
+ * amd/ops_nfs.c (mount_nfs_fh): bug fixed. Should initialize
+ nc_protoname from nfs_proto if available for all TLI systems, not
+ just those that have NFS V3.
+
+ * amd/info_ldap.c: ldap patches from Leif Johansson
+ <leifj@matematik.su.se>, adding two new amd.conf ldap variables:
+ ldap_cache_seconds and ldap_cache_maxmem.
+
+ * hlfsd/hlfsd.c (main): bug fixed. port number must be wrapped in
+ htons().
+
+Sun Aug 3 17:20:05 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * amd/nfs_start.c (mount_automounter): register amd with the rpc
+ program number that may have been overridden by amd.conf.
+
+ * libamu/xutil.c (set_amd_program_number, get_amd_program_number):
+ allow storing and retrieving alternate amd program numbers.
+
+ * amd/conf.c (gopt_portmap_program): new function to parse
+ amd.conf entry portmap_program=1234.
+
+ * amq/amq.c (main): allow specifying an alternate rpc program
+ number for amd via -P prognum.
+
+ * new amq RPC to get the process id of the running amd. This is
+ used in ctl-amd to quickly find the pid of the amd that is to be
+ killed.
+
+ * expanded shared libraries support. Using GNU libtool-1.0,
+ am-utils now builds shared libraries on many platforms that have
+ support for it and proper compilers/linkers. See "INSTALL" file
+ for compatibility chart.
+
+Thu Jul 31 13:07:23 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * fsinfo/fsi_lex.l: removed code that was in support of very old
+ versions of flex. No longer needed.
+
+Tue Jul 29 12:00:13 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * amd/amd.c (init_global_options): find the kernel architecture
+ from uname() if possible.
+
+Mon Jul 28 03:53:59 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * new working port: i386-unknown-openbsd2.1
+
+Fri Jul 25 03:16:31 1997 Erez Zadok <ezk@felix.psl.cs.columbia.edu>
+
+ * working port: sparc-sun-solaris2.4. Had to fix fhandle_t from
+ structure to actual char[], because the structure was passed to
+ xdr_fhandle as data and not a pointer. Surprisingly, gcc
+ -fpcc-struct-return did not help.
+
+ * conf/nfs_prot/nfs_prot_sunos5_4.h: special nfs protocol
+ definitions for solaris 2.4 have to be different from 2.3, and
+ different from 2.5.
+
+ * libamu/xdr_fhstatus.c (xdr_fhstatus): minor code cleanup.
+
+Thu Jul 24 16:22:39 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * conf/mount/mount_svr4.c (mount_svr4): use STREQ to compare mount
+ types, not ==. Fix from Christos Zoulas <christos@deshaw.com>.
+
+ * mk-amd-map/mk-amd-map.8: man page taken from bsd44.
+
+Thu Jul 24 00:01:55 1997 Erez Zadok <ezk@moisil.cs.columbia.edu>
+
+ * conf/mount/mount_linux.c: made the nfs error mapping code into a
+ small function.
+
+Wed Jul 23 16:11:49 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * conf/nfs_prot/nfs_prot_linux.h (NFS_ERROR_MAPPING): special
+ error mappings from errno numbers to NFS errors. From Debian
+ Linux.
+
+ * scripts/am-eject.in (fs): new script (taken from Debian Linux
+ folks) to auto-unmount floppy/cd devices before ejecting them.
+
+ * fsinfo/wr_exportfs.c (write_exportfs): from Debian folks, pass
+ '5' as second arg to show_area_being_processed. I'm not sure why.
+
+ * fsinfo/wr_atab.c (write_atab): from Debian folks, pass '5' as
+ second arg to show_area_being_processed. I'm not sure why.
+
+ * conf/mount/mount_linux.c (parse_opts): as per Debian Linux,
+ ensure that you have a buffer in *xopts to strcat onto.
+ (mount_linux): fixed from Debian folks to ensure that NFS mount
+ sockets are connected only for kernels prior to 1.3.10 (avoids
+ hangs for multi-homed hosts).
+
+ * amd/mapc.c (make_entry_chain): from Debian folks: don't skip
+ over amd map keys that have a prefix, and include the prefix in
+ the returned readdir().
+
+ * amd/info_passwd.c (passwd_init, passwd_search): format
+ enhancements from the Debian Linux folks. They added
+ - var0:=pw-prefix:=anydir
+ - var1:=pw-rhost:=dom3.dom2.dom1
+ - var2:=pw-user:=user
+ - var3:=pw-home:=/anydir/dom1/dom2/dom3/user
+
+ * new ports: sparc-sun-solaris2.4 and sparc-sun-sunos4.1.1.
+
+ * amd/ops_afs.c (afs_retry): patches from Debian Linux. If
+ afs_mount timed out, then explicitly forbid further retries after
+ the timeout. (afs_bgmount): buf fix from Debian Linux. Timeout
+ values (17 and 5) were mistakenly swapped.
+
+Wed Jul 23 15:53:25 1997 Erez Zadok <ezk@felix.psl.cs.columbia.edu>
+
+ * conf/nfs_prot/nfs_prot_sunos5_3.h: turn off non-existent NFS V.3
+ support for Solaris 2.4 (the header files incorrectly define parts
+ of the NFS V.3 protocol, but the kernel does not support it).
+
+Wed Jul 23 00:07:12 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * amd/info_hesiod.c: minor cleanup to use hes_init and hesiod_init
+ for determining if using old or new hesiod libraries,
+ respectively.
+
+ * amd/ops_nfs.c (mount_nfs_fh): If "noconn" option exists in NFS
+ V.3 then use non-connected sockets (both TCP and UDP). Otherwise
+ they cause hangs of mounts from multi-homed hosts when the return
+ route is not the same as the outgoing route (esp. on NetBSD
+ 1.2.1). If "conn" option was supplied, then don't turn on the
+ "noconn" mount option. Otherwise default to "noconn" mount
+ option.
+
+ * aux/configure.in: better check for systems that need libgdbm and
+ <ndbm.h>
+
+Tue Jul 22 04:02:05 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * hlfsd/hlfsd.c (main), amd/ops_afs.c (mount_toplvl): ensure that
+ mounts are hidden from df(1) on systems that keep mount tables in
+ kernel, such as osf1.
+
+Tue Jul 22 02:26:55 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ *******************************************************************
+ *** Released am-utils-6.0a8 ***
+ *******************************************************************
+
+Mon Jul 21 21:33:19 1997 Erez Zadok <ezk@stone.mcl.cs.columbia.edu>
+
+ * hlfsd/hlfsd.c (main): bug fixed: forgot to set sin_family to
+ AF_INET when hlfsd mounts itself, thus getting an errno "protocol
+ family not supported".
+
+Mon Jul 21 20:46:59 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * verified that amd on NetBSD 1.2.1 (i386) works, but the OS
+ itself is flaky.
+
+ * verified that amd AIX 3 works w/ the new readdir() code.
+
+Mon Jul 21 18:59:30 1997 Erez Zadok <ezk@chestnut.mcl.cs.columbia.edu>
+
+ * amd/mapc.c (maptypes): changed the default map caching type for
+ nis and ndbm maps to "MAPC_ALL", so that all possible entries will
+ be available to amd for browsable_dirs=yes.
+
+Mon Jul 21 18:04:16 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * amd/ops_afs.c (afs_readdir, afs_readdir_browsable): split
+ afs_readdir into a browsable version and a non-browsable one.
+ Fixed bugs that resulted in an infinite nfs_readdir loop on the
+ localhost for some operating systems.
+
+Sat Jul 19 19:38:38 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * amd/ops_afs.c (afs_readdir): fixed up the browsable code to
+ comply with nfs_readdir specs. It was returning all entries at
+ once, even if there was not enough space in the RPC packet. Now,
+ it sends a reasonably sized chunk, and sets static state to resume
+ it for the next continuation RPC of readdir.
+
+ * amd/conf.c (set_conf_kv): fixed bug that resulted in the first
+ regular map not getting all of the [global] options defaulted from.
+
+Fri Jul 18 00:45:12 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * amq/amq.c (main): fixed so that portmapper "ping" will only fail
+ upon an RPC timeout
+
+ * scripts/ctl-{amd,hlfsd}.in (killproc): better method to find the
+ amd/hlfsd process to kill.
+
+ * amd/ops_afs.c (mount_toplvl), hlfsd/hlfsd.c (main): code to
+ determine how to avoid df from seeing amd's toplvl mounts. Moved
+ from libamu/mount_fs.c and include/am_utils.h. Ensure that hlfsd
+ is hidden from df.
+ (mount_toplvl): fixed df types for irix.
+
+Thu Jul 17 21:59:45 1997 Erez Zadok <ezk@americas.psl.cs.columbia.edu>
+
+ * amq/amq.c (main): fixed bug that caused the 5 second timeout for
+ TLI amq using udp to be ignored.
+ (main): if amq cannot contact remote host's portmapper within 5
+ seconds, timeout and fail faster than the default longer timeout
+ (this is for non-TLI systems).
+
+Thu Jul 17 17:08:13 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * amq/amq.c (get_secure_amd_client): if amq cannot contact
+ remote host's portmapper within 5 seconds, timeout and fail faster
+ than the default TLI timeout of over 4 minutes.
+
+ * amd/am_ops.c (ops_showfstypes): added styles for new bsd44
+ file systems nullfs, unionfs, and umapfs.
+ (ops_show1): cleanup code and and reuse this function.
+ (vops): empty placeholders for nullfs, unionfs, and umapfs.
+
+ * amd/ops_{nullfs, unionfs, umapfs}.c: 3 new empty templates for
+ bsd44 style file systems.
+
+ * fsinfo/fsinfo.8: man page for fsinfo rewritten from bsd44
+ sources.
+
+ * amd/conf_tok.l, fsinof/fsi_lex.c: undefine ECHO to avoid
+ conflict with systems that define it in <sys/ioctl.h> and also
+ define a conflicting one via lex. Newer versions of f/lex are ok.
+
+Wed Jul 16 00:17:45 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * amd/ops_nfs.c (mount_nfs_fh): fill in nfs_args' fields
+ acdirmin/max and acregmin/max only if they exist.
+
+ * aux/configure.in: test for nfs_args fields acdirmin and
+ acregmin.
+
+Tue Jul 15 17:24:09 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * aux/configure.in: commented out checking for MNT2_GEN_OPT_* for
+ "nondev" and commented back in checking for "nodev". This was a
+ typo.
+
+ * include/am_compat.h: more compatibility options: define "ro"
+ mnttab option all the time. Define "overlay" as needed. Added
+ more fillers of mnttab options (as per Daniel S. Riley
+ <dsr@mail.lns.cornell.edu>), for acdirmax, acdirmin, acregmax,
+ acregmin, noac, grpid, nosuid, and actimo.
+
+ * libamu/mount_fs.c (mnt_flags): fixed a typo in "ro" option as
+ per dsr@mail.lns.cornell.edu (Daniel S. Riley). Then cleaned up
+ the same code for other options in struct opt_tab.
+
+Mon Jul 14 22:36:37 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * amd/get_args.c (get_args): moved amd.conf file parsing to before
+ the switching the default log/debug options. Suggested by
+ dsr@mail.lns.cornell.edu (Daniel S. Riley).
+
+ * aux/update_build_version: new script to record the build version
+ of amd, along with the rest of the info thet comes up with amd -v.
+
+Sun Jul 13 00:30:24 1997 Erez Zadok <ezk@decca.damtp.cam.ac.uk>
+
+ * aux/macros/os_cflags.m4: new macro, to add additional
+ compilation options (also used during configuration), based on the
+ OS and the compiler. Used for solaris-2.6, osf, and irix6.
+
+Fri Jul 11 10:15:11 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * amd/conf_tok.l, conf_parse.y: allow values of keys to include
+ white spaces, if they are double-quoted (key="some value")
+
+ * amd/info_hesiod.c (hesiod_search): cleanup and minor bug fixes
+ of hesiod from Danny Braniss <danny@cs.huji.ac.il>.
+
+Fri Jul 11 02:16:06 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ *******************************************************************
+ *** Released am-utils-6.0a7 ***
+ *******************************************************************
+
+Thu Jul 10 12:19:25 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * include/am_defs.h: fill in extern definition of innetgr() in
+ case system headers don't.
+
+ * aux/macros/path_prog_lex.m4 aux/configure.in: using macros that
+ will show the full pathname to yacc/bison and f/lex. Some systems
+ have older or bad versions of those and this way the user will
+ know for sure what is being invoked.
+
+ * tasks: file populated with todo items.
+
+ * scripts/amd2ldif.in: Script to convert amd maps to plain text
+ LDAP object files. Contributed by Leif Johansson
+ <leifj@matematik.su.se>.
+
+ * aux/config.guess: added recognition for sun3's running 4.2bsd,
+ from Tom Schmidt <tschmidt@micron.com>. Fixed one small typo.
+
+ * amd/conf.c: several variables that were local to a map, were
+ also added to [global], where they apply to all maps. Each map
+ can then individually override those defaults. Flags
+ affected: browsable_dirs and mount_type. Options affected:
+ map_type, map_options, and search_path.
+
+ * amd/amd.h: eliminated several global flags each of which took an
+ int, in favor of one global flags variable used as a bit field.
+
+ * amd/amd.c (init_global_options): moved all global variables into
+ a single struct amu_global_options, so it is easier to add new
+ ones and/or identify existing ones.
+
+ * amd/get_args.c (get_args): removed unused amd -m option.
+
+ * amd/conf.c (gopt_cluster): added new amd.conf option for cluster
+ name, same as amd -C.
+
+ * amd/info_ldap.c: LDAP info map functions from Leif Johansson
+ <leifj@matematik.su.se>.
+
+ * amd/conf.c (gopt_ldap_base, gopt_ldap_hostports): put in global
+ amd.conf options for ldap_base and ldap_hostports.
+
+ * amd/opts.c (f_netgrp): new amd map function netgrp(ARG) to test
+ if the current host is in the ARG host netgroup.
+
+ * aux/configure.in: some systems replaced dbm in libc with GNU's
+ libgdbm, so check for libgdbm, but only if dbm_open is not in
+ libc already. This was a recommendation from Tom Schmidt
+ <tschmidt@micron.com>.
+
+ * conf/nfs_prot/nfs_prot_sunos5_3.h: port to sparc-sun-solaris2.3
+ completed.
+
+Wed Jul 9 18:14:59 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * port to sparc-unknown-netbsd1.2E done, not tested. Includes NFS
+ V.3.
+
+Tue Jul 8 17:35:07 1997 Erez Zadok <ezk@elk.mcl.cs.columbia.edu>
+
+ * port to i386-unknown-freebsd3.0 done and working. Includes NFS
+ V.3.
+
+ * aux/macros/check_nfs_fh_dref.m4: freebsd3 should use
+ file handle dereferencing style similar to freebsd22.
+
+ * include/am_defs.h: need to include <net/if_var.h> before
+ <netinet/if_ether.h> on freebsd3.
+
+ * conf/nfs_prot/nfs_prot_freebsd3.h: new protocol header additions
+ for freeBSD 3.0.
+
+Tue Jul 8 16:53:41 1997 Erez Zadok <ezk@moisil.cs.columbia.edu>
+
+ * amd/info_nis.c (nis_reload): cast nis' callback function so
+ picky compilers won't complain.
+
+ * libamu/xdr_mountres3.c (xdr_mountres3): make sure this function
+ gets compiled only if the system has NFS V3 and does not have
+ xdr_mountres3.
+
+Tue Jul 8 12:42:03 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * COPYING: put in some legal stuff in this file.
+
+Mon Jul 7 19:10:44 1997 Erez Zadok <ezk@prometheus.soscorp.com>
+
+ * NFS V3 now works under Irix5, thanks to patches from
+ Andreas Stolcke <stolcke@speech.sri.com>.
+
+ * conf/mount/mount_irix5.c: sparate mount_irix.c into an irix5
+ version and an irix6 version, since irix5's NFS V3 code is broken
+ and hacky.
+
+ * amd/info_hesiod.c (hs_zone_transfer): minor fixes to buffer
+ sizes for some hesiod queries. Patch from Danny Braniss
+ <danny@cs.huji.ac.il>.
+
+Mon Jul 7 19:04:14 1997 Erez Zadok <ezk@glory.soscorp.com>
+
+ * amd/ops_nfs.c (mount_nfs_fh): fixed NFS V.3 support for bsdi3.
+
+Sun Jul 6 14:22:24 1997 Erez Zadok <ezk@glory.soscorp.com>
+
+ * aux/{configure.in,acconfig.h}: added checks for struct
+ nfs_args's fields proto and sotype (bsdi3).
+
+ * amd/ops_nfs.c (mount_nfs_fh): added nfsv3 option to
+ nfs_args.flags for bsdi3. Set field proto to 0. Set sotype field
+ to SOCK_STREAM or SOCK_DGRAM.
+
+ * aux/macros/check_mount_style.m4: add case for bsdi3.
+
+ * amd/info_hesiod.c: got hesiod support for bsdi3.
+
+Sun Jul 6 11:14:47 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * aux/configure.in: look for hesiod on libc (bsdi3)
+
+ * amd/get_args.c (get_args): reformat usage so it fits in 80
+ columns.
+
+ * scripts/ctl-amd.in: changed directory name for optional tftpboot
+ map to /tftpboot/.amd.
+
+Sat Jul 5 17:46:45 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ *******************************************************************
+ *** Released am-utils-6.0a6 ***
+ *******************************************************************
+
+Sat Jul 5 03:17:09 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * scripts/ctl-amd.in: include -T tag for tftpboot.
+
+ * new port: mips-sgi-irix5.3. Compiled with gcc, but NFS V3 code
+ is broken and needs work on the specific nfs_args that irix5 uses
+ for V3 mounts.
+
+Sat Jul 5 01:44:25 1997 Erez Zadok <ezk@lorien.cs.columbia.edu>
+
+ * amd/get_args.c (get_args): new options amd -T tag. A map with
+ tag=foo matching the -T tag will be processed. All untagged maps
+ get processed all the time.
+
+Fri Jul 4 16:19:26 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * aux/configure.in: need to check for strcasecmp in libucb (ncr2)
+
+ * aux/macros/check_os_libs.m4: a new macro to set the usage of
+ libnsl and libsocket based on the OS.
+
+ * libamu/mount_fs.c (mnt_flags): used MNTTAB_OPT_GRPID if it is
+ defined (problem on osf4).
+
+ * conf/nfs_prot/nfs_prot_irix5.h: new file for irix5 headers.
+
+ * include/am_defs.h, aux/macros/try_compile_nfs.m4,
+ aux/macros/mount_headers.m4: do include <sys/proc.h> because it
+ failes on irix5. Rather, put in the individual
+ conf/nfs_prot/nfs_prot_*.h headers.
+
+ * amd/conf.c: AIX3 doesn't like strdup() being passed a constant
+ char*, so cast all strdup()'s args to a non-const char*.
+
+ * amd/mapc.c (root_newmap): don't try to strdup only a "const
+ char *".
+
+Fri Jul 4 15:47:10 1997 Erez Zadok <ezk@americas.psl.cs.columbia.edu>
+
+ * conf/nfs_prot/nfs_prot_sunos4.h: added rpc/rpc.h and
+ sys/errno.h.
+
+Thu Jul 3 17:49:00 1997 Erez Zadok <ezk@prometheus.soscorp.com>
+
+ * made sure am-utils compiles, links, and runs properly on Irix
+ 6.2 using "cc -32 -Wl,-woff,"
+
+ * aux/macros/check_lib2.m4: a bug fix version to autoconf 2.12
+ AC_CHECK_LIB. If OTHER-LIBRARIES are defined and used, then add
+ them to $LIBS.
+
+ * aux/configure.in: some cleanup of which libraries are needed.
+ Espectially ensure that libnsl is included only on Solaris, where
+ so far it is the only system on which it is absolutely necessary.
+ Also no longer need to include libc explicitly.
+
+ * amd/amd.c,wire-test/wire-test.c (main): convert IP address to
+ network-long order before printing it.
+
+ * wire-test/wire-test.c (main): also check and report on the IP
+ address of the local host, since some systems have bad versions of
+ get_myaddress, or ones that conflict with other versions in
+ multiple libraries (SunOS 4 and Irix especially).
+
+Wed Jul 2 18:19:02 1997 Erez Zadok <ezk@starblazers.soscorp.com>
+
+ * scripts/ctl-amd.in: don't use full pathname so killproc() works
+ better. Also run bsd44 and irix style ps programs.
+
+ * aux/macros/check_lib_funcs.m4: bug fixed: used to define usage
+ of library no matter if it was found or not.
+
+Wed Jul 2 02:11:48 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * aux/macros/check_mnt2_gen_opt.m4: turned back on the checking
+ for M_* macros, but ensure that <sys/stream.h> is not included at
+ all so it won't conflict with other M_* macros.
+
+Tue Jul 1 21:16:51 1997 Erez Zadok <ezk@mako.cs.columbia.edu>
+
+ * conf/mount/mount_irix.c (mount_irix): first arg to mount(2) on
+ irix should be "spec" (host:/path), not the dir of the mnt point.
+
+ * amd/ops_afs.c (afs_lookuppn): bug in enable_default_selectors
+ fixed. If on, it essentially ignored the setting in a
+ selector-controlled /defaults if there were 2 or more entries in it.
+
+Tue Jul 1 12:23:38 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * scripts/ctl-{amd,hlfsd}.in (killproc): if failed to find process
+ using BSD style ps, then try SVR4 style ps.
+
+ * include/am_utils.h (amuDebug): renamed Debug macro to amuDebug
+ etc., to avoid conflicts with similar named macros in <ldap.h>.
+
+ * aux/configure.in, amd/info_ldap.c, etc.: put in all the autoconf
+ support needed for LDAP. Now the code has to be written mostly in
+ amd/info_ldap.c.
+
+ * Included additional patches from Andreas Stolcke
+ <stolcke@speech.sri.com> to support using the best possible
+ combination of NFS version (2,3) and protocol (udp,tcp) for any
+ given host, since it appears that some Irix versions have NFS V3,
+ but do not support TCP.
+
+ * scripts/ctl-amd.in: updated amd control script to look for and
+ use amd.conf file.
+
+Tue Jul 1 00:28:23 1997 Erez Zadok <ezk@lorien.cs.columbia.edu>
+
+ * amd/mapc.c (mapc_create): now, each map in the amd.conf can be
+ initialized off of a different type, or default to cycling through
+ all maps until one of them matches.
+
+Mon Jun 30 20:49:13 1997 Erez Zadok <ezk@lorien.cs.columbia.edu>
+
+ * amd/ops_afs.c (afs_readdir): per-map browsing (readdir) done.
+ if browsable_dirs=yes is set in the map section in amd.conf, that
+ map will return all entries back to a readdir(2).
+
+Sun Jun 29 16:22:11 1997 Erez Zadok <ezk@lorien.cs.columbia.edu>
+
+ * rudimentary amd.conf file support included. You may even
+ override things like os=sos5, to get "backwards" compatibilty with
+ upl102.
+
+Sat Jun 28 13:35:02 1997 Erez Zadok <ezk@lorien.cs.columbia.edu>
+
+ * amd/conf.c: new file to process amd.conf file.
+
+ * amd/get_args.c (get_args): new options -H to print help.
+
+ * moved enable_default_selectors to be amd.conf controlled, and
+ not compiler controlled.
+
+ * amd/get_args.c (get_args): new options -F <conf-file> to parse an
+ amd configuration file.
+
+Wed Jun 25 23:15:42 1997 Erez Zadok <ezk@lorien.cs.columbia.edu>
+
+ * converted all files that used strcmp() == 0 or != 0 to using
+ the STREQ() macro.
+
+Mon Jun 23 22:31:14 1997 Erez Zadok <ezk@lorien.cs.columbia.edu>
+
+ * aux/macros/check_lib_funcs.m4: new M4 macro that can be used to
+ search for any number of functions in any given library.
+
+Mon Jun 23 01:04:23 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * amd/mapc.c: Included patch from Leif Johansson
+ <leifj@matematik.su.se> to ensure that systems without an RE
+ library can compile.
+
+ * libamu/mount_fs.c (mount_fs): Included patch from Andreas
+ Stolcke <stolcke@speech.sri.com>, to ensure that mount options on
+ table are properly delimited by a comma.
+
+ * amd/ops_nfs.c (mount_nfs_fh): Included patch from Andreas
+ Stolcke <stolcke@speech.sri.com>, to allow users to override the
+ proto= and vers= mount options.
+
+Sun Jun 22 01:00:11 1997 Eric Dana (edana@ncr2.bgs.com)
+
+ * conf/nfs_prot/nfs_prot_svr4.h: port completed to
+ i486-ncr-sysv4.3.03. Assorted source cleanups done.
+
+ * include/am_defs.h, aux/macros/mount_headers.m4: don't include
+ mnttab.h if sys/mnttab.h was already included, because on ncr2,
+ they contain conflicting entries for struct mnttab.
+
+Sat Jun 21 23:52:15 1997 Eric Dana (edana@ncr2.bgs.com)
+
+ * aux/macros/check_nfs_prot_headers.m4: added support for ncr2.
+
+Fri Jun 20 06:14:17 1997 Erez Zadok <ezk>
+
+ * amd/*.c: lots of 64-bit "ugly" ports like casts to long ints.
+
+ * aux/macros/struct_nfs_fh.m4: check for "struct nfssvcfh" because
+ on DU-4.0 it is better than the next one to be picked (nfsv2fh_t).
+
+Wed Jun 18 18:59:49 1997 Erez Zadok <ezk@defiant.soscorp.com>
+
+ * aux/macros/struct_nfs_fh.m4 (AC_TRY_COMPILE_NFS): check for
+ nfsv2fh_t before fhandle_t becasue on bsdi2.1 the former is
+ better.
+
+Tue Jun 10 17:06:58 1997 Erez Zadok <ezk@lorien.cs.columbia.edu>
+
+ * amd/rpc_fwd.c (fwd_packet): TLI bug fix. if fwdto socket is
+ NULL, set the maxlen and len fields in netbuf to zero.
+
+Wed May 28 22:52:28 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * scripts/fix-amd-map.in (name): replace all matching patterns on
+ line.
+
+Sun May 25 19:33:41 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ *******************************************************************
+ *** Released am-utils-6.0a5 ***
+ *******************************************************************
+
+Sun May 25 13:37:24 1997 Erez Zadok <ezk@lorien.cs.columbia.edu>
+
+ * amd/amq_subr.c (amqproc_getvers_1_svc): amq's RPC service
+ procedure now returns the full string that amd -v returns.
+
+ * amd/get_args.c (get_version_string): new function to return
+ complete version/info string so it could be used more flexibly.
+
+ * amd/am_ops.c (ops_showamfstypes, ops_showfstypes): put the list
+ of Fs types into a string buffer, rather than a FILE*.
+
+ * amd/mapc.c (mapc_showtypes): put the list of map types into a
+ string buffer, rather than a FILE*.
+
+Sun May 25 01:25:36 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * amd/get_args.c (get_args): amd -v also prints user who built
+ amd, hostname built on, and date of configuration.
+
+Sat May 24 13:40:50 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * scripts/lostaltmail.conf-sample: sample lostaltmail
+ configuration file.
+
+ * scripts/lostaltmail.in: new script lostaltmail used to redeliver
+ "lost" mail that hlfsd redirected to a temp location because the
+ primary home file system of the user was full.
+
+ * scripts/amd2sun.in: new script amd2sun. Used to convert Sub
+ automount maps to amd maps.
+
+ * scripts/ctl-hlfsd.in: new script ctl-hlfsd. Used to start,
+ stop, or restart hlfsd.
+
+ * scripts/expn.1: man page for expn command.
+
+ * scripts/ctl-amd.in: new script ctl-amd. Used to start, stop, or
+ restart amd.
+
+ * scripts/expn.in: new script expn. Actually taken from David
+ Muir Sharnoff <muir@idiom.com>. Used by hlfsd's mail re-delivery
+ script.
+
+ * scripts/wait4amd.in: new script wait4amd added. Waits for amd
+ to come up on a host, and then runs a command (rsh $hostname by
+ default).
+
+ * scripts/Makefile.am: new directory now holds all shell/perl
+ scripts that belong to am-utils.
+
+ * conf/mount/mount_linux.c (parse_opts): new parser for
+ linux-specific mounts. Handles fs-type specific mount-options
+ correctly. Currently implemented: msdos, iso9660.
+
+ * amd/ops_pcfs.c (pcfs_ops): don't timeout cdfs mounts by default
+ (fix for linux).
+
+ * amd/ops_cdfs.c (cdfs_ops): don't timeout cdfs mounts by default
+ (fix for linux).
+
+ * amd/mapc.c (make_entry_chain): fixed bug that resulted in amd
+ core dumps upon "ls" of a mount point; an invalid pointer was
+ being returned and dereferenced.
+
+ * port to Linux sparc working!
+
+ * hlfsd/homedir.c (hlfsd_diskspace): close file descriptor if
+ failed to write test file, or else the fd will leak.
+
+Sun May 18 00:42:41 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * amd/info_hesiod.c (hs_strip_our_domain): use strcasecmp()
+ instead of strcmp().
+
+ * Port to Digital Unix 4.0 (OSF) done.
+
+ * hlfsd/hlfsd.c: renamed dirname to dir_name to avoid conflicts
+ with dirname() function in <string.h> on OSF systems.
+
+ * fsinfo/fsi_util.c (set_ether_if): take out extern defintion of
+ inet_addr(). It should come from <arpa/inet.h> instead.
+
+ * conf/mtab/mtab_osf.c: fixed macros and added NFS V3 specs.
+
+ * include/am_utils.h: renamed FIRST/LAST to AM_FIRST/AM_LAST to
+ avoid conflicts with DU 4.0 system header.
+
+Thu May 15 22:49:12 1997 Erez Zadok <ezk@subzero.cs.columbia.edu>
+
+ * libamu/mount_fs.c (mount_fs): pass along NFS version so I can
+ determine if to include additional options in the mount table file
+ entry (such as proto=tcp, and vers=3).
+
+Thu May 15 00:42:12 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * libamu/mount_fs.c (mount_fs): include NFS version number in the
+ mount table options, as well as proto={tcp,udp}.
+
+Wed May 14 14:30:19 1997 Erez Zadok <ezk@lorien.cs.columbia.edu>
+
+ * NFS V3 code ported to most other systems, so it compiles for V2
+ and V3 NFS systems.
+
+ * NFS VERSION 3 support, first pass. Working for Solaris and
+ using TCP.
+
+Sun May 11 01:14:29 1997 Erez Zadok <ezk@lorien.cs.columbia.edu>
+
+ * amq/amq.c: many newline characters were missing from various
+ stderr printfs.
+
+Sat May 10 13:09:06 1997 Erez Zadok <ezk@lorien.cs.columbia.edu>
+
+ * conf/nfs_prot/nfs_prot_sunos5.h: protocol defintions for
+ Solaris, which include NFS V3 support.
+
+ * conf/nfs_prot/nfs_prot_default.h: default NFS protocol headers.
+
+ * conf/transp/transp_{tli,sockets}.c (get_mount_client): use NFS
+ protocol version while getting a mount client.
+
+ * amd/ops_nfs.c (mount_nfs_fh): set the mount types, mnttab types,
+ file handle size, and proper file handle based on the correct NFS
+ version number.
+
+ * conf/mount/mount_svr4.c (mount_svr4): support NFS3 mounts under
+ Solaris and other SVR4 systems.
+
+ * amd/ops_nfs.c: store NFS file handle version and version
+ specific handle in struct fh_cache.
+ (got_nfs_fh): pick the correct RPC reply based on the NFS version
+ of the server.
+ (prime_nfs_fhandle_cache): figure out the right NFS file handle
+ for a node, but now include NFS protocol version information.
+ (call_mountd): set the correct version for the mount protocol,
+ based on the NFS protocol version.
+ (nfs_init): use the NFS protocol generic structure
+ am_nfs_handle_t.
+
+ * include/am_utils.h: new structure am_nfs_handle_t to contain all
+ possible versions of NFS file handles.
+
+ * amd/srvr_nfs.c (start_ping): ping program performs NFS version
+ specific pings.
+ (recompute_portmap): figure out the correct portmap version mount.
+ (nfs_keepalive): call start_ping() with the correct NFS version.
+ (find_nfs_srvr): if mount option "vers=" is given, use it. Get
+ the NFS server version and verify that it is up.
+
+ * conf/transp/transp_{tli,sockets}.c (get_nfs_version): a new
+ function to find the best NFS version for a particular host.
+
+ * libamu/amu.h: determine what is the highest version of NFS we
+ should try.
+
+ * include/am_utils.h (NFS_VERSION3): added definition for NFS V3
+ protocol number.
+ Added field fs_version to struct fserver, so we can now store the
+ server protocol version.
+
+Fri May 9 01:09:45 1997 Erez Zadok <ezk@lorien.cs.columbia.edu>
+
+ * aux/macros/check_field.m4 (AC_MOUNT_HEADERS): map '.' (dot) into
+ underscores too, so I can test for sub-fields.
+
+ * libamu/wire.c: use test that checks for sub-fields of struct
+ ifreq.
+
+Sat May 6 10:20:17 2000 Erez Zadok <ezk@glory.soscorp.com>
+
+ * port to BSD/OS 3.0 completed! Many xdr_ functions were broken
+ into smaller files. M4 macros fixed to test for existence of
+ file systems using getvfsbyname().
+
+ * amd/sched.c (sigchld): signal handler must waitpid() for all
+ possible children.
+
+ * hlfsd/homedir.c: signal handler must waitpid() for all possible
+ children.
+
+ * aux/macros/check_restartable_signals.m4: new macro to determine
+ if need to reinstall signal handlers per OS is better than trying
+ to figure these out based on system macros.
+
+Fri May 5 19:23:28 2000 Erez Zadok <ezk@glory.soscorp.com>
+
+ * amd/ops_nfs.c (mount_nfs_fh): set nfs_args_t version number.
+
+ * amd/ops_afs.c (mount_toplvl): set nfs_args_t version number.
+
+ * amd/ops_nfs.c (mount_nfs_fh): process nfs mount option resvport
+ (must be supplied for bsdi 3.0)
+
+ * libamu/wire.c: use HAVE_FIELD_STRUCT_IFREQ_IFR_ADDR (and
+ associated new macro) for determining the right way to compute the
+ size of the interface structure.
+
+Fri Apr 11 15:56:13 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * aux/configure.in: need to include libc before libnsl if
+ get_myaddress() is available because on irix libnsl has a buggy
+ get_myaddress().
+
+ * include/am_defs.h: make sure REINSTATE_SIGNAL_HANDLER is defined
+ on Irix 6 (an SVR4 variant)
+
+Thu Mar 27 02:28:40 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ *******************************************************************
+ *** Released am-utils-6.0a4 ***
+ *******************************************************************
+
+Wed Mar 26 21:15:36 1997 Erez "HWank1" Zadok <ezk@lorien.cs.columbia.edu>
+
+ * amd/amq_subr.c (ok_security): turned off insecure code unless
+ user explicitly runs configure --enable-amq-mount.
+
+ * hlfsd/hlfsd.c (main): use new protoypes dor create_nfs_service().
+
+ * amd/nfs_start.c (mount_automounter): use code that separately
+ creates the NFS and the amq service. Register tcp and udp
+ services for amd.
+
+ * conf/transp/transp_*.c (create_amq_service): new function to
+ separately create the amq service for amd. Simplified prototypes
+ for create_nfs_service().
+
+Wed Mar 19 20:40:56 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ *******************************************************************
+ *** Released am-utils-6.0a3 ***
+ *******************************************************************
+
+Wed Mar 19 17:49:18 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * new port: rs6000-ibm-aix3.2.5 and rs6000-ibm-aix4.1.5.0. Lots
+ of small changes to support these two.
+
+Wed Mar 19 14:33:30 1997 Erez Zadok <ezk@grande.cs.columbia.edu>
+
+ * conf/umount/umount_default.c (umount_fs): bug: unlock_mntlist()
+ should run if MOUNT_TABLE_ON_FILE, not otherwise.
+
+Mon Mar 17 22:02:43 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * hlfsd/hlfsd.c (main): fill in fhsize field if exists
+ (freebsd-2.2).
+
+ * amd/ops_{nfs,afs}.c (mount_toplvl): fill in fhsize field if
+ exists (freebsd-2.2).
+
+Sat Mar 15 19:37:48 1997 Erez "HWank1" Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * new port: i386-unknown-freebsd2.2. FreeBSD 2.2 and higher
+ changed the filehandle type in struct nfs_args from nfsv2fh_t to
+ u_char.
+
+Sat Mar 14 10:11:03 1997 Erez "HWank1" Zadok <ezk@lorien.cs.columbia.edu>
+
+ * SHARED LIBRARIES: too many change to list individually. Now,
+ libamu can be built as a shared library, and other programs link
+ with it.
+
+Tue Mar 11 22:24:09 1997 Erez "HWank1" Zadok <ezk@lorien.cs.columbia.edu>
+
+ * aux/macros/opt_shared.m4: new macro for setting options for
+ building shared am-utils libraries.
+
+Sun Mar 9 19:17:46 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * fixmount/fixrmtab: cleaned up the script.
+
+ * fixmount/Makefile.am (EXTRA_DIST): adde $(sbin_SCRIPTS) so that
+ fixrmtab gets distributed too.
+
+ * conf/nfs_prot/nfs_prot_bsdi2.h: added support for MOUNTPROC_DUMP.
+ Added extern for xdr_mountlist().
+
+ * aux/configure.in: trimmed down many tests for MNT2_GEN_OPT,
+ MNT2_NFS_OPT, and MNTTAB_OPT that were not referenced anywhere in
+ the sources.
+
+Sat Mar 8 00:23:58 1997 Erez Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * fixmount/hlfsd ported to bsd44, hpux, and linux systems.
+
+ * aux/macros/check_fixmount_style.m4: add check for bsd44 systems.
+
+ * conf/fixmount/fixmount_default.c: use lockf() if flock() is
+ unavailable.
+
+ * include/am_defs.h: include <string.h> even if don't have
+ STDC_HEADERS defined (sunos4).
+ (seteuid): define seteuid() to be setresuid() for systems that
+ have but not the other (hpux).
+
+ * aux/macros/check_extern.m4: test should include <strings.h>.
+
+Tue Mar 4 17:16:52 1997 Erez "HWank1" Zadok <ezk@lorien.cs.columbia.edu>
+
+ * lib/xutil.c (debug_option): moved from amd/getargs.c to this
+ more proper file.
+
+ * include/am_utils.h: put in structure for debug options, for all
+ progams to use.
+
+ * amd/get_args.c: moved structure for debug options out.
+
+Sun Mar 2 00:13:07 1997 Erez "HWank1" Zadok <ezk@lorien.cs.columbia.edu>
+
+ * ported fixmount.
+
+ * lib/ualarm.c: replacement function for systems that don't have a
+ ualarm().
+
+ * ported fsinfo.
+
+Sun Mar 1 10:23:34 1997 Erez "HWank1" Zadok <ezk@lorien.cs.columbia.edu>
+
+ * ported mk-amd-map.
+
+ * buildall: added -K option to buildall, to run mkconf.
+
+Fri Feb 28 01:10:25 1997 Erez "HWank1" Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ *******************************************************************
+ *** Released am-utils-6.0a2 ***
+ *******************************************************************
+
+Thu Feb 27 13:58:59 1997 Erez "HWank1" Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * all *.[hcyl] files: add copyright line.
+
+ * aux/macros/check_mount_type.m4: test for mount type for
+ linux-like systems, by checking for loadable or statically linked
+ kernel filesystem modules.
+
+ * aux/macros/check_mnttab_type.m4: test for mount table entry
+ for linux-like systems, by checking for loadable or statically
+ linked kernel filesystem modules.
+
+ * aux/configure.in: pcfs filesystems can use vfat type (linux)
+
+ * aux/macros/check_fs_mntent.m4: test for filesystem existence for
+ linux-like systems, by checking for loadable or statically linked
+ kernel filesystem modules.
+
+ * conf/trap/trap_hpux.h: ugly hack added because of stupide HPUX
+ 9.0 header files. This should be removed when HPUX 9.0 is deemed
+ defunct.
+
+ * aux/acconfig.h: set an undefined variable for having an extern
+ to clnt_sperrno().
+
+ * include/am_defs.h: define extern for clnt_sperrno() if needed.
+
+ * aux/configure.in: test for extern clnt_sperrno().
+
+ * aux/macros/check_nfs_fh_dref.m4: HPUX's NFS fhandle dref is
+ similar to irix.
+
+ * aux/macros/check_mount_style.m4: use mount helper on HPUX.
+
+ * aux/macros/check_mtype_type.m4: hpux's mount type is a char*,
+ not integer (but may be mapped to one later).
+
+ * conf/mount/mount_hpux.c (mount_hpux): HPUX has confusing header
+ files. They have mount(2) and vfsmount(2) which don't take same
+ arguments, and they have string mount types MNTTYPE_* as well as
+ integer types MOUNT_*. So I need this new mount function to map
+ from string types to integer types for the vfsmount() function.
+
+ * conf/trap/trap_hpux.h (MOUNT_TRAP): instead of calling
+ vfsmount(), call mount_hpux().
+
+Thu Feb 27 03:34:14 1997 Erez "HWank1" Zadok <ezk@okeeffe.cs.columbia.edu>
+
+ * lib/mount_fs.c (mnt_flags): make sure SYNC option is on only if
+ both MNTTAB_OPT_SYNC and MNT2_GEN_OPT_SYNC are defined.
+
+Thu Feb 27 00:26:00 1997 Erez "HWank1" Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * conf/nfs_prot/nfs_prot_hpux.h: NFS protocol definitions for
+ hpux.
+
+ * include/am_defs.h: no need to include rpc/auth.h or rpc/clnt.h.
+
+ * aux/configure.in: no need to test for rpc/auth.h and rpc/clnt.h.
+
+ * buildall: added ability to configure using other shells than
+ /bin/sh, on those systems where /bin/sh is buggy or limited
+ (HPUX).
+
+ * aux/macros/struct_nfs_fh.m4: reinstated the macro but have it
+ defined am_nfs_fh, and also test for plain typedef "nfs_fh".
+
+ * aux/acconfig.h: #undef am_nfs_fh, which is being defined by a
+ reinstated macro.
+
+ * aux/configure.in: check for <nfs/export.h>
+
+ * include/am_defs.h: include <nfs/export.h> (HPUX) if exists.
+
+ * aux/macros/try_compile_nfs.m4: include <nfs/export.h> (HPUX) if
+ exists.
+
+ * aux/macros/mount_headers.m4 (define): removed some non-working
+ (on HPUX) code that attempts to recreate the fhandle_t rather than
+ load it up from system headers. Also added some NFS protocol
+ header inclusion. include <nfs/export.h> (HPUX) if exists.
+
+Wed Feb 26 00:32:35 1997 Erez "HWank1" Zadok <ezk@okeeffe.cs.columbia.edu>
+
+ * aux/macros/mount_headers.m4 (define): Use M4 changequote to
+ ensure [] brackets are correctly used.
+
+ * aux/macros/expand_cpp_{string,hex,int}.m4: encapsulate
+ multi-line M4 inclusions in [].
+
+Tue Feb 25 22:19:44 1997 Erez "HWank1" Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * conf/mtab/mtab_bsd.c (mnt_dup): use statfs field f_fstypename if
+ it exists.
+
+ * aux/acconfig.h: HAVE_FIELD_STRUCT_STATFS_F_FSTYPENAME, new
+ config.h macro for field f_fstypename of struct statfs.
+
+ * aux/configure.in: testing for field f_fstypename field of struct
+ statfs (NetBSD).
+
+ * aux/acconfig.h: moved all of the "static" definitions out to a
+ fixed file called include/am_defs.h. Added to all */Makefile.am
+ files a dependency on am_defs.h. Created am_defs.h of course, and
+ then added #include <amd_defs.h> to all of the source and header
+ files that required it.
+
+Mon Feb 24 01:27:02 1997 Erez Zadok <ezk@defiant.soscorp.com>
+
+ * aux/acconfig.h: two more externals for xdr functions that may be
+ implemented by libamu.
+
+ * conf/nfs_prot/nfs_prot_bsdi2.h: added missing RPC and XDR
+ definitions.
+
+Sun Feb 23 15:25:26 1997 Erez "HWank1" Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * buildall (default): added -D option for turning on even stricker
+ developer options.
+
+ * */Makefile.am: make sure @AMU_NFS_PROT_HEADERS@ is used to
+ determine dependencies on optional NFS protocol headers.
+
+ * aux/acconfig.h: define and use HAVE_NFS_PROT_HEADERS as needed.
+
+ * aux/macros/check_nfs_prot_header.m4: new test to determine what
+ NFS protocol headers to use.
+
+ * conf/nfs_prot/nfs_prot_{irix6,sunos4,bsdi2}.h: new headers to
+ complete missing system headers for NFS protocol definitions.
+
+Sun Feb 16 15:58:42 1997 Erez "HWank1" Zadok <ezk@vir.cs.columbia.edu>
+
+ * amd/ops_nfs.c (mount_nfs_fh): encapsulated some more code in
+ #ifdef's as needed.
+
+ * amd/ops_afs.c (mount_toplvl): make sure TIMEO/RETRANS code is
+ suitable #ifdef'ed.
+
+ * include/am_compat.h: a couple more mnttab options
+ (timeo/retrans) setting just in case.
+
+ * amd/autil.c (host_normalize): make sure hostent->h_name is
+ passed a non-const char *.
+
+ * conf/mount/mount_linux.c (mount_linux): cleaned up a block of code.
+
+ * {lib,amq,amd}/*.c (many functions): cast xdr_entry to
+ XDRPROC_T_TYPE.
+
+ * lib/nfs_prot_svc.c (nfs_program_2): cast xdr_entry to
+ XDRPROC_T_TYPE.
+
+ * lib/nfs_prot_xdr.c (xdr_entry): cast xdr_entry to
+ XDRPROC_T_TYPE.
+
+ * lib/wire.c (getwire): Make sure safe assignment from const to
+ none is done.
+
+Sat Feb 15 19:38:51 1997 Erez "HWank1" Zadok <ezk@vir.cs.columbia.edu>
+
+ * aux/macros/expand_cpp_int.m4: new macro for expanding integers.
+
+ * aux/configure.in: added linux/fs.h support in configure and
+ several M4 macros.
+
+ * conf/mount/mount_linux.c (mount_linux): use macro instead of
+ "ugly" fixed hex number for mount() arguments.
+
+Sat Feb 15 02:09:47 1997 Erez "HWank1" Zadok <ezk@lorien.cs.columbia.edu>
+
+ * amd/info_hesiod.c: removed macro HAVE_HS_ZONE_TRANSFER and
+ turned all of its code always on. That code always seems to
+ compile, as long as you have hesiod and libresolv.
+
+ * amd/mapc.c: removed macro HAVE_HS_ZONE_TRANSFER and turned all
+ of its code always on.
+
+ * aux/configure.in: removed superfluous test for hs_zone_transfer
+ function (will never find it because it is an amd-internal
+ function).
+
+ * amd/mapc.c (maptypes): must used correct new name for am-utils'
+ hesiod initialization function: hesiod_am_init.
+
+ * amd/info_hesiod.c (hesiod_am_init): renamed hesiod_init to
+ hesiod_am_init so as not to conflict with libhesiod's
+ hesiod_init() function.
+
+ * aux/configure.in: added test and explicit inclusion of
+ libresolv.a for later resolution of library references with
+ libhesiod.
+
+ * aux/macros/opt_cppflags.m4: new file. configure/compile C
+ preprocessor flags.
+
+ * aux/macros/opt_libs.m4: new file. configure/compile library
+ flags.
+
+ * Makefile.am (EXTRA_DIST_AUX): include two new macros
+ opt_cppflags.m4 and opt_libs.m4.
+
+ * aux/configure.in: test for libhesiod needs to additional
+ libresolv to link completely. Additional configuration time
+ options added for setting initial CPPFLAGS and LIBS.
+
+Tue Feb 10 02:15:40 1997 Erez "HWank1" Zadok <ezk@lorien.cs.columbia.edu>
+
+ *******************************************************************
+ *** Released am-utils-6.0a1 ***
+ *******************************************************************
+
+Thu Feb 6 02:55:19 1997 Erez "HWank1" Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * amd/info_ndbm.c (ndbm_search,ndbm_init): fixed the code so that
+ it works on freebsd, where they map dbm_pagfno a non-existing
+ macro on purpose, to force you to port the code to Berkeley DB.
+
+Fri Jan 31 01:35:09 1997 Erez "HWank1" Zadok <ezk@lorien.cs.columbia.edu>
+
+ * amd/ops_afs.c (mount_toplvl): added a sleep(1) right before the
+ actual mount_toplvl, to avoid some possible race conditions.
+
+Thu Jan 30 02:31:49 1997 Erez "HWank1" Zadok <ezk@lorien.cs.columbia.edu>
+
+ * amd/rpc_fwd.c: removed all TLI (HAVE_T_OPEN) code.
+
+Wed Jan 29 18:25:07 1997 Erez "HWank1" Zadok <ezk@lorien.cs.columbia.edu>
+
+ * lib/wire.c (getwire): removed HAVE_T_OPEN (TLI code)
+
+ * amd/amd.c (main): fix local address using htonl(). Free up
+ buffers allocated by netdir_*() functions.
+
+Wed Jan 22 23:53:24 1997 Erez "HWank1" Zadok <ezk@lorien.cs.columbia.edu>
+
+ * amd/fix-amd-map.in: new script that will convert old-style amd
+ maps to new one.
+
+Tue Jan 21 01:18:16 1997 Erez "HWank1" Zadok <ezk@lorien.cs.columbia.edu>
+
+ * FIRST AMD IN AM-UTILS FINALLY WORKS (mounts itself as toplvl,
+ and replies back to simple nfs/link type mount queries).
+
+ * conf/mtab/mtab_svr4.c: reorganized the code in this file to be
+ easier to read, and fixed a few bugs with un/setting the lock file
+ descriptor.
+
+Wed Jan 8 15:27:49 1997 Erez "HWank1" Zadok <ezk@lorien.cs.columbia.edu>
+
+ * amd/sched.c: removed HAS_SVR3_SIGNALS code to handle older
+ signals in Svr3 systems. Only stellix ever used this.
+
+ * amd/rpc_fwd.c (fwd_reply): removed all code that was triggered
+ by DYNAMIC_BUFFERS, since it was not in use.
+
+Tue Jan 7 00:45:59 1997 Erez "HWank1" Zadok <ezk@lorien.cs.columbia.edu>
+
+ * amd/opts.c (eval_opts): removed AMD_COMPAT code. Amd no longer
+ takes '=' sign as old selector. Fix your maps.
+ (deslashify): removed APOLLO code that treated two '//' as one.
+
+Sun Jan 5 01:25:09 1997 Erez "HWank1" Zadok <ezk@lorien.cs.columbia.edu>
+
+ * amd/ops_nfsx.c (nfsx_init): removed HARD_NFSX_ERRORS, code which
+ was not used. It made any submount error fail the whole group.
+
+ * amd/ops_nfs.c (make_nfs_auth): removed code dependent on
+ HAS_NFS_QUALIFIED_NAMES.
+ (mount_nfs_fh): removed macro for NFS_ARGS_NEEDS_PATH, assuming it
+ is never on. So code that was triggered by #ifndef
+ NFS_ARGS_NEEDS_PATH (osf1, aix3, and bsd44 variants) may have to
+ be fixed later.
+ Removed all ULTRIX_HACK code. Ultrix is DEAD!
+ removed all PRESET_AC code, which was only on for Linux to preset
+ the attribute cache values (otherwise they default to zero).
+ (nfs_umounted): revobed KICK_KERNEL code (IRIX systems). Will
+ have to fixed up later.
+ (mount_nfs_fh): removed code that was never included, which
+ attempted to set the port on which amd's nfs mounts on. This
+ isn't supported by the ping algorithm yet. In any case, it is all
+ done in nfs_init().
+
+ * amd/srvr_nfs.c (find_nfs_srvr), amd/ops_nfs.c (mount_nfs_fh):
+ removed HAS_TCP_NFS code. The whole use of NFS V.3 with TCP or
+ specially hacked NFS V.2 that uses TCP (BSD 4.4 variants) will
+ have to be addressed later.
+
+ * amd/nfs_subr.c (nfsproc_getattr_2): removed all code dependent
+ on PRECISE_SYMLINKS.
+
+ * amd/mapc.c (mapc_meta_search): removed code that depended on
+ HAS_DEPOT. It is known buggy code. The whole DEPOT support needs
+ to be rethought.
+ (mapc_add_kv): ported code to use standard regular expression
+ (regexp) library.
+
+ * amd/{map.c,ops_sfs.c,ops_ufs.c}: removed all code dependent on
+ FLUSH_KERNEL_NAME_CACHE and the macro itself.
+
+Sat Jan 4 15:46:58 1997 Erez "HWank1" Zadok <ezk@lorien.cs.columbia.edu>
+
+ * amd/map.c (exported_ap_alloc): removed all code triggered by
+ SHRINK_EXPORTED_AP and the macro itself.
+
+ * amd/info_nis.c: removed HAS_NIS_RELOAD macro, and left the code
+ in all the time. Assuming that there is always NIS realod
+ available.
+ (yp_all_fixed): removed code for BROKEN_YP_ALL. If your yp_all()
+ function is broken such that it does not release filedescriptors
+ it created, then you are hosed. Get a fix for yp_all from your
+ vendor or switch operating systems.
+
+Tue Dec 31 15:34:36 1996 Erez "HWank1" Zadok <ezk@lorien.cs.columbia.edu>
+
+ * amd/info_hesiod.c: renamed macro HAS_HESIOD_RELOAD to
+ HAVE_HS_ZONE_TRANSFER.
+
+Mon Dec 30 17:33:23 1996 Erez "HWank1" Zadok <ezk@lorien.cs.columbia.edu>
+
+ * amd/{nfs,host}_ops.cremoved INFORM_MOUNTD macro, so its code is
+ always included. We should always try to inform the remove mount
+ daemon of mounts we performed. If we want an option for this, it
+ should be a run-time option.
+
+ * amd/host_ops.c: removed all that optionally did not make the
+ directory mount points for amd.
+
+ * HOST_EXEC code removed from everywhere. There is no indication
+ that it was used anywhere. If it needs to be used, then a
+ run-time switch should control it.
+
+Thu Dec 26 00:06:52 1996 Erez "HWank1" Zadok <ezk@lorien.cs.columbia.edu>
+
+ * amd/*_ops.c: removed all references to mntent_t's fields
+ mnt_freq and mnt_passno, since they are no longe rused (and never
+ have been).
+
+ * amd/am_ops.c: removed function sunos4_match, which was off
+ anyway. This was one of the places where an '=' sign was allowed
+ as an assignment operator (which is now ':=').
+
+ * amd/afs_ops.c (afs_bgmount): removed SUNOS4_COMPAT code which
+ was off anyway. This was one of the places where an '=' sign was
+ allowed as an assignment operator (which is now ':=').
+
+Tue Dec 24 01:53:52 1996 Erez "HWank1" Zadok <ezk@lorien.cs.columbia.edu>
+
+ * amq/amq.c: ported to am-utils. Working.
+
+ * conf/mount/mount_svr4.c (mount_svr4): changed mount type on svr4
+ to be string, as it should be (rather than a string mapped to int
+ and back mapped to string).
+
+Sun Dec 22 13:26:05 1996 Erez "HWank1" Zadok <ezk@lorien.cs.columbia.edu>
+
+ * lib/mount_fs.c (mount_fs): removed MNTINFO_PREF code.
+
+ * lib/umount_fs.c: split this into three files: default, osf, and
+ bsd44, that are in conf/umount/umount_*.c.
+
+Thu Dec 19 17:33:46 1996 Erez "HWank1" Zadok <ezk@lorien.cs.columbia.edu>
+
+ * aux/configure.in, aux/aclocal.m4, aux/acconfig.h: added new
+ macro to automatically figure out if an external definition for
+ sys_errlist[] exists.
+
+ * lib/xutil.c (xfree): removed "#undef free" because it's not
+ needed there. Any system that redefines free() needs help.
+
+ * conf/mtab/mtab_file.c (REWRITE_MTAB_IN_PLACE): removed code that
+ was dependent on REWRITE_MTAB_IN_PLACE. It does not appear to be
+ in active use anywhere. If added later, it should be a run-time
+ configuration option.
+
+ * conf/mtab/mtab_mach3.c: removed code that was #ifdef'ed not to
+ be MOUNT_TABLE_ON_FILE, since mach2 always stores its mount tables
+ in a file in /etc.
+
+ * lib/util.c (str3cat): removed _AIX code. AIX should have a
+ working realloc, or else find a cleaner solution to this.
+ (mkdirs): removed SUNOS4_WORKAROUND code. This was to do a sync()
+ after immediately making directories so that fsck will be able to
+ fix the filesystem in the event of an immediate crash. This was
+ because a bug in UFS which is fixed by now.
+
+ * lib/hutil.c (domain_strip): removed PARTIAL_DOMAINS triggered
+ code. Does not appear to be on and used anywhere.
+ (dofork): removed MACH3 code, b/c mach3 NFS bugs are fixed by now.
+
+ * lib/resvport.c: split the binding of reserved ports code to a
+ separate file. A #define (HAVE_T_OPEN) separates the TLI (SVR4)
+ from the BSD code.
+
+Thu Dec 19 02:08:35 1996 Erez "HWank1" Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * lib/Makefile.am (amu_LIBADD): includs @LIBOBJS@ now, auto-filled
+ in by automake and configure, for optional sources that need to be
+ built as part of libamu.a.
+
+ * aux/aclocal.m4: Make sure AC_CHECK_MOUNT_STYLE adds mountutil.o
+ to LIBOBJS.
+
+ * lib/memcmp.c (memcmp): new file added to replace a possible bad
+ implementation of memcmp.
+
+Wed Dec 18 22:20:23 1996 Erez "HWank1" Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * converted the sources to use memset/memmove/memcmp instead of
+ bzero/becopy/bcmp.
+
+ * ran all sources through gnu Indent w/ options specified in
+ aux/amindent.
+
+Wed Dec 11 22:19:29 1996 Erez "HWank1" Zadok <ezk@shekel.mcl.cs.columbia.edu>
+
+ * At this point the initial genration of config.h via autoconf and
+ some preliminary makefiles via automake is done. I will not begin
+ modifying sources, so it's a good time to document from here.
diff --git a/contrib/amd/INSTALL b/contrib/amd/INSTALL
new file mode 100644
index 000000000000..7df423b1be8b
--- /dev/null
+++ b/contrib/amd/INSTALL
@@ -0,0 +1,113 @@
+# -*- text -*-
+ am-utils 6.0 compatibility list
+
+For each system, list if it autoconfigures, compiles, or runs. Fill in
+email id of person who confirms the fact. A missing entry means unverified.
+A 'no' means verified broken.
+
+SYSTEM AUTOCONF COMPILE RUN SHLIB
+========================= ======== ========= ======= =====
+alpha-dec-osf2.1 ezk ezk ezk
+alpha-dec-osf4.0 ezk ezk dsr[3]
+alphaev5-unknown-linux-gnu ezk ezk finkel ezk
+hppa1.0-hp-hpux11.00 ezk ezk ezk
+hppa1.1-hp-hpux10.10 ezk ezk ezk -ezk (!rpcsvc.so)
+hppa1.1-hp-hpux10.20 ezk ezk ezk -ezk (!rpcsvc.so)
+hppa1.1-hp-hpux9.01 ezk[4] ezk[4] nrh/ezk
+hppa1.1-hp-hpux9.05 ezk[4] ezk[4] nrh/ezk
+hppa1.1-hp-hpux9.07 ezk[4] ezk[4] nrh/ezk
+i386-pc-bsdi2.1 ezk ezk ezk
+i386-pc-bsdi3.0 ezk ezk ezk
+i386-pc-bsdi3.1 ezk ezk ezk
+i386-pc-solaris2.5.1 ezk ezk ezk ezk
+i386-pc-solaris2.6 ezk ezk ezk ezk
+i386-unknown-freebsd2.1.0 ezk ezk ezk
+i386-unknown-freebsd2.2.1 ezk ezk ezk ezk
+i386-unknown-freebsd3.0 ezk ezk ezk ezk
+i386-unknown-netbsd1.2.1 ezk ezk ezk ezk
+i386-unknown-netbsd1.3 ezk ezk ezk ezk
+i386-unknown-netbsd1.3.1 ezk ezk ezk ezk
+i386-unknown-openbsd2.1 ezk ezk ezk ezk
+i486-ncr-sysv4.3.03 ezk ezk
+i486-pc-linux-gnulibc1 ezk ezk ezk ezk
+i586-pc-linux-gnulibc1 ezk ezk ezk ezk
+i686-pc-linux-gnu ezk ezk ezk ezk
+m68k-hp-hpux9.00 ezk[4] ezk[4] nrh/ezk
+m68k-next-nextstep3 ezk ezk ezk
+m68k-sun-sunos4.1.1 ezk ezk
+mips-dec-ultrix4.3 ro ro ro
+mips-sgi-irix5.2
+mips-sgi-irix5.3 ezk ezk ezk
+mips-sgi-irix6.2 ezk[1] ezk[1] ezk[1]
+mips-sgi-irix6.4 ezk ezk ezk ezk (!gcc)
+powerpc-ibm-aix4.1.5.0 ezk ezk wpaul
+powerpc-ibm-aix4.2.1.0 ezk ezk ezk
+rs6000-ibm-aix3.2 ezk ezk ezk
+rs6000-ibm-aix3.2.5 ezk ezk ezk
+rs6000-ibm-aix4.1.4.0 ezk ezk
+sparc-sun-solaris2.3 ezk ezk ezk
+sparc-sun-solaris2.4 ezk ezk ezk ezk
+sparc-sun-solaris2.5 ezk ezk ezk ezk
+sparc-sun-solaris2.5.1 ezk ezk ezk ezk
+sparc-sun-solaris2.6 ezk ezk[2] ezk ezk
+sparc-sun-sunos4.1.1 ezk ezk ezk ezk
+sparc-sun-sunos4.1.3 ezk ezk ezk ezk
+sparc-sun-sunos4.1.3C ezk ezk ezk ezk
+sparc-sun-sunos4.1.3_U1 ezk ezk ezk ezk
+sparc-sun-sunos4.1.4 ezk ezk ezk ezk
+sparc-unknown-linux-gnulibc1 ezk ezk ezk ezk
+sparc-unknown-netbsd1.2E ezk ezk ezk
+
+EMAIL ID LEGEND:
+
+bking: Bevis R W King <B.King@ee.surrey.ac.uk>
+dsr: Dan Riley <dsr@mail.lns.cornell.edu>
+ezk: Erez Zadok <ezk@cs.columbia.edu>
+nrh: Nick Hall <nrh@dcs.ed.ac.uk>
+stolke: Andreas Stolcke <stolcke@speech.sri.com>
+wpaul: Bill Paul <wpaul@ctr.columbia.edu>
+finkel: Raphael Finkel <raphael@cs.uky.edu>
+ro: Rainer Orth <ro@TechFak.Uni-Bielefeld.DE>
+
+
+FOOTNOTES:
+
+[1] If compiling with cc on Irix 6, then use
+
+ CC="cc -32 -Wl,-woff,84" ./buildall
+
+to build (good) "old style" 32 bit code and suppress stupid linker warnings
+about unused libraries.
+
+Also, to get NFS V3 working, you need these two patches from SGI:
+
+ patch 1615: NFS over TCP
+ patch 2041: NFS roll-up patch
+
+and then add "-p tcp" to /etc/config/nfsd.options.
+
+[2] If compiling on Solaris 2.6, you need to add -D_LARGEFILE64_SOURCE to
+CFLAGS to enable the 64bit file offset interface:
+
+ make CFLAGS="-O2 -g -D_LARGEFILE64_SOURCE"
+
+If you're using the standard configure script, it will add this flag for you
+automatically.
+
+[3] DU-4.0 may not use NFS (server-side) V3 by default. You may need to
+adjust /etc/init.d/nfs, and change the nfsd startup line from to
+
+ if /usr/sbin/nfsd $NUM_NFSD; then
+to
+ if /usr/sbin/nfsd -t 8 -u 8 ; then
+
+[4] HPUX 9.X has a bad /bin/sh that runs out of fixed memory allocations.
+If you use the configure script, you must run it as
+
+ /bin/ksh ./configure
+
+Also, this system has a bad /bin/make that cannot handle VPATH well. You
+cannot use --srcdir or the buildall script with it. I suggest you install
+GNU make or configure locally with "/bin/ksh ./configure".
+
+Erez.
diff --git a/contrib/amd/MIRRORS b/contrib/amd/MIRRORS
new file mode 100644
index 000000000000..6ea3dbf12193
--- /dev/null
+++ b/contrib/amd/MIRRORS
@@ -0,0 +1,43 @@
+# -*- text -*-
+
+ AM-UTILS-6.0 MIRRORS
+
+Note: in case of any problems accessing the individual FTP sites, please
+contact their respective maintainers. If you wish to be added to the
+official mirror list, please send mail to amd-dev@cs.columbia.edu with the
+full URL, maintainer's email, and geographical location.
+
+U.S.A:
+ New York (Primary Site):
+ ftp://shekel.mcl.cs.columbia.edu/pub/am-utils
+ Maintainer: ezk@cs.columbia.edu
+ Maryland:
+ ftp://ftp.cs.umn.edu/pub/AMD
+ Maintainer: dokas@cs.umn.edu
+ Virginia (Newport News):
+* ftp://www.ferginc.com/pub/unix/am-utils
+ Maintainer: Branson.Matheson@FergInc.com
+
+Europe:
+ Germany:
+ ftp://ftp.fu-berlin.de/pub/unix/network/am-utils
+ Maintainer: ftp-adm@ftp.fu-berlin.de
+ Sweden:
+ ftp://ftp.sunet.se/pub/unix/admin/am-utils
+ Maintainer: archive@ftp.sunet.se
+ Sweden:
+ ftp://ftp.matematik.su.se/pub/mirrors/shekel.mcl.cs.columbia.edu/pub/am-utils
+ Maintainer: lifj@matematik.su.se
+ UK:
+ ftp://sunsite.org.uk/packages/am-utils
+ Maintainer: lmjm@icparc.ic.ac.uk
+
+Asia:
+ Japan:
+ ftp://ftp.u-aizu.ac.jp/pub/net/amd/am-utils
+ Maintainer: ftp-admin@u-aizu.ac.jp
+
+Australia:
+ Melbourne:
+ ftp://ftp.sage-au.org.au/pub/network/filesystem/am-utils
+ Maintainer: mirror@ftp.sage-au.org.au
diff --git a/contrib/amd/NEWS b/contrib/amd/NEWS
new file mode 100644
index 000000000000..f1df1e07a6be
--- /dev/null
+++ b/contrib/amd/NEWS
@@ -0,0 +1,628 @@
+*** Notes specific to am-utils version 6.0a16:
+
+- new ports:
+ hppa1.0-hp-hpux11.00 (works, not NFS V.3 due to missing headers)
+ mips-dec-ultrix4.3 (working, unverified)
+
+- new minor ports:
+ i386-pc-bsdi3.1
+ i386-unknown-netbsd1.3.1
+ alpha-dec-osf2.1
+
+- new options addopt:=ARG will "smartly" add and override options specified
+in opts:=
+
+- new amd.conf options:
+ pid_file: specifies the file to store the PID
+ hesiod_base: specifies the base for the Hesiod service
+ unmount_on_exit: if 'yes' will attempt to unmount all file systems
+ when amd exits.
+
+- amd.conf file is parsed after all other command line options. If no
+options specified at all, then use /etc/amd.conf by default.
+
+- some variables' values are now compared case-insensitive as per specs,
+such as host names, domain names, and more.
+
+- NIS service uses a new isup() function to detect if the service is up
+before using it. Used to ensure amd doesn't clear the existing maps before
+reloading them, unless the remote info service is working.
+
+- new cdfs mount options: rrip, noversion, defperm, nodefperm (OSF)
+
+- support efs/xfs separately on irix
+
+- new -D info trace option to turn on info specific debugging, such as
+RES_DEBUG for hesiod services.
+
+- document updates and fixes
+
+- new file MIRRORS lists official mirror sites (also in am-utils home page)
+
+- new file BUGS lists known amd/OS bugs
+
+- source restructuring: rename all Amd file-systems' sources to amfs_ARG.c
+such that it matches the type:=ARG as well. Free names afs/dfs for Andrew
+F/S and Distributed F/S.
+
+- checkpoint config.guess several times during the long configure, so that
+if it is aborted midway, the bulk of the features discovered will be re-read
+from the config.cache file.
+
+- more systems support shared libraries (libtool 1.2)
+
+- using automake 1.3 + more fixes
+
+- bugs fixed:
+ use dynamic buffer for list of interfaces, not fixed size
+ output of amd -H duplicated if >2 interfaces
+ -D mem for hlfsd not on by default (so it will daemonize)
+ linux looks for ext2fs before ufs
+ CDFS looks for 'isofs' mount type as well
+ compile on Solaris 2.6 with /opt/SUNWspro/bin/cc
+ various additional fixes which gcc 2.8.x reported
+ print syslog help string based on what's supported
+ correctly ignore loopback interface on SunOS 3.x
+ don't use -lucb for strcasecmp
+ hlfsd's dump file securely written in /usr/tmp/hlfsd.dump.XXXXXX
+ inherit NFS V.3 mounts correctly
+ write pid file securely
+
+*** Notes specific to am-utils version 6.0a15:
+
+- new ports:
+ alpha-unknown-linux-gnu: works
+ i386-unknown-netbsd1.3: fully working
+ *-sun-sunos3: compiles, not tested
+
+- updated ports:
+ m68k-next-nextstep3: cleaner compile, works.
+
+- new file system type nfsl (NFS Link). Uses nfs if file system is remote,
+and link if it is local (based on if $rhost equals the host name).
+
+- support for Solaris cachefs. Requires setting fs, rfs, and a new variable
+cachedir. See documentation for explanation, examples, and caveats.
+
+- support negated selector functions such as !exists(/foo/bar)
+
+- wire, network, netnumber, in_network() selectors now match against all
+locally attached networks (by either name or number), not just the first two
+interfaces.
+
+- new program pawd (and man page for it) --- Print Automounter Working
+Directory, to print the proper pathname of the cwd or any other pathname,
+adjusted for automounter paths, while avoiding mount points.
+
+- two new switches to amq: -U will force using UDP only; -T will force using
+only TCP to communicate with amd. If neither (or both) are specified, amq
+will try TCP first, and if that failed, will try UDP.
+
+- support syslog facilities, using "amd -l syslog:facility". Old behavior
+when using only -l syslog is to use the LOG_DAEMON facility.
+
+- you may specify browsable_dirs=full, to get a listing of all entries
+(other than /default), including those with '*' wildcard and '/'
+characters.
+
+- amd -D trace now also includes as much of struct nfs_args as can be
+displayed. Useful in figuring out what the kernel really gets during a
+mount(2), as opposed to what the /etc/mnttab file says. -D trace also
+traces the xdr_* functions.
+
+- support for versions of shared libamu version. upped version from 0.0.0
+to 1.0.0. each am-utils release that will change the library will also
+update its version.
+
+- amd/ops_TEMPLATE.c: a new template file for those brave enough to try and
+implement a new amd file system. Includes comments and other info useful
+for developers.
+
+- if localconfig.h exists in the current directory during the run of
+configure, it is included in all am-utils sources. This allows courageous
+developers to make certain modifications during compilations, and especially
+turn off undesired features (not very recommended).
+
+- documentation types and updates for all new features, ports, etc.
+
+- bugs fixed:
+ support NFS mount options grpid and maxgrps
+ nextstep: set NFS success code to 0 (NFS_OK), not 1 (EPERM)
+ bsdi2: set NFS success code to 0 (NFS_OK), not 1 (EPERM)
+ set NFS V.3 mount table names to "nfs" if vers/proto exist
+ use mkstemp() if possible (more secure)
+ ctl-amd looks for amd.conf in ${prefix}/etc after /etc
+ hpux: use "ignore" mount table type
+ openbsd2.2: turn off "noconn" mount option, so only connected used
+ fixed memory leak in hlfsd (don't setpwent after endpwent)
+ all NFS3 systems should have proto/vers mount/amd options
+ DEBUG_MEM compiles and prints something more useful
+ uninit_mntfs(): free() mf_private *after* it is used
+ browsable_readdir: fewer bytes sent back to kernel for each chunk
+ mount_toplvl: don't free() an automatic variable!
+ amd should chdir() to / before daemonzing (for core dumps etc)
+ cdfs should be called 'cdfs' not whatever the mnttab type is
+ amd -v: don't print "FS:" list twice when >=2 net interfaces
+
+*** Notes specific to am-utils version 6.0a14:
+
+- updated ports:
+ powerpc-ibm-aix4.2.1.0: NFS V.3 works
+
+- minor new ports:
+ sparc-sun-sunos4.1.3C
+ m68k-sun-sunos4.1.1 (sun3)
+ mips-sgi-irix5.2
+
+- new option to amd, -O ARG, will override the operating systems *name* with
+ARG. Corrected documentation for amd -o ARG --- it overrides the operating
+system *version* and not the name as the docs incorrectly stated.
+
+- logging now behave more like syslog: will not print repeated strings, but
+rather a count such as "last message repeated N times". (N will not exceed
+100.)
+
+- restructured the code which deals with the numerous possible fields and
+flags that are set in struct nfs_args. That code was moved to libamu as
+the functions compute_nfs_args() and compute_automounter_nfs_args().
+
+- bugs fixed:
+ mnttab name ufs/cdfs/pcfs/etc filesystems corrected
+ use pmap_ping for amq (a must for secure portmappers, bsdi2/3)
+ test for xfs (irix) as a disk-based filesystem
+ set correct nfs_prot headers for Solaris 2.5
+ removed stale code from lostaltmail.in
+ lostaltmail will look for conf file in multiple locations
+ assorted documentation corrections
+ amq does not print "get_secure_amd_client" if run as root
+
+*** Notes specific to am-utils version 6.0a13:
+
+- new in_network(ARG) nomadic selector, true if ARG is the name (or number)
+of any of this host's network interfaces.
+
+- removed variables primnetname, primnetnum, subsnetname, and subsnetnum.
+(Kept "wire" and its alias "network", and "netnumber".)
+
+- include am-utils.dvi and am-utils.ps in distribution.
+
+- hlfsd supports new option -P ARG, for reading password map off of file
+ARG. Allows you to use the hlfs redirector using paths other than user's
+home directories.
+
+- use a replacement yp_all for some systems (irix) known to have a broken
+one which leaks a file descriptor each time called.
+
+- if remote NFS server is down or does not support portmap, downgrade
+machine to NFS V.2 and retry again later.
+
+- bugs:
+ don't redefine yywrap on systems using a modified flex
+ use correct "ignore" mnttab/mount option on hpux for df(1)
+ use nfs_args' fsname field (hpux) to avoid syncer/mount(1) problems
+ don't add ops_ufs.o twice to Makefile's $(OBJS)
+ don't fail if autofs listener fails to initialize
+ hlfsd should test if run as root after usage() and getopt
+
+- minor code cleanups for netbsd
+
+- html docs now in http://www.cs.columbia.edu/~ezk/am-utils/
+
+- added README file in binaries ftp directory
+
+*** Notes specific to am-utils version 6.0a12:
+
+- minor or updated/broken ports fixed:
+ hppa1.1-hp-hpux10.10: compiles, untested (probably works).
+ hppa1.1-hp-hpux9.05: compiles, untested (probably works).
+ hppa1.1-hp-hpux9.07: compiles, untested (probably works).
+ m68k-hp-hpux9.00: compiles, untested (probably works).
+ rs6000-ibm-aix4.1.4.0: compiles, untested.
+ sparc-sun-solaris2.6: works w/ NFS V.3.
+ sparc-sun-sunos4.1.4: compiles, untested (probably works).
+
+- new ports:
+ powerpc-ibm-aix4.2.1.0: compiles w/ NFS V.3, untested.
+
+- wire-test also checks for combinations of NFS protocol/version from the
+client to a remote (or local) host.
+
+- conf/mtab/mtab_file.c: use flock() to lock the file, and fcntl() if
+flock() is not available. (Used to prefer fcntl() over flock().)
+
+- bug fixes:
+ tli get_nfs_version() gets into an infinite loop
+ tli get_nfs_version() should time out faster
+ sockets get_nfs_version() should work w/ secure portmappers
+ ESTALE returned for NFS mounts for SunOS 4.x fixed
+ do not exceed HOSTNAMESZ for nfs_args.hostname (get ENAMETOOLONG)
+ properly initialize some mntent_t fields (fsck, freq, mnt_time)
+ properly initialize some pcfs_args fields (mask, uid, gid)
+ properly initialize some cdfs_args fields (ssector)
+
+*** Notes specific to am-utils version 6.0a11:
+
+- bug fixes:
+ amd could not NFS mount v.2 servers from v.3 clients
+ hlfsd will only use first occurrence of home dir for same uid
+
+*** Notes specific to am-utils version 6.0a10:
+
+- MAJOR DOCUMENTATION UPDATE! (first time in 6 years)
+
+- new ports:
+ m68k-next-nextstep3: configures, compiles, not tested.
+
+- preliminary autofs support. See README.autofs for details.
+
+- new amd.conf [global] yes/no keywords:
+ show_statfs_entries: shows number of entries for df(1)
+ fully_qualified_hosts: use FQHN for NFS/RPC authentication
+
+- detect down remote hosts faster
+
+- log output of "amd -v" at startup
+
+- removed $osver override for solaris: now it is 2.5.1, not 5.5.1
+
+- buildall will use gmake first if available
+
+- bugs fixed:
+ amd core dumped when remote host was down
+ allow up to 1024 entries back from readdir()
+ amd.conf works even if only [global] option defined
+ avoid using bad memcmp() implementations
+ fixed meaning of plock [global] option (was reversed)
+ hlfsd infinite loop unless compiled with --enable-debug
+ NIS code works with NIS+ servers in NIS compatibility mode
+ reset tag fields in amd.conf so they don't carry to other entries
+
+*** Notes specific to am-utils version 6.0a9:
+
+- new ports:
+ sparc-sun-solaris2.4:
+ configures/compiles, and runs (no NFS V3)
+ i386-unknown-openbsd2.1:
+ configures/compiles, runs (NFS V3)
+
+- updated ports:
+ i486-ncr-sysv4.3.03: configures/compiles, not tested
+
+- Multiple amd support: new amd.conf [global] key "portmap_program" can be
+used to specify an alternate RPC program number for amd to un/register.
+Allowed numbers range from 300019 to 300029. A matching new option for amq:
+-P prognum, will use an alternate program number to contact.
+
+- man pages:
+ amd.conf.5 new
+ mk-amd-map.8 new
+ amd.8 updated
+ amq.8 updated
+ (other man pages required minor updates)
+
+- shared libraries support expanded. Using GNU libtool-1.0. You can build
+a shared version of libamu, and link with it accordingly, by specifying
+--enable-shared to configure. Default is --enable-static --disable-shared,
+and you can mix and match. See "INSTALL" file for listing of systems on
+which shared libraries seem to build and work fine.
+
+- new option: amq -p, will return the PID of the running amd (local or
+remote). Uses a new RPC message. Useful especially in "ctl-amd stop".
+
+- new configure script options --enable-ldflags, for specifying -L flags.
+The older --enable-libs is to be used only for -l options.
+
+- two new LDAP map options for amd.conf: ldap_cache_seconds and
+ldap_cache_maxmem.
+
+- new script, am-eject from Debian linux's version of amd-upl102.
+
+- additional passwd map support using var[0-3], from Debian folks.
+
+- hesiod code cleanup. works for hesiod 1.3 as well as 3.0.
+
+- removed defunct -h option from amd.
+
+- started using automake-1.2. This fixed several bugs that caused some
+versions of yacc/lex and non-GNU make to fail.
+
+- bug fixes:
+ amd/hlfsd mounts should be hidden from df(1)
+ use "noconn" option for nfs mounts (multi-homed hosts)
+ don't use connected sockets on linux before 1.3.10 (from Debian)
+ better checks for [gn]dbm
+ forbid excessive retries after timeouts (from Debian)
+ readdir(): don't skip over map entries with prefix, and include it
+ more assorted linux fixes from Debian folks
+ lofs mount on svr4 was broken
+ find default value of $karch from uname() not $arch
+ hlfsd failed to mount itself on some little-endians
+
+
+*** Notes specific to am-utils version 6.0a8:
+
+- new ports:
+ i386-unknown-netbsd1.2.1: configures/compiles (with NFS V.3), works,
+ but some OS stability problems exist.
+
+- updated ports:
+ hppa1.1-hp-hpux9.01:
+ now tested and working
+ rs6000-ibm-aix3.2 and rs6000-ibm-aix3.2.5:
+ now tested and working
+
+- fixed browsable directories (readdir) code.
+
+- better methods to find amd/hlfsd pid to kill in ctl-{amd,hlfsd}
+
+- "ignore/auto" mount types fixed for irix, sunos, and others, so "df" does not
+show amd mounts by default (but GNU df -a does).
+
+- each time amd is built, a new "build" version is incremented. See amd -v.
+
+- man page for fsinfo added
+
+- empty fillers for new file (bsd44) systems: nullfs, unionfs, umapfs.
+
+- when amd is not running, or portmapper is down, make amq timeout faster (5
+sec) than system default, usually 4-5 minutes.
+
+- bug fixes:
+ hlfsd mount got "protocol not supported"
+ first regular map in amd.conf didn't inherit global options
+ make "bad" versions of lex still work with amd/conf_parse.l
+ check for 'nodev' option, not 'nondev'
+ typo in "ro" option, and fillers to ac{reg,dir}{min,max} and others
+ amd.conf parsing done before switching default log/debug options
+ allow doubly-quoted values in amd.conf
+ hesiod-reload code cleanup
+
+- assorted code cleanup
+
+
+*** Notes specific to am-utils version 6.0a7:
+
+- new ports:
+ i386-unknown-freebsd3.0:
+ fully functional with NFS V.3
+ sparc-sun-solaris2.3:
+ fully functional (should work for 2.4)
+ sparc-unknown-netbsd1.2E:
+ configures/compiles (with NFS V.3), untested
+
+- updated ports:
+ i386-pc-bsdi3.0:
+ NFS V.3 works
+ look for hesiod in libc
+ mips-sgi-irix5.3:
+ fully functional with NFS V.3
+
+- LDAP support! New [global] amd.conf options ldap_base and ldap_hostports.
+Also includes a new script amd2ldif to convert amd maps into plain text LDAP
+object files.
+
+- the following amd.conf variables: browsable_dirs, map_options, map_type,
+mount_type, and search_path --- can now be specified in [global] as well as
+the map entry itself. That way you can declare them only once in [global],
+and override them as needed per map.
+
+- option "cluster" added to [global] (HPUX clusters ala "amd -C").
+
+- assorted info_hesiod map fixes and cleanup. removed HESIOD_RELOAD code.
+
+- added netgrp(name) function to amd map syntax to see if current host is in
+the <name> netgroup.
+
+- removed unused option "amd -m".
+
+- filled in "tasks" file with todo items.
+
+- filled "COPYING" file with legal stuff.
+
+- cleanup: all global variables are now in one big structure (struct
+amu_global_options) that's easy to identify and enhance. Also migrated
+several flags that used be an integer each into one unsigned integer that's
+used as a bit-flag.
+
+- big fixes:
+ xdr_mountres3 should compile only if has NFS V3
+ lex/yacc macros show full pathname (to tell if correct one runs)
+ misc fixes/cleanup
+
+
+*** Notes specific to am-utils version 6.0a6:
+
+- amd configuration file!
+
+See scripts/amd.conf-sample for help and some explanation. This new conf
+file allows for the following new features:
+
+ default selectors can be turned on/off globally.
+ browsable_dirs/readdir() support can be turned on per map.
+ search paths for file type maps.
+ can force the map type to file, nis, ndbm, etc. rather than default
+ to looking at all of them.
+ tag each map for "amd -T tag", useful for grouping maps.
+ can override $os and others (so if you don't like "sunos5" default
+ naming, set os=sos5 and it will work with your old maps).
+ and more goodies...
+
+- enable-default-selectors: No longer turned on by the configure script or
+optionally compiled. Code made dynamic and can be turned on or off from the
+amd.conf file. This code is off by default, and must be turned on by
+amd.conf's [global] section.
+
+- new ports:
+ mips-sgi-irix5.3: configure/compile, not tested
+ i486-ncr-sysv4.3.03: configure/compile, not tested
+
+- updated ports:
+ alpha-dec-osf4.0: major code redone
+ sparc-sun-sunos4.1.3: fixed and working
+ mips-sgi-irix6.2: tested with gcc and "cc -32 -Wl,-woff,84"
+ mips-sgi-irix6.4: tested with gcc and "cc -32 -Wl,-woff,84"
+
+- better NFS3 port, including more support for proto= and vers=, and
+automatic determination of proto/vers combination.
+
+- conf/nfs_prot/nfs_prot_*.h: all of the NFS protocol header files had to be
+redone, because of the osf4 port. OSF used very different names for these,
+and they conflicted with am-utils'. The only solution was to more or less
+conform to OSF4's naming, and change all the others.
+
+- ctl-amd script:
+ improved to look for amd.conf in $prefix/etc and /etc
+ better methods for finding the pid of amd to kill
+
+- autoconf support for LDAP. amd/info_ldap.c needs to be written.
+
+- wire-test also reports the local IP address. Some systems have multiple,
+buggy version of get_myaddress(), esp. SunOS and Irix. Note that Solaris
+x86 has a buggy htonl().
+
+- amd -H prints usage.
+
+- bugs fixed:
+ minor TLI problem in fwd_socket
+ mount options properly comma delimited
+ LIBS is set only to the right set of libraries to include
+ selectors-on-default code ignored last selector ent in /defaults
+ assorted code cleanups
+
+
+*** Notes specific to am-utils version 6.0a5:
+
+- NFS Version 3 support!!!
+
+ Works on Solaris 2.5.1.
+ Minimal testing done on Irix 6.
+ Compiles cleanly on DU-4.0 but no tests performed.
+
+Will fall back to V2 mounts when V3 is not available. Will also use TCP if
+possible, UDP otherwise.
+
+- Ports to new platforms:
+
+ alpha-dec-osf4.0 (not tested)
+ i386-pc-bsdi3.0 (tested and working)
+ i386-unknown-freebsd2.2.1 (tested and working)
+ sparc-unknown-linux-gnu (tested and working)
+
+- New scripts added:
+
+ amd2sun: convert amd maps to Sun automount maps
+ ctl-amd: script to start/stop/restart amd
+ ctl-hlfsd: script to start/stop/restart hlfsd
+ expn: expand mail alias (used by hlfsd)
+ lostaltmail: redeliver "lost" mail redirected by hlfsd
+ lostaltmail.conf-sample: sample conf file for lostaltmail
+ wait4amd: run a command once amd is up on a host
+ wait4amd2die: wait for an amd process to die before returning
+
+- "amd -v" now includes more info and "amq -v" lists all of it.
+
+- new parser for linux specific mount options.
+
+- Main bugs fixed:
+
+ "new toplvl readdir" bug caused amd to dump core
+ handler for SIGCHLD didn't check for all possible children
+ hlfsd leaking file descriptors when home file system was full
+ cdfs/pcfs mounts should not timeout by default
+ hesiod domain names should be compared in case-insensitive manner
+ several printfs in amq were missing \n
+
+
+*** Notes specific to am-utils version 6.0a4:
+
+- amd services both TCP and UDP amq requests. This will help because of the
+limited UDP message size.
+
+- "amq -M" code is disabled by default because it is insecure. It is
+rarely used. Users who wish to use it should run
+"configure --enable-amq-mount".
+
+
+*** Notes specific to am-utils version 6.0a3:
+
+- New tested ports (configures, compiles, and runs):
+
+ i386-unknown-freebsd2.2
+
+- New ports (configures and compiles correctly, not tested):
+
+ rs6000-ibm-aix3.2.5
+ rs6000-ibm-aix4.1.5.0
+
+- More am-utils programs ported to all existing platforms: hlfsd, fsinfo,
+mk-amd-map, and fixmount.
+
+- Shared libraries: a new configure option --enable-shared will build a
+shared libamu.so, link applications with it, and use it. Reduces binary
+sizes by 20-30%. This is the first step towards loadable modules, as many
+changes had to be done to be able to compile and use PIC code. This is code
+that obviously needs to be generalized to be able to build shared libraries
+on many other platforms. It was only tested on Solaris 5.5.1.
+
+- the file INSTALL contains the latest compatibility table of which
+platforms am-utils configures, compiles and runs on.
+
+- Trimmed down the size of the configure script. Some tests that are not
+used anywhere were removed.
+
+
+*** Notes specific to am-utils version 6.0a2:
+
+- New ports (configures and compiles correctly, not tested):
+
+ i386-pc-bsdi2.1
+ hppa1.1-hp-hpux9.01
+ hppa1.1-hp-hpux10.20
+
+- new configure options:
+
+ --enable-cppflags[=ARG]
+ configure/compile with ARG (-I) preprocessor flags
+ --enable-libs[=ARG]
+ configure/compile with ARG (-L/-l) library flags
+
+- filesystem, mount table entries, and mount type tests can now look in
+/lib/modules and /proc/filesystems for statically/dyadically loadable kernel
+modules (linux)
+
+- prefer vfat over msdos/pc/etc filesystem for PCFS.
+
+- moved all fixed headers to include/am_defs. Left only #define/#undef
+entries in aux/acconfig.h.
+
+- make more sense of systems that have full, partial, or no NFS protocol
+headers.
+
+- minor fixes for NetBSD (untested platform).
+
+- hesiod map fixed.
+
+- buildall -D: new option to run even stricter developer options.
+
+- lots of other bugs fixed (see ChangeLog).
+
+
+*** Notes specific to am-utils version 6.0a1:
+
+I have it configure and build correctly for the following systems:
+
+ i386-pc-solaris2.5.1
+ i386-unknown-freebsd2.1.0
+ mips-sgi-irix6.2
+ sparc-sun-solaris2.5.1
+ sparc-sun-sunos4.1.3
+
+Amq, wire-test, and "amd -v" work on all of the above. A real running amd
+was only tested and confirmed working on
+
+ i386-pc-solaris2.5.1
+ sparc-sun-solaris2.5.1
+
+Many things are still missing: options, features, etc. But for now, let's
+concentrate on getting the basic functionality working on the more popular
+systems.
diff --git a/contrib/amd/README b/contrib/amd/README
new file mode 100644
index 000000000000..7ced70bb6571
--- /dev/null
+++ b/contrib/amd/README
@@ -0,0 +1,105 @@
+# -*- text -*-
+
+This is an alpha version of amd. "Buyers" beware!!!
+
+See the file NEWS for news on this and previous releases.
+
+*** General Notes to alpha testers:
+
+[A] an an alpha testers, I expect you to be able to find certain things on
+your own (especially look at the sources to figure out how things work).
+
+[B] if you intend to modify any files, first find out if the file you want
+to modify gets autogenerated from some other place. If so, modify it at the
+source.
+
+You can adjust some of the configuration of am-utils after it has been
+auto-configured by putting whatever definitions you wish in a file called
+localconfig.h, located in the top build directory (the same one where
+config.h is created for you).
+
+[C] there are several ways you can build am-utils:
+
+(1) run the buildall script as follows:
+
+ ./buildall
+
+This would build all the applications inside a special directory relative to
+the root of the source tree, called A.<cpu-company-system>, where the <>
+part is filled in by GNU's config.guess script. This is the preferred
+method, for it will separate the build from the sources, and allow you to
+run buildall for multiple architectures concurrently.
+
+You can run "buildall -h" to see what options it takes.
+
+(2) run the configure script such as:
+
+ ./configure
+
+and then run
+
+ make
+
+This would configure amd in the directory you've run the configure script
+in, and the built it there. Run "make install" to install all the necessary
+files.
+
+Note that this is good for building only one version of amd on one
+architecture! Don't try this for multiple architectures. If you must, then
+after doing one such build, run "make distclean" and then reconfigure for
+another architecture.
+
+(3) run the configure script for build in a different location. Let's say
+that /src/am-utils-6.0 is where you unpacked the sources. So you could
+
+ mkdir /src/build/sunos5
+ cd /src/build/sunos5
+ /src/am-utils-6.0/configure --srcdir=/src/am-utils-6.0
+ make
+
+This is a manual method that will let you build in any directory outside the
+am-utils source tree. It requires that your "make" program understand
+VPATH. This can be used multiple times to build am-utils concurrently in
+multiple (but different) directories. In fact, the buildall script
+described above.
+
+(4) If you need to configure am-utils with extra libraries and/or headers,
+for example to add hesiod support, do so as follows:
+
+ configure --enable-libs="-lhesiod -lresolv" \
+ --enable-ldflags="-L/usr/local/hesiod/lib" \
+ --enable-cppflags="-I/usr/local/hesiod/include"
+
+[D] If you modify any of the *.[chyl] sources in the directories amd, amq,
+hlfsd, lib, etc, all you need to do to get a new version of am-utils is run
+make.
+
+If you modify any of the files in the aux/ or conf/ directories, then you
+must rebuild the configure script, Makefile.in files, aclocal.m4, etc. The
+best way to do so is to run
+
+ ./aux/mkconf
+or
+ ./buildall -K
+
+To be a developer and be able to run mkconf, you must have autoconf-2.12,
+GNU make-3.75 or later, and automake-1.2 (plus my fixes to it) installed on
+your system. You may find my version of automake-1.2 where you ftp'ed this
+version of am-utils. You may also need GNU libtool 1.0.
+
+After you've remade the basic configuration files you must rerun the
+buildall script to rerun configure and then remake the binaries.
+
+Modifying M4 macros may not be very intuitive to anyone that has not done so
+before. Let me know if you are having any problems with them. I fully
+expect, at least initially, to have to be the sole developers of the M4
+macros and let others concentrate on C sources.
+
+[E] Report all bugs to amd-dev@majordomo.cs.columbia.edu. Avoid reporting
+to my personal email address. It is important to involve the whole list in
+bug fixes etc.
+
+Good luck.
+
+Erez Zadok,
+Maintainer, am-utils.
diff --git a/contrib/amd/TODO b/contrib/amd/TODO
new file mode 100644
index 000000000000..9d3b758cc4b7
--- /dev/null
+++ b/contrib/amd/TODO
@@ -0,0 +1,177 @@
+# -*- text -*-
+
+Make a tasks file that people can pick jobs off of.
+
+take a look at am_compat.h and fix everything there...
+
+fix all $Id bla bla bla $ on every file.
+add my own copyright 1997 etc.
+
+NOT NEEDED: convert DEBUG symbol to ENABLE_DEBUG
+NOT NEEDED: convert DEBUG_MEM symbol to ENABLE_DEBUG_MEM
+
+Use these two somewhere:
+AC_DEFINE(AM_UTILS_NAME_PACKAGE, "am-utils")
+AC_DEFINE(AM_UTILS_VERSION, "6.0a1)
+
+check all "dnl XXX: in configure.in
+
+check for: INLINE, Const->const, P_void, VOIDP
+
+rename DEBUG_MTAB to DEBUG_MNTTAB
+rename UPDATE_MTAB to MOUNT_TABLE_ON_FILE
+rename HAS_NIS_MAPS to HAVE_MAP_NIS
+rename HAS_TFS to HAVE_FS_TFS
+rename SIG_HNDL_TYP to RETSIGTYPE
+
+remove HOST_EXEC #define from everywhere. add to am_ops.c a static flag
+initialized_vops, which if not true, should set the host_ops vector field to
+0 or host_umounted. This way let the feature be turned on if -h option to
+amd is given.
+
+nuke all code that is ifdef'd SUNOS4_COMPAT
+
+rename NEED_MNTOPT_PARSER to HAVE_HASMNTOPT and cleanup the code. also take
+the supplied code in amd/mount_fs.c and include it as the library function
+libamd/hasmntopt.c
+
+TLI code needs to be fixed.
+
+a way to include a site-specific configuration file IFF it exists from
+directory local/<${target}.h>
+
+replace #include WAIT with nothing (HAVE_SYS_WAIT_H?)
+
+replace SYS5_SIGNALS with HAVE_SYS_WAIT_H (which is on only if it sys/wait.h
+exists and is using the newer "int" for type, not "union wait". The macro
+AC_HEADER_SYS_WAIT turns on HAVE_SYS_WAIT_H iff wait*() functions are
+posix.1 compliant. Make sure you don't remove SYS5_SIGNALS ifdef's that are
+not related to wait*() syscalls.
+
+add username, hostname, and date at which time amd was compiled.
+
+No more need for MOUNT_HELPER_SOURCE.
+
+any code which is included by NEED_UMOUNT_FS, should be on iff
+MOUNT_TABLE_ON_FILE is on.
+
+replace NFS_FH_FIELD with references fo ".fh" in calls to
+NFS_FH_DREF(src, dst) macro
+
+For *EVERY* M4 Macro with case/esac, check that the $host_os case entries
+are correct.
+
+I'm not using amd's regexp code. Instead, use generic system code. If the
+system has posix style regexp functions, then change amd/map.c to use
+correct new prototype.
+
+use HAVE_SVC_GETREQSET instead of RPC_4.
+
+replace all "jsp" in *.[hc] $Id$ with ezk.
+
+use MNTTAB_FILE_NAME instead of MOUNTED
+
+use MOUNT_TABLE_ON_FILE instead of READ_MTAB_FROM_FILE
+
+no more HAS_EMPTY_AUTOMOUNTS, which was used if a df(1) divide by zero bug
+was invoked. Instead, change nfs_statfs() code to always return non-zero
+values. Then nuke HAS_EMPTY_AUTOMOUNTS.
+
+REGEXP: use whatever regular expressionlibrary you have on the system,
+including regexp.h if available, and using regcomp/regexec. Amd was written
+with BSD regexps in mind, not Sys V, so if I use any of those, I have to
+watch for correct usage. Otherwise, I can include the older include/re.h
+and amd/re.c. Replace HAS_REGEXP with HAVE_REGEXEC.
+
+The regex code in amd/mapc.c has changed a lot. It probably has bugs. Must
+test and debug it!!!
+
+# string name of filesystem in /etc/mnttab file
+Use MNTTAB_TYPE_FOO instead of MTAB_TYPE_FOO.
+# string name of mount option in /etc/mnttab file
+Use MNTTAB_OPT_FOO instead of MNTOPT_FOO.
+# string (or integer?) name of filesystem type in mount(2) syscall
+Use MOUNT_TYPE_FOO instead of MNTTYPE_FOO or MOUNT_FOO.
+# hex/number of FS mount option in nfs_args passed to mount(2) syscall
+Use MNT2_NFS_OPT_FOO instead NFSMNT_FOO.
+# hex/number of generic FS mount option directly passed to mount(2) syscall
+Use MNT2_GEN_OPT_FOO instead of MS_FOO or M_FOO.
+
+
+update AMD_COMPAT to 6.0 in include/am_utils.h
+
+convert all mem* functions b* functions (memset to bzero, etc.) or vice
+verse.
+
+put my name/copyright on every src file
+
+change all struct mnttab/mntent to "mntent_t"
+
+cleanup lib/resvport.c (TLI vs. Sockets). TLI code is #defin'ed
+HAVE_T_OPEN.
+[
+setting MTAB_LOCK_FILE (mtab_svr4.c) should be an amd run-time configuration
+option.
+
+change all UMOUNT_FS macros to umount_fs() fxn calls.
+
+remove getnetbyaddr() from lib/getnetbyaddr.c and then link w/ -lsocket
+
+take care of various (hlfsd et al) scripts in Makefile.am files.
+
+rename HOSTNAMESZ to MAXHOSTNAMELEN
+
+turn on all the NEW_TOPLVL_READDIR code (for "ls" to work in an amd point)
+
+change all NEW_DEFAULTS to ENABLE_DEFAULT_SELECTOTS (which is now on by
+default)
+
+remove refereces to mnt_freq and mnt_passno in mntent_t since it's not in
+use any more.
+
+remove all the (lint?) comments /*ARGSUSED */
+
+change HAS_FOOFS to HAVE_AM_FS_FOO (for example HAS_NFSX -> HAVE_AM_FS_FOO),
+but change HAS_UNION_FS to HAVE_AM_FS_UNION.
+
+some code uses #ifdef M_NEWTYPE to tell if mount(2)'s type field is "char *"
+or int. Use MTYPE_TYPE declaration instead (not ifdef macro!)
+
+change DEBUG_MTAB to DEBUG_MNTTAB
+
+deal with the changes in values of $os, $arch, and $osver!
+
+replace SYS5_SIGNALS with REINSTATE_SIGNAL_HANDLER
+
+figure out how to auto-conf HAS_HESIOD_RELOAD (amd/info_hesiod.c). For now
+I've used the macro HAVE_HS_ZONE_TRANSFER.
+
+
+
+
+******************************************************************************
+PERL SCRIPT TO FIX OLD AMD MAPS:
+- fix '=' to ':='
+- fix sos5 -> solaris2, etc.
+
+******************************************************************************
+USE AS IS:
+
+- SVC_IN_ARG_TYPE (change from SVC_IN_ARGS_TYPE, with an 'S')
+- NFS_FH_TYPE
+- MTYPE_TYPE
+- MOUNT_TYPE_* NO!!!
+
+******************************************************************************
+NEW FEATURES:
+
+- autofs
+- cachefs
+- dbm/gdbm/db file maps
+- add amd option -O (override) to override $os, $osver, $arch, $karch, etc.
+
+******************************************************************************
+DONE:
+
+HAS_TFS is gone and all of it's code too.
+major code cleanup, removed if 0 code and if notdef
diff --git a/contrib/amd/amd/am_ops.c b/contrib/amd/amd/am_ops.c
new file mode 100644
index 000000000000..918c3f1462f8
--- /dev/null
+++ b/contrib/amd/amd/am_ops.c
@@ -0,0 +1,441 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: am_ops.c,v 5.2.2.1 1992/02/09 15:08:17 jsp beta $
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+
+/*
+ * The order of these entries matters, since lookups in this table are done
+ * on a first-match basis. The entries below are a mixture of native
+ * filesystems supported by the OS (HAVE_FS_FOO), and some meta-filesystems
+ * supported by amd (HAVE_AM_FS_FOO). The order is set here in expected
+ * match-hit such that more popular filesystems are listed first (nfs is the
+ * most popular, followed by a symlink F/S)
+ */
+static am_ops *vops[] =
+{
+#ifdef HAVE_FS_NFS
+ &nfs_ops, /* network F/S (version 2) */
+#endif /* HAVE_FS_NFS */
+#ifdef HAVE_AM_FS_LINK
+ &amfs_link_ops, /* symlink F/S */
+#endif /* HAVE_AM_FS_LINK */
+
+ /*
+ * Other amd-supported meta-filesystems.
+ */
+#ifdef HAVE_AM_FS_NFSX
+ &amfs_nfsx_ops, /* multiple-nfs F/S */
+#endif /* HAVE_AM_FS_NFSX */
+#ifdef HAVE_AM_FS_NFSL
+ &amfs_nfsl_ops, /* NFS with local link existence check */
+#endif /* HAVE_AM_FS_NFSL */
+#ifdef HAVE_AM_FS_HOST
+ &amfs_host_ops, /* multiple exported nfs F/S */
+#endif /* HAVE_AM_FS_HOST */
+#ifdef HAVE_AM_FS_LINKX
+ &amfs_linkx_ops, /* symlink F/S with link target verify */
+#endif /* HAVE_AM_FS_LINKX */
+#ifdef HAVE_AM_FS_PROGRAM
+ &amfs_program_ops, /* program F/S */
+#endif /* HAVE_AM_FS_PROGRAM */
+#ifdef HAVE_AM_FS_UNION
+ &amfs_union_ops, /* union F/S */
+#endif /* HAVE_AM_FS_UNION */
+#ifdef HAVE_AM_FS_INHERIT
+ &amfs_inherit_ops, /* inheritance F/S */
+#endif /* HAVE_AM_FS_INHERIT */
+
+ /*
+ * A few more native filesystems.
+ */
+#ifdef HAVE_FS_UFS
+ &ufs_ops, /* Unix F/S */
+#endif /* HAVE_FS_UFS */
+#ifdef HAVE_FS_XFS
+ &xfs_ops, /* Unix (irix) F/S */
+#endif /* HAVE_FS_XFS */
+#ifdef HAVE_FS_EFS
+ &efs_ops, /* Unix (irix) F/S */
+#endif /* HAVE_FS_EFS */
+#ifdef HAVE_FS_LOFS
+ &lofs_ops, /* loopback F/S */
+#endif /* HAVE_FS_LOFS */
+#ifdef HAVE_FS_CDFS
+ &cdfs_ops, /* CDROM/HSFS/ISO9960 F/S */
+#endif /* HAVE_FS_CDFS */
+#ifdef HAVE_FS_PCFS
+ &pcfs_ops, /* Floppy/MSDOS F/S */
+#endif /* HAVE_FS_PCFS */
+#ifdef HAVE_FS_CACHEFS
+ &cachefs_ops, /* caching F/S */
+#endif /* HAVE_FS_CACHEFS */
+#ifdef HAVE_FS_NULLFS
+/* FILL IN */ /* null (loopback) F/S */
+#endif /* HAVE_FS_NULLFS */
+#ifdef HAVE_FS_UNIONFS
+/* FILL IN */ /* union (bsd44) F/S */
+#endif /* HAVE_FS_UNIONFS */
+#ifdef HAVE_FS_UMAPFS
+/* FILL IN */ /* uid/gid mapping F/S */
+#endif /* HAVE_FS_UMAPFS */
+
+ /*
+ * These 5 should be last, in the order:
+ * (1) amfs_auto
+ * (2) amfs_direct
+ * (3) amfs_toplvl
+ * (4) autofs
+ * (5) amfs_error
+ */
+#ifdef HAVE_AM_FS_AUTO
+ &amfs_auto_ops, /* Automounter F/S */
+#endif /* HAVE_AM_FS_AUTO */
+#ifdef HAVE_AM_FS_DIRECT
+ &amfs_direct_ops, /* direct-mount F/S */
+#endif /* HAVE_AM_FS_DIRECT */
+#ifdef HAVE_AM_FS_TOPLVL
+ &amfs_toplvl_ops, /* top-level mount F/S */
+#endif /* HAVE_AM_FS_TOPLVL */
+#ifdef HAVE_FS_AUTOFS
+ &autofs_ops, /* autofs mount F/S */
+#endif /* HAVE_FS_AUTOFS */
+#ifdef HAVE_AM_FS_ERROR
+ &amfs_error_ops, /* error F/S */
+#endif /* HAVE_AM_FS_ERROR */
+ 0
+};
+
+
+void
+ops_showamfstypes(char *buf)
+{
+ struct am_ops **ap;
+ int l = 0;
+
+ buf[0] = '\0';
+ for (ap = vops; *ap; ap++) {
+ strcat(buf, (*ap)->fs_type);
+ if (ap[1])
+ strcat(buf, ", ");
+ l += strlen((*ap)->fs_type) + 2;
+ if (l > 60) {
+ l = 0;
+ strcat(buf, "\n ");
+ }
+ }
+}
+
+
+static void
+ops_show1(char *buf, int *lp, const char *name)
+{
+ strcat(buf, name);
+ strcat(buf, ", ");
+ *lp += strlen(name) + 2;
+ if (*lp > 60) {
+ strcat(buf, "\t\n");
+ *lp = 0;
+ }
+}
+
+
+void
+ops_showfstypes(char *buf)
+{
+ int l = 0;
+
+ buf[0] = '\0';
+
+#ifdef MNTTAB_TYPE_AUTOFS
+ ops_show1(buf, &l, MNTTAB_TYPE_AUTOFS);
+#endif /* MNTTAB_TYPE_AUTOFS */
+
+#ifdef MNTTAB_TYPE_CACHEFS
+ ops_show1(buf, &l, MNTTAB_TYPE_CACHEFS);
+#endif /* MNTTAB_TYPE_CACHEFS */
+
+#ifdef MNTTAB_TYPE_CDFS
+ ops_show1(buf, &l, MNTTAB_TYPE_CDFS);
+#endif /* MNTTAB_TYPE_CDFS */
+
+#ifdef MNTTAB_TYPE_CFS
+ ops_show1(buf, &l, MNTTAB_TYPE_CFS);
+#endif /* MNTTAB_TYPE_CFS */
+
+#ifdef MNTTAB_TYPE_LOFS
+ ops_show1(buf, &l, MNTTAB_TYPE_LOFS);
+#endif /* MNTTAB_TYPE_LOFS */
+
+#ifdef MNTTAB_TYPE_EFS
+ ops_show1(buf, &l, MNTTAB_TYPE_EFS);
+#endif /* MNTTAB_TYPE_EFS */
+
+#ifdef MNTTAB_TYPE_MFS
+ ops_show1(buf, &l, MNTTAB_TYPE_MFS);
+#endif /* MNTTAB_TYPE_MFS */
+
+#ifdef MNTTAB_TYPE_NFS
+ ops_show1(buf, &l, MNTTAB_TYPE_NFS);
+#endif /* MNTTAB_TYPE_NFS */
+
+#ifdef MNTTAB_TYPE_NFS3
+ ops_show1(buf, &l, "nfs3"); /* always hard-code as nfs3 */
+#endif /* MNTTAB_TYPE_NFS3 */
+
+#ifdef MNTTAB_TYPE_NULLFS
+ ops_show1(buf, &l, MNTTAB_TYPE_NULLFS);
+#endif /* MNTTAB_TYPE_NULLFS */
+
+#ifdef MNTTAB_TYPE_PCFS
+ ops_show1(buf, &l, MNTTAB_TYPE_PCFS);
+#endif /* MNTTAB_TYPE_PCFS */
+
+#ifdef MNTTAB_TYPE_TFS
+ ops_show1(buf, &l, MNTTAB_TYPE_TFS);
+#endif /* MNTTAB_TYPE_TFS */
+
+#ifdef MNTTAB_TYPE_TMPFS
+ ops_show1(buf, &l, MNTTAB_TYPE_TMPFS);
+#endif /* MNTTAB_TYPE_TMPFS */
+
+#ifdef MNTTAB_TYPE_UFS
+ ops_show1(buf, &l, MNTTAB_TYPE_UFS);
+#endif /* MNTTAB_TYPE_UFS */
+
+#ifdef MNTTAB_TYPE_UMAPFS
+ ops_show1(buf, &l, MNTTAB_TYPE_UMAPFS);
+#endif /* MNTTAB_TYPE_UMAPFS */
+
+#ifdef MNTTAB_TYPE_UNIONFS
+ ops_show1(buf, &l, MNTTAB_TYPE_UNIONFS);
+#endif /* MNTTAB_TYPE_UNIONFS */
+
+#ifdef MNTTAB_TYPE_XFS
+ ops_show1(buf, &l, MNTTAB_TYPE_XFS);
+#endif /* MNTTAB_TYPE_XFS */
+
+ /* terminate with a period, newline, and NULL */
+ if (buf[strlen(buf)-1] == '\n')
+ buf[strlen(buf) - 4] = '\0';
+ else
+ buf[strlen(buf) - 2] = '\0';
+ strcat(buf, ".\n");
+}
+
+
+/*
+ * return string option which is the reverse of opt.
+ * nosuid -> suid
+ * quota -> noquota
+ * ro -> rw
+ * etc.
+ * may return pointer to static buffer or subpointer within opt.
+ */
+static char *
+reverse_option(const char *opt)
+{
+ static char buf[80];
+
+ /* sanity check */
+ if (!opt)
+ return NULL;
+
+ /* check special cases */
+ /* XXX: if this gets too long, rewrite the code more flexibly */
+ if (STREQ(opt, "ro")) return "rw";
+ if (STREQ(opt, "rw")) return "ro";
+ if (STREQ(opt, "bg")) return "fg";
+ if (STREQ(opt, "fg")) return "bg";
+ if (STREQ(opt, "soft")) return "hard";
+ if (STREQ(opt, "hard")) return "soft";
+
+ /* check if string starts with 'no' and chop it */
+ if (NSTREQ(opt, "no", 2)) {
+ strcpy(buf, &opt[2]);
+ } else {
+ /* finally return a string prepended with 'no' */
+ strcpy(buf, "no");
+ strcat(buf, opt);
+ }
+ return buf;
+}
+
+
+/*
+ * start with an empty string. for each opts1 option that is not
+ * in opts2, add it to the string (make sure the reverse of it
+ * isn't in either). finally add opts2. return new string.
+ * Both opts1 and opts2 must not be null!
+ * Caller must eventually free the string being returned.
+ */
+static char *
+merge_opts(char *opts1, char *opts2)
+{
+ mntent_t mnt2; /* place holder for opts2 */
+ char *newstr; /* new string to return (malloc'ed) */
+ char *tmpstr; /* temp */
+ char *eq; /* pointer to '=' within temp */
+ char oneopt[80]; /* one option w/o value if any */
+ char *revoneopt; /* reverse of oneopt */
+ int len = strlen(opts1) + strlen(opts2) + 2; /* space for "," and NULL */
+ char *s1 = strdup(opts1); /* copy of opts1 to munge */
+
+ /* initialization */
+ mnt2.mnt_opts = opts2;
+ newstr = xmalloc(len);
+ newstr[0] = '\0';
+
+ for (tmpstr = strtok(s1, ",");
+ tmpstr;
+ tmpstr = strtok(NULL, ",")) {
+ /* copy option to temp buffer */
+ strncpy(oneopt, tmpstr, 80);
+ oneopt[79] = '\0';
+ /* if option has a value such as rsize=1024, chop the value part */
+ if ((eq = strchr(oneopt, '=')))
+ eq[1] = '\0';
+ /* find reverse option of oneopt */
+ revoneopt = reverse_option(oneopt);
+ /* if option orits reverse exist in opts2, ignore it */
+ if (hasmntopt(&mnt2, oneopt) || hasmntopt(&mnt2, revoneopt))
+ continue;
+ /* add option to returned string */
+ if (newstr && newstr[0]) {
+ strcat(newstr, ",");
+ strcat(newstr, tmpstr);
+ } else {
+ strcpy(newstr, tmpstr);
+ }
+ }
+
+ /* finally, append opts2 itself */
+ if (newstr && newstr[0]) {
+ strcat(newstr, ",");
+ strcat(newstr, opts2);
+ } else {
+ strcpy(newstr, opts2);
+ }
+
+ XFREE(s1);
+ return newstr;
+}
+
+
+am_ops *
+ops_match(am_opts *fo, char *key, char *g_key, char *path, char *keym, char *map)
+{
+ am_ops **vp;
+ am_ops *rop = 0;
+
+ /*
+ * First crack the global opts and the local opts
+ */
+ if (!eval_fs_opts(fo, key, g_key, path, keym, map)) {
+ rop = &amfs_error_ops;
+ } else if (fo->opt_type == 0) {
+ plog(XLOG_USER, "No fs type specified (key = \"%s\", map = \"%s\")", keym, map);
+ rop = &amfs_error_ops;
+ } else {
+ /*
+ * Next find the correct filesystem type
+ */
+ for (vp = vops; (rop = *vp); vp++)
+ if (STREQ(rop->fs_type, fo->opt_type))
+ break;
+ if (!rop) {
+ plog(XLOG_USER, "fs type \"%s\" not recognized", fo->opt_type);
+ rop = &amfs_error_ops;
+ }
+ }
+
+ /*
+ * Make sure we have a default mount option.
+ * Otherwise skip past any leading '-'.
+ */
+ if (fo->opt_opts == 0)
+ fo->opt_opts = strdup("rw,defaults");
+ else if (*fo->opt_opts == '-') {
+ /*
+ * We cannot simply do fo->opt_opts++ here since the opts
+ * module will try to free the pointer fo->opt_opts later.
+ * So just reallocate the thing -- stolcke 11/11/94
+ */
+ char *old = fo->opt_opts;
+ fo->opt_opts = strdup(old + 1);
+ XFREE(old);
+ }
+
+ /*
+ * If addopts option was used, then append it to the
+ * current options.
+ */
+ if (fo->opt_addopts) {
+ char *mergedstr;
+ mergedstr = merge_opts(fo->opt_opts, fo->opt_addopts);
+ plog(XLOG_USER, "merge opts \"%s\" add \"%s\" => \"%s\"",
+ fo->opt_opts, fo->opt_addopts, mergedstr);
+ XFREE(fo->opt_opts);
+ fo->opt_opts = mergedstr;
+ }
+
+ /*
+ * Check the filesystem is happy
+ */
+ if (fo->fs_mtab)
+ XFREE(fo->fs_mtab);
+
+ if ((fo->fs_mtab = (*rop->fs_match) (fo)))
+ return rop;
+
+ /*
+ * Return error file system
+ */
+ fo->fs_mtab = (*amfs_error_ops.fs_match) (fo);
+ return &amfs_error_ops;
+}
diff --git a/contrib/amd/amd/amd.8 b/contrib/amd/amd/amd.8
new file mode 100644
index 000000000000..a738809e6b40
--- /dev/null
+++ b/contrib/amd/amd/amd.8
@@ -0,0 +1,352 @@
+.\"
+.\" Copyright (c) 1997-1998 Erez Zadok
+.\" Copyright (c) 1989 Jan-Simon Pendry
+.\" Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+.\" Copyright (c) 1989 The Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Jan-Simon Pendry at Imperial College, London.
+.\"
+.\" 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.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgment:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+.\"
+.\" %W% (Berkeley) %G%
+.\"
+.\" $Id: amd.8,v 5.2.2.1 1992/02/09 15:11:11 jsp beta $
+.\"
+.TH AMD 8 "3 November 1989"
+.SH NAME
+amd \- automatically mount file systems
+.SH SYNOPSIS
+.B amd
+.B \-H
+.br
+.B amd
+[
+.BI \-F " conf_file"
+]
+.br
+.B amd
+[
+.B \-nprvHS
+] [
+.BI \-a " mount_point"
+] [
+.BI \-c " duration"
+] [
+.BI \-d " domain"
+] [
+.BI \-k " kernel-arch"
+] [
+.BI \-l " logfile"
+] [
+.BI \-o " op_sys_ver"
+] [
+.BI \-t " interval.interval"
+] [
+.BI \-w " interval"
+] [
+.BI \-x " log-option"
+] [
+.BI \-y " YP-domain"
+] [
+.BI \-C " cluster-name"
+] [
+.BI \-D " option"
+] [
+.BI \-F " conf_file"
+] [
+.BI \-O " op_sys_name"
+] [
+.BI \-T " tag"
+]
+[
+.I directory
+.I mapname
+.RI [ " \-map-options " ]
+] .\|.\|.
+.SH DESCRIPTION
+.B Amd
+is a daemon that automatically mounts filesystems
+whenever a file or directory
+within that filesystem is accessed.
+Filesystems are automatically unmounted when they
+appear to have become quiescent.
+.LP
+.B Amd
+operates by attaching itself as an
+.SM NFS
+server to each of the specified
+.IB directories .
+Lookups within the specified directories
+are handled by
+.BR amd ,
+which uses the map defined by
+.I mapname
+to determine how to resolve the lookup.
+Generally, this will be a host name, some filesystem information
+and some mount options for the given filesystem.
+.LP
+In the first form depicted above,
+.B amd
+will print a short help string. In the second form, if no options are
+specified, or the
+.B -F
+is used,
+.B amd
+will read configuration parameters from the file
+.I conf_file
+which defaults to
+.BR /etc/amd.conf .
+The last form is described below.
+.SH OPTIONS
+
+.\"*******************************************************"
+
+.TP
+.BI \-a " temporary-directory"
+Specify an alternative location for the real mount points.
+The default is
+.BR /a .
+
+.TP
+.BI \-c " duration"
+Specify a
+.IR duration ,
+in seconds, that a looked up name remains
+cached when not in use. The default is 5 minutes.
+
+.TP
+.BI \-d " domain"
+Specify the local domain name. If this option is not
+given the domain name is determined from the hostname.
+
+.TP
+.BI \-k " kernel-arch"
+Specifies the kernel architecture. This is used solely
+to set the ${karch} selector.
+
+.TP
+.BI \-l " logfile"
+Specify a logfile in which to record mount and unmount events.
+If
+.I logfile
+is the string
+.B syslog
+then the log messages will be sent to the system log daemon by
+.IR syslog (3).
+The default syslog facility used is LOG_DAEMON. If you
+wish to change it, append its name to the log file name, delimited by a
+single colon. For example, if
+.I logfile
+is the string
+.B syslog:local7
+then
+.B Amd
+will log messages via
+.IR syslog (3)
+using the LOG_LOCAL7 facility (if it exists on the system).
+
+.TP
+.B \-n
+Normalize hostnames.
+The name refereed to by ${rhost} is normalized relative to the
+host database before being used. The effect is to translate
+aliases into ``official'' names.
+
+.TP
+.BI \-o " op_sys_ver"
+Override the compiled-in version number of the operating system. Useful
+when the built in version is not desired for backward compatibility reasons.
+For example, if the build in version is ``2.5.1'', you can override it to
+``5.5.1'', and use older maps that were written with the latter in mind.
+
+.TP
+.B \-p
+Print PID.
+Outputs the process-id of
+.B amd
+to standard output where it can be saved into a file.
+
+.TP
+.B \-r
+Restart existing mounts.
+.B Amd
+will scan the mount file table to determine which filesystems
+are currently mounted. Whenever one of these would have
+been auto-mounted,
+.B amd
+.I inherits
+it.
+
+.TP
+.BI \-t " interval.interval"
+Specify the
+.IR interval ,
+in tenths of a second, between NFS/RPC/UDP retries.
+The default is 0.8 seconds.
+The second values alters the restransmit counter.
+Useful defaults are supplied if either or both
+values are missing.
+
+.TP
+.B \-v
+Version. Displays version and configuration information on standard error.
+
+.TP
+.BI \-w " interval"
+Specify an
+.IR interval ,
+in seconds, between attempts to dismount
+filesystems that have exceeded their cached times.
+The default is 2 minutes.
+
+.TP
+.BI \-x " options"
+Specify run-time logging options. The options are a comma separated
+list chosen from: fatal, error, user, warn, info, map, stats, all.
+
+.TP
+.BI \-y " domain"
+Specify an alternative NIS domain from which to fetch the NIS maps.
+The default is the system domain name. This option is ignored if NIS
+support is not available.
+
+.TP
+.BI \-C " cluster-name"
+Specify an alternative HP-UX cluster name to use.
+
+.TP
+.BI \-D " option"
+Select from a variety of debug options. Prefixing an
+option with the strings
+.B no
+reverses the effect of that option. Options are cumulative.
+The most useful option is
+.BR all .
+Since
+.I \-D
+is only used for debugging other options are not documented here:
+the current supported set of options is listed by the \-v option
+and a fuller description is available in the program source.
+
+.TP
+.BI \-F " conf_file"
+Specify an amd configuration file to use. See
+.BR amd.conf (5)
+for description of this file's format. This configuration file is used to
+specify any options in lieu of typing many of them on the command line. The
+.I amd.conf
+file includes directives for every command line option amd has, and many
+more that are only available via the configuration file facility. The
+configuration file specified by this option is processed after all other
+options had been processed, regardless of the actual location of this option
+on the command line.
+
+.TP
+.B \-H
+Print help and usage string.
+
+.TP
+.BI \-O " op_sys_name"
+Override the compiled-in name of the operating system. Useful when the
+built in name is not desired for backward compatibility reasons. For
+example, if the build in name is ``sunos5'', you can override it to
+``sos5'', and use older maps which were written with the latter in mind.
+
+.TP
+.B \-S
+Do not lock the running executable pages of amd into memory. To improve
+amd's performance, systems that support the
+.BR plock (3)
+call, could lock the amd process into memory. This way there is less chance
+the operating system will schedule, page out, and swap the amd process as
+needed. This tends improves amd's performance, at the cost of reserving the
+memory used by the amd process (making it unavailable for other processes).
+If this behavior is not desired, use the
+.B \-S
+option.
+
+.TP
+.BI \-T " tag"
+Specify a tag to use with
+.BR amd.conf (5).
+All map entries tagged with
+.I tag
+will be processed. Map entries that are not tagged are always processed.
+Map entries that are tagged with a tag other than
+.I tag
+will not be processed.
+
+.SH FILES
+.PD 0
+.TP 5
+.B /a
+directory under which filesystems are dynamically mounted
+.TP 5
+.B /etc/amd.conf
+default configuration file
+.PD
+.SH CAVEATS
+Some care may be required when creating a mount map.
+.LP
+Symbolic links on an NFS filesystem can be incredibly inefficient.
+In most implementations of NFS, their interpolations are not cached
+by the kernel and each time a symlink is encountered during a
+.I lookuppn
+translation it costs an RPC call to the NFS server.
+It would appear that a large improvement in real-time
+performance could be gained by adding a cache somewhere.
+Replacing symlinks with a suitable incarnation of the auto-mounter
+results in a large real-time speedup, but also causes a large
+number of process context switches.
+.LP
+A weird imagination is most useful to gain full advantage of all
+the features.
+.SH "SEE ALSO"
+.BR amd.conf (5),
+.BR amq (8),
+.BR domainname (1),
+.BR hostname (1),
+.BR automount (8),
+.BR mount (8),
+.BR umount (8),
+.BR mtab (5),
+.BR syslog (3).
+.LP
+.I "Amd \- The 4.4 BSD Automounter"
+.SH AUTHORS
+Jan-Simon Pendry <jsp@doc.ic.ac.uk>, Department of Computing, Imperial College, London, UK.
+.P
+Erez Zadok <ezk@cs.columbia.edu>, Department of Computer Science, Columbia
+University, New York, USA.
+.P
+Other authors and contributors to am-utils are listed in the
+.B AUTHORS
+file distributed with am-utils.
diff --git a/contrib/amd/amd/amd.c b/contrib/amd/amd/amd.c
new file mode 100644
index 000000000000..7ef2ce7ed69d
--- /dev/null
+++ b/contrib/amd/amd/amd.c
@@ -0,0 +1,535 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: amd.c,v 5.2.2.1 1992/02/09 15:08:15 jsp beta $
+ *
+ */
+
+/*
+ * Automounter
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+struct amu_global_options gopt; /* where global options are stored */
+
+char pid_fsname[16 + MAXHOSTNAMELEN]; /* "kiska.southseas.nz:(pid%d)" */
+char *progname; /* "amd" */
+char *hostdomain = "unknown.domain";
+char hostname[MAXHOSTNAMELEN] = "localhost"; /* Hostname */
+char hostd[2 * MAXHOSTNAMELEN]; /* Host+domain */
+char *endian = ARCH_ENDIAN; /* Big or Little endian */
+char *cpu = HOST_CPU; /* CPU type */
+char *PrimNetName; /* name of primary network */
+char *PrimNetNum; /* number of primary network */
+
+int foreground = 1; /* This is the top-level server */
+int immediate_abort; /* Should close-down unmounts be retried */
+int orig_umask;
+int select_intr_valid;
+
+jmp_buf select_intr;
+pid_t mypid; /* Current process id */
+serv_state amd_state;
+struct amd_stats amd_stats; /* Server statistics */
+struct in_addr myipaddr; /* (An) IP address of this host */
+time_t do_mapc_reload = 0; /* mapc_reload() call required? */
+
+#ifdef HAVE_SIGACTION
+sigset_t masked_sigs;
+#endif /* HAVE_SIGACTION */
+
+
+/*
+ * Signal handler:
+ * SIGINT - tells amd to do a full shutdown, including unmounting all
+ * filesystem.
+ * SIGTERM - tells amd to shutdown now. Just unmounts the automount nodes.
+ */
+static RETSIGTYPE
+sigterm(int sig)
+{
+#ifdef REINSTALL_SIGNAL_HANDLER
+ signal(sig, sigterm);
+#endif /* REINSTALL_SIGNAL_HANDLER */
+
+ switch (sig) {
+ case SIGINT:
+ immediate_abort = 15;
+ break;
+
+ case SIGTERM:
+ immediate_abort = -1;
+ /* fall through... */
+
+ default:
+ plog(XLOG_WARNING, "WARNING: automounter going down on signal %d", sig);
+ break;
+ }
+ if (select_intr_valid)
+ longjmp(select_intr, sig);
+}
+
+
+/*
+ * Hook for cache reload.
+ * When a SIGHUP arrives it schedules a call to mapc_reload
+ */
+static RETSIGTYPE
+sighup(int sig)
+{
+#ifdef REINSTALL_SIGNAL_HANDLER
+ signal(sig, sighup);
+#endif /* REINSTALL_SIGNAL_HANDLER */
+
+#ifdef DEBUG
+ if (sig != SIGHUP)
+ dlog("spurious call to sighup");
+#endif /* DEBUG */
+ /*
+ * Force a reload by zero'ing the timer
+ */
+ if (amd_state == Run)
+ do_mapc_reload = 0;
+}
+
+
+static RETSIGTYPE
+parent_exit(int sig)
+{
+ exit(0);
+}
+
+
+static int
+daemon_mode(void)
+{
+ int bgpid;
+
+#ifdef HAVE_SIGACTION
+ struct sigaction sa, osa;
+
+ sa.sa_handler = parent_exit;
+ sa.sa_flags = 0;
+ sigemptyset(&(sa.sa_mask));
+ sigaddset(&(sa.sa_mask), SIGQUIT);
+ sigaction(SIGQUIT, &sa, &osa);
+#else /* not HAVE_SIGACTION */
+ signal(SIGQUIT, parent_exit);
+#endif /* not HAVE_SIGACTION */
+
+ bgpid = background();
+
+ if (bgpid != 0) {
+ /*
+ * Now wait for the automount points to
+ * complete.
+ */
+ for (;;)
+ pause();
+ /* should never reache here */
+ }
+#ifdef HAVE_SIGACTION
+ sigaction(SIGQUIT, &osa, NULL);
+#else /* not HAVE_SIGACTION */
+ signal(SIGQUIT, SIG_DFL);
+#endif /* not HAVE_SIGACTION */
+
+ /*
+ * Record our pid to make it easier to kill the correct amd.
+ */
+ if (gopt.flags & CFM_PRINT_PID) {
+ if (STREQ(gopt.pid_file, "/dev/stdout")) {
+ printf("%ld\n", (long) mypid);
+ fflush(stdout);
+ /* do not fclose stdout */
+ } else {
+ FILE *f;
+ mode_t prev_umask = umask(0022); /* set secure temporary umask */
+
+ f = fopen(gopt.pid_file, "w");
+ if (f) {
+ fprintf(f, "%ld\n", (long) mypid);
+ (void) fclose(f);
+ } else {
+ fprintf(stderr, "cannot open %s (errno=%d)\n", gopt.pid_file, errno);
+ }
+ umask(prev_umask); /* restore umask */
+ }
+ }
+
+ /*
+ * Pretend we are in the foreground again
+ */
+ foreground = 1;
+
+ /*
+ * Dissociate from the controlling terminal
+ */
+ amu_release_controlling_tty();
+
+ return getppid();
+}
+
+
+/*
+ * Initialize global options structure.
+ */
+static void
+init_global_options(void)
+{
+#if defined(HAVE_SYS_UTSNAME_H) && defined(HAVE_UNAME)
+ static struct utsname un;
+#endif /* defined(HAVE_SYS_UTSNAME_H) && defined(HAVE_UNAME) */
+
+ memset(&gopt, 0, sizeof(struct amu_global_options));
+
+ /* name of current architecture */
+ gopt.arch = HOST_ARCH;
+
+ /* automounter temp dir */
+ gopt.auto_dir = "/a";
+
+ /* cluster name */
+ gopt.cluster = NULL;
+
+ /*
+ * kernel architecture: this you must get from uname() if possible.
+ */
+#if defined(HAVE_SYS_UTSNAME_H) && defined(HAVE_UNAME)
+ if (uname(&un) >= 0)
+ gopt.karch = un.machine;
+ else
+#endif /* defined(HAVE_SYS_UTSNAME_H) && defined(HAVE_UNAME) */
+ gopt.karch = HOST_ARCH;
+
+ /* amd log file */
+ gopt.logfile = NULL;
+
+ /* operating system name */
+ gopt.op_sys = HOST_OS_NAME;
+
+ /* OS version */
+ gopt.op_sys_ver = HOST_OS_VERSION;
+
+ /* pid file */
+ gopt.pid_file = "/dev/stdout";
+
+ /* local domain */
+ gopt.sub_domain = NULL;
+
+ /* NFS retransmit counter */
+ gopt.amfs_auto_retrans = -1;
+
+ /* NFS retry interval */
+ gopt.amfs_auto_timeo = -1;
+
+ /* cache duration */
+ gopt.am_timeo = AM_TTL;
+
+ /* dismount interval */
+ gopt.am_timeo_w = AM_TTL_W;
+
+ /*
+ * various CFM_* flags.
+ * by default, only the "plock" option is on (if available).
+ */
+ gopt.flags = CFM_PROCESS_LOCK;
+
+#ifdef HAVE_MAP_HESIOD
+ /* Hesiod rhs zone */
+ gopt.hesiod_base = "automount";
+#endif /* HAVE_MAP_HESIOD */
+
+#ifdef HAVE_MAP_LDAP
+ /* LDAP base */
+ gopt.ldap_base = NULL;
+
+ /* LDAP host ports */
+ gopt.ldap_hostports = NULL;
+
+ /* LDAP cache */
+ gopt.ldap_cache_seconds = 0;
+ gopt.ldap_cache_maxmem = 131072;
+#endif /* HAVE_MAP_LDAP */
+
+#ifdef HAVE_MAP_NIS
+ /* YP domain name */
+ gopt.nis_domain = NULL;
+#endif /* HAVE_MAP_NIS */
+}
+
+
+int
+main(int argc, char *argv[])
+{
+ char *domdot, *verstr;
+ int ppid = 0;
+ int error;
+#ifdef HAVE_SIGACTION
+ struct sigaction sa;
+#endif /* HAVE_SIGACTION */
+
+ /*
+ * Make sure some built-in assumptions are true before we start
+ */
+ assert(sizeof(nfscookie) >= sizeof(u_int));
+ assert(sizeof(int) >= 4);
+
+ /*
+ * Set processing status.
+ */
+ amd_state = Start;
+
+ /*
+ * Determine program name
+ */
+ if (argv[0]) {
+ progname = strrchr(argv[0], '/');
+ if (progname && progname[1])
+ progname++;
+ else
+ progname = argv[0];
+ }
+ if (!progname)
+ progname = "amd";
+
+ /*
+ * Initialise process id. This is kept
+ * cached since it is used for generating
+ * and using file handles.
+ */
+ mypid = getpid();
+
+ /*
+ * Get local machine name
+ */
+ if (gethostname(hostname, sizeof(hostname)) < 0) {
+ plog(XLOG_FATAL, "gethostname: %m");
+ going_down(1);
+ }
+
+ /*
+ * Check it makes sense
+ */
+ if (!*hostname) {
+ plog(XLOG_FATAL, "host name is not set");
+ going_down(1);
+ }
+
+ /*
+ * Initialize global options structure.
+ */
+ init_global_options();
+
+ /*
+ * Partially initialise hostd[]. This
+ * is completed in get_args().
+ */
+ if ((domdot = strchr(hostname, '.'))) {
+ /*
+ * Hostname already contains domainname.
+ * Split out hostname and domainname
+ * components
+ */
+ *domdot++ = '\0';
+ hostdomain = domdot;
+ }
+ strcpy(hostd, hostname);
+
+ /*
+ * Trap interrupts for shutdowns.
+ */
+#ifdef HAVE_SIGACTION
+ sa.sa_handler = sigterm;
+ sa.sa_flags = 0;
+ sigemptyset(&(sa.sa_mask));
+ sigaddset(&(sa.sa_mask), SIGINT);
+ sigaddset(&(sa.sa_mask), SIGTERM);
+ sigaction(SIGINT, &sa, NULL);
+ sigaction(SIGTERM, &sa, NULL);
+#else /* not HAVE_SIGACTION */
+ (void) signal(SIGINT, sigterm);
+#endif /* not HAVE_SIGACTION */
+
+ /*
+ * Trap Terminate so that we can shutdown gracefully (some chance)
+ */
+#ifdef HAVE_SIGACTION
+ sa.sa_handler = sigterm;
+ sa.sa_flags = 0;
+ sigemptyset(&(sa.sa_mask));
+ sigaddset(&(sa.sa_mask), SIGTERM);
+ sigaction(SIGTERM, &sa, NULL);
+#else /* not HAVE_SIGACTION */
+ (void) signal(SIGTERM, sigterm);
+#endif /* not HAVE_SIGACTION */
+
+ /*
+ * Hangups tell us to reload the cache
+ */
+#ifdef HAVE_SIGACTION
+ sa.sa_handler = sighup;
+ sa.sa_flags = 0;
+ sigemptyset(&(sa.sa_mask));
+ sigaddset(&(sa.sa_mask), SIGHUP);
+ sigaction(SIGHUP, &sa, NULL);
+#else /* not HAVE_SIGACTION */
+ (void) signal(SIGHUP, sighup);
+#endif /* not HAVE_SIGACTION */
+
+ /*
+ * Trap Death-of-a-child. These allow us to
+ * pick up the exit status of backgrounded mounts.
+ * See "sched.c".
+ */
+#ifdef HAVE_SIGACTION
+ sa.sa_handler = sigchld;
+ sa.sa_flags = 0;
+ sigemptyset(&(sa.sa_mask));
+ sigaddset(&(sa.sa_mask), SIGCHLD);
+ sigaction(SIGCHLD, &sa, NULL);
+
+ /*
+ * construct global "masked_sigs" used in nfs_start.c
+ */
+ sigemptyset(&masked_sigs);
+ sigaddset(&masked_sigs, SIGHUP);
+ sigaddset(&masked_sigs, SIGCHLD);
+ sigaddset(&masked_sigs, SIGTERM);
+ sigaddset(&masked_sigs, SIGINT);
+#else /* not HAVE_SIGACTION */
+ (void) signal(SIGCHLD, sigchld);
+#endif /* not HAVE_SIGACTION */
+
+ /*
+ * Fix-up any umask problems. Most systems default
+ * to 002 which is not too convenient for our purposes
+ */
+ orig_umask = umask(0);
+
+ /*
+ * Figure out primary network name
+ */
+ getwire(&PrimNetName, &PrimNetNum);
+
+ /*
+ * Determine command-line arguments
+ */
+ get_args(argc, argv);
+
+ /*
+ * Log version information.
+ */
+ verstr = strtok(get_version_string(), "\n");
+ plog(XLOG_INFO, "AM-UTILS VERSION INFORMATION:");
+ while (verstr) {
+ plog(XLOG_INFO, verstr);
+ verstr = strtok(NULL, "\n");
+ }
+
+ /*
+ * Get our own IP address so that we
+ * can mount the automounter.
+ */
+ amu_get_myaddress(&myipaddr);
+ plog(XLOG_INFO, "My ip addr is 0x%x", htonl(myipaddr.s_addr));
+
+ /* avoid hanging on other NFS servers if started elsewhere */
+ if (chdir("/") < 0)
+ plog(XLOG_INFO, "cannot chdir to /: %m");
+
+ /*
+ * Now check we are root.
+ */
+ if (geteuid() != 0) {
+ plog(XLOG_FATAL, "Must be root to mount filesystems (euid = %d)", geteuid());
+ going_down(1);
+ }
+
+ /*
+ * Lock process text and data segment in memory.
+ */
+#ifdef HAVE_PLOCK
+ if (gopt.flags & CFM_PROCESS_LOCK) {
+ if (plock(PROCLOCK) != 0) {
+ plog(XLOG_WARNING, "Couldn't lock process text and data segment in memory: %m");
+ } else {
+ plog(XLOG_INFO, "Locked process text and data segment in memory");
+ }
+ }
+#endif /* HAVE_PLOCK */
+
+#ifdef HAVE_MAP_NIS
+ /*
+ * If the domain was specified then bind it here
+ * to circumvent any default bindings that may
+ * be done in the C library.
+ */
+ if (gopt.nis_domain && yp_bind(gopt.nis_domain)) {
+ plog(XLOG_FATAL, "Can't bind to NIS domain \"%s\"", gopt.nis_domain);
+ going_down(1);
+ }
+#endif /* HAVE_MAP_NIS */
+
+#ifdef DEBUG
+ amuDebug(D_DAEMON)
+#endif /* DEBUG */
+ ppid = daemon_mode();
+
+ sprintf(pid_fsname, "%s:(pid%ld)", hostname, (long) mypid);
+
+ do_mapc_reload = clocktime() + ONE_HOUR;
+
+ /*
+ * Register automounter with system.
+ */
+ error = mount_automounter(ppid);
+ if (error && ppid)
+ kill(SIGALRM, ppid);
+ going_down(error);
+
+ abort();
+ return 1; /* should never get here */
+}
diff --git a/contrib/amd/amd/amd.h b/contrib/amd/amd/amd.h
new file mode 100644
index 000000000000..610dfe28e5e3
--- /dev/null
+++ b/contrib/amd/amd/amd.h
@@ -0,0 +1,303 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: amd.h,v 1.1 1996/01/13 23:23:39 ezk Exp ezk $
+ *
+ */
+
+#ifndef _AMD_H
+#define _AMD_H
+
+
+/*
+ * MACROS:
+ */
+
+/* options for amd.conf */
+#define CFM_BROWSABLE_DIRS 0x0001
+#define CFM_MOUNT_TYPE_AUTOFS 0x0002
+#define CFM_ENABLE_DEFAULT_SELECTORS 0x0004
+#define CFM_NORMALIZE_HOSTNAMES 0x0008
+#define CFM_PROCESS_LOCK 0x0010
+#define CFM_PRINT_PID 0x0020
+#define CFM_RESTART_EXISTING_MOUNTS 0x0040
+#define CFM_SHOW_STATFS_ENTRIES 0x0080
+#define CFM_FULLY_QUALIFIED_HOSTS 0x0100
+#define CFM_BROWSABLE_DIRS_FULL 0x0200 /* allow '/' in readdir() */
+#define CFM_UNMOUNT_ON_EXIT 0x0400 /* when amd finishing */
+
+/* some systems (SunOS 4.x) neglect to define the mount null message */
+#ifndef MOUNTPROC_NULL
+# define MOUNTPROC_NULL ((u_long)(0))
+#endif /* not MOUNTPROC_NULL */
+
+/* Hash table size */
+#define NKVHASH (1 << 2) /* Power of two */
+
+/* interval between forced retries of a mount */
+#define RETRY_INTERVAL 2
+
+#define ereturn(x) { *error_return = x; return 0; }
+
+
+/*
+ * TYPEDEFS:
+ */
+
+typedef struct cf_map cf_map_t;
+typedef struct kv kv;
+/*
+ * Cache map operations
+ */
+typedef void add_fn(mnt_map *, char *, char *);
+typedef int init_fn(mnt_map *, char *, time_t *);
+typedef int mtime_fn(mnt_map *, char *, time_t *);
+typedef int isup_fn(mnt_map *, char *);
+typedef int reload_fn(mnt_map *, char *, add_fn *);
+typedef int search_fn(mnt_map *, char *, char *, char **, time_t *);
+
+
+
+/*
+ * STRUCTURES:
+ */
+
+/* global amd options that are manipulated by conf.c */
+struct amu_global_options {
+ char *arch; /* name of current architecture */
+ char *auto_dir; /* automounter temp dir */
+ char *cluster; /* cluster name */
+ char *karch; /* kernel architecture */
+ char *logfile; /* amd log file */
+ char *op_sys; /* operating system name */
+ char *op_sys_ver; /* OS version */
+ char *pid_file; /* PID file */
+ char *sub_domain; /* local domain */
+ char *map_options; /* global map options */
+ char *map_type; /* global map type */
+ char *search_path; /* search path for maps */
+ char *mount_type; /* mount type for map */
+ u_int flags; /* various CFM_* flags */
+ int amfs_auto_retrans; /* NFS retransmit counter */
+ int amfs_auto_timeo; /* NFS retry interval */
+ int am_timeo; /* cache duration */
+ int am_timeo_w; /* dismount interval */
+ int portmap_program; /* amd RPC program number */
+#ifdef HAVE_MAP_HESIOD
+ char *hesiod_base; /* Hesiod rhs */
+#endif /* HAVE_MAP_HESIOD */
+#ifdef HAVE_MAP_LDAP
+ char *ldap_base; /* LDAP base */
+ char *ldap_hostports; /* LDAP host ports */
+ long ldap_cache_seconds; /* LDAP internal cache - keep seconds */
+ long ldap_cache_maxmem; /* LDAP internal cache - max memory (bytes) */
+#endif /* HAVE_MAP_LDAP */
+#ifdef HAVE_MAP_NIS
+ char *nis_domain; /* YP domain name */
+#endif /* HAVE_MAP_NIS */
+};
+
+/* if you add anything here, update conf.c:reset_cf_map() */
+struct cf_map {
+ char *cfm_dir; /* /home, /u, /src */
+ char *cfm_name; /* amd.home, /etc/amd.home ... */
+ char *cfm_type; /* file, hesiod, ndbm, nis ... */
+ char *cfm_opts; /* -cache:=all, etc. */
+ char *cfm_search_path; /* /etc/local:/etc/amdmaps:/misc/yp */
+ char *cfm_tag; /* optional map tag for amd -T */
+ u_int cfm_flags; /* browsable_dirs? mount_type? */
+};
+
+/*
+ * Key-value pair
+ */
+struct kv {
+ kv *next;
+ char *key;
+#ifdef HAVE_REGEXEC
+ regex_t re; /* store the regexp from regcomp() */
+#endif /* HAVE_REGEXEC */
+ char *val;
+};
+
+struct mnt_map {
+ qelem hdr;
+ int refc; /* Reference count */
+ short flags; /* Allocation flags */
+ short alloc; /* Allocation mode */
+ time_t modify; /* Modify time of map */
+ char *map_name; /* Name of this map */
+ char *wildcard; /* Wildcard value */
+ reload_fn *reload; /* Function to be used for reloads */
+ isup_fn *isup; /* Is service up or not? (1=up, 0=down) */
+ search_fn *search; /* Function to be used for searching */
+ mtime_fn *mtime; /* Modify time function */
+ kv *kvhash[NKVHASH]; /* Cached data */
+ /* options available via amd conf file */
+ char *cf_map_type; /* file, hesiod, ndbm, nis, etc. */
+ char *cf_search_path; /* /etc/local:/etc/amdmaps:/misc/yp */
+ void *map_data; /* Map data black box */
+};
+
+/*
+ * Mounting a file system may take a significant period of time. The
+ * problem is that if this is done in the main process thread then
+ * the entire automounter could be blocked, possibly hanging lots of
+ * processes on the system. Instead we use a continuation scheme to
+ * allow mounts to be attempted in a sub-process. When the sub-process
+ * exits we pick up the exit status (by convention a UN*X error number)
+ * and continue in a notifier. The notifier gets handed a data structure
+ * and can then determine whether the mount was successful or not. If
+ * not, it updates the data structure and tries again until there are no
+ * more ways to try the mount, or some other permanent error occurs.
+ * In the mean time no RPC reply is sent, even after the mount is succesful.
+ * We rely on the RPC retry mechanism to resend the lookup request which
+ * can then be handled.
+ */
+struct continuation {
+ char **ivec; /* Current mount info */
+ am_node *mp; /* Node we are trying to mount */
+ char *key; /* Map key */
+ char *info; /* Info string */
+ char **xivec; /* Saved strsplit vector */
+ char *auto_opts; /* Automount options */
+ am_opts fs_opts; /* Filesystem options */
+ char *def_opts; /* Default automount options */
+ int retry; /* Try again? */
+ int tried; /* Have we tried any yet? */
+ time_t start; /* Time we started this mount */
+ int callout; /* Callout identifier */
+};
+
+
+/*
+ * EXTERNALS:
+ */
+
+/* Amq server global functions */
+extern amq_mount_info_list *amqproc_getmntfs_1_svc(voidp argp, struct svc_req *rqstp);
+extern amq_mount_stats *amqproc_stats_1_svc(voidp argp, struct svc_req *rqstp);
+extern amq_mount_tree_list *amqproc_export_1_svc(voidp argp, struct svc_req *rqstp);
+extern amq_mount_tree_p *amqproc_mnttree_1_svc(voidp argp, struct svc_req *rqstp);
+extern amq_string *amqproc_getvers_1_svc(voidp argp, struct svc_req *rqstp);
+extern int *amqproc_getpid_1_svc(voidp argp, struct svc_req *rqstp);
+extern int *amqproc_mount_1_svc(voidp argp, struct svc_req *rqstp);
+extern int *amqproc_setopt_1_svc(voidp argp, struct svc_req *rqstp);
+extern voidp amqproc_null_1_svc(voidp argp, struct svc_req *rqstp);
+extern voidp amqproc_umnt_1_svc(voidp argp, struct svc_req *rqstp);
+
+/* other external definitions */
+extern am_nfs_fh *root_fh(char *dir);
+extern am_node * autofs_lookuppn(am_node *mp, char *fname, int *error_return, int op);
+extern am_node *find_ap(char *);
+extern am_node *find_ap2(char *, am_node *);
+extern bool_t xdr_amq_mount_info_qelem(XDR *xdrs, qelem *qhead);
+extern fserver *find_nfs_srvr(mntfs *mf);
+extern int auto_fmount(am_node *mp);
+extern int auto_fumount(am_node *mp);
+extern int mount_nfs_fh(am_nfs_handle_t *fhp, char *dir, char *fs_name, char *opts, mntfs *mf);
+extern int process_last_regular_map(void);
+extern int set_conf_kv(const char *section, const char *k, const char *v);
+extern int try_mount(voidp mvp);
+extern int yyparse (void);
+extern nfsentry *make_entry_chain(am_node *mp, const nfsentry *current_chain, int fully_browsable);
+extern void amfs_auto_cont(int rc, int term, voidp closure);
+extern void amfs_auto_mkcacheref(mntfs *mf);
+extern void amfs_auto_retry(int rc, int term, voidp closure);
+extern void assign_error_mntfs(am_node *mp);
+extern void flush_srvr_nfs_cache(void);
+extern void free_continuation(struct continuation *cp);
+extern void mf_mounted(mntfs *mf);
+extern void quick_reply(am_node *mp, int error);
+extern void root_newmap(const char *, const char *, const char *, const cf_map_t *);
+
+/* amd global variables */
+extern FILE *yyin;
+extern SVCXPRT *nfs_program_2_transp; /* For quick_reply() */
+extern char *conf_tag;
+extern int NumChild;
+extern int fwd_sock;
+extern int select_intr_valid;
+extern int usage;
+extern int use_conf_file; /* use amd configuration file */
+extern jmp_buf select_intr;
+extern qelem mfhead;
+extern struct amu_global_options gopt; /* where global options are stored */
+
+#ifdef HAVE_SIGACTION
+extern sigset_t masked_sigs;
+#endif /* HAVE_SIGACTION */
+
+#if defined(HAVE_AM_FS_LINK) || defined(HAVE_AM_FS_LINKX)
+extern char *amfs_link_match(am_opts *fo);
+extern int amfs_link_fumount(mntfs *mf);
+#endif /* defined(HAVE_AM_FS_LINK) || defined(HAVE_AM_FS_LINKX) */
+
+#ifdef HAVE_AM_FS_NFSL
+extern char *nfs_match(am_opts *fo);
+#endif /* HAVE_AM_FS_NFSL */
+
+#if defined(HAVE_FS_NFS3) && !defined(HAVE_XDR_MOUNTRES3)
+extern bool_t xdr_mountres3(XDR *xdrs, mountres3 *objp);
+#endif /* defined(HAVE_FS_NFS3) && !defined(HAVE_XDR_MOUNTRES3) */
+
+#ifdef HAVE_FS_AUTOFS
+extern SVCXPRT *autofsxprt;
+extern u_short autofs_port;
+
+extern int autofs_mount(am_node *mp);
+extern int autofs_umount(am_node *mp);
+extern int create_autofs_service(int *soAUTOFSp, u_short *autofs_portp, SVCXPRT **autofs_xprtp, void (*dispatch_fxn)(struct svc_req *rqstp, SVCXPRT *transp));
+extern int svc_create_local_service(void (*dispatch) (), u_long prognum, u_long versnum, char *nettype, char *servname);
+extern void autofs_mounted(mntfs *mf);
+extern void autofs_program_1(struct svc_req *rqstp, SVCXPRT *transp);
+#endif /* HAVE_FS_AUTOFS */
+
+/* Unix file system (irix) */
+#ifdef HAVE_FS_XFS
+extern am_ops xfs_ops; /* Un*x file system */
+#endif /* HAVE_FS_XFS */
+
+/* Unix file system (irix) */
+#ifdef HAVE_FS_EFS
+extern am_ops efs_ops; /* Un*x file system */
+#endif /* HAVE_FS_EFS */
+
+#endif /* not _AMD_H */
diff --git a/contrib/amd/amd/amfs_auto.c b/contrib/amd/amd/amfs_auto.c
new file mode 100644
index 000000000000..0530142b6938
--- /dev/null
+++ b/contrib/amd/amd/amfs_auto.c
@@ -0,0 +1,1595 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: amfs_auto.c,v 1.1 1997-1998/06/30 19:22:30 ezk Exp ezk $
+ *
+ */
+
+/*
+ * Automount file system
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+/****************************************************************************
+ *** MACROS ***
+ ****************************************************************************/
+#define IN_PROGRESS(cp) ((cp)->mp->am_mnt->mf_flags & MFF_MOUNTING)
+
+/* DEVELOPERS: turn this on for special debugging of readdir code */
+#undef DEBUG_READDIR
+
+/****************************************************************************
+ *** STRUCTURES ***
+ ****************************************************************************/
+
+
+
+/****************************************************************************
+ *** FORWARD DEFINITIONS ***
+ ****************************************************************************/
+static int amfs_auto_bgmount(struct continuation * cp, int mpe);
+static int amfs_auto_mount(am_node *mp);
+static int amfs_auto_readdir_browsable(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, int count, int fully_browsable);
+static void amfs_auto_umounted(am_node *mp);
+
+
+/****************************************************************************
+ *** OPS STRUCTURES ***
+ ****************************************************************************/
+am_ops amfs_auto_ops =
+{
+ "auto",
+ amfs_auto_match,
+ 0, /* amfs_auto_init */
+ amfs_auto_mount,
+ 0,
+ amfs_auto_umount,
+ 0,
+ amfs_auto_lookuppn,
+ amfs_auto_readdir,
+ 0, /* amfs_auto_readlink */
+ 0, /* amfs_auto_mounted */
+ amfs_auto_umounted,
+ find_amfs_auto_srvr,
+ FS_AMQINFO | FS_DIRECTORY
+};
+
+
+/****************************************************************************
+ *** FUNCTIONS ***
+ ****************************************************************************/
+/*
+ * AMFS_AUTO needs nothing in particular.
+ */
+char *
+amfs_auto_match(am_opts *fo)
+{
+ char *p = fo->opt_rfs;
+
+ if (!fo->opt_rfs) {
+ plog(XLOG_USER, "auto: no mount point named (rfs:=)");
+ return 0;
+ }
+ if (!fo->opt_fs) {
+ plog(XLOG_USER, "auto: no map named (fs:=)");
+ return 0;
+ }
+
+ /*
+ * Swap round fs:= and rfs:= options
+ * ... historical (jsp)
+ */
+ fo->opt_rfs = fo->opt_fs;
+ fo->opt_fs = p;
+
+ /*
+ * mtab entry turns out to be the name of the mount map
+ */
+ return strdup(fo->opt_rfs ? fo->opt_rfs : ".");
+}
+
+
+
+
+/*
+ * Build a new map cache for this node, or re-use
+ * an existing cache for the same map.
+ */
+void
+amfs_auto_mkcacheref(mntfs *mf)
+{
+ char *cache;
+
+ if (mf->mf_fo && mf->mf_fo->opt_cache)
+ cache = mf->mf_fo->opt_cache;
+ else
+ cache = "none";
+ mf->mf_private = (voidp) mapc_find(mf->mf_info, cache,
+ mf->mf_fo->opt_maptype);
+ mf->mf_prfree = mapc_free;
+}
+
+
+/*
+ * Mount a sub-mount
+ */
+static int
+amfs_auto_mount(am_node *mp)
+{
+ mntfs *mf = mp->am_mnt;
+
+ /*
+ * Pseudo-directories are used to provide some structure
+ * to the automounted directories instead
+ * of putting them all in the top-level automount directory.
+ *
+ * Here, just increment the parent's link count.
+ */
+ mp->am_parent->am_fattr.na_nlink++;
+
+ /*
+ * Info field of . means use parent's info field.
+ * Historical - not documented.
+ */
+ if (mf->mf_info[0] == '.' && mf->mf_info[1] == '\0')
+ mf->mf_info = strealloc(mf->mf_info, mp->am_parent->am_mnt->mf_info);
+
+ /*
+ * Compute prefix:
+ *
+ * If there is an option prefix then use that else
+ * If the parent had a prefix then use that with name
+ * of this node appended else
+ * Use the name of this node.
+ *
+ * That means if you want no prefix you must say so
+ * in the map.
+ */
+ if (mf->mf_fo->opt_pref) {
+ /* allow pref:=null to set a real null prefix */
+ if (STREQ(mf->mf_fo->opt_pref, "null")) {
+ mp->am_pref = "";
+ } else {
+ /*
+ * the prefix specified as an option
+ */
+ mp->am_pref = strdup(mf->mf_fo->opt_pref);
+ }
+ } else {
+ /*
+ * else the parent's prefix
+ * followed by the name
+ * followed by /
+ */
+ char *ppref = mp->am_parent->am_pref;
+ if (ppref == 0)
+ ppref = "";
+ mp->am_pref = str3cat((char *) 0, ppref, mp->am_name, "/");
+ }
+
+ /*
+ * Attach a map cache
+ */
+ amfs_auto_mkcacheref(mf);
+
+ return 0;
+}
+
+
+
+
+/*
+ * Unmount an automount sub-node
+ */
+int
+amfs_auto_umount(am_node *mp)
+{
+ return 0;
+}
+
+
+/*
+ * Unmount an automount node
+ */
+static void
+amfs_auto_umounted(am_node *mp)
+{
+ /*
+ * If this is a pseudo-directory then just adjust the link count
+ * in the parent, otherwise call the generic unmount routine
+ */
+ if (mp->am_parent && mp->am_parent->am_parent)
+ --mp->am_parent->am_fattr.na_nlink;
+}
+
+
+/*
+ * Discard an old continuation
+ */
+void
+free_continuation(struct continuation *cp)
+{
+ if (cp->callout)
+ untimeout(cp->callout);
+ XFREE(cp->key);
+ XFREE(cp->xivec);
+ XFREE(cp->info);
+ XFREE(cp->auto_opts);
+ XFREE(cp->def_opts);
+ free_opts(&cp->fs_opts);
+ XFREE(cp);
+}
+
+
+/*
+ * Discard the underlying mount point and replace
+ * with a reference to an error filesystem.
+ */
+void
+assign_error_mntfs(am_node *mp)
+{
+ if (mp->am_error > 0) {
+ /*
+ * Save the old error code
+ */
+ int error = mp->am_error;
+ if (error <= 0)
+ error = mp->am_mnt->mf_error;
+ /*
+ * Discard the old filesystem
+ */
+ free_mntfs(mp->am_mnt);
+ /*
+ * Allocate a new error reference
+ */
+ mp->am_mnt = new_mntfs();
+ /*
+ * Put back the error code
+ */
+ mp->am_mnt->mf_error = error;
+ mp->am_mnt->mf_flags |= MFF_ERROR;
+ /*
+ * Zero the error in the mount point
+ */
+ mp->am_error = 0;
+ }
+}
+
+
+/*
+ * The continuation function. This is called by
+ * the task notifier when a background mount attempt
+ * completes.
+ */
+void
+amfs_auto_cont(int rc, int term, voidp closure)
+{
+ struct continuation *cp = (struct continuation *) closure;
+ mntfs *mf = cp->mp->am_mnt;
+
+ /*
+ * Definitely not trying to mount at the moment
+ */
+ mf->mf_flags &= ~MFF_MOUNTING;
+
+ /*
+ * While we are mounting - try to avoid race conditions
+ */
+ new_ttl(cp->mp);
+
+ /*
+ * Wakeup anything waiting for this mount
+ */
+ wakeup((voidp) mf);
+
+ /*
+ * Check for termination signal or exit status...
+ */
+ if (rc || term) {
+ am_node *xmp;
+
+ if (term) {
+ /*
+ * Not sure what to do for an error code.
+ */
+ mf->mf_error = EIO; /* XXX ? */
+ mf->mf_flags |= MFF_ERROR;
+ plog(XLOG_ERROR, "mount for %s got signal %d", cp->mp->am_path, term);
+ } else {
+ /*
+ * Check for exit status...
+ */
+ mf->mf_error = rc;
+ mf->mf_flags |= MFF_ERROR;
+ errno = rc; /* XXX */
+ if (!STREQ(cp->mp->am_mnt->mf_ops->fs_type, "linkx"))
+ plog(XLOG_ERROR, "%s: mount (amfs_auto_cont): %m", cp->mp->am_path);
+ }
+
+ /*
+ * If we get here then that attempt didn't work, so
+ * move the info vector pointer along by one and
+ * call the background mount routine again
+ */
+ amd_stats.d_merr++;
+ cp->ivec++;
+ xmp = cp->mp;
+ (void) amfs_auto_bgmount(cp, 0);
+ assign_error_mntfs(xmp);
+ } else {
+ /*
+ * The mount worked.
+ */
+ am_mounted(cp->mp);
+ free_continuation(cp);
+ }
+
+ reschedule_timeout_mp();
+}
+
+
+/*
+ * Retry a mount
+ */
+void
+amfs_auto_retry(int rc, int term, voidp closure)
+{
+ struct continuation *cp = (struct continuation *) closure;
+ int error = 0;
+
+#ifdef DEBUG
+ dlog("Commencing retry for mount of %s", cp->mp->am_path);
+#endif /* DEBUG */
+
+ new_ttl(cp->mp);
+
+ if ((cp->start + ALLOWED_MOUNT_TIME) < clocktime()) {
+ /*
+ * The entire mount has timed out. Set the error code and skip past all
+ * the info vectors so that amfs_auto_bgmount will not have any more
+ * ways to try the mount, so causing an error.
+ */
+ plog(XLOG_INFO, "mount of \"%s\" has timed out", cp->mp->am_path);
+ error = ETIMEDOUT;
+ while (*cp->ivec)
+ cp->ivec++;
+ /* explicitly forbid further retries after timeout */
+ cp->retry = FALSE;
+ }
+ if (error || !IN_PROGRESS(cp)) {
+ (void) amfs_auto_bgmount(cp, error);
+ }
+ reschedule_timeout_mp();
+}
+
+
+/*
+ * Try to mount a file system. Can be called
+ * directly or in a sub-process by run_task.
+ */
+int
+try_mount(voidp mvp)
+{
+ int error = 0;
+ am_node *mp = (am_node *) mvp;
+ mntfs *mf = mp->am_mnt;
+
+ /*
+ * If the directory is not yet made and it needs to be made, then make it!
+ * This may be run in a background process in which case the flag setting
+ * won't be noticed later - but it is set anyway just after run_task is
+ * called. It should probably go away totally...
+ */
+ if (!(mf->mf_flags & MFF_MKMNT) && mf->mf_ops->fs_flags & FS_MKMNT) {
+ error = mkdirs(mf->mf_mount, 0555);
+ if (!error)
+ mf->mf_flags |= MFF_MKMNT;
+ }
+
+ /*
+ * Mount it!
+ */
+ error = mount_node(mp);
+
+#ifdef DEBUG
+ if (error > 0) {
+ errno = error;
+ dlog("amfs_auto call to mount_node failed: %m");
+ }
+#endif /* DEBUG */
+
+ return error;
+}
+
+
+/*
+ * Pick a file system to try mounting and
+ * do that in the background if necessary
+ *
+ For each location:
+ if it is new -defaults then
+ extract and process
+ continue;
+ fi
+ if it is a cut then
+ if a location has been tried then
+ break;
+ fi
+ continue;
+ fi
+ parse mount location
+ discard previous mount location if required
+ find matching mounted filesystem
+ if not applicable then
+ this_error = No such file or directory
+ continue
+ fi
+ if the filesystem failed to be mounted then
+ this_error = error from filesystem
+ elif the filesystem is mounting or unmounting then
+ this_error = -1
+ elif the fileserver is down then
+ this_error = -1
+ elif the filesystem is already mounted
+ this_error = 0
+ break
+ fi
+ if no error on this mount then
+ this_error = initialise mount point
+ fi
+ if no error on this mount and mount is delayed then
+ this_error = -1
+ fi
+ if this_error < 0 then
+ retry = true
+ fi
+ if no error on this mount then
+ make mount point if required
+ fi
+ if no error on this mount then
+ if mount in background then
+ run mount in background
+ return -1
+ else
+ this_error = mount in foreground
+ fi
+ fi
+ if an error occured on this mount then
+ update stats
+ save error in mount point
+ fi
+ endfor
+ */
+static int
+amfs_auto_bgmount(struct continuation * cp, int mpe)
+{
+ mntfs *mf = cp->mp->am_mnt; /* Current mntfs */
+ mntfs *mf_retry = 0; /* First mntfs which needed retrying */
+ int this_error = -1; /* Per-mount error */
+ int hard_error = -1;
+ int mp_error = mpe;
+
+ /*
+ * Try to mount each location.
+ * At the end:
+ * hard_error == 0 indicates something was mounted.
+ * hard_error > 0 indicates everything failed with a hard error
+ * hard_error < 0 indicates nothing could be mounted now
+ */
+ for (; this_error && *cp->ivec; cp->ivec++) {
+ am_ops *p;
+ am_node *mp = cp->mp;
+ char *link_dir;
+ int dont_retry;
+
+ if (hard_error < 0)
+ hard_error = this_error;
+
+ this_error = -1;
+
+ if (**cp->ivec == '-') {
+ /*
+ * Pick up new defaults
+ */
+ if (cp->auto_opts && *cp->auto_opts)
+ cp->def_opts = str3cat(cp->def_opts, cp->auto_opts, ";", *cp->ivec + 1);
+ else
+ cp->def_opts = strealloc(cp->def_opts, *cp->ivec + 1);
+#ifdef DEBUG
+ dlog("Setting def_opts to \"%s\"", cp->def_opts);
+#endif /* DEBUG */
+ continue;
+ }
+ /*
+ * If a mount has been attempted, and we find
+ * a cut then don't try any more locations.
+ */
+ if (STREQ(*cp->ivec, "/") || STREQ(*cp->ivec, "||")) {
+ if (cp->tried) {
+#ifdef DEBUG
+ dlog("Cut: not trying any more locations for %s",
+ mp->am_path);
+#endif /* DEBUG */
+ break;
+ }
+ continue;
+ }
+
+ /* match the operators */
+ p = ops_match(&cp->fs_opts, *cp->ivec, cp->def_opts, mp->am_path, cp->key, mp->am_parent->am_mnt->mf_info);
+
+ /*
+ * Find a mounted filesystem for this node.
+ */
+ mp->am_mnt = mf = realloc_mntfs(mf, p, &cp->fs_opts,
+ cp->fs_opts.opt_fs,
+ cp->fs_opts.fs_mtab,
+ cp->auto_opts,
+ cp->fs_opts.opt_opts,
+ cp->fs_opts.opt_remopts);
+
+ p = mf->mf_ops;
+#ifdef DEBUG
+ dlog("Got a hit with %s", p->fs_type);
+#endif /* DEBUG */
+
+ /*
+ * Note whether this is a real mount attempt
+ */
+ if (p == &amfs_error_ops) {
+ plog(XLOG_MAP, "Map entry %s for %s failed to match", *cp->ivec, mp->am_path);
+ if (this_error <= 0)
+ this_error = ENOENT;
+ continue;
+ } else {
+ if (cp->fs_opts.fs_mtab) {
+ plog(XLOG_MAP, "Trying mount of %s on %s fstype %s",
+ cp->fs_opts.fs_mtab, mp->am_path, p->fs_type);
+ }
+ cp->tried = TRUE;
+ }
+
+ this_error = 0;
+ dont_retry = FALSE;
+
+ if (mp->am_link) {
+ XFREE(mp->am_link);
+ mp->am_link = 0;
+ }
+ link_dir = mf->mf_fo->opt_sublink;
+
+ if (link_dir && *link_dir) {
+ if (*link_dir == '/') {
+ mp->am_link = strdup(link_dir);
+ } else {
+ /*
+ * try getting fs option from continuation, not mountpoint!
+ * Don't try logging the string from mf, since it may be bad!
+ */
+ if (cp->fs_opts.opt_fs != mf->mf_fo->opt_fs)
+ plog(XLOG_ERROR, "use %s instead of 0x%x",
+ cp->fs_opts.opt_fs, mf->mf_fo->opt_fs);
+
+ mp->am_link = str3cat((char *) 0,
+ cp->fs_opts.opt_fs, "/", link_dir);
+
+ normalize_slash(mp->am_link);
+ }
+ }
+
+ if (mf->mf_error > 0) {
+ this_error = mf->mf_error;
+ } else if (mf->mf_flags & (MFF_MOUNTING | MFF_UNMOUNTING)) {
+ /*
+ * Still mounting - retry later
+ */
+#ifdef DEBUG
+ dlog("Duplicate pending mount fstype %s", p->fs_type);
+#endif /* DEBUG */
+ this_error = -1;
+ } else if (FSRV_ISDOWN(mf->mf_server)) {
+ /*
+ * Would just mount from the same place
+ * as a hung mount - so give up
+ */
+#ifdef DEBUG
+ dlog("%s is already hung - giving up", mf->mf_mount);
+#endif /* DEBUG */
+ mp_error = EWOULDBLOCK;
+ dont_retry = TRUE;
+ this_error = -1;
+ } else if (mf->mf_flags & MFF_MOUNTED) {
+#ifdef DEBUG
+ dlog("duplicate mount of \"%s\" ...", mf->mf_info);
+#endif /* DEBUG */
+
+ /*
+ * Just call mounted()
+ */
+ am_mounted(mp);
+
+ this_error = 0;
+ break;
+ }
+
+ /*
+ * Will usually need to play around with the mount nodes
+ * file attribute structure. This must be done here.
+ * Try and get things initialised, even if the fileserver
+ * is not known to be up. In the common case this will
+ * progress things faster.
+ */
+ if (!this_error) {
+ /*
+ * Fill in attribute fields.
+ */
+ if (mf->mf_ops->fs_flags & FS_DIRECTORY)
+ mk_fattr(mp, NFDIR);
+ else
+ mk_fattr(mp, NFLNK);
+
+ mp->am_fattr.na_fileid = mp->am_gen;
+
+ if (p->fs_init)
+ this_error = (*p->fs_init) (mf);
+ }
+
+ /*
+ * Make sure the fileserver is UP before doing any more work
+ */
+ if (!FSRV_ISUP(mf->mf_server)) {
+#ifdef DEBUG
+ dlog("waiting for server %s to become available", mf->mf_server->fs_host);
+#endif /* DEBUG */
+ this_error = -1;
+ }
+
+ if (!this_error && mf->mf_fo->opt_delay) {
+ /*
+ * If there is a delay timer on the mount
+ * then don't try to mount if the timer
+ * has not expired.
+ */
+ int i = atoi(mf->mf_fo->opt_delay);
+ if (i > 0 && clocktime() < (cp->start + i)) {
+#ifdef DEBUG
+ dlog("Mount of %s delayed by %ds", mf->mf_mount, i - clocktime() + cp->start);
+#endif /* DEBUG */
+ this_error = -1;
+ }
+ }
+
+ if (this_error < 0 && !dont_retry) {
+ if (!mf_retry)
+ mf_retry = dup_mntfs(mf);
+ cp->retry = TRUE;
+ }
+
+ if (!this_error) {
+ if (p->fs_flags & FS_MBACKGROUND) {
+ mf->mf_flags |= MFF_MOUNTING; /* XXX */
+#ifdef DEBUG
+ dlog("backgrounding mount of \"%s\"", mf->mf_mount);
+#endif /* DEBUG */
+ if (cp->callout) {
+ untimeout(cp->callout);
+ cp->callout = 0;
+ }
+ run_task(try_mount, (voidp) mp, amfs_auto_cont, (voidp) cp);
+ mf->mf_flags |= MFF_MKMNT; /* XXX */
+ if (mf_retry)
+ free_mntfs(mf_retry);
+ return -1;
+ } else {
+#ifdef DEBUG
+ dlog("foreground mount of \"%s\" ...", mf->mf_info);
+#endif /* DEBUG */
+ this_error = try_mount((voidp) mp);
+ if (this_error < 0) {
+ if (!mf_retry)
+ mf_retry = dup_mntfs(mf);
+ cp->retry = TRUE;
+ }
+ }
+ }
+
+ if (this_error >= 0) {
+ if (this_error > 0) {
+ amd_stats.d_merr++;
+ if (mf != mf_retry) {
+ mf->mf_error = this_error;
+ mf->mf_flags |= MFF_ERROR;
+ }
+ }
+
+ /*
+ * Wakeup anything waiting for this mount
+ */
+ wakeup((voidp) mf);
+ }
+ }
+
+ if (this_error && cp->retry) {
+ free_mntfs(mf);
+ mf = cp->mp->am_mnt = mf_retry;
+ /*
+ * Not retrying again (so far)
+ */
+ cp->retry = FALSE;
+ cp->tried = FALSE;
+ /*
+ * Start at the beginning.
+ * Rewind the location vector and
+ * reset the default options.
+ */
+ cp->ivec = cp->xivec;
+ cp->def_opts = strealloc(cp->def_opts, cp->auto_opts);
+ /*
+ * Arrange that amfs_auto_bgmount is called
+ * after anything else happens.
+ */
+#ifdef DEBUG
+ dlog("Arranging to retry mount of %s", cp->mp->am_path);
+#endif /* DEBUG */
+ sched_task(amfs_auto_retry, (voidp) cp, (voidp) mf);
+ if (cp->callout)
+ untimeout(cp->callout);
+ cp->callout = timeout(RETRY_INTERVAL, wakeup, (voidp) mf);
+
+ cp->mp->am_ttl = clocktime() + RETRY_INTERVAL;
+
+ /*
+ * Not done yet - so don't return anything
+ */
+ return -1;
+ }
+
+ if (hard_error < 0 || this_error == 0)
+ hard_error = this_error;
+
+ /*
+ * Discard handle on duff filesystem.
+ * This should never happen since it
+ * should be caught by the case above.
+ */
+ if (mf_retry) {
+ if (hard_error)
+ plog(XLOG_ERROR, "discarding a retry mntfs for %s", mf_retry->mf_mount);
+ free_mntfs(mf_retry);
+ }
+
+ /*
+ * If we get here, then either the mount succeeded or
+ * there is no more mount information available.
+ */
+ if (hard_error < 0 && mp_error)
+ hard_error = cp->mp->am_error = mp_error;
+ if (hard_error > 0) {
+ /*
+ * Set a small(ish) timeout on an error node if
+ * the error was not a time out.
+ */
+ switch (hard_error) {
+ case ETIMEDOUT:
+ case EWOULDBLOCK:
+ cp->mp->am_timeo = 17;
+ break;
+ default:
+ cp->mp->am_timeo = 5;
+ break;
+ }
+ new_ttl(cp->mp);
+ }
+
+ /*
+ * Make sure that the error value in the mntfs has a
+ * reasonable value.
+ */
+ if (mf->mf_error < 0) {
+ mf->mf_error = hard_error;
+ if (hard_error)
+ mf->mf_flags |= MFF_ERROR;
+ }
+
+ /*
+ * In any case we don't need the continuation any more
+ */
+ free_continuation(cp);
+
+ return hard_error;
+}
+
+
+/*
+ * Automount interface to RPC lookup routine
+ * Find the corresponding entry and return
+ * the file handle for it.
+ */
+am_node *
+amfs_auto_lookuppn(am_node *mp, char *fname, int *error_return, int op)
+{
+ am_node *ap, *new_mp, *ap_hung;
+ char *info; /* Mount info - where to get the file system */
+ char **ivec, **xivec; /* Split version of info */
+ char *auto_opts; /* Automount options */
+ int error = 0; /* Error so far */
+ char path_name[MAXPATHLEN]; /* General path name buffer */
+ char *pfname; /* Path for database lookup */
+ struct continuation *cp; /* Continuation structure if need to mount */
+ int in_progress = 0; /* # of (un)mount in progress */
+ char *dflts;
+ mntfs *mf;
+
+#ifdef DEBUG
+ dlog("in amfs_auto_lookuppn");
+#endif /* DEBUG */
+
+ /*
+ * If the server is shutting down
+ * then don't return information
+ * about the mount point.
+ */
+ if (amd_state == Finishing) {
+#ifdef DEBUG
+ if ((mf = mp->am_mnt) == 0 || mf->mf_ops == &amfs_direct_ops) {
+ dlog("%s mount ignored - going down", fname);
+ } else {
+ dlog("%s/%s mount ignored - going down", mp->am_path, fname);
+ }
+#endif /* DEBUG */
+ ereturn(ENOENT);
+ }
+
+ /*
+ * Handle special case of "." and ".."
+ */
+ if (fname[0] == '.') {
+ if (fname[1] == '\0')
+ return mp; /* "." is the current node */
+ if (fname[1] == '.' && fname[2] == '\0') {
+ if (mp->am_parent) {
+#ifdef DEBUG
+ dlog(".. in %s gives %s", mp->am_path, mp->am_parent->am_path);
+#endif /* DEBUG */
+ return mp->am_parent; /* ".." is the parent node */
+ }
+ ereturn(ESTALE);
+ }
+ }
+
+ /*
+ * Check for valid key name.
+ * If it is invalid then pretend it doesn't exist.
+ */
+ if (!valid_key(fname)) {
+ plog(XLOG_WARNING, "Key \"%s\" contains a disallowed character", fname);
+ ereturn(ENOENT);
+ }
+
+ /*
+ * Expand key name.
+ * fname is now a private copy.
+ */
+ fname = expand_key(fname);
+
+ for (ap_hung = 0, ap = mp->am_child; ap; ap = ap->am_osib) {
+ /*
+ * Otherwise search children of this node
+ */
+ if (FSTREQ(ap->am_name, fname)) {
+ mf = ap->am_mnt;
+ if (ap->am_error) {
+ error = ap->am_error;
+ continue;
+ }
+ /*
+ * If the error code is undefined then it must be
+ * in progress.
+ */
+ if (mf->mf_error < 0)
+ goto in_progrss;
+
+ /*
+ * Check for a hung node
+ */
+ if (FSRV_ISDOWN(mf->mf_server)) {
+#ifdef DEBUG
+ dlog("server hung");
+#endif /* DEBUG */
+ error = ap->am_error;
+ ap_hung = ap;
+ continue;
+ }
+ /*
+ * If there was a previous error with this node
+ * then return that error code.
+ */
+ if (mf->mf_flags & MFF_ERROR) {
+ error = mf->mf_error;
+ continue;
+ }
+ if (!(mf->mf_flags & MFF_MOUNTED) || (mf->mf_flags & MFF_UNMOUNTING)) {
+ in_progrss:
+ /*
+ * If the fs is not mounted or it is unmounting then there
+ * is a background (un)mount in progress. In this case
+ * we just drop the RPC request (return nil) and
+ * wait for a retry, by which time the (un)mount may
+ * have completed.
+ */
+#ifdef DEBUG
+ dlog("ignoring mount of %s in %s -- flags (%x) in progress",
+ fname, mf->mf_mount, mf->mf_flags);
+#endif /* DEBUG */
+ in_progress++;
+ continue;
+ }
+
+ /*
+ * Otherwise we have a hit: return the current mount point.
+ */
+#ifdef DEBUG
+ dlog("matched %s in %s", fname, ap->am_path);
+#endif /* DEBUG */
+ XFREE(fname);
+ return ap;
+ }
+ }
+
+ if (in_progress) {
+#ifdef DEBUG
+ dlog("Waiting while %d mount(s) in progress", in_progress);
+#endif /* DEBUG */
+ XFREE(fname);
+ ereturn(-1);
+ }
+
+ /*
+ * If an error occured then return it.
+ */
+ if (error) {
+#ifdef DEBUG
+ errno = error; /* XXX */
+ dlog("Returning error: %m", error);
+#endif /* DEBUG */
+ XFREE(fname);
+ ereturn(error);
+ }
+
+ /*
+ * If doing a delete then don't create again!
+ */
+ switch (op) {
+ case VLOOK_DELETE:
+ ereturn(ENOENT);
+
+ case VLOOK_CREATE:
+ break;
+
+ default:
+ plog(XLOG_FATAL, "Unknown op to amfs_auto_lookuppn: 0x%x", op);
+ ereturn(EINVAL);
+ }
+
+ /*
+ * If the server is going down then just return,
+ * don't try to mount any more file systems
+ */
+ if ((int) amd_state >= (int) Finishing) {
+#ifdef DEBUG
+ dlog("not found - server going down anyway");
+#endif /* DEBUG */
+ XFREE(fname);
+ ereturn(ENOENT);
+ }
+
+ /*
+ * If we get there then this is a reference to an,
+ * as yet, unknown name so we need to search the mount
+ * map for it.
+ */
+ if (mp->am_pref) {
+ sprintf(path_name, "%s%s", mp->am_pref, fname);
+ pfname = path_name;
+ } else {
+ pfname = fname;
+ }
+
+ mf = mp->am_mnt;
+
+#ifdef DEBUG
+ dlog("will search map info in %s to find %s", mf->mf_info, pfname);
+#endif /* DEBUG */
+ /*
+ * Consult the oracle for some mount information.
+ * info is malloc'ed and belongs to this routine.
+ * It ends up being free'd in free_continuation().
+ *
+ * Note that this may return -1 indicating that information
+ * is not yet available.
+ */
+ error = mapc_search((mnt_map *) mf->mf_private, pfname, &info);
+ if (error) {
+ if (error > 0)
+ plog(XLOG_MAP, "No map entry for %s", pfname);
+ else
+ plog(XLOG_MAP, "Waiting on map entry for %s", pfname);
+ XFREE(fname);
+ ereturn(error);
+ }
+#ifdef DEBUG
+ dlog("mount info is %s", info);
+#endif /* DEBUG */
+
+ /*
+ * Split info into an argument vector.
+ * The vector is malloc'ed and belongs to
+ * this routine. It is free'd in free_continuation()
+ */
+ xivec = ivec = strsplit(info, ' ', '\"');
+
+ /*
+ * Default error code...
+ */
+ if (ap_hung)
+ error = EWOULDBLOCK;
+ else
+ error = ENOENT;
+
+ /*
+ * Allocate a new map
+ */
+ new_mp = exported_ap_alloc();
+ if (new_mp == 0) {
+ XFREE(xivec);
+ XFREE(info);
+ XFREE(fname);
+ ereturn(ENOSPC);
+ }
+ if (mf->mf_auto)
+ auto_opts = mf->mf_auto;
+ else
+ auto_opts = "";
+
+ auto_opts = strdup(auto_opts);
+
+#ifdef DEBUG
+ dlog("searching for /defaults entry");
+#endif /* DEBUG */
+ if (mapc_search((mnt_map *) mf->mf_private, "/defaults", &dflts) == 0) {
+ char *dfl;
+ char **rvec;
+#ifdef DEBUG
+ dlog("/defaults gave %s", dflts);
+#endif /* DEBUG */
+ if (*dflts == '-')
+ dfl = dflts + 1;
+ else
+ dfl = dflts;
+
+ /*
+ * Chop the defaults up
+ */
+ rvec = strsplit(dfl, ' ', '\"');
+
+ if (gopt.flags & CFM_ENABLE_DEFAULT_SELECTORS) {
+ /*
+ * Pick whichever first entry matched the list of selectors.
+ * Strip the selectors from the string, and assign to dfl the
+ * rest of the string.
+ */
+ if (rvec) {
+ am_opts ap;
+ am_ops *pt;
+ char **sp = rvec;
+ while (*sp) { /* loop until you find something, if any */
+ memset((char *) &ap, 0, sizeof(am_opts));
+ pt = ops_match(&ap, *sp, "", mp->am_path, "/defaults",
+ mp->am_parent->am_mnt->mf_info);
+ if (pt == &amfs_error_ops) {
+ plog(XLOG_MAP, "failed to match defaults for \"%s\"", *sp);
+ } else {
+ dfl = strip_selectors(*sp, "/defaults");
+ plog(XLOG_MAP, "matched default selectors \"%s\"", dfl);
+ break;
+ }
+ ++sp;
+ }
+ }
+ } else { /* not enable_default_selectors */
+ /*
+ * Extract first value
+ */
+ dfl = rvec[0];
+ }
+
+ /*
+ * If there were any values at all...
+ */
+ if (dfl) {
+ /*
+ * Log error if there were other values
+ */
+ if (!(gopt.flags & CFM_ENABLE_DEFAULT_SELECTORS) && rvec[1]) {
+# ifdef DEBUG
+ dlog("/defaults chopped into %s", dfl);
+# endif /* DEBUG */
+ plog(XLOG_USER, "More than a single value for /defaults in %s", mf->mf_info);
+ }
+
+ /*
+ * Prepend to existing defaults if they exist,
+ * otherwise just use these defaults.
+ */
+ if (*auto_opts && *dfl) {
+ char *nopts = (char *) xmalloc(strlen(auto_opts) + strlen(dfl) + 2);
+ sprintf(nopts, "%s;%s", dfl, auto_opts);
+ XFREE(auto_opts);
+ auto_opts = nopts;
+ } else if (*dfl) {
+ auto_opts = strealloc(auto_opts, dfl);
+ }
+ }
+ XFREE(dflts);
+ /*
+ * Don't need info vector any more
+ */
+ XFREE(rvec);
+ }
+
+ /*
+ * Fill it in
+ */
+ init_map(new_mp, fname);
+
+ /*
+ * Put it in the table
+ */
+ insert_am(new_mp, mp);
+
+ /*
+ * Fill in some other fields,
+ * path and mount point.
+ *
+ * bugfix: do not prepend old am_path if direct map
+ * <wls@astro.umd.edu> William Sebok
+ */
+ new_mp->am_path = str3cat(new_mp->am_path,
+ mf->mf_ops == &amfs_direct_ops ? "" : mp->am_path,
+ *fname == '/' ? "" : "/", fname);
+
+#ifdef DEBUG
+ dlog("setting path to %s", new_mp->am_path);
+#endif /* DEBUG */
+
+ /*
+ * Take private copy of pfname
+ */
+ pfname = strdup(pfname);
+
+ /*
+ * Construct a continuation
+ */
+ cp = ALLOC(struct continuation);
+ cp->callout = 0;
+ cp->mp = new_mp;
+ cp->xivec = xivec;
+ cp->ivec = ivec;
+ cp->info = info;
+ cp->key = pfname;
+ cp->auto_opts = auto_opts;
+ cp->retry = FALSE;
+ cp->tried = FALSE;
+ cp->start = clocktime();
+ cp->def_opts = strdup(auto_opts);
+ memset((voidp) &cp->fs_opts, 0, sizeof(cp->fs_opts));
+
+ /*
+ * Try and mount the file system. If this succeeds immediately (possible
+ * for a ufs file system) then return the attributes, otherwise just
+ * return an error.
+ */
+ error = amfs_auto_bgmount(cp, error);
+ reschedule_timeout_mp();
+ if (!error) {
+ XFREE(fname);
+ return new_mp;
+ }
+
+ /*
+ * Code for quick reply. If nfs_program_2_transp is set, then
+ * its the transp that's been passed down from nfs_program_2().
+ * If new_mp->am_transp is not already set, set it by copying in
+ * nfs_program_2_transp. Once am_transp is set, quick_reply() can
+ * use it to send a reply to the client that requested this mount.
+ */
+ if (nfs_program_2_transp && !new_mp->am_transp) {
+ new_mp->am_transp = (SVCXPRT *) xmalloc(sizeof(SVCXPRT));
+ *(new_mp->am_transp) = *nfs_program_2_transp;
+ }
+ if (error && (new_mp->am_mnt->mf_ops == &amfs_error_ops))
+ new_mp->am_error = error;
+
+ assign_error_mntfs(new_mp);
+
+ XFREE(fname);
+
+ ereturn(error);
+}
+
+
+/*
+ * Locate next node in sibling list which is mounted
+ * and is not an error node.
+ */
+am_node *
+next_nonerror_node(am_node *xp)
+{
+ mntfs *mf;
+
+ /*
+ * Bug report (7/12/89) from Rein Tollevik <rein@ifi.uio.no>
+ * Fixes a race condition when mounting direct automounts.
+ * Also fixes a problem when doing a readdir on a directory
+ * containing hung automounts.
+ */
+ while (xp &&
+ (!(mf = xp->am_mnt) || /* No mounted filesystem */
+ mf->mf_error != 0 || /* There was a mntfs error */
+ xp->am_error != 0 || /* There was a mount error */
+ !(mf->mf_flags & MFF_MOUNTED) || /* The fs is not mounted */
+ (mf->mf_server->fs_flags & FSF_DOWN)) /* The fs may be down */
+ )
+ xp = xp->am_osib;
+
+ return xp;
+}
+
+
+/*
+ * This readdir function which call a special version of it that allows
+ * browsing if browsable_dirs=yes was set on the map.
+ */
+int
+amfs_auto_readdir(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, int count)
+{
+ u_int gen = *(u_int *) cookie;
+ am_node *xp;
+ mntent_t mnt;
+
+ dp->dl_eof = FALSE; /* assume readdir not done */
+
+ /* check if map is browsable */
+ if (mp->am_mnt && mp->am_mnt->mf_mopts) {
+ mnt.mnt_opts = mp->am_mnt->mf_mopts;
+ if (hasmntopt(&mnt, "fullybrowsable"))
+ return amfs_auto_readdir_browsable(mp, cookie, dp, ep, count, TRUE);
+ if (hasmntopt(&mnt, "browsable"))
+ return amfs_auto_readdir_browsable(mp, cookie, dp, ep, count, FALSE);
+ }
+
+ if (gen == 0) {
+ /*
+ * In the default instance (which is used to start a search) we return
+ * "." and "..".
+ *
+ * This assumes that the count is big enough to allow both "." and ".."
+ * to be returned in a single packet. If it isn't (which would be
+ * fairly unbelievable) then tough.
+ */
+#ifdef DEBUG
+ dlog("default search");
+#endif /* DEBUG */
+ /*
+ * Check for enough room. This is extremely approximate but is more
+ * than enough space. Really need 2 times:
+ * 4byte fileid
+ * 4byte cookie
+ * 4byte name length
+ * 4byte name
+ * plus the dirlist structure */
+ if (count < (2 * (2 * (sizeof(*ep) + sizeof("..") + 4) + sizeof(*dp))))
+ return EINVAL;
+
+ xp = next_nonerror_node(mp->am_child);
+ dp->dl_entries = ep;
+
+ /* construct "." */
+ ep[0].ne_fileid = mp->am_gen;
+ ep[0].ne_name = ".";
+ ep[0].ne_nextentry = &ep[1];
+ *(u_int *) ep[0].ne_cookie = 0;
+
+ /* construct ".." */
+ if (mp->am_parent)
+ ep[1].ne_fileid = mp->am_parent->am_gen;
+ else
+ ep[1].ne_fileid = mp->am_gen;
+ ep[1].ne_name = "..";
+ ep[1].ne_nextentry = 0;
+ *(u_int *) ep[1].ne_cookie =
+ xp ? xp->am_gen : ~(u_int) 0;
+
+ if (!xp)
+ dp->dl_eof = TRUE; /* by default assume readdir done */
+
+ return 0;
+ }
+#ifdef DEBUG
+ dlog("real child");
+#endif /* DEBUG */
+
+ if (gen == ~(u_int) 0) {
+#ifdef DEBUG
+ dlog("End of readdir in %s", mp->am_path);
+#endif /* DEBUG */
+ dp->dl_eof = TRUE;
+ dp->dl_entries = 0;
+ return 0;
+ }
+
+ /* non-browsable directories code */
+ xp = mp->am_child;
+ while (xp && xp->am_gen != gen)
+ xp = xp->am_osib;
+
+ if (xp) {
+ int nbytes = count / 2; /* conservative */
+ int todo = MAX_READDIR_ENTRIES;
+ dp->dl_entries = ep;
+ do {
+ am_node *xp_next = next_nonerror_node(xp->am_osib);
+
+ if (xp_next) {
+ *(u_int *) ep->ne_cookie = xp_next->am_gen;
+ } else {
+ *(u_int *) ep->ne_cookie = ~(u_int) 0;
+ dp->dl_eof = TRUE;
+ }
+
+ ep->ne_fileid = xp->am_gen;
+ ep->ne_name = xp->am_name;
+ nbytes -= sizeof(*ep) + 1;
+ if (xp->am_name)
+ nbytes -= strlen(xp->am_name);
+
+ xp = xp_next;
+
+ if (nbytes > 0 && !dp->dl_eof && todo > 1) {
+ ep->ne_nextentry = ep + 1;
+ ep++;
+ --todo;
+ } else {
+ todo = 0;
+ }
+ } while (todo > 0);
+
+ ep->ne_nextentry = 0;
+
+ return 0;
+ }
+ return ESTALE;
+}
+
+
+/* This one is called only if map is browsable */
+static int
+amfs_auto_readdir_browsable(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, int count, int fully_browsable)
+{
+ u_int gen = *(u_int *) cookie;
+ int chain_length, i;
+ static nfsentry *te, *te_next;
+#ifdef DEBUG_READDIR
+ nfsentry *ne;
+ static int j;
+#endif /* DEBUG_READDIR */
+
+ dp->dl_eof = FALSE; /* assume readdir not done */
+
+#ifdef DEBUG_READDIR
+ plog(XLOG_INFO, "amfs_auto_readdir_browsable gen=%u, count=%d",
+ gen, count);
+#endif /* DEBUG_READDIR */
+
+ if (gen == 0) {
+ /*
+ * In the default instance (which is used to start a search) we return
+ * "." and "..".
+ *
+ * This assumes that the count is big enough to allow both "." and ".."
+ * to be returned in a single packet. If it isn't (which would be
+ * fairly unbelievable) then tough.
+ */
+#ifdef DEBUG
+ dlog("default search");
+#endif /* DEBUG */
+ /*
+ * Check for enough room. This is extremely approximate but is more
+ * than enough space. Really need 2 times:
+ * 4byte fileid
+ * 4byte cookie
+ * 4byte name length
+ * 4byte name
+ * plus the dirlist structure */
+ if (count < (2 * (2 * (sizeof(*ep) + sizeof("..") + 4) + sizeof(*dp))))
+ return EINVAL;
+
+ /*
+ * compute # of entries to send in this chain.
+ * heuristics: 128 bytes per entry.
+ * This is too much probably, but it seems to work better because
+ * of the re-entrant nature of nfs_readdir, and esp. on systems
+ * like OpenBSD 2.2.
+ */
+ chain_length = count / 128;
+
+ /* reset static state counters */
+ te = te_next = NULL;
+
+ dp->dl_entries = ep;
+
+ /* construct "." */
+ ep[0].ne_fileid = mp->am_gen;
+ ep[0].ne_name = ".";
+ ep[0].ne_nextentry = &ep[1];
+ *(u_int *) ep[0].ne_cookie = 0;
+
+ /* construct ".." */
+ if (mp->am_parent)
+ ep[1].ne_fileid = mp->am_parent->am_gen;
+ else
+ ep[1].ne_fileid = mp->am_gen;
+ ep[1].ne_name = "..";
+ ep[1].ne_nextentry = 0;
+ *(u_int *) ep[1].ne_cookie = ~(u_int) 0;
+
+ /*
+ * If map is browsable, call a function make_entry_chain() to construct
+ * a linked list of unmounted keys, and return it. Then link the chain
+ * to the regular list. Get the chain only once, but return
+ * chunks of it each time.
+ */
+ te = make_entry_chain(mp, dp->dl_entries, fully_browsable);
+ if (!te)
+ return 0;
+#ifdef DEBUG_READDIR
+ j = 0;
+ for (ne=te; ne; ne=ne->ne_nextentry)
+ plog(XLOG_INFO, "gen1 key %4d \"%s\"", j++, ne->ne_name);
+#endif /* DEBUG_READDIR */
+
+ /* return only "chain_length" entries */
+ te_next = te;
+ for (i=1; i<chain_length; ++i) {
+ te_next = te_next->ne_nextentry;
+ if (!te_next)
+ break;
+ }
+ if (te_next) {
+ nfsentry *te_saved = te_next->ne_nextentry;
+ te_next->ne_nextentry = NULL; /* terminate "te" chain */
+ te_next = te_saved; /* save rest of "te" for next interation */
+ dp->dl_eof = FALSE; /* tell readdir there's more */
+ } else {
+ dp->dl_eof = TRUE; /* tell readdir that's it */
+ }
+ ep[1].ne_nextentry = te; /* append this chunk of "te" chain */
+#ifdef DEBUG_READDIR
+ for (ne=te; ne; ne=ne->ne_nextentry)
+ plog(XLOG_INFO, "gen2 key %4d \"%s\"", j++, ne->ne_name);
+#endif /* DEBUG_READDIR */
+ return 0;
+ } /* end of "if (gen == 0)" statement */
+
+#ifdef DEBUG
+ dlog("real child");
+#endif /* DEBUG */
+
+ if (gen == ~(u_int) 0) {
+#ifdef DEBUG
+ dlog("End of readdir in %s", mp->am_path);
+#endif /* DEBUG */
+ dp->dl_eof = TRUE;
+ dp->dl_entries = 0;
+ return 0;
+ }
+
+ /*
+ * If browsable directories, then continue serving readdir() with another
+ * chunk of entries, starting from where we left off (when gen was equal
+ * to 0). Once again, assume last chunk served to readdir.
+ */
+ dp->dl_eof = TRUE;
+ dp->dl_entries = ep;
+
+ te = te_next; /* reset 'te' from last saved te_next */
+ if (!te) { /* another indicator of end of readdir */
+ dp->dl_entries = 0;
+ return 0;
+ }
+ /*
+ * compute # of entries to send in this chain.
+ * heuristics: 128 bytes per entry.
+ */
+ chain_length = count / 128;
+
+ /* return only "chain_length" entries */
+ for (i=1; i<chain_length; ++i) {
+ te_next = te_next->ne_nextentry;
+ if (!te_next)
+ break;
+ }
+ if (te_next) {
+ nfsentry *te_saved = te_next->ne_nextentry;
+ te_next->ne_nextentry = NULL; /* terminate "te" chain */
+ te_next = te_saved; /* save rest of "te" for next interation */
+ dp->dl_eof = FALSE; /* tell readdir there's more */
+ }
+ ep = te; /* send next chunk of "te" chain */
+ dp->dl_entries = ep;
+#ifdef DEBUG_READDIR
+ plog(XLOG_INFO, "dl_entries=0x%x, te_next=0x%x, dl_eof=%d",
+ dp->dl_entries, te_next, dp->dl_eof);
+ for (ne=te; ne; ne=ne->ne_nextentry)
+ plog(XLOG_INFO, "gen3 key %4d \"%s\"", j++, ne->ne_name);
+#endif /* DEBUG_READDIR */
+ return 0;
+}
+
+
+int
+amfs_auto_fmount(am_node *mp)
+{
+ mntfs *mf = mp->am_mnt;
+ return (*mf->mf_ops->fmount_fs) (mf);
+}
+
+
+int
+amfs_auto_fumount(am_node *mp)
+{
+ mntfs *mf = mp->am_mnt;
+ return (*mf->mf_ops->fumount_fs) (mf);
+}
diff --git a/contrib/amd/amd/amfs_direct.c b/contrib/amd/amd/amfs_direct.c
new file mode 100644
index 000000000000..1558ae002392
--- /dev/null
+++ b/contrib/amd/amd/amfs_direct.c
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: amfs_direct.c,v 1.1 1997-1998/06/30 19:22:30 ezk Exp ezk $
+ *
+ */
+
+/*
+ * Direct file system
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+/****************************************************************************
+ *** FORWARD DEFINITIONS ***
+ ****************************************************************************/
+static am_node *amfs_direct_readlink(am_node *mp, int *error_return);
+
+/****************************************************************************
+ *** OPS STRUCTURES ***
+ ****************************************************************************/
+am_ops amfs_direct_ops =
+{
+ "direct",
+ amfs_auto_match,
+ 0, /* amfs_direct_init */
+ amfs_toplvl_mount,
+ 0,
+ amfs_toplvl_umount,
+ 0,
+ amfs_error_lookuppn,
+ amfs_error_readdir,
+ amfs_direct_readlink,
+ amfs_toplvl_mounted,
+ 0, /* amfs_auto_umounted */
+ find_amfs_auto_srvr,
+ FS_MKMNT | FS_NOTIMEOUT | FS_BACKGROUND | FS_AMQINFO
+};
+
+
+/****************************************************************************
+ *** FUNCTIONS ***
+ ****************************************************************************/
+
+static am_node *
+amfs_direct_readlink(am_node *mp, int *error_return)
+{
+ am_node *xp;
+ int rc = 0;
+
+ xp = next_nonerror_node(mp->am_child);
+ if (!xp) {
+ if (!mp->am_mnt->mf_private)
+ amfs_auto_mkcacheref(mp->am_mnt); /* XXX */
+ xp = amfs_auto_lookuppn(mp, mp->am_path + 1, &rc, VLOOK_CREATE);
+ }
+ if (xp) {
+ new_ttl(xp); /* (7/12/89) from Rein Tollevik */
+ return xp;
+ }
+ if (amd_state == Finishing)
+ rc = ENOENT;
+ *error_return = rc;
+ return 0;
+}
diff --git a/contrib/amd/amd/amfs_error.c b/contrib/amd/amd/amfs_error.c
new file mode 100644
index 000000000000..ccb037bf4594
--- /dev/null
+++ b/contrib/amd/amd/amfs_error.c
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: amfs_error.c,v 5.2.2.1 1992/02/09 15:08:21 jsp beta $
+ *
+ */
+
+/*
+ * Error file system.
+ * This is used as a last resort catchall if
+ * nothing else worked. EFS just returns lots
+ * of error codes, except for unmount which
+ * always works of course.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+static char * amfs_error_match(am_opts *fo);
+static int amfs_error_fmount(mntfs *mf);
+static int amfs_error_fumount(mntfs *mf);
+static void amfs_error_umounted(am_node *mp);
+
+
+/*
+ * Ops structure
+ */
+am_ops amfs_error_ops =
+{
+ "error",
+ amfs_error_match,
+ 0, /* amfs_error_init */
+ amfs_auto_fmount,
+ amfs_error_fmount,
+ amfs_auto_fumount,
+ amfs_error_fumount,
+ amfs_error_lookuppn,
+ amfs_error_readdir,
+ 0, /* amfs_error_readlink */
+ 0, /* amfs_error_mounted */
+ amfs_error_umounted,
+ find_amfs_auto_srvr,
+ FS_DISCARD
+};
+
+
+
+/*
+ * EFS file system always matches
+ */
+static char *
+amfs_error_match(am_opts *fo)
+{
+ return strdup("(error-hook)");
+}
+
+
+static int
+amfs_error_fmount(mntfs *mf)
+{
+ return ENOENT;
+}
+
+
+static int
+amfs_error_fumount(mntfs *mf)
+{
+ /*
+ * Always succeed
+ */
+ return 0;
+}
+
+
+/*
+ * EFS interface to RPC lookup() routine.
+ * Should never get here in the automounter.
+ * If we do then just give an error.
+ */
+am_node *
+amfs_error_lookuppn(am_node *mp, char *fname, int *error_return, int op)
+{
+ *error_return = ESTALE;
+ return 0;
+}
+
+
+/*
+ * EFS interface to RPC readdir() routine.
+ * Should never get here in the automounter.
+ * If we do then just give an error.
+ */
+int
+amfs_error_readdir(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, int count)
+{
+ return ESTALE;
+}
+
+
+/*
+ * umounted() callback for EFS.
+ *
+ * This prevents core-dumps on callbacks to error file-systems from
+ * nfsx_fumount.
+ */
+static void
+amfs_error_umounted(am_node *mp)
+{
+ /* nothing to do */
+}
diff --git a/contrib/amd/amd/amfs_host.c b/contrib/amd/amd/amfs_host.c
new file mode 100644
index 000000000000..6be259de917d
--- /dev/null
+++ b/contrib/amd/amd/amfs_host.c
@@ -0,0 +1,686 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: amfs_host.c,v 5.2.2.2 1992/05/31 16:36:08 jsp Exp $
+ *
+ */
+
+/*
+ * NFS host file system.
+ * Mounts all exported filesystems from a given host.
+ * This has now degenerated into a mess but will not
+ * be rewritten. Amd 6 will support the abstractions
+ * needed to make this work correctly.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+static char *amfs_host_match(am_opts *fo);
+static int amfs_host_fmount(mntfs *mf);
+static int amfs_host_fumount(mntfs *mf);
+static int amfs_host_init(mntfs *mf);
+static void amfs_host_umounted(am_node *mp);
+
+/*
+ * Ops structure
+ */
+am_ops amfs_host_ops =
+{
+ "host",
+ amfs_host_match,
+ amfs_host_init,
+ amfs_auto_fmount,
+ amfs_host_fmount,
+ amfs_auto_fumount,
+ amfs_host_fumount,
+ amfs_error_lookuppn,
+ amfs_error_readdir,
+ 0, /* amfs_host_readlink */
+ 0, /* amfs_host_mounted */
+ amfs_host_umounted,
+ find_nfs_srvr,
+ FS_MKMNT | FS_BACKGROUND | FS_AMQINFO
+};
+
+
+/*
+ * Determine the mount point:
+ *
+ * The next change we put in to better handle PCs. This is a bit
+ * disgusting, so you'd better sit down. We change the make_mntpt function
+ * to look for exported file systems without a leading '/'. If they don't
+ * have a leading '/', we add one. If the export is 'a:' through 'z:'
+ * (without a leading slash), we change it to 'a%' (or b% or z%). This
+ * allows the entire PC disk to be mounted.
+ */
+static void
+make_mntpt(char *mntpt, const exports ex, const mntfs *mf)
+{
+ if (ex->ex_dir[0] == '/') {
+ if (ex->ex_dir[1] == 0)
+ strcpy(mntpt, (mf)->mf_mount);
+ else
+ sprintf(mntpt, "%s%s", mf->mf_mount, ex->ex_dir);
+ } else if (ex->ex_dir[0] >= 'a' &&
+ ex->ex_dir[0] <= 'z' &&
+ ex->ex_dir[1] == ':' &&
+ ex->ex_dir[2] == '/' &&
+ ex->ex_dir[3] == 0)
+ sprintf(mntpt, "%s/%c%%", mf->mf_mount, ex->ex_dir[0]);
+ else
+ sprintf(mntpt, "%s/%s", mf->mf_mount, ex->ex_dir);
+}
+
+
+/*
+ * Execute needs the same as NFS plus a helper command
+ */
+static char *
+amfs_host_match(am_opts *fo)
+{
+ extern am_ops nfs_ops;
+
+ /*
+ * Make sure rfs is specified to keep nfs_match happy...
+ */
+ if (!fo->opt_rfs)
+ fo->opt_rfs = "/";
+
+ return (*nfs_ops.fs_match) (fo);
+}
+
+
+static int
+amfs_host_init(mntfs *mf)
+{
+ fserver *fs;
+ u_short port;
+
+ if (strchr(mf->mf_info, ':') == 0)
+ return ENOENT;
+
+ /*
+ * This is primarily to schedule a wakeup so that as soon
+ * as our fileserver is ready, we can continue setting up
+ * the host filesystem. If we don't do this, the standard
+ * amfs_auto code will set up a fileserver structure, but it will
+ * have to wait for another nfs request from the client to come
+ * in before finishing. Our way is faster since we don't have
+ * to wait for the client to resend its request (which could
+ * take a second or two).
+ */
+ /*
+ * First, we find the fileserver for this mntfs and then call
+ * nfs_srvr_port with our mntfs passed as the wait channel.
+ * nfs_srvr_port will check some things and then schedule
+ * it so that when the fileserver is ready, a wakeup is done
+ * on this mntfs. amfs_auto_cont() is already sleeping on this mntfs
+ * so as soon as that wakeup happens amfs_auto_cont() is called and
+ * this mount is retried.
+ */
+ if ((fs = mf->mf_server))
+ /*
+ * We don't really care if there's an error returned.
+ * Since this is just to help speed things along, the
+ * error will get handled properly elsewhere.
+ */
+ (void) nfs_srvr_port(fs, &port, (voidp) mf);
+
+ return 0;
+}
+
+
+static int
+do_mount(am_nfs_handle_t *fhp, char *dir, char *fs_name, char *opts, mntfs *mf)
+{
+ struct stat stb;
+
+#ifdef DEBUG
+ dlog("amfs_host: mounting fs %s on %s\n", fs_name, dir);
+#endif /* DEBUG */
+
+ (void) mkdirs(dir, 0555);
+ if (stat(dir, &stb) < 0 || (stb.st_mode & S_IFMT) != S_IFDIR) {
+ plog(XLOG_ERROR, "No mount point for %s - skipping", dir);
+ return ENOENT;
+ }
+
+ return mount_nfs_fh(fhp, dir, fs_name, opts, mf);
+}
+
+
+static int
+sortfun(const voidp x, const voidp y)
+{
+ exports *a = (exports *) x;
+ exports *b = (exports *) y;
+
+ return strcmp((*a)->ex_dir, (*b)->ex_dir);
+}
+
+
+/*
+ * Get filehandle
+ */
+static int
+fetch_fhandle(CLIENT * client, char *dir, am_nfs_handle_t *fhp, u_long nfs_version)
+{
+ struct timeval tv;
+ enum clnt_stat clnt_stat;
+
+ /*
+ * Pick a number, any number...
+ */
+ tv.tv_sec = 20;
+ tv.tv_usec = 0;
+
+#ifdef DEBUG
+ dlog("Fetching fhandle for %s", dir);
+#endif /* DEBUG */
+
+ /*
+ * Call the mount daemon on the remote host to
+ * get the filehandle. Use NFS version specific call.
+ */
+
+ plog(XLOG_INFO, "fetch_fhandle: NFS version %d", nfs_version);
+#ifdef HAVE_FS_NFS3
+ if (nfs_version == NFS_VERSION3) {
+ memset((char *) &fhp->v3, 0, sizeof(fhp->v3));
+ clnt_stat = clnt_call(client,
+ MOUNTPROC_MNT,
+ (XDRPROC_T_TYPE) xdr_dirpath,
+ (SVC_IN_ARG_TYPE) &dir,
+ (XDRPROC_T_TYPE) xdr_mountres3,
+ (SVC_IN_ARG_TYPE) &fhp->v3,
+ tv);
+ if (clnt_stat != RPC_SUCCESS) {
+ plog(XLOG_ERROR, "mountd rpc failed: %s", clnt_sperrno(clnt_stat));
+ return EIO;
+ }
+ /* Check the status of the filehandle */
+ if ((errno = fhp->v3.fhs_status)) {
+#ifdef DEBUG
+ dlog("fhandle fetch for mount version 3 failed: %m");
+#endif /* DEBUG */
+ return errno;
+ }
+ } else { /* not NFS_VERSION3 mount */
+#endif /* HAVE_FS_NFS3 */
+ clnt_stat = clnt_call(client,
+ MOUNTPROC_MNT,
+ (XDRPROC_T_TYPE) xdr_dirpath,
+ (SVC_IN_ARG_TYPE) &dir,
+ (XDRPROC_T_TYPE) xdr_fhstatus,
+ (SVC_IN_ARG_TYPE) &fhp->v2,
+ tv);
+ if (clnt_stat != RPC_SUCCESS) {
+ char *msg = clnt_sperrno(clnt_stat);
+ plog(XLOG_ERROR, "mountd rpc failed: %s", msg);
+ return EIO;
+ }
+ /* Check status of filehandle */
+ if (fhp->v2.fhs_status) {
+ errno = fhp->v2.fhs_status;
+#ifdef DEBUG
+ dlog("fhandle fetch for mount version 1 failed: %m");
+#endif /* DEBUG */
+ return errno;
+ }
+#ifdef HAVE_FS_NFS3
+ } /* end of "if (nfs_version == NFS_VERSION3)" statement */
+#endif /* HAVE_FS_NFS3 */
+
+ /* all is well */
+ return 0;
+}
+
+
+/*
+ * Scan mount table to see if something already mounted
+ */
+static int
+already_mounted(mntlist *mlist, char *dir)
+{
+ mntlist *ml;
+
+ for (ml = mlist; ml; ml = ml->mnext)
+ if (STREQ(ml->mnt->mnt_dir, dir))
+ return 1;
+ return 0;
+}
+
+
+/*
+ * Mount the export tree from a host
+ */
+static int
+amfs_host_fmount(mntfs *mf)
+{
+ struct timeval tv2;
+ CLIENT *client;
+ enum clnt_stat clnt_stat;
+ int n_export;
+ int j, k;
+ exports exlist = 0, ex;
+ exports *ep = 0;
+ am_nfs_handle_t *fp = 0;
+ char *host;
+ int error = 0;
+ struct sockaddr_in sin;
+ int sock = RPC_ANYSOCK;
+ int ok = FALSE;
+ mntlist *mlist;
+ char fs_name[MAXPATHLEN], *rfs_dir;
+ char mntpt[MAXPATHLEN];
+ struct timeval tv;
+ u_long mnt_version;
+
+ /*
+ * Read the mount list
+ */
+ mlist = read_mtab(mf->mf_mount, mnttab_file_name);
+
+#ifdef MOUNT_TABLE_ON_FILE
+ /*
+ * Unlock the mount list
+ */
+ unlock_mntlist();
+#endif /* MOUNT_TABLE_ON_FILE */
+
+ /*
+ * Take a copy of the server hostname, address, and nfs version
+ * to mount version conversion.
+ */
+ host = mf->mf_server->fs_host;
+ sin = *mf->mf_server->fs_ip;
+ plog(XLOG_INFO, "amfs_host_fmount: NFS version %d", mf->mf_server->fs_version);
+#ifdef HAVE_FS_NFS3
+ if (mf->mf_server->fs_version == NFS_VERSION3)
+ mnt_version = MOUNTVERS3;
+ else
+#endif /* HAVE_FS_NFS3 */
+ mnt_version = MOUNTVERS;
+
+ /*
+ * The original 10 second per try timeout is WAY too large, especially
+ * if we're only waiting 10 or 20 seconds max for the response.
+ * That would mean we'd try only once in 10 seconds, and we could
+ * lose the transmitt or receive packet, and never try again.
+ * A 2-second per try timeout here is much more reasonable.
+ * 09/28/92 Mike Mitchell, mcm@unx.sas.com
+ */
+ tv.tv_sec = 2;
+ tv.tv_usec = 0;
+
+ /*
+ * Create a client attached to mountd
+ */
+ client = get_mount_client(host, &sin, &tv, &sock, mnt_version);
+ if (client == NULL) {
+#ifdef HAVE_CLNT_SPCREATEERROR
+ plog(XLOG_ERROR, "get_mount_client failed for %s: %s",
+ host, clnt_spcreateerror(""));
+#else /* not HAVE_CLNT_SPCREATEERROR */
+ plog(XLOG_ERROR, "get_mount_client failed for %s", host);
+#endif /* not HAVE_CLNT_SPCREATEERROR */
+ error = EIO;
+ goto out;
+ }
+ if (!nfs_auth) {
+ error = make_nfs_auth();
+ if (error)
+ goto out;
+ }
+ client->cl_auth = nfs_auth;
+
+#ifdef DEBUG
+ dlog("Fetching export list from %s", host);
+#endif /* DEBUG */
+
+ /*
+ * Fetch the export list
+ */
+ tv2.tv_sec = 10;
+ tv2.tv_usec = 0;
+ clnt_stat = clnt_call(client,
+ MOUNTPROC_EXPORT,
+ (XDRPROC_T_TYPE) xdr_void,
+ 0,
+ (XDRPROC_T_TYPE) xdr_exports,
+ (SVC_IN_ARG_TYPE) & exlist,
+ tv2);
+ if (clnt_stat != RPC_SUCCESS) {
+ char *msg = clnt_sperrno(clnt_stat);
+ plog(XLOG_ERROR, "host_fmount rpc failed: %s", msg);
+ /* clnt_perror(client, "rpc"); */
+ error = EIO;
+ goto out;
+ }
+
+ /*
+ * Figure out how many exports were returned
+ */
+ for (n_export = 0, ex = exlist; ex; ex = ex->ex_next) {
+ /* printf("export %s\n", ex->ex_dir); */
+ n_export++;
+ }
+
+ /*
+ * Allocate an array of pointers into the list
+ * so that they can be sorted. If the filesystem
+ * is already mounted then ignore it.
+ */
+ ep = (exports *) xmalloc(n_export * sizeof(exports));
+ for (j = 0, ex = exlist; ex; ex = ex->ex_next) {
+ make_mntpt(mntpt, ex, mf);
+ if (!already_mounted(mlist, mntpt))
+ ep[j++] = ex;
+ }
+ n_export = j;
+
+ /*
+ * Sort into order.
+ * This way the mounts are done in order down the tree,
+ * instead of any random order returned by the mount
+ * daemon (the protocol doesn't specify...).
+ */
+ qsort(ep, n_export, sizeof(exports), sortfun);
+
+ /*
+ * Allocate an array of filehandles
+ */
+ fp = (am_nfs_handle_t *) xmalloc(n_export * sizeof(am_nfs_handle_t));
+
+ /*
+ * Try to obtain filehandles for each directory.
+ * If a fetch fails then just zero out the array
+ * reference but discard the error.
+ */
+ for (j = k = 0; j < n_export; j++) {
+ /* Check and avoid a duplicated export entry */
+ if (j > k && ep[k] && STREQ(ep[j]->ex_dir, ep[k]->ex_dir)) {
+#ifdef DEBUG
+ dlog("avoiding dup fhandle requested for %s", ep[j]->ex_dir);
+#endif /* DEBUG */
+ ep[j] = 0;
+ } else {
+ k = j;
+ error = fetch_fhandle(client, ep[j]->ex_dir, &fp[j],
+ mf->mf_server->fs_version);
+ if (error)
+ ep[j] = 0;
+ }
+ }
+
+ /*
+ * Mount each filesystem for which we have a filehandle.
+ * If any of the mounts succeed then mark "ok" and return
+ * error code 0 at the end. If they all fail then return
+ * the last error code.
+ */
+ strncpy(fs_name, mf->mf_info, sizeof(fs_name));
+ if ((rfs_dir = strchr(fs_name, ':')) == (char *) 0) {
+ plog(XLOG_FATAL, "amfs_host_fmount: mf_info has no colon");
+ error = EINVAL;
+ goto out;
+ }
+ ++rfs_dir;
+ for (j = 0; j < n_export; j++) {
+ ex = ep[j];
+ if (ex) {
+ strcpy(rfs_dir, ex->ex_dir);
+ make_mntpt(mntpt, ex, mf);
+ if (do_mount(&fp[j], mntpt, fs_name, mf->mf_mopts, mf) == 0)
+ ok = TRUE;
+ }
+ }
+
+ /*
+ * Clean up and exit
+ */
+out:
+ discard_mntlist(mlist);
+ if (ep)
+ XFREE(ep);
+ if (fp)
+ XFREE(fp);
+ if (sock != RPC_ANYSOCK)
+ (void) amu_close(sock);
+ if (client)
+ clnt_destroy(client);
+ if (exlist)
+ xdr_pri_free((XDRPROC_T_TYPE) xdr_exports, (caddr_t) &exlist);
+ if (ok)
+ return 0;
+ return error;
+}
+
+
+/*
+ * Return true if pref is a directory prefix of dir.
+ *
+ * XXX TODO:
+ * Does not work if pref is "/".
+ */
+static int
+directory_prefix(char *pref, char *dir)
+{
+ int len = strlen(pref);
+
+ if (!NSTREQ(pref, dir, len))
+ return FALSE;
+ if (dir[len] == '/' || dir[len] == '\0')
+ return TRUE;
+ return FALSE;
+}
+
+
+/*
+ * Unmount a mount tree
+ */
+static int
+amfs_host_fumount(mntfs *mf)
+{
+ mntlist *ml, *mprev;
+ int xerror = 0;
+
+ /*
+ * Read the mount list
+ */
+ mntlist *mlist = read_mtab(mf->mf_mount, mnttab_file_name);
+
+#ifdef MOUNT_TABLE_ON_FILE
+ /*
+ * Unlock the mount list
+ */
+ unlock_mntlist();
+#endif /* MOUNT_TABLE_ON_FILE */
+
+ /*
+ * Reverse list...
+ */
+ ml = mlist;
+ mprev = 0;
+ while (ml) {
+ mntlist *ml2 = ml->mnext;
+ ml->mnext = mprev;
+ mprev = ml;
+ ml = ml2;
+ }
+ mlist = mprev;
+
+ /*
+ * Unmount all filesystems...
+ */
+ for (ml = mlist; ml && !xerror; ml = ml->mnext) {
+ char *dir = ml->mnt->mnt_dir;
+ if (directory_prefix(mf->mf_mount, dir)) {
+ int error;
+#ifdef DEBUG
+ dlog("amfs_host: unmounts %s", dir);
+#endif /* DEBUG */
+ /*
+ * Unmount "dir"
+ */
+ error = UMOUNT_FS(dir, mnttab_file_name);
+ /*
+ * Keep track of errors
+ */
+ if (error) {
+ if (!xerror)
+ xerror = error;
+ if (error != EBUSY) {
+ errno = error;
+ plog(XLOG_ERROR, "Tree unmount of %s failed: %m", ml->mnt->mnt_dir);
+ }
+ } else {
+ (void) rmdirs(dir);
+ }
+ }
+ }
+
+ /*
+ * Throw away mount list
+ */
+ discard_mntlist(mlist);
+
+ /*
+ * Try to remount, except when we are shutting down.
+ */
+ if (xerror && amd_state != Finishing) {
+ xerror = amfs_host_fmount(mf);
+ if (!xerror) {
+ /*
+ * Don't log this - it's usually too verbose
+ plog(XLOG_INFO, "Remounted host %s", mf->mf_info);
+ */
+ xerror = EBUSY;
+ }
+ }
+ return xerror;
+}
+
+
+/*
+ * Tell mountd we're done.
+ * This is not quite right, because we may still
+ * have other filesystems mounted, but the existing
+ * mountd protocol is badly broken anyway.
+ */
+static void
+amfs_host_umounted(am_node *mp)
+{
+ mntfs *mf = mp->am_mnt;
+ char *host;
+ CLIENT *client;
+ enum clnt_stat clnt_stat;
+ struct sockaddr_in sin;
+ int sock = RPC_ANYSOCK;
+ struct timeval tv;
+ u_long mnt_version;
+
+ if (mf->mf_error || mf->mf_refc > 1 || !mf->mf_server)
+ return;
+
+ /*
+ * Take a copy of the server hostname, address, and NFS version
+ * to mount version conversion.
+ */
+ host = mf->mf_server->fs_host;
+ sin = *mf->mf_server->fs_ip;
+ plog(XLOG_INFO, "amfs_host_umounted: NFS version %d", mf->mf_server->fs_version);
+#ifdef HAVE_FS_NFS3
+ if (mf->mf_server->fs_version == NFS_VERSION3)
+ mnt_version = MOUNTVERS3;
+ else
+#endif /* HAVE_FS_NFS3 */
+ mnt_version = MOUNTVERS;
+
+ /*
+ * Create a client attached to mountd
+ */
+ tv.tv_sec = 10;
+ tv.tv_usec = 0;
+ client = get_mount_client(host, &sin, &tv, &sock, mnt_version);
+ if (client == NULL) {
+#ifdef HAVE_CLNT_SPCREATEERROR
+ plog(XLOG_ERROR, "get_mount_client failed for %s: %s",
+ host, clnt_spcreateerror(""));
+#else /* not HAVE_CLNT_SPCREATEERROR */
+ plog(XLOG_ERROR, "get_mount_client failed for %s", host);
+#endif /* not HAVE_CLNT_SPCREATEERROR */
+ goto out;
+ }
+
+ if (!nfs_auth) {
+ if (make_nfs_auth())
+ goto out;
+ }
+ client->cl_auth = nfs_auth;
+
+#ifdef DEBUG
+ dlog("Unmounting all from %s", host);
+#endif /* DEBUG */
+
+ clnt_stat = clnt_call(client,
+ MOUNTPROC_UMNTALL,
+ (XDRPROC_T_TYPE) xdr_void,
+ 0,
+ (XDRPROC_T_TYPE) xdr_void,
+ 0,
+ tv);
+ if (clnt_stat != RPC_SUCCESS && clnt_stat != RPC_SYSTEMERROR) {
+ /* RPC_SYSTEMERROR seems to be returned for no good reason ... */
+ char *msg = clnt_sperrno(clnt_stat);
+ plog(XLOG_ERROR, "unmount all from %s rpc failed: %s", host, msg, clnt_stat);
+ goto out;
+ }
+
+out:
+ if (sock != RPC_ANYSOCK)
+ (void) amu_close(sock);
+ if (client)
+ clnt_destroy(client);
+}
diff --git a/contrib/amd/amd/amfs_inherit.c b/contrib/amd/amd/amfs_inherit.c
new file mode 100644
index 000000000000..e9709bd5343c
--- /dev/null
+++ b/contrib/amd/amd/amfs_inherit.c
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: amfs_inherit.c,v 5.2.2.1 1992/02/09 15:08:26 jsp beta $
+ *
+ */
+
+/*
+ * Inheritance file system.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+/*
+ * This implements a filesystem restart.
+ *
+ * This is a *gross* hack - it knows far too
+ * much about the way other parts of the
+ * system work. See restart.c too.
+ */
+
+static char *amfs_inherit_match(am_opts *fo);
+static int amfs_inherit_fmount(mntfs *mf);
+static int amfs_inherit_fumount(mntfs *mf);
+static int amfs_inherit_init(mntfs *mf);
+static int amfs_inherit_mount(am_node *mp);
+
+
+/*
+ * Ops structure
+ */
+am_ops amfs_inherit_ops =
+{
+ "inherit",
+ amfs_inherit_match,
+ amfs_inherit_init,
+ amfs_inherit_mount,
+ amfs_inherit_fmount,
+ amfs_auto_fumount,
+ amfs_inherit_fumount,
+ amfs_error_lookuppn,
+ amfs_error_readdir,
+ 0, /* amfs_inherit_readlink */
+ 0, /* amfs_inherit_mounted */
+ 0, /* amfs_inherit_umounted */
+ find_amfs_auto_srvr,
+ FS_DISCARD
+};
+
+
+/*
+ * This should never be called.
+ */
+static char *
+amfs_inherit_match(am_opts *fo)
+{
+ plog(XLOG_FATAL, "amfs_inherit_match called!");
+ return 0;
+}
+
+
+static int
+amfs_inherit_init(mntfs *mf)
+{
+ mntfs *mf_link = (mntfs *) mf->mf_private;
+
+ if (mf_link == 0) {
+ plog(XLOG_ERROR, "Remount collision on %s?", mf->mf_mount);
+ plog(XLOG_FATAL, "Attempting to init not-a-filesystem");
+ return EINVAL;
+ }
+
+ if (mf_link->mf_ops->fs_init)
+ return (*mf_link->mf_ops->fs_init) (mf_link);
+ return 0;
+}
+
+
+/*
+ * Take the linked mount point and
+ * propogate.
+ */
+static mntfs *
+amfs_inherit_inherit(mntfs *mf)
+{
+ mntfs *mf_link = (mntfs *) mf->mf_private;
+
+ if (mf_link == 0) {
+ plog(XLOG_FATAL, "Attempting to inherit not-a-filesystem");
+ return 0; /* XXX */
+ }
+ mf_link->mf_fo = mf->mf_fo;
+
+ /*
+ * Discard the old map.
+ * Don't call am_unmounted since this
+ * node was never really mounted in the
+ * first place.
+ */
+ mf->mf_private = 0;
+ free_mntfs(mf);
+
+ /*
+ * Free the dangling reference
+ * to the mount link.
+ */
+ free_mntfs(mf_link);
+
+ /*
+ * Get a hold of the other entry
+ */
+ mf_link->mf_flags &= ~MFF_RESTART;
+
+ /* Say what happened */
+ plog(XLOG_INFO, "restarting %s on %s", mf_link->mf_info, mf_link->mf_mount);
+
+ return mf_link;
+}
+
+
+static int
+amfs_inherit_mount(am_node *mp)
+{
+ mntfs *newmf = amfs_inherit_inherit(mp->am_mnt);
+
+ if (newmf) {
+ mp->am_mnt = newmf;
+ /*
+ * XXX - must do the am_mounted call here
+ */
+ if (newmf->mf_ops->fs_flags & FS_MBACKGROUND)
+ am_mounted(mp);
+
+ new_ttl(mp);
+ return 0;
+ }
+ return EINVAL;
+}
+
+
+static int
+amfs_inherit_fmount(mntfs *mf)
+{
+ am_node *mp = find_mf(mf);
+
+ if (mp)
+ return amfs_inherit_mount(mp);
+ return amfs_inherit_inherit(mf) ? 0 : EINVAL;
+}
+
+
+static int
+amfs_inherit_fumount(mntfs *mf)
+{
+ /*
+ * Always succeed
+ */
+ return 0;
+}
diff --git a/contrib/amd/amd/amfs_link.c b/contrib/amd/amd/amfs_link.c
new file mode 100644
index 000000000000..251c5ebb47f2
--- /dev/null
+++ b/contrib/amd/amd/amfs_link.c
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: amfs_link.c,v 5.2.2.1 1992/02/09 15:09:04 jsp beta $
+ *
+ */
+
+/*
+ * Symbol-link file system
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+
+/*
+ * Ops structures
+ */
+am_ops amfs_link_ops =
+{
+ "link",
+ amfs_link_match,
+ 0, /* amfs_link_init */
+ amfs_auto_fmount,
+ amfs_link_fmount,
+ amfs_auto_fumount,
+ amfs_link_fumount,
+ amfs_error_lookuppn,
+ amfs_error_readdir,
+ 0, /* amfs_link_readlink */
+ 0, /* amfs_link_mounted */
+ 0, /* amfs_link_umounted */
+ find_amfs_auto_srvr,
+ 0
+};
+
+
+/*
+ * SFS needs a link.
+ */
+char *
+amfs_link_match(am_opts *fo)
+{
+
+ if (!fo->opt_fs) {
+ plog(XLOG_USER, "link: no fs specified");
+ return 0;
+ }
+
+ /*
+ * Bug report (14/12/89) from Jay Plett <jay@princeton.edu>
+ * If an automount point has the same name as an existing
+ * link type mount Amd hits a race condition and either hangs
+ * or causes a symlink loop.
+ *
+ * If fs begins with a '/' change the opt_fs & opt_sublink
+ * fields so that the fs option doesn't end up pointing at
+ * an existing symlink.
+ *
+ * If sublink is nil then set sublink to fs
+ * else set sublink to fs / sublink
+ *
+ * Finally set fs to ".".
+ */
+ if (*fo->opt_fs == '/') {
+ char *fullpath;
+ char *link = fo->opt_sublink;
+ if (link) {
+ if (*link == '/')
+ fullpath = strdup(link);
+ else
+ fullpath = str3cat((char *) 0, fo->opt_fs, "/", link);
+ } else {
+ fullpath = strdup(fo->opt_fs);
+ }
+
+ if (fo->opt_sublink)
+ XFREE(fo->opt_sublink);
+ fo->opt_sublink = fullpath;
+ fo->opt_fs = str3cat(fo->opt_fs, ".", fullpath, "");
+ }
+
+ return strdup(fo->opt_fs);
+}
+
+
+int
+amfs_link_fmount(mntfs *mf)
+{
+ /*
+ * Wow - this is hard to implement! :-)
+ */
+ return 0;
+}
+
+
+int
+amfs_link_fumount(mntfs *mf)
+{
+ return 0;
+}
diff --git a/contrib/amd/amd/amfs_linkx.c b/contrib/amd/amd/amfs_linkx.c
new file mode 100644
index 000000000000..e46206f57fe5
--- /dev/null
+++ b/contrib/amd/amd/amfs_linkx.c
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: amfs_linkx.c,v 5.2.2.1 1992/02/09 15:09:04 jsp beta $
+ *
+ */
+
+/*
+ * Symbol-link file system, with test that the target of the link exists.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+/* forward declarations */
+static int amfs_linkx_mount(am_node *mp);
+
+/*
+ * linkx operations
+ */
+struct am_ops amfs_linkx_ops =
+{
+ "linkx",
+ amfs_link_match,
+ 0, /* amfs_linkx_init */
+ amfs_linkx_mount,
+ 0,
+ amfs_auto_fumount,
+ amfs_link_fumount,
+ amfs_error_lookuppn,
+ amfs_error_readdir,
+ 0, /* amfs_linkx_readlink */
+ 0, /* amfs_linkx_mounted */
+ 0, /* amfs_linkx_umounted */
+ find_amfs_auto_srvr,
+ FS_MBACKGROUND
+};
+
+
+static int
+amfs_linkx_mount(am_node *mp)
+{
+ /*
+ * Check for existence of target.
+ */
+ struct stat stb;
+ char *ln;
+
+ if (mp->am_link)
+ ln = mp->am_link;
+ else /* should never occur */
+ ln = mp->am_mnt->mf_mount;
+
+ /*
+ * Use lstat, not stat, since we don't
+ * want to know if the ultimate target of
+ * a symlink chain exists, just the first.
+ */
+ if (lstat(ln, &stb) < 0)
+ return errno;
+
+ return 0;
+}
+
diff --git a/contrib/amd/amd/amfs_nfsl.c b/contrib/amd/amd/amfs_nfsl.c
new file mode 100644
index 000000000000..8132afdeb71c
--- /dev/null
+++ b/contrib/amd/amd/amfs_nfsl.c
@@ -0,0 +1,237 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: amfs_nfsl.c,v 5.2.2.3 1992/08/02 10:42:21 jsp Exp $
+ *
+ */
+
+/*
+ * NFSL: Network file system with local existence check. If the local
+ * path denoted by $rfs exists, it behaves as type:=link.
+ *
+ * Example:
+ * pkg type:=nfsl;rhost:=jonny;rfs:=/n/johnny/src/pkg;fs:=${rfs}
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+
+/* forward declarations */
+static char *amfs_nfsl_match(am_opts *fo);
+static int amfs_nfsl_init(mntfs *mf);
+static int amfs_nfsl_fmount(mntfs *mf);
+static int amfs_nfsl_fumount(mntfs *mf);
+static void amfs_nfsl_umounted(am_node *mp);
+static fserver *amfs_nfsl_ffserver(mntfs *mf);
+
+/*
+ * NFS-Link operations
+ */
+am_ops amfs_nfsl_ops =
+{
+ "nfsl", /* name of file system */
+ amfs_nfsl_match, /* match */
+ amfs_nfsl_init, /* initialize */
+ amfs_auto_fmount, /* mount vnode */
+ amfs_nfsl_fmount, /* mount vfs */
+ amfs_auto_fumount, /* unmount vnode */
+ amfs_nfsl_fumount, /* unmount VFS */
+ amfs_error_lookuppn, /* lookup path-name */
+ amfs_error_readdir, /* read directory */
+ 0, /* read link */
+ 0, /* after-mount extra actions */
+ amfs_nfsl_umounted, /* after-umount extra actions */
+ amfs_nfsl_ffserver, /* find a file server */
+ FS_MKMNT | FS_BACKGROUND | FS_AMQINFO /* flags */
+};
+
+
+/*
+ * Check that f/s has all needed fields.
+ * Returns: matched string if found, NULL otherwise.
+ */
+static char *
+amfs_nfsl_match(am_opts *fo)
+{
+ char *cp = fo->opt_fs;
+ char *ho = fo->opt_rhost;
+ struct stat stb;
+
+ if (!cp || !ho) {
+ plog(XLOG_USER, "amfs_nfsl: nost $fs and $rhost must be specified");
+ return NULL;
+ }
+
+ /*
+ * If this host is not the same as $rhost, or if link does not exist,
+ * perform nfs_match(), same as for type:=nfs.
+ * If link value exists (or same host), then perform amfs_link_match(),
+ * same as for linkx.
+ */
+ if (!STRCEQ(ho, hostname)) {
+ plog(XLOG_INFO, "amfs_nfsl: \"%s\" is not local host, using type:=nfs", ho);
+ return nfs_match(fo);
+ } else if (lstat(cp, &stb) < 0) {
+ plog(XLOG_INFO, "amfs_nfsl: \"%s\" does not exist, using type:=nfs", cp);
+ return nfs_match(fo);
+ } else {
+ plog(XLOG_INFO, "amfs_nfsl: \"%s\" exists, using type:=link", cp);
+ return amfs_link_match(fo);
+ }
+}
+
+
+/*
+ * Initialize.
+ * Returns: 0 if OK, non-zero (errno) if failed.
+ */
+static int
+amfs_nfsl_init(mntfs *mf)
+{
+ /*
+ * If a link, do nothing (same as type:=link).
+ * If non-link, do nfs_init (same as type:=nfs).
+ */
+ if (mf->mf_flags & MFF_NFSLINK) {
+ return 0;
+ } else {
+ return nfs_init(mf);
+ }
+}
+
+
+/*
+ * Mount vfs.
+ * Returns: 0 if OK, non-zero (errno) if failed.
+ */
+static int
+amfs_nfsl_fmount(mntfs *mf)
+{
+ /*
+ * If a link, do run amfs_link_fmount() (same as type:=link)
+ * If non-link, do nfs_fmount (same as type:=nfs).
+ */
+ if (mf->mf_flags & MFF_NFSLINK) {
+ return amfs_link_fmount(mf);
+ } else {
+ return nfs_fmount(mf);
+ }
+}
+
+
+/*
+ * Unmount VFS.
+ * Returns: 0 if OK, non-zero (errno) if failed.
+ */
+static int
+amfs_nfsl_fumount(mntfs *mf)
+{
+ /*
+ * If a link, do run amfs_link_fumount() (same as type:=link)
+ * If non-link, do nfs_fumount (same as type:=nfs).
+ */
+ if (mf->mf_flags & MFF_NFSLINK) {
+ return amfs_link_fumount(mf);
+ } else {
+ return nfs_fumount(mf);
+ }
+}
+
+
+/*
+ * Async unmount callback function.
+ * After the base umount() succeeds, we may want to take extra actions,
+ * such as informing remote mount daemons that we've unmounted them.
+ * See amfs_auto_umounted(), host_umounted(), nfs_umounted().
+ */
+static void
+amfs_nfsl_umounted(am_node *mp)
+{
+ mntfs *mf = mp->am_mnt;
+
+ /*
+ * If a link, do nothing (same as type:=link)
+ * If non-link, do nfs_fumount (same as type:=nfs).
+ */
+ if (mf->mf_flags & MFF_NFSLINK) {
+ return;
+ } else {
+ nfs_umounted(mp);
+ /*
+ * MUST remove mount point directories, because if they remain
+ * behind, the next nfsl access will think they are a link
+ * type file system, and not NFS! (when it performs link target
+ * existence test)
+ */
+ if (mf->mf_flags & MFF_MKMNT)
+ rmdirs(mf->mf_mount);
+ return;
+ }
+}
+
+
+/*
+ * Find a file server.
+ * Returns: fserver of found server, or NULL if not found.
+ */
+static fserver *
+amfs_nfsl_ffserver(mntfs *mf)
+{
+ char *cp = mf->mf_fo->opt_fs;
+ char *ho = mf->mf_fo->opt_rhost;
+ struct stat stb;
+
+ /*
+ * If this host is not the same as $rhost, or if link does not exist,
+ * perform find_nfs_srvr(), same as for type:=nfs.
+ * If link value exists (or same host), then perform
+ * find_amfs_auto_srvr(), same as for linkx.
+ */
+ if (!STREQ(ho, hostname) || lstat(cp, &stb) < 0) {
+ return find_nfs_srvr(mf);
+ } else {
+ mf->mf_flags |= MFF_NFSLINK;
+ return find_amfs_auto_srvr(mf);
+ }
+}
diff --git a/contrib/amd/amd/amfs_nfsx.c b/contrib/amd/amd/amfs_nfsx.c
new file mode 100644
index 000000000000..272e10d59b07
--- /dev/null
+++ b/contrib/amd/amd/amfs_nfsx.c
@@ -0,0 +1,532 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: amfs_linxx.c,v 5.2.2.3 1992/05/31 16:13:07 jsp Exp $
+ *
+ */
+
+/*
+ * NFS hierarchical mounts
+ *
+ * TODO: Re-implement.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+/*
+ * The rfs field contains a list of mounts to be done from
+ * the remote host.
+ */
+typedef struct amfs_nfsx_mnt {
+ mntfs *n_mnt;
+ int n_error;
+} amfs_nfsx_mnt;
+
+struct amfs_nfsx {
+ int nx_c; /* Number of elements in nx_v */
+ amfs_nfsx_mnt *nx_v; /* Underlying mounts */
+ amfs_nfsx_mnt *nx_try;
+};
+
+/* forward definitions */
+static char *amfs_nfsx_match(am_opts *fo);
+static int amfs_nfsx_fmount (mntfs *);
+static int amfs_nfsx_fmount(mntfs *mf);
+static int amfs_nfsx_fumount(mntfs *mf);
+static int amfs_nfsx_init(mntfs *mf);
+
+/*
+ * Ops structure
+ */
+am_ops amfs_nfsx_ops =
+{
+ "nfsx",
+ amfs_nfsx_match,
+ amfs_nfsx_init,
+ amfs_auto_fmount,
+ amfs_nfsx_fmount,
+ amfs_auto_fumount,
+ amfs_nfsx_fumount,
+ amfs_error_lookuppn,
+ amfs_error_readdir,
+ 0, /* amfs_nfsx_readlink */
+ 0, /* amfs_nfsx_mounted */
+ 0, /* amfs_nfsx_umounted */
+ find_nfs_srvr, /* XXX */
+ /* FS_UBACKGROUND| */ FS_AMQINFO
+};
+
+
+static char *
+amfs_nfsx_match(am_opts *fo)
+{
+ char *xmtab;
+ char *ptr;
+ int len;
+
+ if (!fo->opt_rfs) {
+ plog(XLOG_USER, "amfs_nfsx: no remote filesystem specified");
+ return FALSE;
+ }
+
+ if (!fo->opt_rhost) {
+ plog(XLOG_USER, "amfs_nfsx: no remote host specified");
+ return FALSE;
+ }
+
+ /* set default sublink */
+ if (fo->opt_sublink == 0) {
+ ptr = strchr(fo->opt_rfs, ',');
+ if (ptr && ptr != (fo->opt_rfs + 1))
+ fo->opt_sublink = strnsave(fo->opt_rfs + 1, ptr - fo->opt_rfs - 1);
+ }
+
+ /*
+ * Remove trailing ",..." from ${fs}
+ * After deslashifying, overwrite the end of ${fs} with "/"
+ * to make sure it is unique.
+ */
+ if ((ptr = strchr(fo->opt_fs, ',')))
+ *ptr = '\0';
+ deslashify(fo->opt_fs);
+
+ /*
+ * Bump string length to allow trailing /
+ */
+ len = strlen(fo->opt_fs);
+ fo->opt_fs = xrealloc(fo->opt_fs, len + 1 + 1);
+ ptr = fo->opt_fs + len;
+
+ /*
+ * Make unique...
+ */
+ *ptr++ = '/';
+ *ptr = '\0';
+
+ /*
+ * Determine magic cookie to put in mtab
+ */
+ xmtab = str3cat((char *) 0, fo->opt_rhost, ":", fo->opt_rfs);
+#ifdef DEBUG
+ dlog("NFS: mounting remote server \"%s\", remote fs \"%s\" on \"%s\"",
+ fo->opt_rhost, fo->opt_rfs, fo->opt_fs);
+#endif /* DEBUG */
+
+ return xmtab;
+}
+
+
+static void
+amfs_nfsx_prfree(voidp vp)
+{
+ struct amfs_nfsx *nx = (struct amfs_nfsx *) vp;
+ int i;
+
+ for (i = 0; i < nx->nx_c; i++) {
+ mntfs *m = nx->nx_v[i].n_mnt;
+ if (m)
+ free_mntfs(m);
+ }
+
+ XFREE(nx->nx_v);
+ XFREE(nx);
+}
+
+
+static int
+amfs_nfsx_init(mntfs *mf)
+{
+ /*
+ * mf_info has the form:
+ * host:/prefix/path,sub,sub,sub
+ */
+ int i;
+ int glob_error;
+ struct amfs_nfsx *nx;
+ int asked_for_wakeup = 0;
+
+ nx = (struct amfs_nfsx *) mf->mf_private;
+
+ if (nx == 0) {
+ char **ivec;
+ char *info = 0;
+ char *host;
+ char *pref;
+ int error = 0;
+
+ info = strdup(mf->mf_info);
+ host = strchr(info, ':');
+ if (!host) {
+ error = EINVAL;
+ goto errexit;
+ }
+ pref = host +1;
+ host = info;
+
+ /*
+ * Split the prefix off from the suffices
+ */
+ ivec = strsplit(pref, ',', '\'');
+
+ /*
+ * Count array size
+ */
+ for (i = 0; ivec[i]; i++) ;
+
+ nx = ALLOC(struct amfs_nfsx);
+ mf->mf_private = (voidp) nx;
+ mf->mf_prfree = amfs_nfsx_prfree;
+
+ nx->nx_c = i - 1; /* i-1 because we don't want the prefix */
+ nx->nx_v = (amfs_nfsx_mnt *) xmalloc(nx->nx_c * sizeof(amfs_nfsx_mnt));
+ {
+ char *mp = 0;
+ char *xinfo = 0;
+ char *fs = mf->mf_fo->opt_fs;
+ char *rfs = 0;
+ for (i = 0; i < nx->nx_c; i++) {
+ char *path = ivec[i + 1];
+ rfs = str3cat(rfs, pref, "/", path);
+ /*
+ * Determine the mount point.
+ * If this is the root, then don't remove
+ * the trailing slash to avoid mntfs name clashes.
+ */
+ mp = str3cat(mp, fs, "/", rfs);
+ normalize_slash(mp);
+ deslashify(mp);
+ /*
+ * Determine the mount info
+ */
+ xinfo = str3cat(xinfo, host, *path == '/' ? "" : "/", path);
+ normalize_slash(xinfo);
+ if (pref[1] != '\0')
+ deslashify(xinfo);
+#ifdef DEBUG
+ dlog("amfs_nfsx: init mount for %s on %s", xinfo, mp);
+#endif /* DEBUG */
+ nx->nx_v[i].n_error = -1;
+ nx->nx_v[i].n_mnt = find_mntfs(&nfs_ops, mf->mf_fo, mp, xinfo, "", mf->mf_mopts, mf->mf_remopts);
+ }
+ if (rfs)
+ XFREE(rfs);
+ if (mp)
+ XFREE(mp);
+ if (xinfo)
+ XFREE(xinfo);
+ }
+
+ XFREE(ivec);
+ errexit:
+ if (info)
+ XFREE(info);
+ if (error)
+ return error;
+ }
+
+ /*
+ * Iterate through the mntfs's and call
+ * the underlying init routine on each
+ */
+ glob_error = 0;
+
+ for (i = 0; i < nx->nx_c; i++) {
+ amfs_nfsx_mnt *n = &nx->nx_v[i];
+ mntfs *m = n->n_mnt;
+ int error = (*m->mf_ops->fs_init) (m);
+ /*
+ * if you just "return error" here, you will have made a failure
+ * in any submounts to fail the whole group. There was old unused code
+ * here before.
+ */
+ if (error > 0)
+ n->n_error = error;
+
+ else if (error < 0) {
+ glob_error = -1;
+ if (!asked_for_wakeup) {
+ asked_for_wakeup = 1;
+ sched_task(wakeup_task, (voidp) mf, (voidp) m);
+ }
+ }
+ }
+
+ return glob_error;
+}
+
+
+static void
+amfs_nfsx_cont(int rc, int term, voidp closure)
+{
+ mntfs *mf = (mntfs *) closure;
+ struct amfs_nfsx *nx = (struct amfs_nfsx *) mf->mf_private;
+ amfs_nfsx_mnt *n = nx->nx_try;
+
+ n->n_mnt->mf_flags &= ~(MFF_ERROR | MFF_MOUNTING);
+ mf->mf_flags &= ~MFF_ERROR;
+
+ /*
+ * Wakeup anything waiting for this mount
+ */
+ wakeup((voidp) n->n_mnt);
+
+ if (rc || term) {
+ if (term) {
+ /*
+ * Not sure what to do for an error code.
+ */
+ plog(XLOG_ERROR, "mount for %s got signal %d", n->n_mnt->mf_mount, term);
+ n->n_error = EIO;
+ } else {
+ /*
+ * Check for exit status
+ */
+ errno = rc; /* XXX */
+ plog(XLOG_ERROR, "%s: mount (amfs_nfsx_cont): %m", n->n_mnt->mf_mount);
+ n->n_error = rc;
+ }
+ free_mntfs(n->n_mnt);
+ n->n_mnt = new_mntfs();
+ n->n_mnt->mf_error = n->n_error;
+ n->n_mnt->mf_flags |= MFF_ERROR;
+ } else {
+ /*
+ * The mount worked.
+ */
+ mf_mounted(n->n_mnt);
+ n->n_error = 0;
+ }
+
+ /*
+ * Do the remaining bits
+ */
+ if (amfs_nfsx_fmount(mf) >= 0) {
+ wakeup((voidp) mf);
+ mf->mf_flags &= ~MFF_MOUNTING;
+ mf_mounted(mf);
+ }
+}
+
+
+static int
+try_amfs_nfsx_mount(voidp mv)
+{
+ mntfs *mf = (mntfs *) mv;
+ int error;
+
+ mf->mf_flags |= MFF_MOUNTING;
+ error = (*mf->mf_ops->fmount_fs) (mf);
+ mf->mf_flags &= ~MFF_MOUNTING;
+
+ return error;
+}
+
+
+static int
+amfs_nfsx_remount(mntfs *mf, int fg)
+{
+ struct amfs_nfsx *nx = (struct amfs_nfsx *) mf->mf_private;
+ amfs_nfsx_mnt *n;
+ int glob_error = -1;
+
+ for (n = nx->nx_v; n < nx->nx_v + nx->nx_c; n++) {
+ mntfs *m = n->n_mnt;
+ if (n->n_error < 0) {
+ if (!(m->mf_flags & MFF_MKMNT) && m->mf_ops->fs_flags & FS_MKMNT) {
+ int error = mkdirs(m->mf_mount, 0555);
+ if (!error)
+ m->mf_flags |= MFF_MKMNT;
+ }
+ }
+ }
+
+ /*
+ * Iterate through the mntfs's and mount each filesystem
+ * which is not yet mounted.
+ */
+ for (n = nx->nx_v; n < nx->nx_v + nx->nx_c; n++) {
+ mntfs *m = n->n_mnt;
+ if (n->n_error < 0) {
+ /*
+ * Check fmount entry pt. exists
+ * and then mount...
+ */
+ if (!m->mf_ops->fmount_fs) {
+ n->n_error = EINVAL;
+ } else {
+#ifdef DEBUG
+ dlog("calling underlying fmount on %s", m->mf_mount);
+#endif /* DEBUG */
+ if (!fg && foreground && (m->mf_ops->fs_flags & FS_MBACKGROUND)) {
+ m->mf_flags |= MFF_MOUNTING; /* XXX */
+#ifdef DEBUG
+ dlog("backgrounding mount of \"%s\"", m->mf_info);
+#endif /* DEBUG */
+ nx->nx_try = n;
+ run_task(try_amfs_nfsx_mount, (voidp) m, amfs_nfsx_cont, (voidp) mf);
+ n->n_error = -1;
+ return -1;
+ } else {
+#ifdef DEBUG
+ dlog("foreground mount of \"%s\" ...", mf->mf_info);
+#endif /* DEBUG */
+ n->n_error = (*m->mf_ops->fmount_fs) (m);
+ }
+ }
+
+#ifdef DEBUG
+ if (n->n_error > 0) {
+ errno = n->n_error; /* XXX */
+ dlog("underlying fmount of %s failed: %m", m->mf_mount);
+ }
+#endif /* DEBUG */
+
+ if (n->n_error == 0) {
+ glob_error = 0;
+ } else if (glob_error < 0) {
+ glob_error = n->n_error;
+ }
+ }
+ }
+
+ return glob_error < 0 ? 0 : glob_error;
+}
+
+
+static int
+amfs_nfsx_fmount(mntfs *mf)
+{
+ return amfs_nfsx_remount(mf, FALSE);
+}
+
+
+/*
+ * Unmount an NFS hierarchy.
+ * Note that this is called in the foreground
+ * and so may hang under extremely rare conditions.
+ */
+static int
+amfs_nfsx_fumount(mntfs *mf)
+{
+ struct amfs_nfsx *nx = (struct amfs_nfsx *) mf->mf_private;
+ amfs_nfsx_mnt *n;
+ int glob_error = 0;
+
+ /*
+ * Iterate in reverse through the mntfs's and unmount each filesystem
+ * which is mounted.
+ */
+ for (n = nx->nx_v + nx->nx_c - 1; n >= nx->nx_v; --n) {
+ mntfs *m = n->n_mnt;
+ /*
+ * If this node has not been messed with
+ * and there has been no error so far
+ * then try and unmount.
+ * If an error had occured then zero
+ * the error code so that the remount
+ * only tries to unmount those nodes
+ * which had been successfully unmounted.
+ */
+ if (n->n_error == 0) {
+#ifdef DEBUG
+ dlog("calling underlying fumount on %s", m->mf_mount);
+#endif /* DEBUG */
+ n->n_error = (*m->mf_ops->fumount_fs) (m);
+ if (n->n_error) {
+ glob_error = n->n_error;
+ n->n_error = 0;
+ } else {
+ /*
+ * Make sure remount gets this node
+ */
+ n->n_error = -1;
+ }
+ }
+ }
+
+ /*
+ * If any unmounts failed then remount the
+ * whole lot...
+ */
+ if (glob_error) {
+ glob_error = amfs_nfsx_remount(mf, TRUE);
+ if (glob_error) {
+ errno = glob_error; /* XXX */
+ plog(XLOG_USER, "amfs_nfsx: remount of %s failed: %m", mf->mf_mount);
+ }
+ glob_error = EBUSY;
+ } else {
+ /*
+ * Remove all the mount points
+ */
+ for (n = nx->nx_v; n < nx->nx_v + nx->nx_c; n++) {
+ mntfs *m = n->n_mnt;
+ am_node am;
+
+ /*
+ * XXX: all the umounted handler needs is a
+ * mntfs pointer, so pass an am_node with the right
+ * pointer in it.
+ */
+ memset((voidp) &am, 0, sizeof(am));
+ am.am_mnt = m;
+#ifdef DEBUG
+ dlog("calling underlying umounted on %s", m->mf_mount);
+#endif /* DEBUG */
+ (*m->mf_ops->umounted) (&am);
+
+ if (n->n_error < 0) {
+ if (m->mf_ops->fs_flags & FS_MKMNT) {
+ (void) rmdirs(m->mf_mount);
+ m->mf_flags &= ~MFF_MKMNT;
+ }
+ }
+ free_mntfs(m);
+ n->n_mnt = 0;
+ n->n_error = -1;
+ }
+ }
+
+ return glob_error;
+}
diff --git a/contrib/amd/amd/amfs_program.c b/contrib/amd/amd/amfs_program.c
new file mode 100644
index 000000000000..2bd077873cb5
--- /dev/null
+++ b/contrib/amd/amd/amfs_program.c
@@ -0,0 +1,191 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: amfs_program.c,v 5.2.2.1 1992/02/09 15:08:56 jsp beta $
+ *
+ */
+
+/*
+ * Program file system
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+/* forward definitions */
+static char *amfs_program_match(am_opts *fo);
+static int amfs_program_fmount(mntfs *mf);
+static int amfs_program_fumount(mntfs *mf);
+static int amfs_program_init(mntfs *mf);
+
+/*
+ * Ops structure
+ */
+am_ops amfs_program_ops =
+{
+ "program",
+ amfs_program_match,
+ amfs_program_init,
+ amfs_auto_fmount,
+ amfs_program_fmount,
+ amfs_auto_fumount,
+ amfs_program_fumount,
+ amfs_error_lookuppn,
+ amfs_error_readdir,
+ 0, /* amfs_program_readlink */
+ 0, /* amfs_program_mounted */
+ 0, /* amfs_program_umounted */
+ find_amfs_auto_srvr,
+ FS_BACKGROUND | FS_AMQINFO
+};
+
+
+/*
+ * Execute needs a mount and unmount command.
+ */
+static char *
+amfs_program_match(am_opts *fo)
+{
+ char *prog;
+
+ if (!fo->opt_mount || !fo->opt_unmount) {
+ plog(XLOG_USER, "program: no mount/unmount specified");
+ return 0;
+ }
+ prog = strchr(fo->opt_mount, ' ');
+
+ return strdup(prog ? prog + 1 : fo->opt_mount);
+}
+
+
+static int
+amfs_program_init(mntfs *mf)
+{
+ /*
+ * Save unmount command
+ */
+ if (mf->mf_refc == 1) {
+ mf->mf_private = (voidp) strdup(mf->mf_fo->opt_unmount);
+ mf->mf_prfree = (void (*)(voidp)) free;
+ }
+
+ return 0;
+}
+
+
+static int
+amfs_program_exec(char *info)
+{
+ char **xivec;
+ int error;
+
+ /*
+ * Split copy of command info string
+ */
+ info = strdup(info);
+ if (info == 0)
+ return ENOBUFS;
+ xivec = strsplit(info, ' ', '\'');
+
+ /*
+ * Put stdout to stderr
+ */
+ (void) fclose(stdout);
+ (void) dup(fileno(logfp));
+ if (fileno(logfp) != fileno(stderr)) {
+ (void) fclose(stderr);
+ (void) dup(fileno(logfp));
+ }
+
+ /*
+ * Try the exec
+ */
+#ifdef DEBUG
+ amuDebug(D_FULL) {
+ char **cp = xivec;
+ plog(XLOG_DEBUG, "executing (un)mount command...");
+ while (*cp) {
+ plog(XLOG_DEBUG, "arg[%d] = '%s'", cp - xivec, *cp);
+ cp++;
+ }
+ }
+#endif /* DEBUG */
+
+ if (xivec[0] == 0 || xivec[1] == 0) {
+ errno = EINVAL;
+ plog(XLOG_USER, "1st/2nd args missing to (un)mount program");
+ } else {
+ (void) execv(xivec[0], xivec + 1);
+ }
+
+ /*
+ * Save error number
+ */
+ error = errno;
+ plog(XLOG_ERROR, "exec failed: %m");
+
+ /*
+ * Free allocate memory
+ */
+ XFREE(info);
+ XFREE(xivec);
+
+ /*
+ * Return error
+ */
+ return error;
+}
+
+
+static int
+amfs_program_fmount(mntfs *mf)
+{
+ return amfs_program_exec(mf->mf_fo->opt_mount);
+}
+
+
+static int
+amfs_program_fumount(mntfs *mf)
+{
+ return amfs_program_exec((char *) mf->mf_private);
+}
diff --git a/contrib/amd/amd/amfs_root.c b/contrib/amd/amd/amfs_root.c
new file mode 100644
index 000000000000..3c1cd17cda9b
--- /dev/null
+++ b/contrib/amd/amd/amfs_root.c
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: amfs_root.c,v 1.1 1997-1998/06/30 19:22:30 ezk Exp ezk $
+ *
+ */
+
+/*
+ * Root file system
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+/****************************************************************************
+ *** FORWARD DEFINITIONS ***
+ ****************************************************************************/
+static int amfs_root_mount(am_node *mp);
+
+/****************************************************************************
+ *** OPS STRUCTURES ***
+ ****************************************************************************/
+am_ops amfs_root_ops =
+{
+ "root",
+ 0, /* amfs_root_match */
+ 0, /* amfs_root_init */
+ amfs_root_mount,
+ 0,
+ amfs_auto_umount,
+ 0,
+ amfs_auto_lookuppn,
+ amfs_auto_readdir,
+ 0, /* amfs_root_readlink */
+ 0, /* amfs_root_mounted */
+ 0, /* amfs_root_umounted */
+ find_amfs_auto_srvr,
+ FS_NOTIMEOUT | FS_AMQINFO | FS_DIRECTORY
+};
+
+
+/****************************************************************************
+ *** FUNCTIONS ***
+ ****************************************************************************/
+
+/*
+ * Mount the root...
+ */
+static int
+amfs_root_mount(am_node *mp)
+{
+ mntfs *mf = mp->am_mnt;
+
+ mf->mf_mount = strealloc(mf->mf_mount, pid_fsname);
+ mf->mf_private = (voidp) mapc_find(mf->mf_info, "", NULL);
+ mf->mf_prfree = mapc_free;
+
+ return 0;
+}
diff --git a/contrib/amd/amd/amfs_toplvl.c b/contrib/amd/amd/amfs_toplvl.c
new file mode 100644
index 000000000000..f36c66fc96b0
--- /dev/null
+++ b/contrib/amd/amd/amfs_toplvl.c
@@ -0,0 +1,355 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: amfs_toplvl.c,v 1.1 1997-1998/06/30 19:22:30 ezk Exp ezk $
+ *
+ */
+
+/*
+ * Top-level file system
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+/****************************************************************************
+ *** FORWARD DEFINITIONS ***
+ ****************************************************************************/
+
+
+/****************************************************************************
+ *** OPS STRUCTURES ***
+ ****************************************************************************/
+am_ops amfs_toplvl_ops =
+{
+ "toplvl",
+ amfs_auto_match,
+ 0, /* amfs_auto_init */
+ amfs_toplvl_mount,
+ 0,
+ amfs_toplvl_umount,
+ 0,
+ amfs_auto_lookuppn,
+ amfs_auto_readdir, /* browsable version of readdir() */
+ 0, /* amfs_toplvl_readlink */
+ amfs_toplvl_mounted,
+ 0, /* amfs_toplvl_umounted */
+ find_amfs_auto_srvr,
+ FS_MKMNT | FS_NOTIMEOUT | FS_BACKGROUND | FS_AMQINFO | FS_DIRECTORY
+};
+
+
+/****************************************************************************
+ *** FUNCTIONS ***
+ ****************************************************************************/
+
+/*
+ * Mount an automounter directory.
+ * The automounter is connected into the system
+ * as a user-level NFS server. mount_amfs_toplvl constructs
+ * the necessary NFS parameters to be given to the
+ * kernel so that it will talk back to us.
+ *
+ * NOTE: automounter mounts in themselves are using NFS Version 2.
+ */
+static int
+mount_amfs_toplvl(char *dir, char *opts)
+{
+ char fs_hostname[MAXHOSTNAMELEN + MAXPATHLEN + 1];
+ int retry, error, genflags;
+ mntent_t mnt;
+ nfs_args_t nfs_args;
+ am_nfs_fh *fhp;
+ am_nfs_handle_t anh;
+ MTYPE_TYPE type = MOUNT_TYPE_NFS;
+#ifndef HAVE_TRANSPORT_TYPE_TLI
+ u_short port;
+ struct sockaddr_in sin;
+#endif /* not HAVE_TRANSPORT_TYPE_TLI */
+
+ memset((voidp) &mnt, 0, sizeof(mnt));
+ mnt.mnt_dir = dir;
+ mnt.mnt_fsname = pid_fsname;
+ mnt.mnt_opts = opts;
+
+ /*
+ * Make sure that amd's top-level NFS mounts are hidden by default
+ * from df.
+ * If they don't appear to support the either the "ignore" mnttab
+ * option entry, or the "auto" one, set the mount type to "nfs".
+ */
+ mnt.mnt_type = HIDE_MOUNT_TYPE;
+
+ retry = hasmntval(&mnt, MNTTAB_OPT_RETRY);
+ if (retry <= 0)
+ retry = 2; /* XXX */
+
+ /*
+ * SET MOUNT ARGS
+ */
+ /*
+ * get fhandle of remote path for automount point
+ */
+ fhp = root_fh(dir);
+ if (!fhp) {
+ plog(XLOG_FATAL, "Can't find root file handle for %s", dir);
+ return EINVAL;
+ }
+
+#ifndef HAVE_TRANSPORT_TYPE_TLI
+ /*
+ * Create sockaddr to point to the local machine. 127.0.0.1
+ * is not used since that will not work in HP-UX clusters and
+ * this is no more expensive.
+ */
+ memset((voidp) &sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_addr = myipaddr;
+ port = hasmntval(&mnt, MNTTAB_OPT_PORT);
+ if (port) {
+ sin.sin_port = htons(port);
+ } else {
+ plog(XLOG_ERROR, "no port number specified for %s", dir);
+ return EINVAL;
+ }
+#endif /* not HAVE_TRANSPORT_TYPE_TLI */
+
+ /*
+ * Make a ``hostname'' string for the kernel
+ */
+ sprintf(fs_hostname, "pid%ld@%s:%s",
+ (long) (foreground ? mypid : getppid()),
+ hostname,
+ dir);
+ /*
+ * Most kernels have a name length restriction (64 bytes)...
+ */
+ if (strlen(fs_hostname) >= MAXHOSTNAMELEN)
+ strcpy(fs_hostname + MAXHOSTNAMELEN - 3, "..");
+#ifdef HOSTNAMESZ
+ /*
+ * ... and some of these restrictions are 32 bytes (HOSTNAMESZ)
+ * If you need to get the definition for HOSTNAMESZ found, you may
+ * add the proper header file to the conf/nfs_prot/nfs_prot_*.h file.
+ */
+ if (strlen(fs_hostname) >= HOSTNAMESZ)
+ strcpy(fs_hostname + HOSTNAMESZ - 3, "..");
+#endif /* HOSTNAMESZ */
+
+ /*
+ * Finally we can compute the mount genflags set above.
+ */
+ genflags = compute_mount_flags(&mnt);
+
+ /* setup the many fields and flags within nfs_args */
+ memmove(&anh.v2.fhs_fh, fhp, sizeof(*fhp));
+#ifdef HAVE_TRANSPORT_TYPE_TLI
+ compute_nfs_args(&nfs_args,
+ &mnt,
+ genflags,
+ nfsncp,
+ NULL, /* remote host IP addr is set below */
+ NFS_VERSION, /* version 2 */
+ "udp",
+ &anh,
+ fs_hostname,
+ pid_fsname);
+ /*
+ * IMPORTANT: set the correct IP address AFTERWARDS. It cannot
+ * be done using the normal mechanism of compute_nfs_args(), because
+ * that one will allocate a new address and use NFS_SA_DREF() to copy
+ * parts to it, while assuming that the ip_addr passed is always
+ * a "struct sockaddr_in". That assumption is incorrect on TLI systems,
+ * because they define a special macro HOST_SELF which is DIFFERENT
+ * than localhost (127.0.0.1)!
+ */
+ nfs_args.addr = &nfsxprt->xp_ltaddr;
+#else /* not HAVE_TRANSPORT_TYPE_TLI */
+ compute_nfs_args(&nfs_args,
+ &mnt,
+ genflags,
+ &sin,
+ NFS_VERSION, /* version 2 */
+ "udp",
+ &anh,
+ fs_hostname,
+ pid_fsname);
+#endif /* not HAVE_TRANSPORT_TYPE_TLI */
+
+ /*************************************************************************
+ * NOTE: while compute_nfs_args() works ok for regular NFS mounts *
+ * the toplvl one is not, and so some options must be corrected by hand *
+ * more carefully, *after* compute_nfs_args() runs. *
+ *************************************************************************/
+ compute_automounter_nfs_args(&nfs_args, &mnt);
+
+ /* This is it! Here we try to mount amd on its mount points */
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ print_nfs_args(&nfs_args, 0);
+#endif /* DEBUG */
+ error = mount_fs(&mnt, genflags, (caddr_t) &nfs_args, retry, type,
+ 0, NULL, mnttab_file_name);
+
+#ifdef HAVE_TRANSPORT_TYPE_TLI
+ free_knetconfig(nfs_args.knconf);
+ /*
+ * local automounter mounts do not allocate a special address, so
+ * no need to XFREE(nfs_args.addr) under TLI.
+ */
+#endif /* HAVE_TRANSPORT_TYPE_TLI */
+
+ return error;
+}
+
+
+/*
+ * Mount the top-level
+ */
+int
+amfs_toplvl_mount(am_node *mp)
+{
+ mntfs *mf = mp->am_mnt;
+ struct stat stb;
+ char opts[256], preopts[256];
+ int error;
+ char *mnttype;
+
+ /*
+ * Mounting the automounter.
+ * Make sure the mount directory exists, construct
+ * the mount options and call the mount_amfs_toplvl routine.
+ */
+
+ if (stat(mp->am_path, &stb) < 0) {
+ return errno;
+ } else if ((stb.st_mode & S_IFMT) != S_IFDIR) {
+ plog(XLOG_WARNING, "%s is not a directory", mp->am_path);
+ return ENOTDIR;
+ }
+ if (mf->mf_ops == &amfs_toplvl_ops)
+ mnttype = "indirect";
+ else if (mf->mf_ops == &amfs_direct_ops)
+ mnttype = "direct";
+#ifdef HAVE_AM_FS_UNION
+ else if (mf->mf_ops == &amfs_union_ops)
+ mnttype = "union";
+#endif /* HAVE_AM_FS_UNION */
+ else
+ mnttype = "auto";
+
+ /*
+ * Construct some mount options:
+ *
+ * Tack on magic map=<mapname> option in mtab to emulate
+ * SunOS automounter behavior.
+ */
+ preopts[0] = '\0';
+#ifdef MNTTAB_OPT_INTR
+ strcat(preopts, MNTTAB_OPT_INTR);
+ strcat(preopts, ",");
+#endif /* MNTTAB_OPT_INTR */
+#ifdef MNTTAB_OPT_IGNORE
+ strcat(preopts, MNTTAB_OPT_IGNORE);
+ strcat(preopts, ",");
+#endif /* MNTTAB_OPT_IGNORE */
+ sprintf(opts, "%s%s,%s=%d,%s=%d,%s=%d,%s,map=%s",
+ preopts,
+ MNTTAB_OPT_RW,
+ MNTTAB_OPT_PORT, nfs_port,
+ MNTTAB_OPT_TIMEO, gopt.amfs_auto_timeo,
+ MNTTAB_OPT_RETRANS, gopt.amfs_auto_retrans,
+ mnttype, mf->mf_info);
+
+ /* now do the mount */
+ error = mount_amfs_toplvl(mf->mf_mount, opts);
+ if (error) {
+ errno = error;
+ plog(XLOG_FATAL, "mount_amfs_toplvl: %m");
+ return error;
+ }
+ return 0;
+}
+
+
+void
+amfs_toplvl_mounted(mntfs *mf)
+{
+ amfs_auto_mkcacheref(mf);
+}
+
+
+/*
+ * Unmount a top-level automount node
+ */
+int
+amfs_toplvl_umount(am_node *mp)
+{
+ int error;
+ struct stat stb;
+
+again:
+ /*
+ * The lstat is needed if this mount is type=direct.
+ * When that happens, the kernel cache gets confused
+ * between the underlying type (dir) and the mounted
+ * type (link) and so needs to be re-synced before
+ * the unmount. This is all because the unmount system
+ * call follows links and so can't actually unmount
+ * a link (stupid!). It was noted that doing an ls -ld
+ * of the mount point to see why things were not working
+ * actually fixed the problem - so simulate an ls -ld here.
+ */
+ if (lstat(mp->am_path, &stb) < 0) {
+#ifdef DEBUG
+ dlog("lstat(%s): %m", mp->am_path);
+#endif /* DEBUG */
+ }
+ error = UMOUNT_FS(mp->am_path, mnttab_file_name);
+ if (error == EBUSY) {
+ plog(XLOG_WARNING, "amfs_toplvl_unmount retrying %s in 1s", mp->am_path);
+ sleep(1); /* XXX */
+ goto again;
+ }
+ return error;
+}
diff --git a/contrib/amd/amd/amfs_union.c b/contrib/amd/amd/amfs_union.c
new file mode 100644
index 000000000000..95fc8fd6d5b5
--- /dev/null
+++ b/contrib/amd/amd/amfs_union.c
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: amfs_union.c,v 1.1 1997-1998/06/30 19:22:30 ezk Exp ezk $
+ *
+ */
+
+/*
+ * Union automounter file system
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+/****************************************************************************
+ *** FORWARD DEFINITIONS ***
+ ****************************************************************************/
+static void amfs_union_mounted(mntfs *mf);
+
+
+/****************************************************************************
+ *** OPS STRUCTURES ***
+ ****************************************************************************/
+am_ops amfs_union_ops =
+{
+ "union",
+ amfs_auto_match,
+ 0, /* amfs_auto_init */
+ amfs_toplvl_mount,
+ 0,
+ amfs_toplvl_umount,
+ 0,
+ amfs_auto_lookuppn,
+ amfs_auto_readdir,
+ 0, /* amfs_toplvl_readlink */
+ amfs_union_mounted,
+ 0, /* amfs_toplvl_umounted */
+ find_amfs_auto_srvr,
+ FS_MKMNT | FS_NOTIMEOUT | FS_BACKGROUND | FS_AMQINFO | FS_DIRECTORY
+};
+
+
+/*
+ * Create a reference to a union'ed entry
+ * XXX: this function may not be used anywhere...
+ */
+static int
+create_amfs_union_node(char *dir, voidp arg)
+{
+ if (!STREQ(dir, "/defaults")) {
+ int error = 0;
+ (void) amfs_toplvl_ops.lookuppn(arg, dir, &error, VLOOK_CREATE);
+ if (error > 0) {
+ errno = error; /* XXX */
+ plog(XLOG_ERROR, "Could not mount %s: %m", dir);
+ }
+ return error;
+ }
+ return 0;
+}
+
+
+static void
+amfs_union_mounted(mntfs *mf)
+{
+ int i;
+
+ amfs_auto_mkcacheref(mf);
+
+ /*
+ * Having made the union mount point,
+ * populate all the entries...
+ */
+ for (i = 0; i <= last_used_map; i++) {
+ am_node *mp = exported_ap[i];
+ if (mp && mp->am_mnt == mf) {
+ /* return value from create_amfs_union_node is ignored by mapc_keyiter */
+ (void) mapc_keyiter((mnt_map *) mp->am_mnt->mf_private,
+ (void (*)(char *, voidp)) create_amfs_union_node,
+ mp);
+ break;
+ }
+ }
+}
diff --git a/contrib/amd/amd/amq_subr.c b/contrib/amd/amd/amq_subr.c
new file mode 100644
index 000000000000..775ee81080eb
--- /dev/null
+++ b/contrib/amd/amd/amq_subr.c
@@ -0,0 +1,501 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: amq_subr.c,v 5.2.2.1 1992/02/09 15:08:18 jsp beta $
+ *
+ */
+/*
+ * Auxilliary routines for amq tool
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+/* forward definitions */
+bool_t xdr_amq_mount_tree_node(XDR *xdrs, amq_mount_tree *objp);
+bool_t xdr_amq_mount_subtree(XDR *xdrs, amq_mount_tree *objp);
+
+
+voidp
+amqproc_null_1_svc(voidp argp, struct svc_req *rqstp)
+{
+ static char res;
+
+ return (voidp) &res;
+}
+
+
+/*
+ * Return a sub-tree of mounts
+ */
+amq_mount_tree_p *
+amqproc_mnttree_1_svc(voidp argp, struct svc_req *rqstp)
+{
+ static am_node *mp;
+
+ mp = find_ap(*(char **) argp);
+ return (amq_mount_tree_p *) &mp;
+}
+
+
+/*
+ * Unmount a single node
+ */
+voidp
+amqproc_umnt_1_svc(voidp argp, struct svc_req *rqstp)
+{
+ static char res;
+ am_node *mp = find_ap(*(char **) argp);
+
+ if (mp)
+ forcibly_timeout_mp(mp);
+
+ return (voidp) &res;
+}
+
+
+/*
+ * Return global statistics
+ */
+amq_mount_stats *
+amqproc_stats_1_svc(voidp argp, struct svc_req *rqstp)
+{
+ return (amq_mount_stats *) &amd_stats;
+}
+
+
+/*
+ * Return the entire tree of mount nodes
+ */
+amq_mount_tree_list *
+amqproc_export_1_svc(voidp argp, struct svc_req *rqstp)
+{
+ static amq_mount_tree_list aml;
+
+ aml.amq_mount_tree_list_val = (amq_mount_tree_p *) &exported_ap[0];
+ aml.amq_mount_tree_list_len = 1; /* XXX */
+
+ return &aml;
+}
+
+
+int *
+amqproc_setopt_1_svc(voidp argp, struct svc_req *rqstp)
+{
+ static int rc;
+ amq_setopt *opt = (amq_setopt *) argp;
+
+ rc = 0;
+
+ switch (opt->as_opt) {
+
+ case AMOPT_DEBUG:
+#ifdef DEBUG
+ if (debug_option(opt->as_str))
+#endif /* DEBUG */
+ rc = EINVAL;
+ break;
+
+ case AMOPT_LOGFILE:
+ if (gopt.logfile && opt->as_str
+ && STREQ(gopt.logfile, opt->as_str)) {
+ if (switch_to_logfile(opt->as_str))
+ rc = EINVAL;
+ } else {
+ rc = EACCES;
+ }
+ break;
+
+ case AMOPT_XLOG:
+ if (switch_option(opt->as_str))
+ rc = EINVAL;
+ break;
+
+ case AMOPT_FLUSHMAPC:
+ if (amd_state == Run) {
+ plog(XLOG_INFO, "amq says flush cache");
+ do_mapc_reload = 0;
+ flush_nfs_fhandle_cache((fserver *) 0);
+ flush_srvr_nfs_cache();
+ }
+ break;
+ }
+
+ return &rc;
+}
+
+
+amq_mount_info_list *
+amqproc_getmntfs_1_svc(voidp argp, struct svc_req *rqstp)
+{
+ return (amq_mount_info_list *) &mfhead; /* XXX */
+}
+
+#ifdef ENABLE_AMQ_MOUNT
+/*
+ * This is code that is vulnerable to IP spoofing attacks. Unless you
+ * absolutely need it, I suggest you do not enable it
+ * (using configure --enable-amq-mount)
+ */
+static int
+ok_security(struct svc_req *rqstp)
+{
+ struct sockaddr_in *sin = (struct sockaddr_in *) NULL;
+
+ if ((sin = amu_svc_getcaller(rqstp->rq_xprt)) == NULL) {
+ plog(XLOG_ERROR, "amu_svc_getcaller returned NULL");
+ return(0); /* assume security is therefore not OK */
+ }
+
+ if (ntohs(sin->sin_port) >= 1024 ||
+ !(sin->sin_addr.s_addr == htonl(0x7f000001) ||
+ sin->sin_addr.s_addr == myipaddr.s_addr)) {
+ char dq[20];
+ plog(XLOG_INFO, "AMQ request from %s.%d DENIED",
+ inet_dquad(dq, sin->sin_addr.s_addr),
+ ntohs(sin->sin_port));
+ return (0);
+ }
+
+ return (1);
+}
+
+
+int *
+amqproc_mount_1_svc(voidp argp, struct svc_req *rqstp)
+{
+ static int rc;
+ char *s = *(amq_string *) argp;
+ char *cp;
+
+ plog(XLOG_INFO, "amq requested mount of %s", s);
+ /*
+ * Minimalist security check.
+ */
+ if (!ok_security(rqstp)) {
+ rc = EACCES;
+ return &rc;
+ }
+ /*
+ * Find end of key
+ */
+ for (cp = (char *) s; *cp && (!isascii(*cp) || !isspace(*cp)); cp++) ;
+
+ if (!*cp) {
+ plog(XLOG_INFO, "amqproc_mount: Invalid arguments");
+ rc = EINVAL;
+ return &rc;
+ }
+ *cp++ = '\0';
+
+ /*
+ * Find start of value
+ */
+ while (*cp && isascii(*cp) && isspace(*cp))
+ cp++;
+
+ root_newmap(s, cp, (char *) 0, NULL);
+ rc = mount_auto_node(s, (voidp) root_node);
+ if (rc < 0)
+ return 0;
+ return &rc;
+}
+
+#else /* not ENABLE_AMQ_MOUNT */
+
+int *
+amqproc_mount_1_svc(voidp argp, struct svc_req *rqstp)
+{
+ static int rc;
+ char *s = *(amq_string *) argp;
+
+ plog(XLOG_ERROR, "amq requested mount of %s, but code is disabled", s);
+
+ rc = EINVAL;
+ return &rc;
+}
+#endif /* not ENABLE_AMQ_MOUNT */
+
+
+amq_string *
+amqproc_getvers_1_svc(voidp argp, struct svc_req *rqstp)
+{
+ static amq_string res;
+
+ res = get_version_string();
+ return &res;
+}
+
+
+/* get PID of remote amd */
+int *
+amqproc_getpid_1_svc(voidp argp, struct svc_req *rqstp)
+{
+ static int res;
+
+ res = getpid();
+ return &res;
+}
+
+
+/*
+ * XDR routines.
+ */
+
+
+bool_t
+xdr_amq_setopt(XDR *xdrs, amq_setopt *objp)
+{
+ if (!xdr_enum(xdrs, (enum_t *) & objp->as_opt)) {
+ return (FALSE);
+ }
+ if (!xdr_string(xdrs, &objp->as_str, AMQ_STRLEN)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+
+/*
+ * More XDR routines - Should be used for OUTPUT ONLY.
+ */
+bool_t
+xdr_amq_mount_tree_node(XDR *xdrs, amq_mount_tree *objp)
+{
+ am_node *mp = (am_node *) objp;
+
+ if (!xdr_amq_string(xdrs, &mp->am_mnt->mf_info)) {
+ return (FALSE);
+ }
+ if (!xdr_amq_string(xdrs, &mp->am_path)) {
+ return (FALSE);
+ }
+ if (!xdr_amq_string(xdrs, mp->am_link ? &mp->am_link : &mp->am_mnt->mf_mount)) {
+ return (FALSE);
+ }
+ if (!xdr_amq_string(xdrs, &mp->am_mnt->mf_ops->fs_type)) {
+ return (FALSE);
+ }
+ if (!xdr_long(xdrs, (long *) &mp->am_stats.s_mtime)) {
+ return (FALSE);
+ }
+ if (!xdr_u_short(xdrs, &mp->am_stats.s_uid)) {
+ return (FALSE);
+ }
+ if (!xdr_int(xdrs, &mp->am_stats.s_getattr)) {
+ return (FALSE);
+ }
+ if (!xdr_int(xdrs, &mp->am_stats.s_lookup)) {
+ return (FALSE);
+ }
+ if (!xdr_int(xdrs, &mp->am_stats.s_readdir)) {
+ return (FALSE);
+ }
+ if (!xdr_int(xdrs, &mp->am_stats.s_readlink)) {
+ return (FALSE);
+ }
+ if (!xdr_int(xdrs, &mp->am_stats.s_statfs)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+
+bool_t
+xdr_amq_mount_subtree(XDR *xdrs, amq_mount_tree *objp)
+{
+ am_node *mp = (am_node *) objp;
+
+ if (!xdr_amq_mount_tree_node(xdrs, objp)) {
+ return (FALSE);
+ }
+ if (!xdr_pointer(xdrs, (char **) &mp->am_osib, sizeof(amq_mount_tree), (XDRPROC_T_TYPE) xdr_amq_mount_subtree)) {
+ return (FALSE);
+ }
+ if (!xdr_pointer(xdrs, (char **) &mp->am_child, sizeof(amq_mount_tree), (XDRPROC_T_TYPE) xdr_amq_mount_subtree)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+
+bool_t
+xdr_amq_mount_tree(XDR *xdrs, amq_mount_tree *objp)
+{
+ am_node *mp = (am_node *) objp;
+ am_node *mnil = 0;
+
+ if (!xdr_amq_mount_tree_node(xdrs, objp)) {
+ return (FALSE);
+ }
+ if (!xdr_pointer(xdrs, (char **) &mnil, sizeof(amq_mount_tree), (XDRPROC_T_TYPE) xdr_amq_mount_subtree)) {
+ return (FALSE);
+ }
+ if (!xdr_pointer(xdrs, (char **) &mp->am_child, sizeof(amq_mount_tree), (XDRPROC_T_TYPE) xdr_amq_mount_subtree)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+
+bool_t
+xdr_amq_mount_tree_p(XDR *xdrs, amq_mount_tree_p *objp)
+{
+ if (!xdr_pointer(xdrs, (char **) objp, sizeof(amq_mount_tree), (XDRPROC_T_TYPE) xdr_amq_mount_tree)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+
+bool_t
+xdr_amq_mount_stats(XDR *xdrs, amq_mount_stats *objp)
+{
+ if (!xdr_int(xdrs, &objp->as_drops)) {
+ return (FALSE);
+ }
+ if (!xdr_int(xdrs, &objp->as_stale)) {
+ return (FALSE);
+ }
+ if (!xdr_int(xdrs, &objp->as_mok)) {
+ return (FALSE);
+ }
+ if (!xdr_int(xdrs, &objp->as_merr)) {
+ return (FALSE);
+ }
+ if (!xdr_int(xdrs, &objp->as_uerr)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+
+
+bool_t
+xdr_amq_mount_tree_list(XDR *xdrs, amq_mount_tree_list *objp)
+{
+ if (!xdr_array(xdrs,
+ (char **) &objp->amq_mount_tree_list_val,
+ (u_int *) &objp->amq_mount_tree_list_len,
+ ~0,
+ sizeof(amq_mount_tree_p),
+ (XDRPROC_T_TYPE) xdr_amq_mount_tree_p)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+
+
+/*
+ * Compute length of list
+ */
+bool_t
+xdr_amq_mount_info_qelem(XDR *xdrs, qelem *qhead)
+{
+ mntfs *mf;
+ u_int len = 0;
+
+ for (mf = AM_LAST(mntfs, qhead); mf != HEAD(mntfs, qhead); mf = PREV(mntfs, mf)) {
+ if (!(mf->mf_ops->fs_flags & FS_AMQINFO))
+ continue;
+ len++;
+ }
+ xdr_u_int(xdrs, &len);
+
+ /*
+ * Send individual data items
+ */
+ for (mf = AM_LAST(mntfs, qhead); mf != HEAD(mntfs, qhead); mf = PREV(mntfs, mf)) {
+ int up;
+ if (!(mf->mf_ops->fs_flags & FS_AMQINFO))
+ continue;
+
+ if (!xdr_amq_string(xdrs, &mf->mf_ops->fs_type)) {
+ return (FALSE);
+ }
+ if (!xdr_amq_string(xdrs, &mf->mf_mount)) {
+ return (FALSE);
+ }
+ if (!xdr_amq_string(xdrs, &mf->mf_info)) {
+ return (FALSE);
+ }
+ if (!xdr_amq_string(xdrs, &mf->mf_server->fs_host)) {
+ return (FALSE);
+ }
+ if (!xdr_int(xdrs, &mf->mf_error)) {
+ return (FALSE);
+ }
+ if (!xdr_int(xdrs, &mf->mf_refc)) {
+ return (FALSE);
+ }
+ if (mf->mf_server->fs_flags & FSF_ERROR)
+ up = 0;
+ else
+ switch (mf->mf_server->fs_flags & (FSF_DOWN | FSF_VALID)) {
+ case FSF_DOWN | FSF_VALID:
+ up = 0;
+ break;
+ case FSF_VALID:
+ up = 1;
+ break;
+ default:
+ up = -1;
+ break;
+ }
+ if (!xdr_int(xdrs, &up)) {
+ return (FALSE);
+ }
+ }
+ return (TRUE);
+}
+
+
+bool_t
+xdr_pri_free(XDRPROC_T_TYPE xdr_args, caddr_t args_ptr)
+{
+ XDR xdr;
+
+ xdr.x_op = XDR_FREE;
+ return ((*xdr_args) (&xdr, (caddr_t *) args_ptr));
+}
diff --git a/contrib/amd/amd/amq_svc.c b/contrib/amd/amd/amq_svc.c
new file mode 100644
index 000000000000..5e4c9fd25084
--- /dev/null
+++ b/contrib/amd/amd/amq_svc.c
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: amq_svc.c,v 5.2.2.1 1992/02/09 15:09:26 jsp beta $
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+/* typedefs */
+typedef char *(*amqsvcproc_t)(voidp, struct svc_req *);
+
+
+void
+amq_program_1(struct svc_req *rqstp, SVCXPRT *transp)
+{
+ union {
+ amq_string amqproc_mnttree_1_arg;
+ amq_string amqproc_umnt_1_arg;
+ amq_setopt amqproc_setopt_1_arg;
+ amq_string amqproc_mount_1_arg;
+ } argument;
+ char *result;
+ xdrproc_t xdr_argument, xdr_result;
+ amqsvcproc_t local;
+
+ switch (rqstp->rq_proc) {
+
+ case AMQPROC_NULL:
+ xdr_argument = (xdrproc_t) xdr_void;
+ xdr_result = (xdrproc_t) xdr_void;
+ local = (amqsvcproc_t) amqproc_null_1_svc;
+ break;
+
+ case AMQPROC_MNTTREE:
+ xdr_argument = (xdrproc_t) xdr_amq_string;
+ xdr_result = (xdrproc_t) xdr_amq_mount_tree_p;
+ local = (amqsvcproc_t) amqproc_mnttree_1_svc;
+ break;
+
+ case AMQPROC_UMNT:
+ xdr_argument = (xdrproc_t) xdr_amq_string;
+ xdr_result = (xdrproc_t) xdr_void;
+ local = (amqsvcproc_t) amqproc_umnt_1_svc;
+ break;
+
+ case AMQPROC_STATS:
+ xdr_argument = (xdrproc_t) xdr_void;
+ xdr_result = (xdrproc_t) xdr_amq_mount_stats;
+ local = (amqsvcproc_t) amqproc_stats_1_svc;
+ break;
+
+ case AMQPROC_EXPORT:
+ xdr_argument = (xdrproc_t) xdr_void;
+ xdr_result = (xdrproc_t) xdr_amq_mount_tree_list;
+ local = (amqsvcproc_t) amqproc_export_1_svc;
+ break;
+
+ case AMQPROC_SETOPT:
+ xdr_argument = (xdrproc_t) xdr_amq_setopt;
+ xdr_result = (xdrproc_t) xdr_int;
+ local = (amqsvcproc_t) amqproc_setopt_1_svc;
+ break;
+
+ case AMQPROC_GETMNTFS:
+ xdr_argument = (xdrproc_t) xdr_void;
+ xdr_result = (xdrproc_t) xdr_amq_mount_info_qelem;
+ local = (amqsvcproc_t) amqproc_getmntfs_1_svc;
+ break;
+
+ case AMQPROC_MOUNT:
+ xdr_argument = (xdrproc_t) xdr_amq_string;
+ xdr_result = (xdrproc_t) xdr_int;
+ local = (amqsvcproc_t) amqproc_mount_1_svc;
+ break;
+
+ case AMQPROC_GETVERS:
+ xdr_argument = (xdrproc_t) xdr_void;
+ xdr_result = (xdrproc_t) xdr_amq_string;
+ local = (amqsvcproc_t) amqproc_getvers_1_svc;
+ break;
+
+ case AMQPROC_GETPID:
+ xdr_argument = (xdrproc_t) xdr_void;
+ xdr_result = (xdrproc_t) xdr_int;
+ local = (amqsvcproc_t) amqproc_getpid_1_svc;
+ break;
+
+ default:
+ svcerr_noproc(transp);
+ return;
+ }
+
+ memset((char *) &argument, 0, sizeof(argument));
+ if (!svc_getargs(transp,
+ (XDRPROC_T_TYPE) xdr_argument,
+ (SVC_IN_ARG_TYPE) & argument)) {
+ svcerr_decode(transp);
+ return;
+ }
+
+ result = (*local) (&argument, rqstp);
+
+ if (result != NULL && !svc_sendreply(transp,
+ (XDRPROC_T_TYPE) xdr_result,
+ result)) {
+ svcerr_systemerr(transp);
+ }
+
+ if (!svc_freeargs(transp,
+ (XDRPROC_T_TYPE) xdr_argument,
+ (SVC_IN_ARG_TYPE) & argument)) {
+ plog(XLOG_FATAL, "unable to free rpc arguments in amqprog_1");
+ going_down(1);
+ }
+}
diff --git a/contrib/amd/amd/autil.c b/contrib/amd/amd/autil.c
new file mode 100644
index 000000000000..ca089bd17f5c
--- /dev/null
+++ b/contrib/amd/amd/autil.c
@@ -0,0 +1,418 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: autil.c,v 5.2.2.2 1992/03/07 17:52:06 jsp Exp $
+ *
+ */
+
+/*
+ * utilities specified to amd, taken out of the older amd/util.c.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+int NumChild = 0; /* number of children of primary amd */
+static char invalid_keys[] = "\"'!;@ \t\n";
+
+#ifdef HAVE_TRANSPORT_TYPE_TLI
+# define PARENT_USLEEP_TIME 100000 /* 0.1 seconds */
+#endif /* HAVE_TRANSPORT_TYPE_TLI */
+
+
+char *
+strealloc(char *p, char *s)
+{
+ int len = strlen(s) + 1;
+
+ p = (char *) xrealloc((voidp) p, len);
+
+ strcpy(p, s);
+#ifdef DEBUG_MEM
+ malloc_verify();
+#endif /* DEBUG_MEM */
+ return p;
+}
+
+
+char **
+strsplit(char *s, int ch, int qc)
+{
+ char **ivec;
+ int ic = 0;
+ int done = 0;
+
+ ivec = (char **) xmalloc((ic + 1) * sizeof(char *));
+
+ while (!done) {
+ char *v;
+
+ /*
+ * skip to split char
+ */
+ while (*s && (ch == ' ' ? (isascii(*s) && isspace((int)*s)) : *s == ch))
+ *s++ = '\0';
+
+ /*
+ * End of string?
+ */
+ if (!*s)
+ break;
+
+ /*
+ * remember start of string
+ */
+ v = s;
+
+ /*
+ * skip to split char
+ */
+ while (*s && !(ch == ' ' ? (isascii(*s) && isspace((int)*s)) : *s == ch)) {
+ if (*s++ == qc) {
+ /*
+ * Skip past string.
+ */
+ s++;
+ while (*s && *s != qc)
+ s++;
+ if (*s == qc)
+ s++;
+ }
+ }
+
+ if (!*s)
+ done = 1;
+ *s++ = '\0';
+
+ /*
+ * save string in new ivec slot
+ */
+ ivec[ic++] = v;
+ ivec = (char **) xrealloc((voidp) ivec, (ic + 1) * sizeof(char *));
+#ifdef DEBUG
+ amuDebug(D_STR)
+ plog(XLOG_DEBUG, "strsplit saved \"%s\"", v);
+#endif /* DEBUG */
+ }
+
+#ifdef DEBUG
+ amuDebug(D_STR)
+ plog(XLOG_DEBUG, "strsplit saved a total of %d strings", ic);
+#endif /* DEBUG */
+
+ ivec[ic] = 0;
+
+ return ivec;
+}
+
+
+/*
+ * Strip off the trailing part of a domain
+ * to produce a short-form domain relative
+ * to the local host domain.
+ * Note that this has no effect if the domain
+ * names do not have the same number of
+ * components. If that restriction proves
+ * to be a problem then the loop needs recoding
+ * to skip from right to left and do partial
+ * matches along the way -- ie more expensive.
+ */
+static void
+domain_strip(char *otherdom, char *localdom)
+{
+ char *p1, *p2;
+
+ if ((p1 = strchr(otherdom, '.')) &&
+ (p2 = strchr(localdom, '.')) &&
+ STREQ(p1 + 1, p2 + 1))
+ *p1 = '\0';
+}
+
+
+/*
+ * Normalize a host name
+ */
+void
+host_normalize(char **chp)
+{
+ /*
+ * Normalize hosts is used to resolve host name aliases
+ * and replace them with the standard-form name.
+ * Invoked with "-n" command line option.
+ */
+ if (gopt.flags & CFM_NORMALIZE_HOSTNAMES) {
+ struct hostent *hp;
+ clock_valid = 0;
+ hp = gethostbyname(*chp);
+ if (hp && hp->h_addrtype == AF_INET) {
+#ifdef DEBUG
+ dlog("Hostname %s normalized to %s", *chp, hp->h_name);
+#endif /* DEBUG */
+ *chp = strealloc(*chp, (char *) hp->h_name);
+ }
+ }
+ domain_strip(*chp, hostd);
+}
+
+
+
+/*
+ * Keys are not allowed to contain " ' ! or ; to avoid
+ * problems with macro expansions.
+ */
+int
+valid_key(char *key)
+{
+ while (*key)
+ if (strchr(invalid_keys, *key++))
+ return FALSE;
+ return TRUE;
+}
+
+
+void
+forcibly_timeout_mp(am_node *mp)
+{
+ mntfs *mf = mp->am_mnt;
+ /*
+ * Arrange to timeout this node
+ */
+ if (mf && ((mp->am_flags & AMF_ROOT) ||
+ (mf->mf_flags & (MFF_MOUNTING | MFF_UNMOUNTING)))) {
+ if (!(mf->mf_flags & MFF_UNMOUNTING))
+ plog(XLOG_WARNING, "ignoring timeout request for active node %s", mp->am_path);
+ } else {
+ plog(XLOG_INFO, "\"%s\" forcibly timed out", mp->am_path);
+ mp->am_flags &= ~AMF_NOTIMEOUT;
+ mp->am_ttl = clocktime();
+ reschedule_timeout_mp();
+ }
+}
+
+
+void
+mf_mounted(mntfs *mf)
+{
+ int quoted;
+ int wasmounted = mf->mf_flags & MFF_MOUNTED;
+
+ if (!wasmounted) {
+ /*
+ * If this is a freshly mounted
+ * filesystem then update the
+ * mntfs structure...
+ */
+ mf->mf_flags |= MFF_MOUNTED;
+ mf->mf_error = 0;
+
+ /*
+ * Do mounted callback
+ */
+ if (mf->mf_ops->mounted) {
+ (*mf->mf_ops->mounted) (mf);
+ }
+ mf->mf_fo = 0;
+ }
+
+ /*
+ * Log message
+ */
+ quoted = strchr(mf->mf_info, ' ') != 0;
+ plog(XLOG_INFO, "%s%s%s %s fstype %s on %s",
+ quoted ? "\"" : "",
+ mf->mf_info,
+ quoted ? "\"" : "",
+ wasmounted ? "referenced" : "mounted",
+ mf->mf_ops->fs_type, mf->mf_mount);
+}
+
+
+void
+am_mounted(am_node *mp)
+{
+ mntfs *mf = mp->am_mnt;
+
+ mf_mounted(mf);
+
+ /*
+ * Patch up path for direct mounts
+ */
+ if (mp->am_parent && mp->am_parent->am_mnt->mf_ops == &amfs_direct_ops)
+ mp->am_path = str3cat(mp->am_path, mp->am_parent->am_path, "/", ".");
+
+ /*
+ * Check whether this mount should be cached permanently
+ */
+ if (mf->mf_ops->fs_flags & FS_NOTIMEOUT) {
+ mp->am_flags |= AMF_NOTIMEOUT;
+ } else if (mf->mf_mount[1] == '\0' && mf->mf_mount[0] == '/') {
+ mp->am_flags |= AMF_NOTIMEOUT;
+ } else {
+ mntent_t mnt;
+ if (mf->mf_mopts) {
+ mnt.mnt_opts = mf->mf_mopts;
+ if (hasmntopt(&mnt, "nounmount"))
+ mp->am_flags |= AMF_NOTIMEOUT;
+ if ((mp->am_timeo = hasmntval(&mnt, "utimeout")) == 0)
+ mp->am_timeo = gopt.am_timeo;
+ }
+ }
+
+ /*
+ * If this node is a symlink then
+ * compute the length of the returned string.
+ */
+ if (mp->am_fattr.na_type == NFLNK)
+ mp->am_fattr.na_size = strlen(mp->am_link ? mp->am_link : mp->am_mnt->mf_mount);
+
+ /*
+ * Record mount time
+ */
+ mp->am_fattr.na_mtime.nt_seconds = mp->am_stats.s_mtime = clocktime();
+ new_ttl(mp);
+
+ /*
+ * Update mtime of parent node
+ */
+ if (mp->am_parent && mp->am_parent->am_mnt)
+ mp->am_parent->am_fattr.na_mtime.nt_seconds = mp->am_stats.s_mtime;
+
+ /*
+ * Now, if we can, do a reply to our NFS client here
+ * to speed things up.
+ */
+ quick_reply(mp, 0);
+
+ /*
+ * Update stats
+ */
+ amd_stats.d_mok++;
+}
+
+
+int
+mount_node(am_node *mp)
+{
+ mntfs *mf = mp->am_mnt;
+ int error = 0;
+
+ mf->mf_flags |= MFF_MOUNTING;
+ error = (*mf->mf_ops->mount_fs) (mp);
+
+ mf = mp->am_mnt;
+ if (error >= 0)
+ mf->mf_flags &= ~MFF_MOUNTING;
+ if (!error && !(mf->mf_ops->fs_flags & FS_MBACKGROUND)) {
+ /* ...but see ifs_mount */
+ am_mounted(mp);
+ }
+
+ return error;
+}
+
+
+void
+am_unmounted(am_node *mp)
+{
+ mntfs *mf = mp->am_mnt;
+
+ if (!foreground) /* firewall - should never happen */
+ return;
+
+ /*
+ * Do unmounted callback
+ */
+ if (mf->mf_ops->umounted)
+ (*mf->mf_ops->umounted) (mp);
+
+ /*
+ * Update mtime of parent node
+ */
+ if (mp->am_parent && mp->am_parent->am_mnt)
+ mp->am_parent->am_fattr.na_mtime.nt_seconds = clocktime();
+
+ free_map(mp);
+}
+
+
+/*
+ * Fork the automounter
+ *
+ * TODO: Need a better strategy for handling errors
+ */
+static int
+dofork(void)
+{
+ int pid;
+
+top:
+ pid = fork();
+
+ if (pid < 0) { /* fork error, retry in 1 second */
+ sleep(1);
+ goto top;
+ }
+ if (pid == 0) { /* child process (foreground==false) */
+ mypid = getpid();
+ foreground = 0;
+ } else { /* parent process, has one more child */
+ NumChild++;
+ }
+
+ return pid;
+}
+
+
+int
+background(void)
+{
+ int pid = dofork();
+
+ if (pid == 0) {
+#ifdef DEBUG
+ dlog("backgrounded");
+#endif /* DEBUG */
+ foreground = 0;
+ }
+ return pid;
+}
diff --git a/contrib/amd/amd/clock.c b/contrib/amd/amd/clock.c
new file mode 100644
index 000000000000..3c0494a5e7a0
--- /dev/null
+++ b/contrib/amd/amd/clock.c
@@ -0,0 +1,247 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: clock.c,v 5.2.2.1 1992/02/09 15:08:20 jsp beta $
+ *
+ */
+
+/*
+ * Callouts.
+ *
+ * Modelled on kernel object of the same name.
+ * See usual references.
+ *
+ * Use of a heap-based mechanism was rejected:
+ * 1. more complex implementation needed.
+ * 2. not obvious that a list is too slow for Amd.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+int timeout(u_int secs, void (*fn) (voidp), voidp closure);
+void reschedule_timeouts(time_t now, time_t then);
+
+typedef struct callout callout;
+struct callout {
+ callout *c_next; /* List of callouts */
+ void (*c_fn) (voidp); /* Function to call */
+ voidp c_closure; /* Closure to pass to call */
+ time_t c_time; /* Time of call */
+ int c_id; /* Unique identifier */
+};
+
+static callout callouts; /* List of pending callouts */
+static callout *free_callouts; /* Cache of free callouts */
+static int nfree_callouts; /* Number on free list */
+static int callout_id; /* Next free callout identifier */
+
+time_t next_softclock; /* Time of next call to softclock() */
+
+
+/*
+ * Number of callout slots we keep on the free list
+ */
+#define CALLOUT_FREE_SLOP 10
+
+/*
+ * Global assumption: valid id's are non-zero.
+ */
+#define CID_ALLOC(struct ) (++callout_id)
+#define CID_UNDEF (0)
+
+
+static callout *
+alloc_callout(void)
+{
+ callout *cp = free_callouts;
+
+ if (cp) {
+ --nfree_callouts;
+ free_callouts = free_callouts->c_next;
+ return cp;
+ }
+ return ALLOC(struct callout);
+}
+
+
+static void
+free_callout(callout *cp)
+{
+ if (nfree_callouts > CALLOUT_FREE_SLOP) {
+ XFREE(cp);
+ } else {
+ cp->c_next = free_callouts;
+ free_callouts = cp;
+ nfree_callouts++;
+ }
+}
+
+
+/*
+ * Schedule a callout.
+ *
+ * (*fn)(closure) will be called at clocktime() + secs
+ */
+int
+timeout(u_int secs, void (*fn) (voidp), voidp closure)
+{
+ callout *cp, *cp2;
+ time_t t = clocktime() + secs;
+
+ /*
+ * Allocate and fill in a new callout structure
+ */
+ callout *cpnew = alloc_callout();
+ cpnew->c_closure = closure;
+ cpnew->c_fn = fn;
+ cpnew->c_time = t;
+ cpnew->c_id = CID_ALLOC(struct );
+
+ if (t < next_softclock)
+ next_softclock = t;
+
+ /*
+ * Find the correct place in the list
+ */
+ for (cp = &callouts; (cp2 = cp->c_next); cp = cp2)
+ if (cp2->c_time >= t)
+ break;
+
+ /*
+ * And link it in
+ */
+ cp->c_next = cpnew;
+ cpnew->c_next = cp2;
+
+ /*
+ * Return callout identifier
+ */
+ return cpnew->c_id;
+}
+
+
+/*
+ * De-schedule a callout
+ */
+void
+untimeout(int id)
+{
+ callout *cp, *cp2;
+ for (cp = &callouts; (cp2 = cp->c_next); cp = cp2) {
+ if (cp2->c_id == id) {
+ cp->c_next = cp2->c_next;
+ free_callout(cp2);
+ break;
+ }
+ }
+}
+
+
+/*
+ * Reschedule after clock changed
+ */
+void
+reschedule_timeouts(time_t now, time_t then)
+{
+ callout *cp;
+
+ for (cp = callouts.c_next; cp; cp = cp->c_next) {
+ if (cp->c_time >= now && cp->c_time <= then) {
+ plog(XLOG_WARNING, "job %d rescheduled to run immediately", cp->c_id);
+#ifdef DEBUG
+ dlog("rescheduling job %d back %d seconds", cp->c_id, cp->c_time - now);
+#endif /* DEBUG */
+ next_softclock = cp->c_time = now;
+ }
+ }
+}
+
+
+/*
+ * Clock handler
+ */
+int
+softclock(void)
+{
+ time_t now;
+ callout *cp;
+
+ do {
+ if (task_notify_todo)
+ do_task_notify();
+
+ now = clocktime();
+
+ /*
+ * While there are more callouts waiting...
+ */
+ while ((cp = callouts.c_next) && cp->c_time <= now) {
+ /*
+ * Extract first from list, save fn & closure and
+ * unlink callout from list and free.
+ * Finally call function.
+ *
+ * The free is done first because
+ * it is quite common that the
+ * function will call timeout()
+ * and try to allocate a callout
+ */
+ void (*fn) (voidp) = cp->c_fn;
+ voidp closure = cp->c_closure;
+
+ callouts.c_next = cp->c_next;
+ free_callout(cp);
+ (*fn) (closure);
+ }
+
+ } while (task_notify_todo);
+
+ /*
+ * Return number of seconds to next event,
+ * or 0 if there is no event.
+ */
+ if ((cp = callouts.c_next))
+ return cp->c_time - now;
+ return 0;
+}
diff --git a/contrib/amd/amd/conf.c b/contrib/amd/amd/conf.c
new file mode 100644
index 000000000000..a97b1b18c42a
--- /dev/null
+++ b/contrib/amd/amd/conf.c
@@ -0,0 +1,939 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: conf.c,v 5.2.2.1 1992/02/09 15:08:23 jsp beta $
+ *
+ */
+
+/*
+ * Functions to handle the configuration file.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+
+/*
+ * MACROS:
+ */
+/* Turn on to show some info about maps being configured */
+/* #define DEBUG_CONF */
+
+/*
+ * TYPEDEFS:
+ */
+typedef int (*OptFuncPtr)(const char *);
+
+/*
+ * STRUCTURES:
+ */
+struct _func_map {
+ char *name;
+ OptFuncPtr func;
+};
+
+/*
+ * FORWARD DECLARATIONS:
+ */
+static int gopt_arch(const char *val);
+static int gopt_auto_dir(const char *val);
+static int gopt_browsable_dirs(const char *val);
+static int gopt_cache_duration(const char *val);
+static int gopt_cluster(const char *val);
+static int gopt_debug_options(const char *val);
+static int gopt_dismount_interval(const char *val);
+static int gopt_fully_qualified_hosts(const char *val);
+static int gopt_hesiod_base(const char *val);
+static int gopt_karch(const char *val);
+static int gopt_ldap_base(const char *val);
+static int gopt_ldap_cache_maxmem(const char *val);
+static int gopt_ldap_cache_seconds(const char *val);
+static int gopt_ldap_hostports(const char *val);
+static int gopt_local_domain(const char *val);
+static int gopt_log_file(const char *val);
+static int gopt_log_options(const char *val);
+static int gopt_map_options(const char *val);
+static int gopt_map_type(const char *val);
+static int gopt_mount_type(const char *val);
+static int gopt_pid_file(const char *val);
+static int gopt_portmap_program(const char *val);
+static int gopt_nfs_retransmit_counter(const char *val);
+static int gopt_nfs_retry_interval(const char *val);
+static int gopt_nis_domain(const char *val);
+static int gopt_normalize_hostnames(const char *val);
+static int gopt_os(const char *val);
+static int gopt_osver(const char *val);
+static int gopt_plock(const char *val);
+static int gopt_print_pid(const char *val);
+static int gopt_print_version(const char *val);
+static int gopt_restart_mounts(const char *val);
+static int gopt_search_path(const char *val);
+static int gopt_selectors_on_default(const char *val);
+static int gopt_show_statfs_entries(const char *val);
+static int gopt_unmount_on_exit(const char *val);
+static int process_global_option(const char *key, const char *val);
+static int process_regular_map(cf_map_t *cfm);
+static int process_regular_option(const char *section, const char *key, const char *val, cf_map_t *cfm);
+static int ropt_browsable_dirs(const char *val, cf_map_t *cfm);
+static int ropt_map_name(const char *val, cf_map_t *cfm);
+static int ropt_map_options(const char *val, cf_map_t *cfm);
+static int ropt_map_type(const char *val, cf_map_t *cfm);
+static int ropt_mount_type(const char *val, cf_map_t *cfm);
+static int ropt_search_path(const char *val, cf_map_t *cfm);
+static int ropt_tag(const char *val, cf_map_t *cfm);
+static void reset_cf_map(cf_map_t *cfm);
+
+
+/*
+ * STATIC VARIABLES:
+ */
+static cf_map_t cur_map;
+static struct _func_map glob_functable[] = {
+ {"arch", gopt_arch},
+ {"auto_dir", gopt_auto_dir},
+ {"browsable_dirs", gopt_browsable_dirs},
+ {"cache_duration", gopt_cache_duration},
+ {"cluster", gopt_cluster},
+ {"debug_options", gopt_debug_options},
+ {"dismount_interval", gopt_dismount_interval},
+ {"fully_qualified_hosts", gopt_fully_qualified_hosts},
+ {"hesiod_base", gopt_hesiod_base},
+ {"karch", gopt_karch},
+ {"ldap_base", gopt_ldap_base},
+ {"ldap_cache_maxmem", gopt_ldap_cache_maxmem},
+ {"ldap_cache_seconds", gopt_ldap_cache_seconds},
+ {"ldap_hostports", gopt_ldap_hostports},
+ {"local_domain", gopt_local_domain},
+ {"log_file", gopt_log_file},
+ {"log_options", gopt_log_options},
+ {"map_options", gopt_map_options},
+ {"map_type", gopt_map_type},
+ {"mount_type", gopt_mount_type},
+ {"pid_file", gopt_pid_file},
+ {"portmap_program", gopt_portmap_program},
+ {"nfs_retransmit_counter", gopt_nfs_retransmit_counter},
+ {"nfs_retry_interval", gopt_nfs_retry_interval},
+ {"nis_domain", gopt_nis_domain},
+ {"normalize_hostnames", gopt_normalize_hostnames},
+ {"os", gopt_os},
+ {"osver", gopt_osver},
+ {"plock", gopt_plock},
+ {"print_pid", gopt_print_pid},
+ {"print_version", gopt_print_version},
+ {"restart_mounts", gopt_restart_mounts},
+ {"search_path", gopt_search_path},
+ {"selectors_on_default", gopt_selectors_on_default},
+ {"show_statfs_entries", gopt_show_statfs_entries},
+ {"unmount_on_exit", gopt_unmount_on_exit},
+ {NULL, NULL}
+};
+
+
+/*
+ * Reset a map.
+ */
+static void
+reset_cf_map(cf_map_t *cfm)
+{
+ if (!cfm)
+ return;
+
+ if (cfm->cfm_dir) {
+ XFREE(cfm->cfm_dir);
+ cfm->cfm_dir = NULL;
+ }
+
+ if (cfm->cfm_name) {
+ XFREE(cfm->cfm_name);
+ cfm->cfm_name = NULL;
+ }
+
+ if (cfm->cfm_tag) {
+ XFREE(cfm->cfm_tag);
+ cfm->cfm_tag = NULL;
+ }
+
+ /*
+ * reset/initialize a regular map's flags and other variables from the
+ * global ones, so that they are applied to all maps. Of course, each map
+ * can then override the flags individually.
+ *
+ * NOTES:
+ * (1): Will only work for maps that appear after [global].
+ * (2): Also be careful not to free() a global option.
+ * (3): I'm doing direct char* pointer comparison, and not strcmp(). This
+ * is correct!
+ */
+
+ /* initialize map_type from [global] */
+ if (cfm->cfm_type && cfm->cfm_type != gopt.map_type)
+ XFREE(cfm->cfm_type);
+ cfm->cfm_type = gopt.map_type;
+
+ /* initialize map_opts from [global] */
+ if (cfm->cfm_opts && cfm->cfm_opts != gopt.map_options)
+ XFREE(cfm->cfm_opts);
+ cfm->cfm_opts = gopt.map_options;
+
+ /* initialize search_path from [global] */
+ if (cfm->cfm_search_path && cfm->cfm_search_path != gopt.search_path)
+ XFREE(cfm->cfm_search_path);
+ cfm->cfm_search_path = gopt.search_path;
+
+ /*
+ * Initialize flags that are common both to [global] and a local map.
+ */
+ cfm->cfm_flags = gopt.flags & (CFM_BROWSABLE_DIRS |
+ CFM_BROWSABLE_DIRS_FULL |
+ CFM_MOUNT_TYPE_AUTOFS |
+ CFM_ENABLE_DEFAULT_SELECTORS);
+}
+
+
+/*
+ * Process configuration file options.
+ * Return 0 if OK, 1 otherwise.
+ */
+int
+set_conf_kv(const char *section, const char *key, const char *val)
+{
+ int ret;
+
+#ifdef DEBUG_CONF
+ fprintf(stderr,"set_conf_kv: section=%s, key=%s, val=%s\n",
+ section, key, val);
+#endif /* DEBUG_CONF */
+
+ /*
+ * If global section, process them one at a time.
+ */
+ if (STREQ(section, "global")) {
+ /*
+ * Check if a regular map was configured before "global",
+ * and process it as needed.
+ */
+ if (cur_map.cfm_dir) {
+ fprintf(stderr,"processing regular map \"%s\" before global one.\n",
+ section);
+ ret = process_regular_map(&cur_map); /* will reset map */
+ if (ret != 0)
+ return ret;
+ }
+
+ /* process the global option first */
+ ret = process_global_option(key, val);
+
+ /* reset default options for regular maps from just updated globals */
+ if (ret == 0)
+ reset_cf_map(&cur_map);
+
+ /* return status from the processing of the global option */
+ return ret;
+ }
+
+ /*
+ * otherwise save options and process a single map all at once.
+ */
+
+ /* check if we found a new map, so process one already collected */
+ if (cur_map.cfm_dir && !STREQ(cur_map.cfm_dir, section)) {
+ ret = process_regular_map(&cur_map); /* will reset map */
+ if (ret != 0)
+ return ret;
+ }
+
+ /* now process a single entry of a regular map */
+ return process_regular_option(section, key, val, &cur_map);
+}
+
+
+/*
+ * Process global section of configuration file options.
+ * Return 0 upon success, 1 otherwise.
+ */
+static int
+process_global_option(const char *key, const char *val)
+{
+ struct _func_map *gfp;
+
+ /* ensure that val is valid */
+ if (!val || val[0] == '\0')
+ return 1;
+
+ /*
+ * search for global function.
+ */
+ for (gfp = glob_functable; gfp->name; gfp++)
+ if (FSTREQ(gfp->name, key))
+ return (gfp->func)(val);
+
+ fprintf(stderr, "conf: unknown global key: \"%s\"\n", key);
+ return 1; /* failed to match any command */
+}
+
+
+static int
+gopt_arch(const char *val)
+{
+ gopt.arch = strdup((char *)val);
+ return 0;
+}
+
+
+static int
+gopt_auto_dir(const char *val)
+{
+ gopt.auto_dir = strdup((char *)val);
+ return 0;
+}
+
+
+static int
+gopt_browsable_dirs(const char *val)
+{
+ if (STREQ(val, "full")) {
+ gopt.flags |= CFM_BROWSABLE_DIRS_FULL;
+ return 0;
+ } else if (STREQ(val, "yes")) {
+ gopt.flags |= CFM_BROWSABLE_DIRS;
+ return 0;
+ } else if (STREQ(val, "no")) {
+ gopt.flags &= ~CFM_BROWSABLE_DIRS;
+ return 0;
+ }
+
+ fprintf(stderr, "conf: unknown value to browsable_dirs \"%s\"\n", val);
+ return 1; /* unknown value */
+}
+
+
+static int
+gopt_cache_duration(const char *val)
+{
+ gopt.am_timeo = atoi(val);
+ if (gopt.am_timeo <= 0)
+ gopt.am_timeo = AM_TTL;
+ return 0;
+}
+
+
+static int
+gopt_cluster(const char *val)
+{
+ gopt.cluster = strdup((char *)val);
+ return 0;
+}
+
+
+static int
+gopt_debug_options(const char *val)
+{
+#ifdef DEBUG
+ usage += debug_option(strdup((char *)val));
+ return 0;
+#else /* not DEBUG */
+ fprintf(stderr, "%s: not compiled with DEBUG option -- sorry.\n",
+ progname);
+ return 1;
+#endif /* not DEBUG */
+}
+
+
+static int
+gopt_dismount_interval(const char *val)
+{
+ gopt.am_timeo_w = atoi(val);
+ if (gopt.am_timeo_w <= 0)
+ gopt.am_timeo_w = AM_TTL_W;
+ return 0;
+}
+
+
+static int
+gopt_fully_qualified_hosts(const char *val)
+{
+ if (STREQ(val, "yes")) {
+ gopt.flags |= CFM_FULLY_QUALIFIED_HOSTS;
+ return 0;
+ } else if (STREQ(val, "no")) {
+ gopt.flags &= ~CFM_FULLY_QUALIFIED_HOSTS;
+ return 0;
+ }
+
+ fprintf(stderr, "conf: unknown value to fully_qualified_hosts \"%s\"\n", val);
+ return 1; /* unknown value */
+}
+
+
+static int
+gopt_hesiod_base(const char *val)
+{
+#ifdef HAVE_MAP_HESIOD
+ gopt.hesiod_base = strdup((char *)val);
+ return 0;
+#else /* not HAVE_MAP_HESIOD */
+ fprintf(stderr, "conf: hesiod_base option ignored. No Hesiod support available.\n");
+ return 1;
+#endif /* not HAVE_MAP_HESIOD */
+}
+
+
+static int
+gopt_karch(const char *val)
+{
+ gopt.karch = strdup((char *)val);
+ return 0;
+}
+
+
+static int
+gopt_pid_file(const char *val)
+{
+ gopt.pid_file = strdup((char *)val);
+ return 0;
+}
+
+
+static int
+gopt_local_domain(const char *val)
+{
+ gopt.sub_domain = strdup((char *)val);
+ return 0;
+}
+
+
+static int
+gopt_ldap_base(const char *val)
+{
+#ifdef HAVE_MAP_LDAP
+ gopt.ldap_base = strdup((char *)val);
+ return 0;
+#else /* not HAVE_MAP_LDAP */
+ fprintf(stderr, "conf: ldap_base option ignored. No LDAP support available.\n");
+ return 1;
+#endif /* not HAVE_MAP_LDAP */
+}
+
+
+static int
+gopt_ldap_cache_seconds(const char *val)
+{
+#ifdef HAVE_MAP_LDAP
+ char *end;
+
+ gopt.ldap_cache_seconds = strtol((char *)val, &end, 10);
+ if (end == val) {
+ fprintf(stderr, "conf: bad LDAP cache (seconds) option: %s\n",val);
+ return 1;
+ }
+ return 0;
+#else /* not HAVE_MAP_LDAP */
+ fprintf(stderr, "conf: ldap_cache option ignored. No LDAP support available.\n");
+ return 1;
+#endif /* not HAVE_MAP_LDAP */
+}
+
+
+static int
+gopt_ldap_cache_maxmem(const char *val)
+{
+#ifdef HAVE_MAP_LDAP
+ char *end;
+
+ gopt.ldap_cache_maxmem = strtol((char *)val, &end, 10);
+ if (end == val) {
+ fprintf(stderr, "conf: bad LDAP cache (maxmem) option: %s\n",val);
+ return 1;
+ }
+ return 0;
+#else /* not HAVE_MAP_LDAP */
+ fprintf(stderr, "conf: ldap_cache option ignored. No LDAP support available.\n");
+ return 1;
+#endif /* not HAVE_MAP_LDAP */
+}
+
+
+static int
+gopt_ldap_hostports(const char *val)
+{
+#ifdef HAVE_MAP_LDAP
+ gopt.ldap_hostports = strdup((char *)val);
+ return 0;
+#else /* not HAVE_MAP_LDAP */
+ fprintf(stderr, "conf: ldap_hostports option ignored. No LDAP support available.\n");
+ return 1;
+#endif /* not HAVE_MAP_LDAP */
+
+}
+
+
+static int
+gopt_log_file(const char *val)
+{
+ gopt.logfile = strdup((char *)val);
+ return 0;
+}
+
+
+static int
+gopt_log_options(const char *val)
+{
+ usage += switch_option(strdup((char *)val));
+ return 0;
+}
+
+
+static int
+gopt_map_options(const char *val)
+{
+ gopt.map_options = strdup((char *)val);
+ return 0;
+}
+
+
+static int
+gopt_map_type(const char *val)
+{
+ gopt.map_type = strdup((char *)val);
+ return 0;
+}
+
+
+static int
+gopt_mount_type(const char *val)
+{
+ if (STREQ(val, "autofs")) {
+#ifdef HAVE_FS_AUTOFS
+ gopt.flags |= CFM_MOUNT_TYPE_AUTOFS;
+ return 0;
+#else /* not HAVE_FS_AUTOFS */
+ fprintf(stderr, "conf: no autofs support available\n");
+ return 1;
+#endif /* not HAVE_FS_AUTOFS */
+ } else if (STREQ(val, "nfs")) {
+ gopt.flags &= ~CFM_MOUNT_TYPE_AUTOFS;
+ return 0;
+ }
+
+ fprintf(stderr, "conf: unknown value to mount_type \"%s\"\n", val);
+ return 1; /* unknown value */
+}
+
+
+static int
+gopt_portmap_program(const char *val)
+{
+ gopt.portmap_program = atoi(val);
+ /*
+ * allow alternate program numbers to be no more than 10 offset from
+ * official amd program number (300019).
+ */
+ if (gopt.portmap_program < AMQ_PROGRAM ||
+ gopt.portmap_program > AMQ_PROGRAM + 10) {
+ gopt.portmap_program = AMQ_PROGRAM;
+ set_amd_program_number(gopt.portmap_program);
+ fprintf(stderr, "conf: illegal amd program numver \"%s\"\n", val);
+ return 1;
+ }
+
+ set_amd_program_number(gopt.portmap_program);
+ return 0; /* all is OK */
+}
+
+
+static int
+gopt_nfs_retransmit_counter(const char *val)
+{
+ gopt.amfs_auto_retrans = atoi(val);
+ return 0;
+}
+
+
+static int
+gopt_nfs_retry_interval(const char *val)
+{
+ gopt.amfs_auto_timeo = atoi(val);
+ return 0;
+}
+
+
+static int
+gopt_nis_domain(const char *val)
+{
+#ifdef HAVE_MAP_NIS
+ gopt.nis_domain = strdup((char *)val);
+ return 0;
+#else /* not HAVE_MAP_NIS */
+ fprintf(stderr, "conf: nis_domain option ignored. No NIS support available.\n");
+ return 1;
+#endif /* not HAVE_MAP_NIS */
+}
+
+
+static int
+gopt_normalize_hostnames(const char *val)
+{
+ if (STREQ(val, "yes")) {
+ gopt.flags |= CFM_NORMALIZE_HOSTNAMES;
+ return 0;
+ } else if (STREQ(val, "no")) {
+ gopt.flags &= ~CFM_NORMALIZE_HOSTNAMES;
+ return 0;
+ }
+
+ fprintf(stderr, "conf: unknown value to normalize_hostnames \"%s\"\n", val);
+ return 1; /* unknown value */
+}
+
+
+static int
+gopt_os(const char *val)
+{
+ gopt.op_sys = strdup((char *)val);
+ return 0;
+}
+
+
+static int
+gopt_osver(const char *val)
+{
+ gopt.op_sys_ver = strdup((char *)val);
+ return 0;
+}
+
+
+static int
+gopt_plock(const char *val)
+{
+ if (STREQ(val, "yes")) {
+ gopt.flags |= CFM_PROCESS_LOCK;
+ return 0;
+ } else if (STREQ(val, "no")) {
+ gopt.flags &= ~CFM_PROCESS_LOCK;
+ return 0;
+ }
+
+ fprintf(stderr, "conf: unknown value to plock \"%s\"\n", val);
+ return 1; /* unknown value */
+}
+
+
+static int
+gopt_print_pid(const char *val)
+{
+ if (STREQ(val, "yes")) {
+ gopt.flags |= CFM_PRINT_PID;
+ return 0;
+ } else if (STREQ(val, "no")) {
+ gopt.flags &= ~CFM_PRINT_PID;
+ return 0;
+ }
+
+ fprintf(stderr, "conf: unknown value to print_pid \"%s\"\n", val);
+ return 1; /* unknown value */
+}
+
+
+static int
+gopt_print_version(const char *val)
+{
+ if (STREQ(val, "yes")) {
+ fputs(get_version_string(), stderr);
+ return 0;
+ } else if (STREQ(val, "no")) {
+ return 0;
+ }
+
+ fprintf(stderr, "conf: unknown value to print_version \"%s\"\n", val);
+ return 1; /* unknown value */
+}
+
+
+static int
+gopt_restart_mounts(const char *val)
+{
+ if (STREQ(val, "yes")) {
+ gopt.flags |= CFM_RESTART_EXISTING_MOUNTS;
+ return 0;
+ } else if (STREQ(val, "no")) {
+ gopt.flags &= ~CFM_RESTART_EXISTING_MOUNTS;
+ return 0;
+ }
+
+ fprintf(stderr, "conf: unknown value to restart_mounts \"%s\"\n", val);
+ return 1; /* unknown value */
+}
+
+
+static int
+gopt_search_path(const char *val)
+{
+ gopt.search_path = strdup((char *)val);
+ return 0;
+}
+
+
+static int
+gopt_selectors_on_default(const char *val)
+{
+ if (STREQ(val, "yes")) {
+ gopt.flags |= CFM_ENABLE_DEFAULT_SELECTORS;
+ return 0;
+ } else if (STREQ(val, "no")) {
+ gopt.flags &= ~CFM_ENABLE_DEFAULT_SELECTORS;
+ return 0;
+ }
+
+ fprintf(stderr, "conf: unknown value to enable_default_selectors \"%s\"\n", val);
+ return 1; /* unknown value */
+}
+
+
+static int
+gopt_show_statfs_entries(const char *val)
+{
+ if (STREQ(val, "yes")) {
+ gopt.flags |= CFM_SHOW_STATFS_ENTRIES;
+ return 0;
+ } else if (STREQ(val, "no")) {
+ gopt.flags &= ~CFM_SHOW_STATFS_ENTRIES;
+ return 0;
+ }
+
+ fprintf(stderr, "conf: unknown value to show_statfs_entries \"%s\"\n", val);
+ return 1; /* unknown value */
+}
+
+
+static int
+gopt_unmount_on_exit(const char *val)
+{
+ if (STREQ(val, "yes")) {
+ gopt.flags |= CFM_UNMOUNT_ON_EXIT;
+ return 0;
+ } else if (STREQ(val, "no")) {
+ gopt.flags &= ~CFM_UNMOUNT_ON_EXIT;
+ return 0;
+ }
+
+ fprintf(stderr, "conf: unknown value to unmount_on_exit \"%s\"\n", val);
+ return 1; /* unknown value */
+}
+
+
+/*
+ * Collect one entry for a regular map
+ */
+static int
+process_regular_option(const char *section, const char *key, const char *val, cf_map_t *cfm)
+{
+ /* ensure that val is valid */
+ if (!section || section[0] == '\0' ||
+ !key || key[0] == '\0' ||
+ !val || val[0] == '\0' ||
+ !cfm) {
+ fprintf(stderr, "conf: process_regular_option: null entries\n");
+ return 1;
+ }
+
+ /* check if initializing a new map */
+ if (!cfm->cfm_dir)
+ cfm->cfm_dir = strdup((char *)section);
+
+ /* check for each possible field */
+ if (STREQ(key, "browsable_dirs"))
+ return ropt_browsable_dirs(val, cfm);
+
+ if (STREQ(key, "map_name"))
+ return ropt_map_name(val, cfm);
+
+ if (STREQ(key, "map_options"))
+ return ropt_map_options(val, cfm);
+
+ if (STREQ(key, "map_type"))
+ return ropt_map_type(val, cfm);
+
+ if (STREQ(key, "mount_type"))
+ return ropt_mount_type(val, cfm);
+
+ if (STREQ(key, "search_path"))
+ return ropt_search_path(val, cfm);
+
+ if (STREQ(key, "tag"))
+ return ropt_tag(val, cfm);
+
+ fprintf(stderr, "conf: unknown regular key \"%s\" for section \"%s\"\n",
+ key, section);
+ return 1; /* failed to match any command */
+}
+
+
+static int
+ropt_browsable_dirs(const char *val, cf_map_t *cfm)
+{
+ if (STREQ(val, "full")) {
+ cfm->cfm_flags |= CFM_BROWSABLE_DIRS_FULL;
+ return 0;
+ } else if (STREQ(val, "yes")) {
+ cfm->cfm_flags |= CFM_BROWSABLE_DIRS;
+ return 0;
+ } else if (STREQ(val, "no")) {
+ cfm->cfm_flags &= ~CFM_BROWSABLE_DIRS;
+ return 0;
+ }
+
+ fprintf(stderr, "conf: unknown value to browsable_dirs \"%s\"\n", val);
+ return 1; /* unknown value */
+}
+
+
+static int
+ropt_map_name(const char *val, cf_map_t *cfm)
+{
+ cfm->cfm_name = strdup((char *)val);
+ return 0;
+}
+
+
+static int
+ropt_map_options(const char *val, cf_map_t *cfm)
+{
+ cfm->cfm_opts = strdup((char *)val);
+ return 0;
+}
+
+
+static int
+ropt_map_type(const char *val, cf_map_t *cfm)
+{
+ cfm->cfm_type = strdup((char *)val);
+ return 0;
+}
+
+
+static int
+ropt_mount_type(const char *val, cf_map_t *cfm)
+{
+ if (STREQ(val, "autofs")) {
+#ifdef HAVE_FS_AUTOFS
+ cfm->cfm_flags |= CFM_MOUNT_TYPE_AUTOFS;
+ return 0;
+#else /* not HAVE_FS_AUTOFS */
+ fprintf(stderr, "conf: no autofs support available\n");
+ return 1;
+#endif /* not HAVE_FS_AUTOFS */
+ } else if (STREQ(val, "nfs")) {
+ cfm->cfm_flags &= ~CFM_MOUNT_TYPE_AUTOFS;
+ return 0;
+ }
+
+ fprintf(stderr, "conf: unknown value to mount_type \"%s\"\n", val);
+ return 1; /* unknown value */
+}
+
+
+static int
+ropt_search_path(const char *val, cf_map_t *cfm)
+{
+ cfm->cfm_search_path = strdup((char *)val);
+ return 0;
+}
+
+
+static int
+ropt_tag(const char *val, cf_map_t *cfm)
+{
+ cfm->cfm_tag = strdup((char *)val);
+ return 0;
+}
+
+
+/*
+ * Process one collected map.
+ */
+static int
+process_regular_map(cf_map_t *cfm)
+{
+
+ if (!cfm->cfm_name) {
+ fprintf(stderr, "conf: map_name must be defined for map \"%s\"\n", cfm->cfm_dir);
+ return 1;
+ }
+ /*
+ * If map has no tag defined, process the map.
+ * If no conf_tag was set in amd -T, process all untagged entries.
+ * If a tag is defined, then process it only if it matches the map tag.
+ */
+ if (!cfm->cfm_tag ||
+ (conf_tag && STREQ(cfm->cfm_tag, conf_tag))) {
+#ifdef DEBUG_CONF
+ fprintf(stderr, "processing map %s (flags=0x%x)...\n",
+ cfm->cfm_dir, cfm->cfm_flags);
+#endif /* DEBUG_CONF */
+ root_newmap(cfm->cfm_dir,
+ cfm->cfm_opts ? cfm->cfm_opts : "",
+ cfm->cfm_name,
+ cfm);
+ } else {
+ fprintf(stderr, "skipping map %s...\n", cfm->cfm_dir);
+ }
+
+ reset_cf_map(cfm);
+ return 0;
+}
+
+
+/*
+ * Process last map in conf file (if any)
+ */
+int
+process_last_regular_map(void)
+{
+ /*
+ * If the amd.conf file only has a [global] section (pretty useless
+ * IMHO), do not try to process a map that does not exist.
+ */
+ if (!cur_map.cfm_dir)
+ return 0;
+ return process_regular_map(&cur_map);
+}
diff --git a/contrib/amd/amd/conf_parse.y b/contrib/amd/amd/conf_parse.y
new file mode 100644
index 000000000000..2a9877a8fe4b
--- /dev/null
+++ b/contrib/amd/amd/conf_parse.y
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: conf_parse.y,v 5.2.2.1 1992/02/09 15:09:35 jsp beta $
+ *
+ */
+
+%{
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+extern char *yytext;
+extern int yylineno;
+extern int yylex(void);
+
+static int yyerror(const char *s);
+static int retval;
+static char *header_section = NULL; /* start with no header section */
+
+#define YYDEBUG 1
+
+#define PARSE_DEBUG 0
+
+#if PARSE_DEBUG
+# define dprintf(f,s) fprintf(stderr, (f), yylineno, (s))
+# define amu_return(v)
+#else
+# define dprintf(f,s)
+# define amu_return(v) return((v))
+#endif /* PARSE_DEBUG */
+
+%}
+
+%union {
+char *strtype;
+}
+
+%token LEFT_BRACKET RIGHT_BRACKET EQUAL
+%token NEWLINE
+%token <strtype> NONWS_STRING
+%token <strtype> NONWSEQ_STRING
+%token <strtype> QUOTED_NONWSEQ_STRING
+
+%start file
+%%
+
+/****************************************************************************/
+file : { yydebug = PARSE_DEBUG; } newlines map_sections
+ | { yydebug = PARSE_DEBUG; } map_sections
+ ;
+
+newlines : NEWLINE
+ | NEWLINE newlines
+ ;
+
+map_sections : map_section
+ | map_section map_sections
+ ;
+
+map_section : sec_header kv_pairs
+ ;
+
+sec_header : LEFT_BRACKET NONWS_STRING RIGHT_BRACKET NEWLINE
+ {
+ if (yydebug)
+ fprintf(stderr, "sec_header1 = \"%s\"\n", $2);
+ header_section = $2;
+ }
+ ;
+
+kv_pairs : kv_pair
+ | kv_pair kv_pairs
+ ;
+
+kv_pair : NONWS_STRING EQUAL NONWS_STRING NEWLINE
+ {
+ if (yydebug)
+ fprintf(stderr,"parse1: key=\"%s\", val=\"%s\"\n", $1, $3);
+ retval = set_conf_kv(header_section, $1, $3);
+ if (retval != 0) {
+ yyerror("syntax error");
+ YYABORT;
+ }
+ }
+ | NONWS_STRING EQUAL NONWSEQ_STRING NEWLINE
+ {
+ if (yydebug)
+ fprintf(stderr,"parse2: key=\"%s\", val=\"%s\"\n", $1, $3);
+ retval = set_conf_kv(header_section, $1, $3);
+ if (retval != 0) {
+ yyerror("syntax error");
+ YYABORT;
+ }
+ }
+ | NONWS_STRING EQUAL QUOTED_NONWSEQ_STRING NEWLINE
+ {
+ if (yydebug)
+ fprintf(stderr,"parse3: key=\"%s\", val=\"%s\"\n", $1, $3);
+ retval = set_conf_kv(header_section, $1, $3);
+ if (retval != 0) {
+ yyerror("syntax error");
+ YYABORT;
+ }
+ }
+ | NEWLINE
+ ;
+
+/****************************************************************************/
+%%
+
+static int
+yyerror(const char *s)
+{
+ fprintf(stderr, "AMDCONF: %s on line %d (section %s)\n",
+ s, yylineno,
+ (header_section ? header_section : "null"));
+ exit(1);
+ return 1; /* to full compilers that insist on a return statement */
+}
diff --git a/contrib/amd/amd/conf_tok.l b/contrib/amd/amd/conf_tok.l
new file mode 100644
index 000000000000..0ec067bbf300
--- /dev/null
+++ b/contrib/amd/amd/conf_tok.l
@@ -0,0 +1,186 @@
+%{
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: conf_tok.l,v 5.2.2.1 1992/02/09 15:09:36 jsp beta $
+ *
+ */
+
+/*
+ * Lexical analyzer for AMD configuration parser.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+/*
+ * Some systems include a definition for the macro ECHO in <sys/ioctl.h>,
+ * and their (bad) version of lex defines it too at the very beginning of
+ * the generated lex.yy.c file (before it can be easily undefined),
+ * resulting in a conflict. So undefine it here before needed.
+ * Luckily, it does not appear that this macro is actually used in the rest
+ * of the generated lex.yy.c file.
+ */
+#ifdef ECHO
+# undef ECHO
+#endif /* ECHO */
+#include <am_defs.h>
+#include <amd.h>
+#include <conf_parse.h>
+/* and once again undefine this, just in case */
+#ifdef ECHO
+# undef ECHO
+#endif /* ECHO */
+
+/*
+ * There are some things that need to be defined only if useing GNU flex.
+ * These must not be defined if using standard lex
+ */
+#ifdef FLEX_SCANNER
+# ifndef ECHO
+# define ECHO (void) fwrite( yytext, yyleng, 1, yyout )
+# endif /* not ECHO */
+int yylineno = 0;
+#endif /* FLEX_SCANNER */
+
+int yylex(void);
+/*
+ * some systems such as DU-4.x have a different GNU flex in /usr/bin
+ * which automatically generates yywrap macros and symbols. So I must
+ * distinguish between them and when yywrap is actually needed.
+ */
+#ifndef yywrap
+int yywrap(void);
+#endif /* not yywrap */
+
+#define TOK_DEBUG 0
+
+#if TOK_DEBUG
+# define dprintf(f,s) fprintf(stderr, (f), yylineno, (s))
+# define amu_return(v)
+#else
+# define dprintf(f,s)
+# define amu_return(v) return((v))
+#endif /* TOK_DEBUG */
+
+/* no need to use yyunput() or yywrap() */
+#define YY_NO_UNPUT
+#define YY_SKIP_YYWRAP
+
+%}
+
+DIGIT [0-9]
+ALPHA [A-Za-z]
+ALPHANUM [A-Za-z0-9]
+SYMBOL [A-Za-z0-9_-]
+PATH [A-Za-z0-9_-/]
+NONWSCHAR [^ \t\n\[\]=]
+NONWSEQCHAR [^ \t\n\[\]]
+NONNL [^\n]
+NONQUOTE [^\"]
+
+%%
+
+\n {
+ yylineno++;
+ amu_return(NEWLINE);
+ }
+
+\[ {
+ dprintf("%8d: Left bracket \"%s\"\n", yytext);
+ yylval.strtype = strdup((char *)yytext);
+ amu_return(LEFT_BRACKET);
+ }
+
+\] {
+ dprintf("%8d: Right bracket \"%s\"\n", yytext);
+ yylval.strtype = strdup((char *)yytext);
+ amu_return(RIGHT_BRACKET);
+ }
+
+= {
+ dprintf("%8d: Equal \"%s\"\n", yytext);
+ yylval.strtype = strdup((char *)yytext);
+ amu_return(EQUAL);
+ }
+
+[ \t]* {
+ dprintf("%8d: Whitespace \"%s\"\n", yytext);
+ }
+"#"[^\n]*\n {
+ /* a comment line includes the terminating \n */
+ yylineno++;
+ yytext[strlen((char *)yytext)-1] = '\0';
+ dprintf("%8d: Comment \"%s\"\n", yytext);
+ }
+
+{NONWSCHAR}{NONWSCHAR}* {
+ dprintf("%8d: Non-WS string \"%s\"\n", yytext);
+ yylval.strtype = strdup((char *)yytext);
+ amu_return(NONWS_STRING);
+ }
+
+\"{NONQUOTE}{NONQUOTE}*\" {
+ dprintf("%8d: QUOTED-Non-WS-EQ string \"%s\"\n", yytext);
+ /* must strip quotes */
+ yytext[strlen((char *)yytext)-1] = '\0';
+ yylval.strtype = strdup((char *)&yytext[1]);
+ amu_return(QUOTED_NONWSEQ_STRING);
+ }
+
+{NONWSEQCHAR}{NONWSEQCHAR}* {
+ dprintf("%8d: Non-WS-EQ string \"%s\"\n", yytext);
+ yylval.strtype = strdup((char *)yytext);
+ amu_return(NONWSEQ_STRING);
+ }
+
+%%
+
+/*
+ * some systems such as DU-4.x have a different GNU flex in /usr/bin
+ * which automatically generates yywrap macros and symbols. So I must
+ * distinguish between them and when yywrap is actually needed.
+ */
+#ifndef yywrap
+int yywrap(void)
+{
+ return 1;
+}
+#endif /* not yywrap */
diff --git a/contrib/amd/amd/get_args.c b/contrib/amd/amd/get_args.c
new file mode 100644
index 000000000000..b365ff79c6ce
--- /dev/null
+++ b/contrib/amd/amd/get_args.c
@@ -0,0 +1,389 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: get_args.c,v 5.2.2.1 1992/02/09 15:08:23 jsp beta $
+ *
+ */
+
+/*
+ * Argument decode
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+/* include auto-generated version file */
+#include <build_version.h>
+
+char *conf_file = "/etc/amd.conf"; /* default amd configuration file */
+char *conf_tag = NULL; /* default conf file tags to use */
+int usage = 0;
+int use_conf_file = 0; /* default don't use amd.conf file */
+char *mnttab_file_name = NULL; /* symbol must be available always */
+#ifdef DEBUG
+int debug_flags = D_AMQ /* Register AMQ */
+ | D_DAEMON; /* Enter daemon mode */
+#endif /* DEBUG */
+
+
+/*
+ * Return the version string (dynamic buffer)
+ */
+char *
+get_version_string(void)
+{
+ static char *vers = NULL;
+ char tmpbuf[1024];
+ char *wire_buf;
+ int wire_buf_len = 0;
+
+ /* first get dynamic string listing all known networks */
+ wire_buf = print_wires();
+ if (wire_buf)
+ wire_buf_len = strlen(wire_buf);
+
+ vers = xmalloc(2048 + wire_buf_len);
+ sprintf(vers, "%s\n%s\n%s\n%s\n",
+ "Copyright (c) 1997-1998 Erez Zadok",
+ "Copyright (c) 1990 Jan-Simon Pendry",
+ "Copyright (c) 1990 Imperial College of Science, Technology & Medicine",
+ "Copyright (c) 1990 The Regents of the University of California.");
+ sprintf(tmpbuf, "%s version %s (build %d).\n",
+ PACKAGE, VERSION, AMU_BUILD_VERSION);
+ strcat(vers, tmpbuf);
+ sprintf(tmpbuf, "Built by %s@%s on date %s.\n",
+ USER_NAME, HOST_NAME, CONFIG_DATE);
+ strcat(vers, tmpbuf);
+ sprintf(tmpbuf, "cpu=%s (%s-endian), arch=%s, karch=%s.\n",
+ cpu, endian, gopt.arch, gopt.karch);
+ strcat(vers, tmpbuf);
+ sprintf(tmpbuf, "full_os=%s, os=%s, osver=%s, vendor=%s.\n",
+ HOST_OS, gopt.op_sys, gopt.op_sys_ver, HOST_VENDOR);
+ strcat(vers, tmpbuf);
+
+ strcat(vers, "Map support for: ");
+ mapc_showtypes(tmpbuf);
+ strcat(vers, tmpbuf);
+ strcat(vers, ".\nAMFS: ");
+ ops_showamfstypes(tmpbuf);
+ strcat(vers, tmpbuf);
+ strcat(vers, ".\nFS: ");
+ ops_showfstypes(tmpbuf);
+ strcat(vers, tmpbuf);
+
+ /* append list of networks if available */
+ if (wire_buf) {
+ strcat(vers, wire_buf);
+ XFREE(wire_buf);
+ }
+
+ return vers;
+}
+
+
+void
+get_args(int argc, char *argv[])
+{
+ int opt_ch;
+ FILE *fp = stdin;
+
+ /* if no arguments were passed, try to use /etc/amd.conf file */
+ if (argc <= 1)
+ use_conf_file = 1;
+
+ while ((opt_ch = getopt(argc, argv, "nprvSa:c:d:k:l:o:t:w:x:y:C:D:F:T:O:H")) != EOF)
+ switch (opt_ch) {
+
+ case 'a':
+ if (*optarg != '/') {
+ fprintf(stderr, "%s: -a option must begin with a '/'\n",
+ progname);
+ exit(1);
+ }
+ gopt.auto_dir = optarg;
+ break;
+
+ case 'c':
+ gopt.am_timeo = atoi(optarg);
+ if (gopt.am_timeo <= 0)
+ gopt.am_timeo = AM_TTL;
+ break;
+
+ case 'd':
+ gopt.sub_domain = optarg;
+ break;
+
+ case 'k':
+ gopt.karch = optarg;
+ break;
+
+ case 'l':
+ gopt.logfile = optarg;
+ break;
+
+ case 'n':
+ gopt.flags |= CFM_NORMALIZE_HOSTNAMES;
+ break;
+
+ case 'o':
+ gopt.op_sys_ver = optarg;
+ break;
+
+ case 'p':
+ gopt.flags |= CFM_PRINT_PID;
+ break;
+
+ case 'r':
+ gopt.flags |= CFM_RESTART_EXISTING_MOUNTS;
+ break;
+
+ case 't':
+ /* timeo.retrans */
+ {
+ char *dot = strchr(optarg, '.');
+ if (dot)
+ *dot = '\0';
+ if (*optarg) {
+ gopt.amfs_auto_timeo = atoi(optarg);
+ }
+ if (dot) {
+ gopt.amfs_auto_retrans = atoi(dot + 1);
+ *dot = '.';
+ }
+ }
+ break;
+
+ case 'v':
+ fputs(get_version_string(), stderr);
+ exit(0);
+ break;
+
+ case 'w':
+ gopt.am_timeo_w = atoi(optarg);
+ if (gopt.am_timeo_w <= 0)
+ gopt.am_timeo_w = AM_TTL_W;
+ break;
+
+ case 'x':
+ usage += switch_option(optarg);
+ break;
+
+ case 'y':
+#ifdef HAVE_MAP_NIS
+ gopt.nis_domain = optarg;
+#else /* not HAVE_MAP_NIS */
+ plog(XLOG_USER, "-y: option ignored. No NIS support available.");
+#endif /* not HAVE_MAP_NIS */
+ break;
+
+ case 'C':
+ gopt.cluster = optarg;
+ break;
+
+ case 'D':
+#ifdef DEBUG
+ usage += debug_option(optarg);
+#else /* not DEBUG */
+ fprintf(stderr, "%s: not compiled with DEBUG option -- sorry.\n", progname);
+#endif /* not DEBUG */
+ break;
+
+ case 'F':
+ conf_file = optarg;
+ use_conf_file = 1;
+ break;
+
+ case 'H':
+ goto show_usage;
+ break;
+
+ case 'O':
+ gopt.op_sys = optarg;
+ break;
+
+ case 'S':
+ gopt.flags &= ~CFM_PROCESS_LOCK; /* turn process locking off */
+ break;
+
+ case 'T':
+ conf_tag = optarg;
+ break;
+
+ default:
+ usage = 1;
+ break;
+ }
+
+ /*
+ * amd.conf file: if not command-line arguments were used, or if -F was
+ * specified, then use that amd.conf file. If the file cannot be opened,
+ * abort amd. If it can be found, open it, parse it, and then close it.
+ */
+ if (use_conf_file && conf_file) {
+ fp = fopen(conf_file, "r");
+ if (!fp) {
+ char buf[128];
+ sprintf(buf, "Amd configuration file (%s)", conf_file);
+ perror(buf);
+ exit(1);
+ }
+ yyin = fp;
+ yyparse();
+ fclose(fp);
+ if (process_last_regular_map() != 0)
+ exit(1);
+ }
+
+ /* make sure there are some default options defined */
+ if (xlog_level_init == ~0) {
+ switch_option("");
+ }
+#ifdef DEBUG
+ usage += switch_option("debug");
+#endif /* DEBUG */
+
+ /* log information regarding amd.conf file */
+ if (use_conf_file && conf_file)
+ plog(XLOG_INFO, "using configuration file %s", conf_file);
+
+#ifdef HAVE_MAP_LDAP
+ /* ensure that if ldap_base is specified, that also ldap_hostports is */
+ if (gopt.ldap_hostports && !gopt.ldap_base) {
+ fprintf(stderr, "must specify both ldap_hostports and ldap_base\n");
+ exit(1);
+ }
+#endif /* HAVE_MAP_LDAP */
+
+ if (usage)
+ goto show_usage;
+
+ while (optind <= argc - 2) {
+ char *dir = argv[optind++];
+ char *map = argv[optind++];
+ char *opts = "";
+ if (argv[optind] && *argv[optind] == '-')
+ opts = &argv[optind++][1];
+
+ root_newmap(dir, opts, map, NULL);
+ }
+
+ if (optind == argc) {
+ /*
+ * Append domain name to hostname.
+ * sub_domain overrides hostdomain
+ * if given.
+ */
+ if (gopt.sub_domain)
+ hostdomain = gopt.sub_domain;
+ if (*hostdomain == '.')
+ hostdomain++;
+ strcat(hostd, ".");
+ strcat(hostd, hostdomain);
+
+#ifdef MOUNT_TABLE_ON_FILE
+# ifdef DEBUG
+ if (debug_flags & D_MTAB)
+ mnttab_file_name = DEBUG_MNTTAB;
+ else
+# endif /* DEBUG */
+ mnttab_file_name = MNTTAB_FILE_NAME;
+#else /* not MOUNT_TABLE_ON_FILE */
+# ifdef DEBUG
+ if (debug_flags & D_MTAB)
+ dlog("-D mtab option ignored");
+# endif /* DEBUG */
+#endif /* not MOUNT_TABLE_ON_FILE */
+
+ if (switch_to_logfile(gopt.logfile) != 0)
+ plog(XLOG_USER, "Cannot switch logfile");
+
+ /*
+ * If the kernel architecture was not specified
+ * then use the machine architecture.
+ */
+ if (gopt.karch == 0)
+ gopt.karch = gopt.arch;
+
+ if (gopt.cluster == 0)
+ gopt.cluster = hostdomain;
+
+ if (gopt.amfs_auto_timeo <= 0)
+ gopt.amfs_auto_timeo = AMFS_AUTO_TIMEO;
+ if (gopt.amfs_auto_retrans <= 0)
+ gopt.amfs_auto_retrans = AMFS_AUTO_RETRANS;
+ if (gopt.amfs_auto_retrans <= 0)
+ gopt.amfs_auto_retrans = 3; /* XXX */
+ return;
+ }
+
+show_usage:
+ fprintf(stderr,
+ "Usage: %s [-nprvHS] [-a mount_point] [-c cache_time] [-d domain]\n\
+\t[-k kernel_arch] [-l logfile%s\n\
+\t[-t timeout.retrans] [-w wait_timeout] [-C cluster_name]\n\
+\t[-o op_sys_ver] [-O op_sys_name]\n\
+\t[-F conf_file] [-T conf_tag]", progname,
+#ifdef HAVE_SYSLOG
+# ifdef LOG_DAEMON
+ "|\"syslog[:facility]\"]"
+# else /* not LOG_DAEMON */
+ "|\"syslog\"]"
+# endif /* not LOG_DAEMON */
+#else /* not HAVE_SYSLOG */
+ "]"
+#endif /* not HAVE_SYSLOG */
+ );
+
+#ifdef HAVE_MAP_NIS
+ fputs(" [-y nis-domain]\n", stderr);
+#else /* not HAVE_MAP_NIS */
+ fputc('\n', stderr);
+#endif /* HAVE_MAP_NIS */
+
+ show_opts('x', xlog_opt);
+#ifdef DEBUG
+ show_opts('D', dbg_opt);
+#endif /* DEBUG */
+ fprintf(stderr, "\t[directory mapname [-map_options]] ...\n");
+ exit(1);
+}
diff --git a/contrib/amd/amd/info_file.c b/contrib/amd/amd/info_file.c
new file mode 100644
index 000000000000..55c24bdb5c23
--- /dev/null
+++ b/contrib/amd/amd/info_file.c
@@ -0,0 +1,265 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: info_file.c,v 5.2.2.1 1992/02/09 15:08:28 jsp beta $
+ *
+ */
+
+/*
+ * Get info from file
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+#define MAX_LINE_LEN 1500
+
+/* forward declarations */
+int file_init(mnt_map *m, char *map, time_t *tp);
+int file_reload(mnt_map *m, char *map, void (*fn) (mnt_map *, char *, char *));
+int file_search(mnt_map *m, char *map, char *key, char **pval, time_t *tp);
+int file_mtime(mnt_map *m, char *map, time_t *tp);
+
+
+static int
+read_line(char *buf, int size, FILE * fp)
+{
+ int done = 0;
+
+ do {
+ while (fgets(buf, size, fp)) {
+ int len = strlen(buf);
+ done += len;
+ if (len > 1 && buf[len - 2] == '\\' &&
+ buf[len - 1] == '\n') {
+ int ch;
+ buf += len - 2;
+ size -= len - 2;
+ *buf = '\n';
+ buf[1] = '\0';
+ /*
+ * Skip leading white space on next line
+ */
+ while ((ch = getc(fp)) != EOF &&
+ isascii(ch) && isspace(ch)) ;
+ (void) ungetc(ch, fp);
+ } else {
+ return done;
+ }
+ }
+ } while (size > 0 && !feof(fp));
+
+ return done;
+}
+
+
+/*
+ * Try to locate a key in a file
+ */
+static int
+search_or_reload_file(FILE * fp, char *map, char *key, char **val, mnt_map *m, void (*fn) (mnt_map *m, char *, char *))
+{
+ char key_val[MAX_LINE_LEN];
+ int chuck = 0;
+ int line_no = 0;
+
+ while (read_line(key_val, sizeof(key_val), fp)) {
+ char *kp;
+ char *cp;
+ char *hash;
+ int len = strlen(key_val);
+ line_no++;
+
+ /*
+ * Make sure we got the whole line
+ */
+ if (key_val[len - 1] != '\n') {
+ plog(XLOG_WARNING, "line %d in \"%s\" is too long", line_no, map);
+ chuck = 1;
+ } else {
+ key_val[len - 1] = '\0';
+ }
+
+ /*
+ * Strip comments
+ */
+ hash = strchr(key_val, '#');
+ if (hash)
+ *hash = '\0';
+
+ /*
+ * Find start of key
+ */
+ for (kp = key_val; *kp && isascii(*kp) && isspace((int)*kp); kp++) ;
+
+ /*
+ * Ignore blank lines
+ */
+ if (!*kp)
+ goto again;
+
+ /*
+ * Find end of key
+ */
+ for (cp = kp; *cp && (!isascii(*cp) || !isspace((int)*cp)); cp++) ;
+
+ /*
+ * Check whether key matches
+ */
+ if (*cp)
+ *cp++ = '\0';
+
+ if (fn || (*key == *kp && STREQ(key, kp))) {
+ while (*cp && isascii(*cp) && isspace((int)*cp))
+ cp++;
+ if (*cp) {
+ /*
+ * Return a copy of the data
+ */
+ char *dc = strdup(cp);
+ if (fn) {
+ (*fn) (m, strdup(kp), dc);
+ } else {
+ *val = dc;
+#ifdef DEBUG
+ dlog("%s returns %s", key, dc);
+#endif /* DEBUG */
+ }
+ if (!fn)
+ return 0;
+ } else {
+ plog(XLOG_USER, "%s: line %d has no value field", map, line_no);
+ }
+ }
+
+ again:
+ /*
+ * If the last read didn't get a whole line then
+ * throw away the remainder before continuing...
+ */
+ if (chuck) {
+ while (fgets(key_val, sizeof(key_val), fp) &&
+ !strchr(key_val, '\n')) ;
+ chuck = 0;
+ }
+ }
+
+ return fn ? 0 : ENOENT;
+}
+
+
+static FILE *
+file_open(char *map, time_t *tp)
+{
+ FILE *mapf = fopen(map, "r");
+
+ if (mapf && tp) {
+ struct stat stb;
+ if (fstat(fileno(mapf), &stb) < 0)
+ *tp = clocktime();
+ else
+ *tp = stb.st_mtime;
+ }
+ return mapf;
+}
+
+
+int
+file_init(mnt_map *m, char *map, time_t *tp)
+{
+ FILE *mapf = file_open(map, tp);
+
+ if (mapf) {
+ fclose(mapf);
+ return 0;
+ }
+ return errno;
+}
+
+
+int
+file_reload(mnt_map *m, char *map, void (*fn) (mnt_map *, char *, char *))
+{
+ FILE *mapf = file_open(map, (time_t *) 0);
+
+ if (mapf) {
+ int error = search_or_reload_file(mapf, map, 0, 0, m, fn);
+ (void) fclose(mapf);
+ return error;
+ }
+ return errno;
+}
+
+
+int
+file_search(mnt_map *m, char *map, char *key, char **pval, time_t *tp)
+{
+ time_t t;
+ FILE *mapf = file_open(map, &t);
+
+ if (mapf) {
+ int error;
+ if (*tp < t) {
+ *tp = t;
+ error = -1;
+ } else {
+ error = search_or_reload_file(mapf, map, key, pval, 0, 0);
+ }
+ (void) fclose(mapf);
+ return error;
+ }
+ return errno;
+}
+
+
+int
+file_mtime(mnt_map *m, char *map, time_t *tp)
+{
+ FILE *mapf = file_open(map, tp);
+
+ if (mapf) {
+ (void) fclose(mapf);
+ return 0;
+ }
+ return errno;
+}
diff --git a/contrib/amd/amd/info_hesiod.c b/contrib/amd/amd/info_hesiod.c
new file mode 100644
index 000000000000..a4607e151505
--- /dev/null
+++ b/contrib/amd/amd/info_hesiod.c
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: info_hesiod.c,v 1.1 1997-1998/07/11 08:34:52 danny Exp danny $
+ *
+ */
+
+/*
+ * Get info from Hesiod
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+#define HES_PREFIX "hesiod."
+#define HES_PREFLEN 7
+
+#ifdef HAVE_HESIOD_INIT
+/* bsdi3 does not define this extern in any header file */
+extern char **hesiod_resolve(void *context, const char *name, const char *type);
+
+static voidp hesiod_context;
+#endif /* HAVE_HESIOD_INIT */
+
+/*
+ * No easy way to probe the server - check the map name begins with "hesiod."
+ * Note: this name includes 'amu_' so as to not conflict with libhesiod's
+ * hesiod_init() function.
+ */
+int
+amu_hesiod_init(mnt_map *m, char *map, time_t *tp)
+{
+#ifdef DEBUG
+ dlog("amu_hesiod_init(%s)", map);
+#endif /* DEBUG */
+ *tp = 0;
+
+#ifdef HAVE_HESIOD_INIT
+ if(!hesiod_context && hesiod_init(&hesiod_context) != 0)
+ return ENOENT;
+#endif /* HAVE_HESIOD_INIT */
+
+ return NSTREQ(map, HES_PREFIX, HES_PREFLEN) ? 0 : ENOENT;
+}
+
+
+/*
+ * Do a Hesiod nameserver call.
+ * Modify time is ignored by Hesiod - XXX
+ */
+int
+hesiod_search(mnt_map *m, char *map, char *key, char **pval, time_t *tp)
+{
+ char hes_key[MAXPATHLEN];
+ char **rvec;
+#ifndef HAVE_HESIOD_INIT
+ int error;
+#endif /* not HAVE_HESIOD_INIT */
+
+#ifdef DEBUG
+ dlog("hesiod_search(m=%x, map=%s, key=%s, pval=%x tp=%x)", m, map, key, pval, tp);
+#endif /* DEBUG */
+
+ sprintf(hes_key, "%s.%s", key, map + HES_PREFLEN);
+
+ /*
+ * Call the resolver
+ */
+#ifdef DEBUG
+ dlog("Hesiod base is: %s\n", gopt.hesiod_base);
+ dlog("hesiod_search: hes_resolve(%s, %s)", hes_key, gopt.hesiod_base);
+ if (debug_flags & D_INFO)
+ _res.options |= RES_DEBUG;
+#endif /* DEBUG */
+
+#ifdef HAVE_HESIOD_INIT
+ /* new style hesiod */
+ rvec = hesiod_resolve(hesiod_context, hes_key, gopt.hesiod_base);
+#else /* not HAVE_HESIOD_INIT */
+ rvec = hes_resolve(hes_key, gopt.hesiod_base);
+#endif /* not HAVE_HESIOD_INIT */
+
+ /*
+ * If a reply was forthcoming then return
+ * it (and free subsequent replies)
+ */
+ if (rvec && *rvec) {
+ *pval = *rvec;
+ while (*++rvec)
+ XFREE(*rvec);
+ return 0;
+ }
+
+#ifdef HAVE_HESIOD_INIT
+ /* new style hesiod */
+ return errno;
+#else /* not HAVE_HESIOD_INIT */
+ /*
+ * Otherwise reflect the hesiod error into a Un*x error
+ */
+# ifdef DEBUG
+ dlog("hesiod_search: Error: %d", hes_error());
+# endif /* DEBUG */
+ switch (hes_error()) {
+ case HES_ER_NOTFOUND:
+ error = ENOENT;
+ break;
+ case HES_ER_CONFIG:
+ error = EIO;
+ break;
+ case HES_ER_NET:
+ error = ETIMEDOUT;
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
+# ifdef DEBUG
+ dlog("hesiod_search: Returning: %d", error);
+# endif /* DEBUG */
+ return error;
+#endif /* not HAVE_HESIOD_INIT */
+}
diff --git a/contrib/amd/amd/info_ldap.c b/contrib/amd/amd/info_ldap.c
new file mode 100644
index 000000000000..e2f03674c5b0
--- /dev/null
+++ b/contrib/amd/amd/info_ldap.c
@@ -0,0 +1,465 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: info_ldap.c,v 5.2.2.1 1992/02/09 15:08:29 jsp beta $
+ *
+ */
+
+
+/*
+ * Get info from LDAP (Lightweight Directory Access Protocol)
+ * LDAP Home Page: http://www.umich.edu/~rsug/ldap/
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+
+/*
+ * MACROS:
+ */
+#define AMD_LDAP_TYPE "ldap"
+/* Time to live for an LDAP cached in an mnt_map */
+#define AMD_LDAP_TTL 3600
+#define AMD_LDAP_RETRIES 5
+#define AMD_LDAP_HOST "ldap"
+#ifndef LDAP_PORT
+# define LDAP_PORT 389
+#endif /* LDAP_PORT */
+
+/* How timestamps are searched */
+#define AMD_LDAP_TSFILTER "(&(objectClass=amdmapTimestamp)(amdmapName=%s))"
+/* How maps are searched */
+#define AMD_LDAP_FILTER "(&(objectClass=amdmap)(amdmapName=%s)(amdmapKey=%s))"
+/* How timestamps are stored */
+#define AMD_LDAP_TSATTR "amdmaptimestamp"
+/* How maps are stored */
+#define AMD_LDAP_ATTR "amdmapvalue"
+
+/*
+ * TYPEDEFS:
+ */
+typedef struct ald_ent ALD;
+typedef struct cr_ent CR;
+typedef struct he_ent HE;
+
+/*
+ * STRUCTURES:
+ */
+struct ald_ent {
+ LDAP *ldap;
+ HE *hostent;
+ CR *credentials;
+ time_t timestamp;
+};
+
+struct cr_ent {
+ char *who;
+ char *pw;
+ int method;
+};
+
+struct he_ent {
+ char *host;
+ int port;
+ struct he_ent *next;
+};
+
+/*
+ * FORWARD DECLARATIONS:
+ */
+static int amu_ldap_rebind(ALD *a);
+static int get_ldap_timestamp(LDAP *ld, char *map, time_t *ts);
+
+
+/*
+ * FUNCTIONS:
+ */
+
+static void
+he_free(HE *h)
+{
+ XFREE(h->host);
+ if (h->next != NULL)
+ he_free(h->next);
+ XFREE(h);
+}
+
+
+static HE *
+string2he(char *s)
+{
+ char *c, *p;
+ HE *new, *old = NULL;
+
+ if (s == NULL)
+ return (NULL);
+ for (p = s; p; p = strchr(p, ',')) {
+ if (old != NULL) {
+ new = (HE *) xmalloc(sizeof(HE));
+ old->next = new;
+ old = new;
+ } else {
+ old = (HE *) xmalloc(sizeof(HE));
+ old->next = NULL;
+ }
+ c = strchr(p, ':');
+ if (c) { /* Host and port */
+ *c++ = '\0';
+ old->host = strdup(p);
+ old->port = atoi(c);
+ } else
+ old->host = strdup(p);
+
+ }
+ return (old);
+}
+
+
+static void
+cr_free(CR *c)
+{
+ XFREE(c->who);
+ XFREE(c->pw);
+ XFREE(c);
+}
+
+
+static void
+ald_free(ALD *a)
+{
+ he_free(a->hostent);
+ cr_free(a->credentials);
+ if (a->ldap != NULL)
+ ldap_unbind(a->ldap);
+ XFREE(a);
+}
+
+
+int
+amu_ldap_init(mnt_map *m, char *map, time_t *ts)
+{
+ ALD *aldh;
+ CR *creds;
+
+ if (!STREQ(gopt.map_type, AMD_LDAP_TYPE)) {
+ return (ENOENT);
+ }
+#ifdef DEBUG
+ else {
+ dlog("Map %s is ldap\n", map);
+ }
+#endif /* DEBUG */
+
+ aldh = (ALD *) xmalloc(sizeof(ALD));
+ creds = (CR *) xmalloc(sizeof(CR));
+
+ aldh->hostent = string2he(gopt.ldap_hostports);
+ if (aldh->hostent == NULL) {
+ plog(XLOG_USER, "Unable to parse hostport %s for ldap map %s",
+ gopt.ldap_hostports, map);
+ return (ENOENT);
+ }
+ creds->who = "";
+ creds->pw = "";
+ creds->method = LDAP_AUTH_SIMPLE;
+ aldh->credentials = creds;
+ aldh->timestamp = 0;
+#ifdef DEBUG
+ dlog("Trying for %s:%d\n", aldh->hostent->host, aldh->hostent->port);
+#endif /* DEBUG */
+ if (amu_ldap_rebind(aldh)) {
+ ald_free(aldh);
+ return (ENOENT);
+ }
+ m->map_data = (void *) aldh;
+#ifdef DEBUG
+ dlog("Bound to %s:%d\n", aldh->hostent->host, aldh->hostent->port);
+#endif /* DEBUG */
+ if (get_ldap_timestamp(aldh->ldap, map, ts))
+ return (ENOENT);
+#ifdef DEBUG
+ dlog("Got timestamp for map %s: %d\n", map, *ts);
+#endif /* DEBUG */
+
+ return (0);
+}
+
+
+static int
+amu_ldap_rebind(ALD *a)
+{
+ LDAP *ld;
+ HE *h;
+ CR *c = a->credentials;
+ time_t now = clocktime();
+
+ if (a->ldap != NULL) {
+ if ((a->timestamp - now) > AMD_LDAP_TTL) {
+#ifdef DEBUG
+ dlog("Reestablishing ldap connection\n");
+#endif /* DEBUG */
+ ldap_unbind(a->ldap);
+ a->timestamp = now;
+ } else
+ return (0);
+ }
+
+ while (TRUE) {
+ for (h = a->hostent; h != NULL; h = h->next) {
+ if ((ld = ldap_open(h->host, h->port)) == NULL) {
+ plog(XLOG_WARNING, "Unable to ldap_open to %s:%d\n", h->host, h->port);
+ break;
+ }
+ if (ldap_bind_s(ld, c->who, c->pw, c->method) != LDAP_SUCCESS) {
+ plog(XLOG_WARNING, "Unable to ldap_bind to %s:%d as %s\n",
+ h->host, h->port, c->who);
+ break;
+ }
+ if (gopt.ldap_cache_seconds > 0) {
+ ldap_enable_cache(ld, gopt.ldap_cache_seconds, gopt.ldap_cache_maxmem);
+ a->ldap = ld;
+ a->timestamp = now;
+ return (0);
+ }
+ }
+ plog(XLOG_WARNING, "Exausted list of ldap servers, looping.\n");
+ }
+
+ plog(XLOG_USER, "Unable to (re)bind to any ldap hosts\n");
+ return (ENOENT);
+}
+
+
+static int
+get_ldap_timestamp(LDAP * ld, char *map, time_t *ts)
+{
+ struct timeval tv;
+ char **vals, *end;
+ char filter[MAXPATHLEN];
+ int i, err, nentries = 0;
+ LDAPMessage *res, *entry;
+
+ tv.tv_sec = 3;
+ tv.tv_usec = 0;
+ sprintf(filter, AMD_LDAP_TSFILTER, map);
+#ifdef DEBUG
+ dlog("Getting timestamp for map %s\n", map);
+ dlog("Filter is: %s\n", filter);
+ dlog("Base is: %s\n", gopt.ldap_base);
+#endif /* DEBUG */
+ for (i = 0; i < AMD_LDAP_RETRIES; i++) {
+ err = ldap_search_st(ld,
+ gopt.ldap_base,
+ LDAP_SCOPE_SUBTREE,
+ filter,
+ 0,
+ 0,
+ &tv,
+ &res);
+ if (err == LDAP_SUCCESS)
+ break;
+ dlog("Timestamp search timed out, trying again...\n");
+ }
+
+ if (err != LDAP_SUCCESS) {
+ *ts = 0;
+ plog(XLOG_USER, "LDAP timestamp search failed: %s\n",
+ ldap_err2string(ld->ld_errno));
+ return (ENOENT);
+ }
+
+ nentries = ldap_count_entries(ld, res);
+ if (nentries == 0) {
+ plog(XLOG_USER, "No timestamp entry for map %s\n", map);
+ *ts = 0;
+ ldap_msgfree(res);
+ return (ENOENT);
+ }
+
+ entry = ldap_first_entry(ld, res);
+ vals = ldap_get_values(ld, entry, AMD_LDAP_TSATTR);
+ if (ldap_count_values(vals) == 0) {
+ plog(XLOG_USER, "Missing timestamp value for map %s\n", map);
+ *ts = 0;
+ ldap_value_free(vals);
+ ldap_msgfree(res);
+ ldap_msgfree(entry);
+ return (ENOENT);
+ }
+#ifdef DEBUG
+ dlog("TS value is:%s:\n", vals[0]);
+#endif /* DEBUG */
+
+ if (vals[0]) {
+ *ts = (time_t) strtol(vals[0], &end, 10);
+ if (end == vals[0]) {
+ plog(XLOG_USER, "Unable to decode ldap timestamp %s for map %s\n",
+ vals[0], map);
+ err = ENOENT;
+ }
+ if (!*ts > 0) {
+ plog(XLOG_USER, "Nonpositive timestamp %d for map %s\n",
+ *ts, map);
+ err = ENOENT;
+ }
+ } else {
+ plog(XLOG_USER, "Empty timestamp value for map %s\n", map);
+ *ts = 0;
+ err = ENOENT;
+ }
+
+ ldap_value_free(vals);
+ ldap_msgfree(res);
+ ldap_msgfree(entry);
+#ifdef DEBUG
+ dlog("The timestamp for %s is %d (err=%d)\n", map, *ts, err);
+#endif /* DEBUG */
+ return (err);
+}
+
+
+int
+amu_ldap_search(mnt_map *m, char *map, char *key, char **pval, time_t *ts)
+{
+ char **vals, filter[MAXPATHLEN];
+ struct timeval tv;
+ int i, err, nvals = 0, nentries = 0;
+ LDAPMessage *entry, *res;
+ ALD *a = (ALD *) (m->map_data);
+
+ tv.tv_sec = 2;
+ tv.tv_usec = 0;
+ if (a == NULL) {
+ plog(XLOG_USER, "LDAP panic: no map data\n");
+ return (EIO);
+ }
+ if (amu_ldap_rebind(a)) /* Check that's the handle is still valid */
+ return (ENOENT);
+
+ sprintf(filter, AMD_LDAP_FILTER, map, key);
+#ifdef DEBUG
+ dlog("Search with filter: %s\n", filter);
+#endif /* DEBUG */
+ for (i = 0; i < AMD_LDAP_RETRIES; i++) {
+ err = ldap_search_st(a->ldap,
+ gopt.ldap_base,
+ LDAP_SCOPE_SUBTREE,
+ filter,
+ 0,
+ 0,
+ &tv,
+ &res);
+ if (err == LDAP_SUCCESS)
+ break;
+ }
+
+ switch (err) {
+ case LDAP_SUCCESS:
+ break;
+ case LDAP_NO_SUCH_OBJECT:
+#ifdef DEBUG
+ dlog("No object\n");
+#endif /* DEBUG */
+ ldap_msgfree(res);
+ return (ENOENT);
+ default:
+ plog(XLOG_USER, "LDAP search failed: %s\n",
+ ldap_err2string(a->ldap->ld_errno));
+ ldap_msgfree(res);
+ return (EIO);
+ }
+
+ nentries = ldap_count_entries(a->ldap, res);
+#ifdef DEBUG
+ dlog("Search found %d entries\n", nentries);
+#endif /* DEBUG */
+ if (nentries == 0) {
+ ldap_msgfree(res);
+ return (ENOENT);
+ }
+ entry = ldap_first_entry(a->ldap, res);
+ vals = ldap_get_values(a->ldap, entry, AMD_LDAP_ATTR);
+ nvals = ldap_count_values(vals);
+ if (nvals == 0) {
+ plog(XLOG_USER, "Missing value for %s in map %s\n", key, map);
+ ldap_value_free(vals);
+ ldap_msgfree(res);
+ ldap_msgfree(entry);
+ return (EIO);
+ }
+#ifdef DEBUG
+ dlog("Map %s, %s => %s\n", map, key, vals[0]);
+#endif /* DEBUG */
+ if (vals[0]) {
+ *pval = strdup(vals[0]);
+ err = 0;
+ } else {
+ plog(XLOG_USER, "Empty value for %s in map %s\n", key, map);
+ err = ENOENT;
+ }
+ ldap_msgfree(res);
+ ldap_msgfree(entry);
+ ldap_value_free(vals);
+
+ return (err);
+}
+
+
+int
+amu_ldap_mtime(mnt_map *m, char *map, time_t *ts)
+{
+ ALD *aldh = (ALD *) (m->map_data);
+
+ if (aldh == NULL) {
+ dlog("LDAP panic: unable to find map data\n");
+ return (ENOENT);
+ }
+ if (amu_ldap_rebind(aldh)) {
+ return (ENOENT);
+ }
+ if (get_ldap_timestamp(aldh->ldap, map, ts)) {
+ return (ENOENT);
+ }
+ return (0);
+}
diff --git a/contrib/amd/amd/info_ndbm.c b/contrib/amd/amd/info_ndbm.c
new file mode 100644
index 000000000000..0b93fa652180
--- /dev/null
+++ b/contrib/amd/amd/info_ndbm.c
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: info_ndbm.c,v 5.2.2.1 1992/02/09 15:08:31 jsp beta $
+ *
+ */
+
+/*
+ * Get info from NDBM map
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+/* forward declarations */
+int ndbm_init(mnt_map *m, char *map, time_t *tp);
+int ndbm_mtime(mnt_map *m, char *map, time_t *tp);
+int ndbm_search(mnt_map *m, char *map, char *key, char **pval, time_t *tp);
+
+
+static int
+search_ndbm(DBM *db, char *key, char **val)
+{
+ datum k, v;
+
+ k.dptr = key;
+ k.dsize = strlen(key) + 1;
+ v = dbm_fetch(db, k);
+ if (v.dptr) {
+ *val = strdup(v.dptr);
+ return 0;
+ }
+ return ENOENT;
+}
+
+
+int
+ndbm_search(mnt_map *m, char *map, char *key, char **pval, time_t *tp)
+{
+ DBM *db;
+
+ db = dbm_open(map, O_RDONLY, 0);
+ if (db) {
+ struct stat stb;
+ int error;
+#ifdef DBM_SUFFIX
+ char dbfilename[256];
+
+ strcpy(dbfilename, map);
+ strcat(dbfilename, DBM_SUFFIX);
+ error = stat(dbfilename, &stb);
+#else /* not DBM_SUFFIX */
+ error = fstat(dbm_pagfno(db), &stb);
+#endif /* not DBM_SUFFIX */
+ if (!error && *tp < stb.st_mtime) {
+ *tp = stb.st_mtime;
+ error = -1;
+ } else {
+ error = search_ndbm(db, key, pval);
+ }
+ (void) dbm_close(db);
+ return error;
+ }
+ return errno;
+}
+
+
+int
+ndbm_init(mnt_map *m, char *map, time_t *tp)
+{
+ DBM *db;
+
+ db = dbm_open(map, O_RDONLY, 0);
+ if (db) {
+ struct stat stb;
+ int error;
+#ifdef DBM_SUFFIX
+ char dbfilename[256];
+
+ strcpy(dbfilename, map);
+ strcat(dbfilename, DBM_SUFFIX);
+ error = stat(dbfilename, &stb);
+#else /* not DBM_SUFFIX */
+ error = fstat(dbm_pagfno(db), &stb);
+#endif /* not DBM_SUFFIX */
+ if (error < 0)
+ *tp = clocktime();
+ else
+ *tp = stb.st_mtime;
+ dbm_close(db);
+ return 0;
+ }
+ return errno;
+}
+
+
+int
+ndbm_mtime(mnt_map *m, char *map, time_t *tp)
+{
+ return ndbm_init(m,map, tp);
+}
diff --git a/contrib/amd/amd/info_nis.c b/contrib/amd/amd/info_nis.c
new file mode 100644
index 000000000000..eceb73a04591
--- /dev/null
+++ b/contrib/amd/amd/info_nis.c
@@ -0,0 +1,401 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: info_nis.c,v 5.2.2.1 1992/02/09 15:08:32 jsp beta $
+ *
+ */
+
+/*
+ * Get info from NIS map
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+/*
+ * NIS+ servers in NIS compat mode don't have yp_order()
+ */
+static int has_yp_order = FALSE;
+
+/* forward declarations */
+int nis_reload(mnt_map *m, char *map, void (*fn) (mnt_map *, char *, char *));
+int nis_search(mnt_map *m, char *map, char *key, char **val, time_t *tp);
+int nis_init(mnt_map *m, char *map, time_t *tp);
+int nis_mtime(mnt_map *m, char *map, time_t *tp);
+
+/* typedefs */
+typedef void (*nis_callback_fxn_t)(mnt_map *, char *, char *);
+#ifndef DEFINED_YPALL_CALLBACK_FXN_T
+typedef int (*ypall_callback_fxn_t)();
+#endif /* DEFINED_YPALL_CALLBACK_FXN_T */
+
+struct nis_callback_data {
+ mnt_map *ncd_m;
+ char *ncd_map;
+ nis_callback_fxn_t ncd_fn;
+};
+
+/* Map to the right version of yp_all */
+#ifdef HAVE_BAD_YP_ALL
+# define yp_all am_yp_all
+static int am_yp_all(char *indomain, char *inmap, struct ypall_callback *incallback);
+#endif /* HAVE_BAD_YP_ALL */
+
+
+/*
+ * Figure out the nis domain name
+ */
+static int
+determine_nis_domain(void)
+{
+ static int nis_not_running = 0;
+ char default_domain[YPMAXDOMAIN];
+
+ if (nis_not_running)
+ return ENOENT;
+
+ if (getdomainname(default_domain, sizeof(default_domain)) < 0) {
+ nis_not_running = 1;
+ plog(XLOG_ERROR, "getdomainname: %m");
+ return EIO;
+ }
+ if (!*default_domain) {
+ nis_not_running = 1;
+ plog(XLOG_WARNING, "NIS domain name is not set. NIS ignored.");
+ return ENOENT;
+ }
+ gopt.nis_domain = strdup(default_domain);
+
+ return 0;
+}
+
+
+/*
+ * Callback from yp_all
+ */
+static int
+callback(int status, char *key, int kl, char *val, int vl, char *data)
+{
+ struct nis_callback_data *ncdp = (struct nis_callback_data *) data;
+
+ if (status == YP_TRUE) {
+
+ /*
+ * Add to list of maps
+ */
+ char *kp = strnsave(key, kl);
+ char *vp = strnsave(val, vl);
+ (*ncdp->ncd_fn) (ncdp->ncd_m, kp, vp);
+
+ /*
+ * We want more ...
+ */
+ return FALSE;
+
+ } else {
+
+ /*
+ * NOMORE means end of map - otherwise log error
+ */
+ if (status != YP_NOMORE) {
+ /*
+ * Check what went wrong
+ */
+ int e = ypprot_err(status);
+
+#ifdef DEBUG
+ plog(XLOG_ERROR, "yp enumeration of %s: %s, status=%d, e=%d",
+ ncdp->ncd_map, yperr_string(e), status, e);
+#else /* not DEBUG */
+ plog(XLOG_ERROR, "yp enumeration of %s: %s", ncdp->ncd_map, yperr_string(e));
+#endif /* not DEBUG */
+ }
+ return TRUE;
+ }
+}
+
+
+int
+nis_reload(mnt_map *m, char *map, void (*fn) (mnt_map *, char *, char *))
+{
+ int error;
+ struct nis_callback_data data;
+ struct ypall_callback cbinfo;
+
+ if (!gopt.nis_domain) {
+ error = determine_nis_domain();
+ if (error)
+ return error;
+ }
+ data.ncd_m = m;
+ data.ncd_map = map;
+ data.ncd_fn = fn;
+ cbinfo.data = (voidp) &data;
+ cbinfo.foreach = (ypall_callback_fxn_t) callback;
+
+ /*
+ * If you are using NIS and your yp_all function is "broken", you have to
+ * get it fixed. The bug in yp_all() is that it does not close a TCP
+ * connection to ypserv, and this ypserv runs out of open file descriptors,
+ * getting into an infinite loop, thus all YP clients eventually unbind
+ * and hang too.
+ */
+ error = yp_all(gopt.nis_domain, map, &cbinfo);
+
+ if (error)
+ plog(XLOG_ERROR, "error grabbing nis map of %s: %s", map, yperr_string(ypprot_err(error)));
+ return error;
+}
+
+
+/*
+ * Check if NIS is up, so we can determine if to clear the map or not.
+ * Test it by checking the yp order.
+ * Returns: 0 if NIS is down, 1 if it is up.
+ */
+int
+nis_isup(mnt_map *m, char *map)
+{
+ YP_ORDER_OUTORDER_TYPE order;
+ int error;
+ static int last_status = 1; /* assume up by default */
+
+ if (has_yp_order) {
+ error = yp_order(gopt.nis_domain, map, &order);
+ if (error != 0) {
+ plog(XLOG_ERROR,
+ "nis_isup: error getting the order of map of %s: %s",
+ map, yperr_string(ypprot_err(error)));
+ last_status = 0;
+ return 0; /* NIS is down */
+ }
+ }
+ if (last_status == 0) { /* if was down before */
+ time_t dummy;
+ plog(XLOG_INFO, "nis_isup: NIS came back up for map %s", map);
+ /* XXX: do we really need to reinitialize nis? */
+ error = nis_init(m, map, &dummy);
+ if (!error)
+ last_status = 1;
+ }
+ return 1; /* NIS is up */
+}
+
+
+/*
+ * Try to locate a key using NIS.
+ */
+int
+nis_search(mnt_map *m, char *map, char *key, char **val, time_t *tp)
+{
+ int outlen;
+ int res;
+ YP_ORDER_OUTORDER_TYPE order;
+
+ /*
+ * Make sure domain initialised
+ */
+ if (!gopt.nis_domain) {
+ int error = determine_nis_domain();
+ if (error)
+ return error;
+ }
+
+
+ if (has_yp_order) {
+ /*
+ * Check if map has changed
+ */
+ if (yp_order(gopt.nis_domain, map, &order))
+ return EIO;
+ if ((time_t) order > *tp) {
+ *tp = (time_t) order;
+ return -1;
+ }
+ } else {
+ /*
+ * NIS+ server without yp_order
+ * Check if timeout has expired to invalidate the cache
+ */
+ order = time(NULL);
+ if ((time_t)order - *tp > gopt.am_timeo) {
+ *tp = (time_t)order;
+ return(-1);
+ }
+ }
+
+ /*
+ * Lookup key
+ */
+ res = yp_match(gopt.nis_domain, map, key, strlen(key), val, &outlen);
+
+ /*
+ * Do something interesting with the return code
+ */
+ switch (res) {
+ case 0:
+ return 0;
+
+ case YPERR_KEY:
+ return ENOENT;
+
+ default:
+ plog(XLOG_ERROR, "%s: %s", map, yperr_string(res));
+ return EIO;
+ }
+}
+
+
+int
+nis_init(mnt_map *m, char *map, time_t *tp)
+{
+ YP_ORDER_OUTORDER_TYPE order;
+ int yp_order_result;
+ char *master;
+
+ if (!gopt.nis_domain) {
+ int error = determine_nis_domain();
+ if (error)
+ return error;
+ }
+
+ /*
+ * To see if the map exists, try to find
+ * a master for it.
+ */
+ yp_order_result = yp_order(gopt.nis_domain, map, &order);
+ switch (yp_order_result) {
+ case 0:
+ has_yp_order = TRUE;
+ *tp = (time_t) order;
+#ifdef DEBUG
+ dlog("NIS master for %s@%s has order %d", map, gopt.nis_domain, order);
+#endif /* DEBUG */
+ break;
+ case YPERR_YPERR:
+ /* NIS+ server found ! */
+ has_yp_order = FALSE;
+ /* try yp_master() instead */
+ if (yp_master(gopt.nis_domain, map, &master)) {
+ return ENOENT;
+ } else {
+#ifdef DEBUG
+ dlog("NIS master for %s@%s is a NIS+ server", map, gopt.nis_domain);
+#endif /* DEBUG */
+ /* Use fake timestamps */
+ *tp = time(NULL);
+ }
+ break;
+ default:
+ return ENOENT;
+ }
+ return 0;
+}
+
+
+int
+nis_mtime(mnt_map *m, char *map, time_t *tp)
+{
+ return nis_init(m, map, tp);
+}
+
+
+#ifdef HAVE_BAD_YP_ALL
+/*
+ * If you are using NIS and your yp_all function is "broken", use an
+ * alternate code which avoids a bug in yp_all(). The bug in yp_all() is
+ * that it does not close a TCP connection to ypserv, and this ypserv runs
+ * out of open filedescriptors, getting into an infinite loop, thus all YP
+ * clients enevtually unbind and hang too.
+ *
+ * Systems known to be plagued with this bug:
+ * earlier SunOS 4.x
+ * all irix systems (at this time, up to 6.4 was checked)
+ *
+ * -Erez Zadok <ezk@cs.columbia.edu>
+ * -James Tanis <jtt@cs.columbia.edu> */
+static int
+am_yp_all(char *indomain, char *inmap, struct ypall_callback *incallback)
+{
+ int i, j;
+ char *outkey, *outval;
+ int outkeylen, outvallen;
+ char *outkey_old;
+ int outkeylen_old;
+
+ plog(XLOG_INFO, "NIS map %s reloading using am_yp_all", inmap);
+
+ i = yp_first(indomain, inmap, &outkey, &outkeylen, &outval, &outvallen);
+ if (i) {
+ plog(XLOG_ERROR, "yp_first() returned error: %s\n", yperr_string(i));
+ }
+ do {
+ j = (incallback->foreach)(YP_TRUE,
+ outkey,
+ outkeylen,
+ outval,
+ outvallen,
+ incallback->data);
+ if (j != FALSE) /* terminate loop */
+ break;
+ outkey_old = outkey;
+ outkeylen_old = outkeylen;
+ i = yp_next(indomain,
+ inmap,
+ outkey_old,
+ outkeylen_old,
+ &outkey,
+ &outkeylen,
+ &outval,
+ &outvallen);
+ } while (!i);
+#ifdef DEBUG
+ if (i) {
+ dlog("yp_next() returned error: %s\n", yperr_string(i));
+ }
+#endif /* DEBUG */
+ if (i == YPERR_NOMORE)
+ return 0;
+ return i;
+}
+#endif /* HAVE_BAD_YP_ALL */
diff --git a/contrib/amd/amd/info_nisplus.c b/contrib/amd/amd/info_nisplus.c
new file mode 100644
index 000000000000..71f29e956c0b
--- /dev/null
+++ b/contrib/amd/amd/info_nisplus.c
@@ -0,0 +1,321 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: info_nisplus.c,v 5.2.2.1 1992/02/09 15:08:32 jsp beta $
+ *
+ */
+
+/*
+ * Get info from NIS+ (version 3) map
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+#define NISPLUS_KEY "key="
+#define NISPLUS_ORGDIR ".org_dir"
+
+struct nis_callback_data {
+ mnt_map *ncd_m;
+ char *ncd_map;
+ void (*ncd_fn)();
+};
+
+struct nisplus_search_callback_data {
+ nis_name key;
+ char *value;
+};
+
+
+static int
+nisplus_callback(const nis_name key, const nis_object *value, voidp opaquedata)
+{
+ char *kp = strnsave(ENTRY_VAL(value, 0), ENTRY_LEN(value, 0));
+ char *vp = strnsave(ENTRY_VAL(value, 1), ENTRY_LEN(value, 1));
+ struct nis_callback_data *data = (struct nis_callback_data *) opaquedata;
+
+#ifdef DEBUG
+ dlog("NISplus callback for <%s,%s>", kp, vp);
+#endif /* DEBUG */
+
+ (*data->ncd_fn) (data->ncd_m, kp, vp);
+
+ /*
+ * We want more ...
+ */
+ return FALSE;
+}
+
+
+int
+nisplus_reload(mnt_map *m, char *map, void (*fn) ())
+{
+ int error = 0;
+ struct nis_callback_data data;
+ nis_result *result;
+ char *org; /* if map does not have ".org_dir" then append it */
+ nis_name map_name;
+
+ org = strstr(map, NISPLUS_ORGDIR);
+ if (org == NULL)
+ org = NISPLUS_ORGDIR;
+ else
+ org = "";
+
+ /* make some room for the NIS map_name */
+ map_name = xmalloc(strlen(map) + sizeof(NISPLUS_ORGDIR));
+ if (map_name == NULL) {
+ plog(XLOG_ERROR, "Unable to create map_name %s: %s",
+ map, strerror(ENOMEM));
+ return ENOMEM;
+ }
+ sprintf(map_name, "%s%s", map, org);
+
+ data.ncd_m = m;
+ data.ncd_map = map_name;
+ data.ncd_fn = fn;
+
+#ifdef DEBUG
+ dlog("NISplus reload for %s", map);
+#endif /* DEBUG */
+
+ result = nis_list(map_name,
+ EXPAND_NAME | FOLLOW_LINKS | FOLLOW_PATH,
+ (int (*)()) nisplus_callback,
+ &data);
+
+ /* free off the NIS map_name */
+ XFREE(map_name);
+
+ if (result->status != NIS_SUCCESS && result->status != NIS_CBRESULTS)
+ error = 1;
+
+ if (error)
+ plog(XLOG_ERROR, "error grabbing nisplus map of %s: %s",
+ map,
+ nis_sperrno(result->status));
+
+ nis_freeresult(result);
+ return error;
+}
+
+
+static int
+nisplus_search_callback(const nis_name key, const nis_object *value, voidp opaquedata)
+{
+ struct nisplus_search_callback_data *data = (struct nisplus_search_callback_data *) opaquedata;
+
+#ifdef DEBUG
+ dlog("NISplus search callback for <%s>", ENTRY_VAL(value, 0));
+ dlog("NISplus search callback value <%s>", ENTRY_VAL(value, 1));
+#endif /* DEBUG */
+
+ data->value = strnsave(ENTRY_VAL(value, 1), ENTRY_LEN(value, 1));
+ return TRUE;
+}
+
+
+/*
+ * Try to locate a key using NIS+.
+ */
+int
+nisplus_search(mnt_map *m, char *map, char *key, char **val, time_t *tp)
+{
+ nis_result *result;
+ int error = 0;
+ struct nisplus_search_callback_data data;
+ nis_name index;
+ char *org; /* if map does not have ".org_dir" then append it */
+
+ org = strstr(map, NISPLUS_ORGDIR);
+ if (org == NULL)
+ org = NISPLUS_ORGDIR;
+ else
+ org = "";
+
+ /* make some room for the NIS index */
+ index = xmalloc(sizeof('[') /* for opening selection criteria */
+ +sizeof(NISPLUS_KEY)
+ + strlen(key)
+ + sizeof(']') /* for closing selection criteria */
+ +sizeof(',') /* + 1 for , separator */
+ +strlen(map)
+ + sizeof(NISPLUS_ORGDIR)
+ );
+ if (index == NULL) {
+ plog(XLOG_ERROR,
+ "Unable to create index %s: %s",
+ map,
+ strerror(ENOMEM));
+ return ENOMEM;
+ }
+ sprintf(index, "[%s%s],%s%s", NISPLUS_KEY, key, map, org);
+
+ data.key = key;
+ data.value = NULL;
+
+#ifdef DEBUG
+ dlog("NISplus search for %s", index);
+#endif /* DEBUG */
+
+ result = nis_list(index,
+ EXPAND_NAME | FOLLOW_LINKS | FOLLOW_PATH,
+ (int (*)()) nisplus_search_callback,
+ &data);
+
+ /* free off the NIS index */
+ XFREE(index);
+
+ if (result == NULL) {
+ plog(XLOG_ERROR, "%s: %s", map, strerror(ENOMEM));
+ return ENOMEM;
+ }
+
+ /*
+ * Do something interesting with the return code
+ */
+ switch (result->status) {
+ case NIS_SUCCESS:
+ case NIS_CBRESULTS:
+
+ if (data.value == NULL) {
+ nis_object *value = result->objects.objects_val;
+#ifdef DEBUG
+ dlog("NISplus search found <nothing>");
+ dlog("NISplus search for %s: %s(%d)",
+ map, nis_sperrno(result->status), result->status);
+#endif /* DEBUG */
+
+ if (value != NULL)
+ data.value = strnsave(ENTRY_VAL(value, 1), ENTRY_LEN(value, 1));
+ }
+ *val = data.value;
+
+ if (*val) {
+ error = 0;
+#ifdef DEBUG
+ dlog("NISplus search found %s", *val);
+#endif /* DEBUG */
+ } else {
+ error = ENOENT;
+#ifdef DEBUG
+ dlog("NISplus search found nothing");
+#endif /* DEBUG */
+ }
+
+ *tp = 0;
+ break;
+
+ case NIS_NOSUCHNAME:
+#ifdef DEBUG
+ dlog("NISplus search returned %d", result->status);
+#endif /* DEBUG */
+ error = ENOENT;
+ break;
+
+ default:
+ plog(XLOG_ERROR, "%s: %s", map, nis_sperrno(result->status));
+ error = EIO;
+ break;
+ }
+ nis_freeresult(result);
+
+ return error;
+}
+
+
+int
+nisplus_init(mnt_map *m, char *map, time_t *tp)
+{
+ nis_result *result;
+ char *org; /* if map does not have ".org_dir" then append it */
+ nis_name map_name;
+ int error = 0;
+
+ org = strstr(map, NISPLUS_ORGDIR);
+ if (org == NULL)
+ org = NISPLUS_ORGDIR;
+ else
+ org = "";
+
+ /* make some room for the NIS map_name */
+ map_name = xmalloc(strlen(map) + sizeof(NISPLUS_ORGDIR));
+ if (map_name == NULL) {
+ plog(XLOG_ERROR,
+ "Unable to create map_name %s: %s",
+ map,
+ strerror(ENOMEM));
+ return ENOMEM;
+ }
+ sprintf(map_name, "%s%s", map, org);
+
+ result = nis_lookup(map_name, (EXPAND_NAME | FOLLOW_LINKS | FOLLOW_PATH));
+
+ /* free off the NIS map_name */
+ XFREE(map_name);
+
+ if (result == NULL) {
+ plog(XLOG_ERROR, "NISplus init <%s>: %s", map, strerror(ENOMEM));
+ return ENOMEM;
+ }
+
+ if (result->status != NIS_SUCCESS) {
+#ifdef DEBUG
+ dlog("NISplus init <%s>: %s (%d)",
+ map, nis_sperrno(result->status), result->status);
+#endif /* DEBUG */
+
+ error = ENOENT;
+ }
+
+ *tp = 0; /* no time */
+ nis_freeresult(result);
+ return error;
+}
+
+
+int
+nisplus_mtime(mnt_map *m, char *map, time_t *tp)
+{
+ return nisplus_init(m,map, tp);
+}
diff --git a/contrib/amd/amd/info_passwd.c b/contrib/amd/amd/info_passwd.c
new file mode 100644
index 000000000000..32d92f4bf361
--- /dev/null
+++ b/contrib/amd/amd/info_passwd.c
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: info_passwd.c,v 5.2.2.1 1992/02/09 15:08:33 jsp beta $
+ *
+ */
+
+/*
+ * Get info from password "file"
+ *
+ * This is experimental and probably doesn't do what you expect.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+#define PASSWD_MAP "/etc/passwd"
+
+/* forward declarations */
+int passwd_init(mnt_map *m, char *map, time_t *tp);
+int passwd_search(mnt_map *m, char *map, char *key, char **pval, time_t *tp);
+
+
+/*
+ * Nothing to probe - check the map name is PASSWD_MAP.
+ */
+int
+passwd_init(mnt_map *m, char *map, time_t *tp)
+{
+ *tp = 0;
+
+ /*
+ * Recognize the old format "PASSWD_MAP"
+ * Uses default return string
+ * "type:=nfs;rfs:=/${var0}/${var1};rhost:=${var1};sublink:=${var2};fs:=${autodir}${var3}"
+ */
+ if (STREQ(map, PASSWD_MAP))
+ return 0;
+ /*
+ * Recognize the new format "PASSWD_MAP:pval-format"
+ */
+ if (!NSTREQ(map, PASSWD_MAP, sizeof(PASSWD_MAP) - 1))
+ return ENOENT;
+ if (map[sizeof(PASSWD_MAP)-1] != ':')
+ return ENOENT;
+
+ return 0;
+}
+
+
+/*
+ * Grab the entry via the getpwname routine
+ * Modify time is ignored by passwd - XXX
+ */
+int
+passwd_search(mnt_map *m, char *map, char *key, char **pval, time_t *tp)
+{
+ char *dir = 0;
+ struct passwd *pw;
+
+ if (STREQ(key, "/defaults")) {
+ *pval = strdup("type:=nfs");
+ return 0;
+ }
+ pw = getpwnam(key);
+
+ if (pw) {
+ /*
+ * We chop the home directory up as follows:
+ * /anydir/dom1/dom2/dom3/user
+ *
+ * and return
+ * rfs:=/anydir/dom3;rhost:=dom3.dom2.dom1;sublink:=user
+ * and now have
+ * var0:=pw-prefix:=anydir
+ * var1:=pw-rhost:=dom3.dom2.dom1
+ * var2:=pw-user:=user
+ * var3:=pw-home:=/anydir/dom1/dom2/dom3/user
+ *
+ * This allows cross-domain entries in your passwd file.
+ * ... but forget about security!
+ */
+ char *user;
+ char *p, *q;
+ char val[MAXPATHLEN];
+ char rhost[MAXHOSTNAMELEN];
+ dir = strdup(pw->pw_dir);
+
+ /*
+ * Find user name. If no / then Invalid...
+ */
+ user = strrchr(dir, '/');
+ if (!user)
+ goto enoent;
+ *user++ = '\0';
+
+ /*
+ * Find start of host "path". If no / then Invalid...
+ */
+ p = strchr(dir + 1, '/');
+ if (!p)
+ goto enoent;
+ *p++ = '\0';
+
+ /*
+ * At this point, p is dom1/dom2/dom3
+ * Copy, backwards, into rhost replacing
+ * / with .
+ */
+ rhost[0] = '\0';
+ do {
+ q = strrchr(p, '/');
+ if (q) {
+ strcat(rhost, q + 1);
+ strcat(rhost, ".");
+ *q = '\0';
+ } else {
+ strcat(rhost, p);
+ }
+ } while (q);
+
+ /*
+ * Sanity check
+ */
+ if (*rhost == '\0' || *user == '\0' || *dir == '\0')
+ goto enoent;
+
+ /*
+ * Make up return string
+ */
+ q = strchr(rhost, '.');
+ if (q)
+ *q = '\0';
+ p = strchr(map, ':');
+ if (p)
+ p++;
+ else
+ p = "type:=nfs;rfs:=/${var0}/${var1};rhost:=${var1};sublink:=${var2};fs:=${autodir}${var3}";
+ sprintf(val, "var0:=%s;var1:=%s;var2:=%s;var3:=%s;%s",
+ dir+1, rhost, user, pw->pw_dir, p);
+#ifdef DEBUG
+ dlog("passwd_search: map=%s key=%s -> %s", map, key, val);
+#endif /* DEBUG */
+ if (q)
+ *q = '.';
+ *pval = strdup(val);
+ return 0;
+ }
+
+enoent:
+ if (dir)
+ XFREE(dir);
+
+ return ENOENT;
+}
diff --git a/contrib/amd/amd/info_union.c b/contrib/amd/amd/info_union.c
new file mode 100644
index 000000000000..7f757f978159
--- /dev/null
+++ b/contrib/amd/amd/info_union.c
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: info_union.c,v 5.2.2.1 1992/02/09 15:08:34 jsp beta $
+ *
+ */
+
+/*
+ * Get info from the system namespace
+ *
+ * NOTE: Cannot handle reads back through the automounter.
+ * THIS WILL CAUSE A DEADLOCK!
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+#define UNION_PREFIX "union:"
+#define UNION_PREFLEN 6
+
+/* forward declarations */
+int union_init(mnt_map *m, char *map, time_t *tp);
+int union_search(mnt_map *m, char *map, char *key, char **pval, time_t *tp);
+int union_reload(mnt_map *m, char *map, void (*fn) (mnt_map *, char *, char *));
+
+
+/*
+ * No way to probe - check the map name begins with "union:"
+ */
+int
+union_init(mnt_map *m, char *map, time_t *tp)
+{
+ *tp = 0;
+ return NSTREQ(map, UNION_PREFIX, UNION_PREFLEN) ? 0 : ENOENT;
+}
+
+
+int
+union_search(mnt_map *m, char *map, char *key, char **pval, time_t *tp)
+{
+ char *mapd = strdup(map + UNION_PREFLEN);
+ char **v = strsplit(mapd, ':', '\"');
+ char **p;
+
+ for (p = v; p[1]; p++) ;
+ *pval = xmalloc(strlen(*p) + 5);
+ sprintf(*pval, "fs:=%s", *p);
+ XFREE(mapd);
+ XFREE(v);
+ return 0;
+}
+
+
+int
+union_reload(mnt_map *m, char *map, void (*fn) (mnt_map *, char *, char *))
+{
+ char *mapd = strdup(map + UNION_PREFLEN);
+ char **v = strsplit(mapd, ':', '\"');
+ char **dir;
+
+ /*
+ * Add fake /defaults entry
+ */
+ (*fn) (m, strdup("/defaults"), strdup("type:=link;opts:=nounmount;sublink:=${key}"));
+
+ for (dir = v; *dir; dir++) {
+ int dlen;
+ struct dirent *dp;
+
+ DIR *dirp = opendir(*dir);
+ if (!dirp) {
+ plog(XLOG_USER, "Cannot read directory %s: %m", *dir);
+ continue;
+ }
+ dlen = strlen(*dir);
+
+#ifdef DEBUG
+ dlog("Reading directory %s...", *dir);
+#endif /* DEBUG */
+ while ((dp = readdir(dirp))) {
+ char *val, *dpname = &dp->d_name[0];
+ if (dpname[0] == '.' &&
+ (dpname[1] == '\0' ||
+ (dpname[1] == '.' && dpname[2] == '\0')))
+ continue;
+
+#ifdef DEBUG
+ dlog("... gives %s", dp->d_name);
+#endif /* DEBUG */
+ val = xmalloc(dlen + 5);
+ sprintf(val, "fs:=%s", *dir);
+ (*fn) (m, strdup(dp->d_name), val);
+ }
+ closedir(dirp);
+ }
+
+ /*
+ * Add wildcard entry
+ */
+ {
+ char *val = xmalloc(strlen(dir[-1]) + 5);
+
+ sprintf(val, "fs:=%s", dir[-1]);
+ (*fn) (m, strdup("*"), val);
+ }
+ XFREE(mapd);
+ XFREE(v);
+ return 0;
+}
diff --git a/contrib/amd/amd/map.c b/contrib/amd/amd/map.c
new file mode 100644
index 000000000000..20320d923330
--- /dev/null
+++ b/contrib/amd/amd/map.c
@@ -0,0 +1,1112 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: map.c,v 5.2.2.2 1992/08/02 10:42:21 jsp Exp $
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+#define smallest_t(t1, t2) (t1 != NEVER ? (t2 != NEVER ? (t1 < t2 ? t1 : t2) : t1) : t2)
+#define IGNORE_FLAGS (MFF_MOUNTING|MFF_UNMOUNTING|MFF_RESTART)
+#define NEVER (time_t) 0
+#define new_gen() (am_gen++)
+
+/*
+ * Generation Numbers.
+ *
+ * Generation numbers are allocated to every node created
+ * by amd. When a filehandle is computed and sent to the
+ * kernel, the generation number makes sure that it is safe
+ * to reallocate a node slot even when the kernel has a cached
+ * reference to its old incarnation.
+ * No garbage collection is done, since it is assumed that
+ * there is no way that 2^32 generation numbers could ever
+ * be allocated by a single run of amd - there is simply
+ * not enough cpu time available.
+ */
+static u_int am_gen = 2; /* Initial generation number */
+static int timeout_mp_id; /* Id from last call to timeout */
+
+am_node *root_node; /* The root of the mount tree */
+am_node **exported_ap = (am_node **) 0;
+int exported_ap_size = 0;
+int first_free_map = 0; /* First available free slot */
+int last_used_map = -1; /* Last unavailable used slot */
+
+
+/*
+ * This is the default attributes field which
+ * is copied into every new node to be created.
+ * The individual filesystem fs_init() routines
+ * patch the copy to represent the particular
+ * details for the relevant filesystem type
+ */
+static nfsfattr gen_fattr =
+{
+ NFLNK, /* type */
+ NFSMODE_LNK | 0777, /* mode */
+ 1, /* nlink */
+ 0, /* uid */
+ 0, /* gid */
+ 0, /* size */
+ 4096, /* blocksize */
+ 0, /* rdev */
+ 1, /* blocks */
+ 0, /* fsid */
+ 0, /* fileid */
+ {0, 0}, /* atime */
+ {0, 0}, /* mtime */
+ {0, 0}, /* ctime */
+};
+
+/* forward declarations */
+static int unmount_node(am_node *mp);
+static void exported_ap_free(am_node *mp);
+static void remove_am(am_node *mp);
+
+
+/*
+ * Resize exported_ap map
+ */
+static int
+exported_ap_realloc_map(int nsize)
+{
+ /*
+ * this shouldn't happen, but...
+ */
+ if (nsize < 0 || nsize == exported_ap_size)
+ return 0;
+
+ exported_ap = (am_node **) xrealloc((voidp) exported_ap, nsize * sizeof(am_node *));
+
+ if (nsize > exported_ap_size)
+ memset((char *) (exported_ap + exported_ap_size), 0,
+ (nsize - exported_ap_size) * sizeof(am_node *));
+ exported_ap_size = nsize;
+
+ return 1;
+}
+
+
+/*
+ * Allocate a new mount slot and create
+ * a new node.
+ * Fills in the map number of the node,
+ * but leaves everything else uninitialised.
+ */
+am_node *
+exported_ap_alloc(void)
+{
+ am_node *mp, **mpp;
+
+ /*
+ * First check if there are any slots left, realloc if needed
+ */
+ if (first_free_map >= exported_ap_size)
+ if (!exported_ap_realloc_map(exported_ap_size + NEXP_AP))
+ return 0;
+
+ /*
+ * Grab the next free slot
+ */
+ mpp = exported_ap + first_free_map;
+ mp = *mpp = ALLOC(struct am_node);
+ memset((char *) mp, 0, sizeof(*mp));
+
+ mp->am_mapno = first_free_map++;
+
+ /*
+ * Update free pointer
+ */
+ while (first_free_map < exported_ap_size && exported_ap[first_free_map])
+ first_free_map++;
+
+ if (first_free_map > last_used_map)
+ last_used_map = first_free_map - 1;
+
+ return mp;
+}
+
+
+/*
+ * Free a mount slot
+ */
+static void
+exported_ap_free(am_node *mp)
+{
+ /*
+ * Sanity check
+ */
+ if (!mp)
+ return;
+
+ /*
+ * Zero the slot pointer to avoid double free's
+ */
+ exported_ap[mp->am_mapno] = 0;
+
+ /*
+ * Update the free and last_used indices
+ */
+ if (mp->am_mapno == last_used_map)
+ while (last_used_map >= 0 && exported_ap[last_used_map] == 0)
+ --last_used_map;
+
+ if (first_free_map > mp->am_mapno)
+ first_free_map = mp->am_mapno;
+
+ /*
+ * Free the mount node
+ */
+ XFREE(mp);
+}
+
+
+/*
+ * Insert mp into the correct place,
+ * where p_mp is its parent node.
+ * A new node gets placed as the youngest sibling
+ * of any other children, and the parent's child
+ * pointer is adjusted to point to the new child node.
+ */
+void
+insert_am(am_node *mp, am_node *p_mp)
+{
+ /*
+ * If this is going in at the root then flag it
+ * so that it cannot be unmounted by amq.
+ */
+ if (p_mp == root_node)
+ mp->am_flags |= AMF_ROOT;
+ /*
+ * Fill in n-way links
+ */
+ mp->am_parent = p_mp;
+ mp->am_osib = p_mp->am_child;
+ if (mp->am_osib)
+ mp->am_osib->am_ysib = mp;
+ p_mp->am_child = mp;
+}
+
+
+/*
+ * Remove am from its place in the mount tree
+ */
+static void
+remove_am(am_node *mp)
+{
+ /*
+ * 1. Consistency check
+ */
+ if (mp->am_child && mp->am_parent) {
+ plog(XLOG_WARNING, "children of \"%s\" still exist - deleting anyway", mp->am_path);
+ }
+
+ /*
+ * 2. Update parent's child pointer
+ */
+ if (mp->am_parent && mp->am_parent->am_child == mp)
+ mp->am_parent->am_child = mp->am_osib;
+
+ /*
+ * 3. Unlink from sibling chain
+ */
+ if (mp->am_ysib)
+ mp->am_ysib->am_osib = mp->am_osib;
+ if (mp->am_osib)
+ mp->am_osib->am_ysib = mp->am_ysib;
+}
+
+
+/*
+ * Compute a new time to live value for a node.
+ */
+void
+new_ttl(am_node *mp)
+{
+ mp->am_timeo_w = 0;
+ mp->am_ttl = clocktime();
+ mp->am_fattr.na_atime.nt_seconds = mp->am_ttl;
+ mp->am_ttl += mp->am_timeo; /* sun's -tl option */
+}
+
+
+void
+mk_fattr(am_node *mp, nfsftype vntype)
+{
+ switch (vntype) {
+ case NFDIR:
+ mp->am_fattr.na_type = NFDIR;
+ mp->am_fattr.na_mode = NFSMODE_DIR | 0555;
+ mp->am_fattr.na_nlink = 2;
+ mp->am_fattr.na_size = 512;
+ break;
+ case NFLNK:
+ mp->am_fattr.na_type = NFLNK;
+ mp->am_fattr.na_mode = NFSMODE_LNK | 0777;
+ mp->am_fattr.na_nlink = 1;
+ mp->am_fattr.na_size = 0;
+ break;
+ default:
+ plog(XLOG_FATAL, "Unknown fattr type %d - ignored", vntype);
+ break;
+ }
+}
+
+
+/*
+ * Initialise an allocated mount node.
+ * It is assumed that the mount node was b-zero'd
+ * before getting here so anything that would
+ * be set to zero isn't done here.
+ */
+void
+init_map(am_node *mp, char *dir)
+{
+ /*
+ * mp->am_mapno is initialized by exported_ap_alloc
+ * other fields don't need to be set to zero.
+ */
+ mp->am_mnt = new_mntfs();
+ mp->am_name = strdup(dir);
+ mp->am_path = strdup(dir);
+ mp->am_gen = new_gen();
+
+ mp->am_timeo = gopt.am_timeo;
+ mp->am_attr.ns_status = NFS_OK;
+ mp->am_fattr = gen_fattr;
+ mp->am_fattr.na_fsid = 42;
+ mp->am_fattr.na_fileid = 0;
+ mp->am_fattr.na_atime.nt_seconds = clocktime();
+ mp->am_fattr.na_atime.nt_useconds = 0;
+ mp->am_fattr.na_mtime = mp->am_fattr.na_ctime = mp->am_fattr.na_atime;
+
+ new_ttl(mp);
+ mp->am_stats.s_mtime = mp->am_fattr.na_atime.nt_seconds;
+}
+
+
+/*
+ * Free a mount node.
+ * The node must be already unmounted.
+ */
+void
+free_map(am_node *mp)
+{
+
+ remove_am(mp);
+
+ if (mp->am_link)
+ XFREE(mp->am_link);
+ if (mp->am_name)
+ XFREE(mp->am_name);
+ if (mp->am_path)
+ XFREE(mp->am_path);
+ if (mp->am_pref)
+ XFREE(mp->am_pref);
+ if (mp->am_transp)
+ XFREE(mp->am_transp);
+
+ if (mp->am_mnt)
+ free_mntfs(mp->am_mnt);
+
+ exported_ap_free(mp);
+}
+
+
+/*
+ * Convert from file handle to automount node.
+ */
+am_node *
+fh_to_mp3(am_nfs_fh *fhp, int *rp, int c_or_d)
+{
+ struct am_fh *fp = (struct am_fh *) fhp;
+ am_node *ap = 0;
+
+ /*
+ * Check process id matches
+ * If it doesn't then it is probably
+ * from an old kernel cached filehandle
+ * which is now out of date.
+ */
+ if (fp->fhh_pid != mypid)
+ goto drop;
+
+ /*
+ * Make sure the index is valid before
+ * exported_ap is referenced.
+ */
+ if (fp->fhh_id < 0 || fp->fhh_id >= exported_ap_size)
+ goto drop;
+
+ /*
+ * Get hold of the supposed mount node
+ */
+ ap = exported_ap[fp->fhh_id];
+
+ /*
+ * If it exists then maybe...
+ */
+ if (ap) {
+ /*
+ * Check the generation number in the node
+ * matches the one from the kernel. If not
+ * then the old node has been timed out and
+ * a new one allocated.
+ */
+ if (ap->am_gen != fp->fhh_gen) {
+ ap = 0;
+ goto drop;
+ }
+ /*
+ * If the node is hung then locate a new node
+ * for it. This implements the replicated filesystem
+ * retries.
+ */
+ if (ap->am_mnt && FSRV_ISDOWN(ap->am_mnt->mf_server) && ap->am_parent) {
+ int error;
+ am_node *orig_ap = ap;
+
+#ifdef DEBUG
+ dlog("fh_to_mp3: %s (%s) is hung:- call lookup",
+ orig_ap->am_path, orig_ap->am_mnt->mf_info);
+#endif /* DEBUG */
+
+ /*
+ * Update modify time of parent node.
+ * With any luck the kernel will re-stat
+ * the child node and get new information.
+ */
+ orig_ap->am_fattr.na_mtime.nt_seconds = clocktime();
+
+ /*
+ * Call the parent's lookup routine for an object
+ * with the same name. This may return -1 in error
+ * if a mount is in progress. In any case, if no
+ * mount node is returned the error code is propagated
+ * to the caller.
+ */
+ if (c_or_d == VLOOK_CREATE) {
+ ap = (*orig_ap->am_parent->am_mnt->mf_ops->lookuppn)
+ (orig_ap->am_parent, orig_ap->am_name, &error, c_or_d);
+ } else {
+ ap = 0;
+ error = ESTALE;
+ }
+ if (ap == 0) {
+ if (error < 0 && amd_state == Finishing)
+ error = ENOENT;
+ *rp = error;
+ return 0;
+ }
+
+ /*
+ * Update last access to original node. This
+ * avoids timing it out and so sending ESTALE
+ * back to the kernel.
+ * XXX - Not sure we need this anymore (jsp, 90/10/6).
+ */
+ new_ttl(orig_ap);
+
+ }
+
+ /*
+ * Disallow references to objects being unmounted, unless
+ * they are automount points.
+ */
+ if (ap->am_mnt && (ap->am_mnt->mf_flags & MFF_UNMOUNTING) &&
+ !(ap->am_flags & AMF_ROOT)) {
+ if (amd_state == Finishing)
+ *rp = ENOENT;
+ else
+ *rp = -1;
+ return 0;
+ }
+ new_ttl(ap);
+ }
+
+drop:
+ if (!ap || !ap->am_mnt) {
+ /*
+ * If we are shutting down then it is likely
+ * that this node has disappeared because of
+ * a fast timeout. To avoid things thrashing
+ * just pretend it doesn't exist at all. If
+ * ESTALE is returned, some NFS clients just
+ * keep retrying (stupid or what - if it's
+ * stale now, what's it going to be in 5 minutes?)
+ */
+ if (amd_state == Finishing)
+ *rp = ENOENT;
+ else
+ *rp = ESTALE;
+ amd_stats.d_stale++;
+ }
+
+ return ap;
+}
+
+
+am_node *
+fh_to_mp(am_nfs_fh *fhp)
+{
+ int dummy;
+
+ return fh_to_mp2(fhp, &dummy);
+}
+
+
+/*
+ * Convert from automount node to file handle.
+ */
+void
+mp_to_fh(am_node *mp, am_nfs_fh *fhp)
+{
+ struct am_fh *fp = (struct am_fh *) fhp;
+
+ memset((char *) fhp, 0, sizeof(am_nfs_fh));
+
+ /*
+ * Take the process id
+ */
+ fp->fhh_pid = mypid;
+
+ /*
+ * ... the map number
+ */
+ fp->fhh_id = mp->am_mapno;
+
+ /*
+ * ... and the generation number
+ */
+ fp->fhh_gen = mp->am_gen;
+
+ /*
+ * ... to make a "unique" triple that will never
+ * be reallocated except across reboots (which doesn't matter)
+ * or if we are unlucky enough to be given the same
+ * pid as a previous amd (very unlikely).
+ */
+}
+
+
+am_node *
+find_ap2(char *dir, am_node *mp)
+{
+ if (mp) {
+ am_node *mp2;
+ if (STREQ(mp->am_path, dir))
+ return mp;
+
+ if ((mp->am_mnt->mf_flags & MFF_MOUNTED) &&
+ STREQ(mp->am_mnt->mf_mount, dir))
+ return mp;
+
+ mp2 = find_ap2(dir, mp->am_osib);
+ if (mp2)
+ return mp2;
+ return find_ap2(dir, mp->am_child);
+ }
+
+ return 0;
+}
+
+
+/*
+ * Find the mount node corresponding to dir. dir can match either the
+ * automount path or, if the node is mounted, the mount location.
+ */
+am_node *
+find_ap(char *dir)
+{
+ int i;
+
+ for (i = last_used_map; i >= 0; --i) {
+ am_node *mp = exported_ap[i];
+ if (mp && (mp->am_flags & AMF_ROOT)) {
+ mp = find_ap2(dir, exported_ap[i]);
+ if (mp) {
+ return mp;
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+/*
+ * Find the mount node corresponding
+ * to the mntfs structure.
+ */
+am_node *
+find_mf(mntfs *mf)
+{
+ int i;
+
+ for (i = last_used_map; i >= 0; --i) {
+ am_node *mp = exported_ap[i];
+ if (mp && mp->am_mnt == mf)
+ return mp;
+ }
+
+ return 0;
+}
+
+
+/*
+ * Get the filehandle for a particular named directory.
+ * This is used during the bootstrap to tell the kernel
+ * the filehandles of the initial automount points.
+ */
+am_nfs_fh *
+root_fh(char *dir)
+{
+ static am_nfs_fh nfh;
+ am_node *mp = root_ap(dir, TRUE);
+ if (mp) {
+ mp_to_fh(mp, &nfh);
+ /*
+ * Patch up PID to match main server...
+ */
+ if (!foreground) {
+ long pid = getppid();
+ ((struct am_fh *) &nfh)->fhh_pid = pid;
+#ifdef DEBUG
+ dlog("root_fh substitutes pid %d", pid);
+#endif /* DEBUG */
+ }
+ return &nfh;
+ }
+
+ /*
+ * Should never get here...
+ */
+ plog(XLOG_ERROR, "Can't find root filehandle for %s", dir);
+
+ return 0;
+}
+
+
+am_node *
+root_ap(char *dir, int path)
+{
+ am_node *mp = find_ap(dir);
+
+ if (mp && mp->am_parent == root_node)
+ return mp;
+
+ return 0;
+}
+
+
+/*
+ * Timeout all nodes waiting on
+ * a given Fserver.
+ */
+void
+map_flush_srvr(fserver *fs)
+{
+ int i;
+ int done = 0;
+
+ for (i = last_used_map; i >= 0; --i) {
+ am_node *mp = exported_ap[i];
+ if (mp && mp->am_mnt && mp->am_mnt->mf_server == fs) {
+ plog(XLOG_INFO, "Flushed %s; dependent on %s", mp->am_path, fs->fs_host);
+ mp->am_ttl = clocktime();
+ done = 1;
+ }
+ }
+ if (done)
+ reschedule_timeout_mp();
+}
+
+
+/*
+ * Mount a top level automount node
+ * by calling lookup in the parent
+ * (root) node which will cause the
+ * automount node to be automounted.
+ */
+int
+mount_auto_node(char *dir, voidp arg)
+{
+ int error = 0;
+
+ (void) amfs_auto_ops.lookuppn((am_node *) arg, dir, &error, VLOOK_CREATE);
+ if (error > 0) {
+ errno = error; /* XXX */
+ plog(XLOG_ERROR, "Could not mount %s: %m", dir);
+ }
+ return error;
+}
+
+
+/*
+ * Cause all the top-level mount nodes
+ * to be automounted
+ */
+int
+mount_exported(void)
+{
+ /*
+ * Iterate over all the nodes to be started
+ */
+ return root_keyiter((void (*)P((char *, voidp))) mount_auto_node, root_node);
+}
+
+
+/*
+ * Construct top-level node
+ */
+void
+make_root_node(void)
+{
+ mntfs *root_mnt;
+ char *rootmap = ROOT_MAP;
+ root_node = exported_ap_alloc();
+
+ /*
+ * Allocate a new map
+ */
+ init_map(root_node, "");
+
+ /*
+ * Allocate a new mounted filesystem
+ */
+ root_mnt = find_mntfs(&amfs_root_ops, (am_opts *) 0, "", rootmap, "", "", "");
+
+ /*
+ * Replace the initial null reference
+ */
+ free_mntfs(root_node->am_mnt);
+ root_node->am_mnt = root_mnt;
+
+ /*
+ * Initialise the root
+ */
+ if (root_mnt->mf_ops->fs_init)
+ (*root_mnt->mf_ops->fs_init) (root_mnt);
+
+ /*
+ * Mount the root
+ */
+ root_mnt->mf_error = (*root_mnt->mf_ops->mount_fs) (root_node);
+}
+
+
+/*
+ * Cause all the nodes to be unmounted by timing
+ * them out.
+ */
+void
+umount_exported(void)
+{
+ int i;
+
+ for (i = last_used_map; i >= 0; --i) {
+ am_node *mp = exported_ap[i];
+
+ if (mp) {
+ mntfs *mf = mp->am_mnt;
+ if (mf->mf_flags & MFF_UNMOUNTING) {
+ /*
+ * If this node is being unmounted then just ignore it. However,
+ * this could prevent amd from finishing if the unmount gets blocked
+ * since the am_node will never be free'd. am_unmounted needs
+ * telling about this possibility. - XXX
+ */
+ continue;
+ }
+
+ if (mf && !(mf->mf_ops->fs_flags & FS_DIRECTORY)) {
+ /*
+ * When shutting down this had better
+ * look like a directory, otherwise it
+ * can't be unmounted!
+ */
+ mk_fattr(mp, NFDIR);
+ }
+
+ if ((--immediate_abort < 0 &&
+ !(mp->am_flags & AMF_ROOT) && mp->am_parent) ||
+ (mf->mf_flags & MFF_RESTART)) {
+
+ /*
+ * Just throw this node away without bothering to unmount it. If
+ * the server is not known to be up then don't discard the mounted
+ * on directory or Amd might hang...
+ */
+ if (mf->mf_server &&
+ (mf->mf_server->fs_flags & (FSF_DOWN | FSF_VALID)) != FSF_VALID)
+ mf->mf_flags &= ~MFF_MKMNT;
+ if (gopt.flags & CFM_UNMOUNT_ON_EXIT) {
+ plog(XLOG_INFO, "on-exit attempt to unmount %s", mf->mf_mount);
+ unmount_node(mp);
+ }
+ am_unmounted(mp);
+
+ } else {
+ /*
+ * Any other node gets forcibly timed out.
+ */
+ mp->am_flags &= ~AMF_NOTIMEOUT;
+ mp->am_mnt->mf_flags &= ~MFF_RSTKEEP;
+ mp->am_ttl = 0;
+ mp->am_timeo = 1;
+ mp->am_timeo_w = 0;
+ }
+ }
+ }
+}
+
+
+static int
+unmount_node(am_node *mp)
+{
+ mntfs *mf = mp->am_mnt;
+ int error;
+
+ if ((mf->mf_flags & MFF_ERROR) || mf->mf_refc > 1) {
+ /*
+ * Just unlink
+ */
+#ifdef DEBUG
+ if (mf->mf_flags & MFF_ERROR)
+ dlog("No-op unmount of error node %s", mf->mf_info);
+#endif /* DEBUG */
+ error = 0;
+ } else {
+#ifdef DEBUG
+ dlog("Unmounting %s (%s)", mf->mf_mount, mf->mf_info);
+#endif /* DEBUG */
+ error = (*mf->mf_ops->umount_fs) (mp);
+ }
+
+ if (error) {
+ errno = error; /* XXX */
+#ifdef DEBUG
+ dlog("%s: unmount: %m", mf->mf_mount);
+#endif /* DEBUG */
+ }
+
+ return error;
+}
+
+
+static int
+unmount_node_wrap(voidp vp)
+{
+ return unmount_node((am_node *) vp);
+
+ /*
+ * Below is the comment left from the old code
+ * that was dependent on the macro FLUSH_KERNEL_NAME_CACHE
+ */
+ /*
+ * This code should just say:
+ * return unmount_node((am_node *) vp);
+ *
+ * However...
+ * The kernel keeps a cached copy of filehandles,
+ * and doesn't ever uncache them (apparently). So
+ * when Amd times out a node the kernel will have a
+ * stale filehandle. When the kernel next uses the
+ * filehandle it gets ESTALE.
+ *
+ * The workaround:
+ * Arrange that when a node is removed an unlink or
+ * rmdir is done on that path so that the kernel
+ * cache is done. Yes - yuck.
+ *
+ * This can all be removed (and the background
+ * unmount flag in amfs_link_ops) if/when the kernel does
+ * something smarter.
+ *
+ * If the unlink or rmdir failed then just log a warning,
+ * don't fail the unmount. This can occur if the kernel
+ * client code decides that the object is still referenced
+ * and should be renamed rather than discarded.
+ *
+ * There is still a race condition here...
+ * if another process is trying to access the same
+ * filesystem at the time we get here, then
+ * it will block, since the MF_UNMOUNTING flag will
+ * be set. That may, or may not, cause the entire
+ * system to deadlock. Hmmm...
+ */
+}
+
+
+static void
+free_map_if_success(int rc, int term, voidp closure)
+{
+ am_node *mp = (am_node *) closure;
+ mntfs *mf = mp->am_mnt;
+
+ /*
+ * Not unmounting any more
+ */
+ mf->mf_flags &= ~MFF_UNMOUNTING;
+
+ /*
+ * If a timeout was defered because the underlying filesystem
+ * was busy then arrange for a timeout as soon as possible.
+ */
+ if (mf->mf_flags & MFF_WANTTIMO) {
+ mf->mf_flags &= ~MFF_WANTTIMO;
+ reschedule_timeout_mp();
+ }
+ if (term) {
+ plog(XLOG_ERROR, "unmount for %s got signal %d", mp->am_path, term);
+#if defined(DEBUG) && defined(SIGTRAP)
+ /*
+ * dbx likes to put a trap on exit().
+ * Pretend it succeeded for now...
+ */
+ if (term == SIGTRAP) {
+ am_unmounted(mp);
+ }
+#endif /* DEBUG */
+ amd_stats.d_uerr++;
+ } else if (rc) {
+ if (rc == EBUSY) {
+ plog(XLOG_STATS, "\"%s\" on %s still active", mp->am_path, mf->mf_mount);
+ } else {
+ errno = rc; /* XXX */
+ plog(XLOG_ERROR, "%s: unmount: %m", mp->am_path);
+ }
+ amd_stats.d_uerr++;
+ } else {
+ am_unmounted(mp);
+ }
+
+ /*
+ * Wakeup anything waiting for this mount
+ */
+ wakeup((voidp) mf);
+}
+
+
+static int
+unmount_mp(am_node *mp)
+{
+ int was_backgrounded = 0;
+ mntfs *mf = mp->am_mnt;
+
+#ifdef notdef
+ plog(XLOG_INFO, "\"%s\" on %s timed out", mp->am_path, mp->am_mnt->mf_mount);
+#endif /* notdef */
+
+ if ((mf->mf_ops->fs_flags & FS_UBACKGROUND) &&
+ (mf->mf_flags & MFF_MOUNTED)) {
+ if (mf->mf_refc == 1 && !FSRV_ISUP(mf->mf_server)) {
+ /*
+ * Don't try to unmount from a server that is known to be down
+ */
+ if (!(mf->mf_flags & MFF_LOGDOWN)) {
+ /* Only log this once, otherwise gets a bit boring */
+ plog(XLOG_STATS, "file server %s is down - timeout of \"%s\" ignored", mf->mf_server->fs_host, mp->am_path);
+ mf->mf_flags |= MFF_LOGDOWN;
+ }
+ } else {
+ /* Clear logdown flag - since the server must be up */
+ mf->mf_flags &= ~MFF_LOGDOWN;
+#ifdef DEBUG
+ dlog("\"%s\" on %s timed out", mp->am_path, mp->am_mnt->mf_mount);
+ /* dlog("Will background the unmount attempt"); */
+#endif /* DEBUG */
+ /*
+ * Note that we are unmounting this node
+ */
+ mf->mf_flags |= MFF_UNMOUNTING;
+ run_task(unmount_node_wrap, (voidp) mp,
+ free_map_if_success, (voidp) mp);
+ was_backgrounded = 1;
+#ifdef DEBUG
+ dlog("unmount attempt backgrounded");
+#endif /* DEBUG */
+ }
+ } else {
+#ifdef DEBUG
+ dlog("\"%s\" on %s timed out", mp->am_path, mp->am_mnt->mf_mount);
+ dlog("Trying unmount in foreground");
+#endif /* DEBUG */
+ mf->mf_flags |= MFF_UNMOUNTING;
+ free_map_if_success(unmount_node(mp), 0, (voidp) mp);
+#ifdef DEBUG
+ dlog("unmount attempt done");
+#endif /* DEBUG */
+ }
+
+ return was_backgrounded;
+}
+
+
+void
+timeout_mp(voidp v)
+{
+ int i;
+ time_t t = NEVER;
+ time_t now = clocktime();
+ int backoff = NumChild / 4;
+
+#ifdef DEBUG
+ dlog("Timing out automount points...");
+#endif /* DEBUG */
+
+ for (i = last_used_map; i >= 0; --i) {
+ am_node *mp = exported_ap[i];
+ mntfs *mf;
+
+ /*
+ * Just continue if nothing mounted, or can't be timed out.
+ */
+ if (!mp || (mp->am_flags & AMF_NOTIMEOUT))
+ continue;
+
+ /*
+ * Pick up mounted filesystem
+ */
+ mf = mp->am_mnt;
+ if (!mf)
+ continue;
+
+ /*
+ * Don't delete last reference to a restarted filesystem.
+ */
+ if ((mf->mf_flags & MFF_RSTKEEP) && mf->mf_refc == 1)
+ continue;
+
+ /*
+ * If there is action on this filesystem then ignore it
+ */
+ if (!(mf->mf_flags & IGNORE_FLAGS)) {
+ int expired = 0;
+ mf->mf_flags &= ~MFF_WANTTIMO;
+ if (now >= mp->am_ttl) {
+ if (!backoff) {
+ expired = 1;
+
+ /*
+ * Move the ttl forward to avoid thrashing effects
+ * on the next call to timeout!
+ */
+ /* sun's -tw option */
+ if (mp->am_timeo_w < 4 * gopt.am_timeo_w)
+ mp->am_timeo_w += gopt.am_timeo_w;
+ mp->am_ttl = now + mp->am_timeo_w;
+
+ } else {
+ /*
+ * Just backoff this unmount for
+ * a couple of seconds to avoid
+ * many multiple unmounts being
+ * started in parallel.
+ */
+ mp->am_ttl = now + backoff + 1;
+ }
+ }
+
+ /*
+ * If the next ttl is smallest, use that
+ */
+ t = smallest_t(t, mp->am_ttl);
+
+ if (!mp->am_child && mf->mf_error >= 0 && expired) {
+ /*
+ * If the unmount was backgrounded then
+ * bump the backoff counter.
+ */
+ if (unmount_mp(mp)) {
+ backoff = 2;
+ }
+ }
+ } else if (mf->mf_flags & MFF_UNMOUNTING) {
+ mf->mf_flags |= MFF_WANTTIMO;
+ }
+ }
+
+ if (t == NEVER) {
+#ifdef DEBUG
+ dlog("No further timeouts");
+#endif /* DEBUG */
+ t = now + ONE_HOUR;
+ }
+
+ /*
+ * Sanity check to avoid runaways.
+ * Absolutely should never get this but
+ * if you do without this trap amd will thrash.
+ */
+ if (t <= now) {
+ t = now + 6; /* XXX */
+ plog(XLOG_ERROR, "Got a zero interval in timeout_mp()!");
+ }
+
+ /*
+ * XXX - when shutting down, make things happen faster
+ */
+ if ((int) amd_state >= (int) Finishing)
+ t = now + 1;
+#ifdef DEBUG
+ dlog("Next mount timeout in %ds", t - now);
+#endif /* DEBUG */
+
+ timeout_mp_id = timeout(t - now, timeout_mp, 0);
+}
+
+
+/*
+ * Cause timeout_mp to be called soonest
+ */
+void
+reschedule_timeout_mp(void)
+{
+ if (timeout_mp_id)
+ untimeout(timeout_mp_id);
+ timeout_mp_id = timeout(0, timeout_mp, 0);
+}
diff --git a/contrib/amd/amd/mapc.c b/contrib/amd/amd/mapc.c
new file mode 100644
index 000000000000..de95e133404b
--- /dev/null
+++ b/contrib/amd/amd/mapc.c
@@ -0,0 +1,1205 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: mapc.c,v 5.2.2.2 1992/08/02 10:42:21 jsp Exp $
+ *
+ */
+
+/*
+ * Mount map cache
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+/*
+ * Make a duplicate reference to an existing map
+ */
+#define mapc_dup(m) ((m)->refc++, (m))
+
+/*
+ * Map cache types
+ * default, none, incremental, all, regexp
+ * MAPC_RE implies MAPC_ALL and must be numerically
+ * greater.
+ */
+#define MAPC_DFLT 0x000
+#define MAPC_NONE 0x001
+#define MAPC_INC 0x002
+#define MAPC_ROOT 0x004
+#define MAPC_ALL 0x010
+#define MAPC_CACHE_MASK 0x0ff
+#define MAPC_SYNC 0x100
+
+#ifdef HAVE_REGEXEC
+# define MAPC_RE 0x020
+# define MAPC_ISRE(m) ((m)->alloc == MAPC_RE)
+#else /* not HAVE_REGEXEC */
+# define MAPC_ISRE(m) FALSE
+#endif /* not HAVE_REGEXEC */
+
+/*
+ * Lookup recursion
+ */
+#define MREC_FULL 2
+#define MREC_PART 1
+#define MREC_NONE 0
+
+#define MAX_CHAIN 2048
+
+static struct opt_tab mapc_opt[] =
+{
+ {"all", MAPC_ALL},
+ {"default", MAPC_DFLT},
+ {"inc", MAPC_INC},
+ {"mapdefault", MAPC_DFLT},
+ {"none", MAPC_NONE},
+#ifdef HAVE_REGEXEC
+ {"re", MAPC_RE},
+ {"regexp", MAPC_RE},
+#endif /* HAVE_REGEXEC */
+ {"sync", MAPC_SYNC},
+ {0, 0}
+};
+
+/*
+ * Wildcard key
+ */
+static char wildcard[] = "*";
+
+/*
+ * Map type
+ */
+typedef struct map_type map_type;
+struct map_type {
+ char *name; /* Name of this map type */
+ init_fn *init; /* Initialisation */
+ reload_fn *reload; /* Reload or fill */
+ isup_fn *isup; /* Is service up or not? (1=up, 0=down) */
+ search_fn *search; /* Search for new entry */
+ mtime_fn *mtime; /* Find modify time */
+ int def_alloc; /* Default allocation mode */
+};
+
+/*
+ * Map for root node
+ */
+static mnt_map *root_map;
+
+/*
+ * List of known maps
+ */
+qelem map_list_head = {&map_list_head, &map_list_head};
+
+/*
+ * Configuration
+ */
+
+/* forward definitions */
+static const char *get_full_path(const char *map, const char *path, const char *type);
+static int mapc_meta_search(mnt_map *, char *, char **, int);
+static void mapc_sync(mnt_map *);
+
+/* ROOT MAP */
+static int root_init(mnt_map *, char *, time_t *);
+
+/* ERROR MAP */
+static int error_init(mnt_map *, char *, time_t *);
+static int error_reload(mnt_map *, char *, add_fn *);
+static int error_search(mnt_map *, char *, char *, char **, time_t *);
+static int error_mtime(mnt_map *, char *, time_t *);
+
+/* PASSWD MAPS */
+#ifdef HAVE_MAP_PASSWD
+extern int passwd_init(mnt_map *, char *, time_t *);
+extern int passwd_search(mnt_map *, char *, char *, char **, time_t *);
+#endif /* HAVE_MAP_PASSWD */
+
+/* HESIOD MAPS */
+#ifdef HAVE_MAP_HESIOD
+extern int amu_hesiod_init(mnt_map *, char *map, time_t *tp);
+extern int hesiod_search(mnt_map *, char *, char *, char **, time_t *);
+#endif /* HAVE_MAP_HESIOD */
+
+/* LDAP MAPS */
+#ifdef HAVE_MAP_LDAP
+extern int amu_ldap_init(mnt_map *, char *map, time_t *tp);
+extern int amu_ldap_search(mnt_map *, char *, char *, char **, time_t *);
+extern int amu_ldap_mtime(mnt_map *, char *, time_t *);
+#endif /* HAVE_MAP_LDAP */
+
+/* UNION MAPS */
+#ifdef HAVE_MAP_UNION
+extern int union_init(mnt_map *, char *, time_t *);
+extern int union_search(mnt_map *, char *, char *, char **, time_t *);
+extern int union_reload(mnt_map *, char *, add_fn *);
+#endif /* HAVE_MAP_UNION */
+
+/* Network Information Service PLUS (NIS+) */
+#ifdef HAVE_MAP_NISPLUS
+extern int nisplus_init(mnt_map *, char *, time_t *);
+extern int nisplus_reload(mnt_map *, char *, add_fn *);
+extern int nisplus_search(mnt_map *, char *, char *, char **, time_t *);
+extern int nisplus_mtime(mnt_map *, char *, time_t *);
+#endif /* HAVE_MAP_NISPLUS */
+
+/* Network Information Service (YP, Yellow Pages) */
+#ifdef HAVE_MAP_NIS
+extern int nis_init(mnt_map *, char *, time_t *);
+extern int nis_reload(mnt_map *, char *, add_fn *);
+extern int nis_isup(mnt_map *, char *);
+extern int nis_search(mnt_map *, char *, char *, char **, time_t *);
+extern int nis_mtime(mnt_map *, char *, time_t *);
+#endif /* HAVE_MAP_NIS */
+
+/* NDBM MAPS */
+#ifdef HAVE_MAP_NDBM
+extern int ndbm_init(mnt_map *, char *, time_t *);
+extern int ndbm_search(mnt_map *, char *, char *, char **, time_t *);
+extern int ndbm_mtime(mnt_map *, char *, time_t *);
+#endif /* HAVE_MAP_NDBM */
+
+/* FILE MAPS */
+#ifdef HAVE_MAP_FILE
+extern int file_init(mnt_map *, char *, time_t *);
+extern int file_reload(mnt_map *, char *, add_fn *);
+extern int file_search(mnt_map *, char *, char *, char **, time_t *);
+extern int file_mtime(mnt_map *, char *, time_t *);
+#endif /* HAVE_MAP_FILE */
+
+
+/* note that the choice of MAPC_{INC,ALL} will affect browsable_dirs */
+static map_type maptypes[] =
+{
+ {
+ "root",
+ root_init,
+ error_reload,
+ NULL, /* isup function */
+ error_search,
+ error_mtime,
+ MAPC_ROOT
+ },
+#ifdef HAVE_MAP_PASSWD
+ {
+ "passwd",
+ passwd_init,
+ error_reload,
+ NULL, /* isup function */
+ passwd_search,
+ error_mtime,
+ MAPC_ALL
+ },
+#endif /* HAVE_MAP_PASSWD */
+#ifdef HAVE_MAP_HESIOD
+ {
+ "hesiod",
+ amu_hesiod_init,
+ error_reload,
+ NULL, /* isup function */
+ hesiod_search,
+ error_mtime,
+ MAPC_ALL
+ },
+#endif /* HAVE_MAP_HESIOD */
+#ifdef HAVE_MAP_LDAP
+ {
+ "ldap",
+ amu_ldap_init,
+ error_reload,
+ NULL, /* isup function */
+ amu_ldap_search,
+ amu_ldap_mtime,
+ MAPC_ALL
+ },
+#endif /* HAVE_MAP_LDAP */
+#ifdef HAVE_MAP_UNION
+ {
+ "union",
+ union_init,
+ union_reload,
+ NULL, /* isup function */
+ union_search,
+ error_mtime,
+ MAPC_ALL
+ },
+#endif /* HAVE_MAP_UNION */
+#ifdef HAVE_MAP_NISPLUS
+ {
+ "nisplus",
+ nisplus_init,
+ nisplus_reload,
+ NULL, /* isup function */
+ nisplus_search,
+ nisplus_mtime,
+ MAPC_INC
+ },
+#endif /* HAVE_MAP_NISPLUS */
+#ifdef HAVE_MAP_NIS
+ {
+ "nis",
+ nis_init,
+ nis_reload,
+ nis_isup, /* is NIS up or not? */
+ nis_search,
+ nis_mtime,
+ MAPC_ALL
+ },
+#endif /* HAVE_MAP_NIS */
+#ifdef HAVE_MAP_NDBM
+ {
+ "ndbm",
+ ndbm_init,
+ error_reload,
+ NULL, /* isup function */
+ ndbm_search,
+ ndbm_mtime,
+ MAPC_ALL
+ },
+#endif /* HAVE_MAP_NDBM */
+#ifdef HAVE_MAP_FILE
+ {
+ "file",
+ file_init,
+ file_reload,
+ NULL, /* isup function */
+ file_search,
+ file_mtime,
+ MAPC_ALL
+ },
+#endif /* HAVE_MAP_FILE */
+ {
+ "error",
+ error_init,
+ error_reload,
+ NULL, /* isup function */
+ error_search,
+ error_mtime,
+ MAPC_NONE
+ },
+};
+
+
+/*
+ * Hash function
+ */
+static u_int
+kvhash_of(char *key)
+{
+ u_int i, j;
+
+ for (i = 0; (j = *key++); i += j) ;
+
+ return i % NKVHASH;
+}
+
+
+void
+mapc_showtypes(char *buf)
+{
+ map_type *mt;
+ char *sep = "";
+
+ buf[0] = '\0';
+ for (mt = maptypes; mt < maptypes + sizeof(maptypes) / sizeof(maptypes[0]); mt++) {
+ strcat(buf, sep);
+ strcat(buf, mt->name);
+ sep = ", ";
+ }
+}
+
+
+/*
+ * Add key and val to the map m.
+ * key and val are assumed to be safe copies
+ */
+void mapc_add_kv(mnt_map *m, char *key, char *val)
+{
+ kv **h;
+ kv *n;
+ int hash = kvhash_of(key);
+#ifdef HAVE_REGEXEC
+ regex_t re;
+#endif /* HAVE_REGEXEC */
+
+#ifdef DEBUG
+ dlog("add_kv: %s -> %s", key, val);
+#endif /* DEBUG */
+
+#ifdef HAVE_REGEXEC
+ if (MAPC_ISRE(m)) {
+ char pattern[MAXPATHLEN];
+ int retval;
+
+ /*
+ * Make sure the string is bound to the start and end
+ */
+ sprintf(pattern, "^%s$", key);
+ retval = regcomp(&re, pattern, REG_ICASE);
+ if (retval != 0) {
+ char errstr[256];
+
+ /* XXX: this code was recently ported, and must be tested -Erez */
+ errstr[0] = '\0';
+ regerror(retval, &re, errstr, 256);
+ plog(XLOG_USER, "error compiling RE \"%s\": %s", pattern, errstr);
+ return;
+ }
+ }
+#endif /* HAVE_REGEXEC */
+
+ h = &m->kvhash[hash];
+ n = ALLOC(struct kv);
+ n->key = key;
+#ifdef HAVE_REGEXEC
+ memcpy(&n->re, &re, sizeof(regex_t));
+#endif /* HAVE_REGEXEC */
+ n->val = val;
+ n->next = *h;
+ *h = n;
+}
+
+
+static void
+mapc_repl_kv(mnt_map *m, char *key, char *val)
+{
+ kv *k;
+
+ /*
+ * Compute the hash table offset
+ */
+ k = m->kvhash[kvhash_of(key)];
+
+ /*
+ * Scan the linked list for the key
+ */
+ while (k && !FSTREQ(k->key, key))
+ k = k->next;
+
+ if (k) {
+ XFREE(k->val);
+ k->val = val;
+ } else {
+ mapc_add_kv(m, key, val);
+ }
+}
+
+
+/*
+ * Search a map for a key.
+ * Calls map specific search routine.
+ * While map is out of date, keep re-syncing.
+ */
+static int
+search_map(mnt_map *m, char *key, char **valp)
+{
+ int rc;
+
+ do {
+ rc = (*m->search) (m, m->map_name, key, valp, &m->modify);
+ if (rc < 0) {
+ plog(XLOG_MAP, "Re-synchronizing cache for map %s", m->map_name);
+ mapc_sync(m);
+ }
+ } while (rc < 0);
+
+ return rc;
+}
+
+
+/*
+ * Do a wildcard lookup in the map and
+ * save the result.
+ */
+static void
+mapc_find_wildcard(mnt_map *m)
+{
+ /*
+ * Attempt to find the wildcard entry
+ */
+ int rc = search_map(m, wildcard, &m->wildcard);
+
+ if (rc != 0)
+ m->wildcard = 0;
+}
+
+
+/*
+ * Do a map reload
+ */
+static int
+mapc_reload_map(mnt_map *m)
+{
+ int error;
+
+#ifdef DEBUG
+ dlog("calling map reload on %s", m->map_name);
+#endif /* DEBUG */
+ error = (*m->reload) (m, m->map_name, mapc_add_kv);
+ if (error)
+ return error;
+ m->wildcard = 0;
+
+#ifdef DEBUG
+ dlog("calling mapc_search for wildcard");
+#endif /* DEBUG */
+ error = mapc_search(m, wildcard, &m->wildcard);
+ if (error)
+ m->wildcard = 0;
+
+ return 0;
+}
+
+
+/*
+ * Create a new map
+ */
+static mnt_map *
+mapc_create(char *map, char *opt, const char *type)
+{
+ mnt_map *m = ALLOC(struct mnt_map);
+ map_type *mt;
+ time_t modify;
+ int alloc = 0;
+
+ cmdoption(opt, mapc_opt, &alloc);
+
+ /*
+ * If using a configuration file, and the map_type is defined, then look
+ * for it, in the maptypes array. If found, initialize the map using that
+ * map_type. If not found, return error. If no map_type was defined,
+ * default to cycling through all maptypes.
+ */
+ if (use_conf_file && type) {
+ /* find what type of map this one is */
+ for (mt = maptypes;
+ mt < maptypes + sizeof(maptypes) / sizeof(maptypes[0]);
+ mt++) {
+ if (STREQ(type, mt->name)) {
+ plog(XLOG_INFO, "initializing amd conf map %s of type %s", map, type);
+ if ((*mt->init) (m, map, &modify) == 0) {
+ break;
+ } else {
+ plog(XLOG_ERROR, "failed to initialize map %s", map);
+ error_init(m, map, &modify);
+ break;
+ }
+ }
+ } /* end of "for (mt =" loop */
+
+ } else { /* cycle through all known maptypes */
+
+ /*
+ * not using amd conf file or using it by w/o specifying map type
+ */
+ for (mt = maptypes;
+ mt < maptypes + sizeof(maptypes) / sizeof(maptypes[0]);
+ mt++) {
+#ifdef DEBUG
+ dlog("trying to initialize map %s of type %s ...", map, mt->name);
+#endif /* DEBUG */
+ if ((*mt->init) (m, map, &modify) == 0) {
+ break;
+ }
+ }
+ } /* end of "if (use_conf_file && (colpos = strchr ..." statement */
+
+ /* assert: mt in maptypes */
+
+ m->flags = alloc & ~MAPC_CACHE_MASK;
+ alloc &= MAPC_CACHE_MASK;
+
+ if (alloc == MAPC_DFLT)
+ alloc = mt->def_alloc;
+
+ switch (alloc) {
+ default:
+ plog(XLOG_USER, "Ambiguous map cache type \"%s\"; using \"inc\"", opt);
+ alloc = MAPC_INC;
+ /* fallthrough... */
+ case MAPC_NONE:
+ case MAPC_INC:
+ case MAPC_ROOT:
+ break;
+
+ case MAPC_ALL:
+ /*
+ * If there is no support for reload and it was requested
+ * then back off to incremental instead.
+ */
+ if (mt->reload == error_reload) {
+ plog(XLOG_WARNING, "Map type \"%s\" does not support cache type \"all\"; using \"inc\"", mt->name);
+ alloc = MAPC_INC;
+ }
+ break;
+
+#ifdef HAVE_REGEXEC
+ case MAPC_RE:
+ if (mt->reload == error_reload) {
+ plog(XLOG_WARNING, "Map type \"%s\" does not support cache type \"re\"", mt->name);
+ mt = &maptypes[sizeof(maptypes) / sizeof(maptypes[0]) - 1];
+ /* assert: mt->name == "error" */
+ }
+ break;
+#endif /* HAVE_REGEXEC */
+ }
+
+#ifdef DEBUG
+ dlog("Map for %s coming from maptype %s", map, mt->name);
+#endif /* DEBUG */
+
+ m->alloc = alloc;
+ m->reload = mt->reload;
+ m->isup = mt->isup;
+ m->modify = modify;
+ m->search = alloc >= MAPC_ALL ? error_search : mt->search;
+ m->mtime = mt->mtime;
+ memset((voidp) m->kvhash, 0, sizeof(m->kvhash));
+ m->map_name = strdup(map);
+ m->refc = 1;
+ m->wildcard = 0;
+
+ /*
+ * synchronize cache with reality
+ */
+ mapc_sync(m);
+
+ return m;
+}
+
+
+/*
+ * Free the cached data in a map
+ */
+static void
+mapc_clear(mnt_map *m)
+{
+ int i;
+
+ /*
+ * For each of the hash slots, chain
+ * along free'ing the data.
+ */
+ for (i = 0; i < NKVHASH; i++) {
+ kv *k = m->kvhash[i];
+ while (k) {
+ kv *n = k->next;
+ XFREE(k->key);
+ if (k->val)
+ XFREE(k->val);
+ XFREE(k);
+ k = n;
+ }
+ }
+
+ /*
+ * Zero the hash slots
+ */
+ memset((voidp) m->kvhash, 0, sizeof(m->kvhash));
+
+ /*
+ * Free the wildcard if it exists
+ */
+ if (m->wildcard) {
+ XFREE(m->wildcard);
+ m->wildcard = 0;
+ }
+}
+
+
+/*
+ * Find a map, or create one if it does not exist
+ */
+mnt_map *
+mapc_find(char *map, char *opt, const char *maptype)
+{
+ mnt_map *m;
+
+ /*
+ * Search the list of known maps to see if
+ * it has already been loaded. If it is found
+ * then return a duplicate reference to it.
+ * Otherwise make a new map as required and
+ * add it to the list of maps
+ */
+ ITER(m, mnt_map, &map_list_head)
+ if (STREQ(m->map_name, map))
+ return mapc_dup(m);
+ m = mapc_create(map, opt, maptype);
+ ins_que(&m->hdr, &map_list_head);
+
+ return m;
+}
+
+
+/*
+ * Free a map.
+ */
+void
+mapc_free(voidp v)
+{
+ mnt_map *m = v;
+
+ /*
+ * Decrement the reference count.
+ * If the reference count hits zero
+ * then throw the map away.
+ */
+ if (m && --m->refc == 0) {
+ mapc_clear(m);
+ XFREE(m->map_name);
+ rem_que(&m->hdr);
+ XFREE(m);
+ }
+}
+
+
+/*
+ * Search the map for the key. Put a safe (malloc'ed) copy in *pval or
+ * return an error code
+ */
+static int
+mapc_meta_search(mnt_map *m, char *key, char **pval, int recurse)
+{
+ int error = 0;
+ kv *k = 0;
+
+ /*
+ * Firewall
+ */
+ if (!m) {
+ plog(XLOG_ERROR, "Null map request for %s", key);
+ return ENOENT;
+ }
+ if (m->flags & MAPC_SYNC) {
+ /*
+ * Get modify time...
+ */
+ time_t t;
+ error = (*m->mtime) (m, m->map_name, &t);
+ if (error || t > m->modify) {
+ m->modify = t;
+ plog(XLOG_INFO, "Map %s is out of date", m->map_name);
+ mapc_sync(m);
+ }
+ }
+
+ if (!MAPC_ISRE(m)) {
+ /*
+ * Compute the hash table offset
+ */
+ k = m->kvhash[kvhash_of(key)];
+
+ /*
+ * Scan the linked list for the key
+ */
+ while (k && !FSTREQ(k->key, key))
+ k = k->next;
+
+ }
+
+#ifdef HAVE_REGEXEC
+ else if (recurse == MREC_FULL) {
+ /*
+ * Try for an RE match against the entire map.
+ * Note that this will be done in a "random"
+ * order.
+ */
+ int i;
+
+ for (i = 0; i < NKVHASH; i++) {
+ k = m->kvhash[i];
+ while (k) {
+ int retval;
+
+ /* XXX: this code was recently ported, and must be tested -Erez */
+ retval = regexec(&k->re, key, 0, 0, 0);
+ if (retval == 0) { /* succeeded */
+ break;
+ } else { /* failed to match, log error */
+ char errstr[256];
+
+ errstr[0] = '\0';
+ regerror(retval, &k->re, errstr, 256);
+ plog(XLOG_USER, "error matching RE \"%s\" against \"%s\": %s",
+ key, k->key, errstr);
+ }
+ k = k->next;
+ }
+ if (k)
+ break;
+ }
+ }
+#endif /* HAVE_REGEXEC */
+
+ /*
+ * If found then take a copy
+ */
+ if (k) {
+ if (k->val)
+ *pval = strdup(k->val);
+ else
+ error = ENOENT;
+ } else if (m->alloc >= MAPC_ALL) {
+ /*
+ * If the entire map is cached then this
+ * key does not exist.
+ */
+ error = ENOENT;
+ } else {
+ /*
+ * Otherwise search the map. If we are
+ * in incremental mode then add the key
+ * to the cache.
+ */
+ error = search_map(m, key, pval);
+ if (!error && m->alloc == MAPC_INC)
+ mapc_add_kv(m, strdup(key), strdup(*pval));
+ }
+
+ /*
+ * If an error, and a wildcard exists,
+ * and the key is not internal then
+ * return a copy of the wildcard.
+ */
+ if (error > 0) {
+ if (recurse == MREC_FULL && !MAPC_ISRE(m)) {
+ char wildname[MAXPATHLEN];
+ char *subp;
+ if (*key == '/')
+ return error;
+ /*
+ * Keep chopping sub-directories from the RHS
+ * and replacing with "/ *" and repeat the lookup.
+ * For example:
+ * "src/gnu/gcc" -> "src / gnu / *" -> "src / *"
+ */
+ strcpy(wildname, key);
+ while (error && (subp = strrchr(wildname, '/'))) {
+ strcpy(subp, "/*");
+#ifdef DEBUG
+ dlog("mapc recurses on %s", wildname);
+#endif /* DEBUG */
+ error = mapc_meta_search(m, wildname, pval, MREC_PART);
+ if (error)
+ *subp = 0;
+ }
+
+ if (error > 0 && m->wildcard) {
+ *pval = strdup(m->wildcard);
+ error = 0;
+ }
+ }
+ }
+ return error;
+}
+
+
+int
+mapc_search(mnt_map *m, char *key, char **pval)
+{
+ return mapc_meta_search(m, key, pval, MREC_FULL);
+}
+
+
+/*
+ * Get map cache in sync with physical representation
+ */
+static void
+mapc_sync(mnt_map *m)
+{
+ if (m->alloc != MAPC_ROOT) {
+
+ /* do not clear map if map service is down */
+ if (m->isup) {
+ if (!((*m->isup)(m, m->map_name))) {
+ plog(XLOG_ERROR, "mapc_sync: map %s is down: not clearing map", m->map_name);
+ return;
+ }
+ }
+
+ mapc_clear(m);
+
+ if (m->alloc >= MAPC_ALL)
+ if (mapc_reload_map(m))
+ m->alloc = MAPC_INC;
+ /*
+ * Attempt to find the wildcard entry
+ */
+ if (m->alloc < MAPC_ALL)
+ mapc_find_wildcard(m);
+ }
+}
+
+
+/*
+ * Reload all the maps
+ * Called when Amd gets hit by a SIGHUP.
+ */
+void
+mapc_reload(void)
+{
+ mnt_map *m;
+
+ /*
+ * For all the maps,
+ * Throw away the existing information.
+ * Do a reload
+ * Find the wildcard
+ */
+ ITER(m, mnt_map, &map_list_head)
+ mapc_sync(m);
+}
+
+
+/*
+ * Root map.
+ * The root map is used to bootstrap amd.
+ * All the require top-level mounts are added
+ * into the root map and then the map is iterated
+ * and a lookup is done on all the mount points.
+ * This causes the top level mounts to be automounted.
+ */
+static int
+root_init(mnt_map *m, char *map, time_t *tp)
+{
+ *tp = clocktime();
+ return STREQ(map, ROOT_MAP) ? 0 : ENOENT;
+}
+
+
+/*
+ * Add a new entry to the root map
+ *
+ * dir - directory (key)
+ * opts - mount options
+ * map - map name
+ * cfm - optional amd configuration file map section structure
+ */
+void
+root_newmap(const char *dir, const char *opts, const char *map, const cf_map_t *cfm)
+{
+ char str[MAXPATHLEN];
+
+ /*
+ * First make sure we have a root map to talk about...
+ */
+ if (!root_map)
+ root_map = mapc_find(ROOT_MAP, "mapdefault", NULL);
+
+ /*
+ * Then add the entry...
+ */
+
+ /*
+ * Here I plug in the code to process other amd.conf options like
+ * map_type, search_path, and flags (browsable_dirs, mount_type).
+ */
+
+ if (cfm) {
+ if (map) {
+ sprintf(str, "cache:=mapdefault;type:=%s;fs:=\"%s\"",
+ cfm->cfm_flags & CFM_MOUNT_TYPE_AUTOFS ? "autofs" : "toplvl",
+ get_full_path(map, cfm->cfm_search_path, cfm->cfm_type));
+ if (opts && opts[0] != '\0') {
+ strcat(str, ";");
+ strcat(str, opts);
+ }
+ if (cfm->cfm_flags & CFM_BROWSABLE_DIRS_FULL)
+ strcat(str, ";opts:=rw,fullybrowsable");
+ if (cfm->cfm_flags & CFM_BROWSABLE_DIRS)
+ strcat(str, ";opts:=rw,browsable");
+ if (cfm->cfm_type) {
+ strcat(str, ";maptype:=");
+ strcat(str, cfm->cfm_type);
+ }
+ } else {
+ strcpy(str, opts);
+ }
+ } else {
+ if (map)
+ sprintf(str, "cache:=mapdefault;type:=toplvl;fs:=\"%s\";%s",
+ map, opts ? opts : "");
+ else
+ strcpy(str, opts);
+ }
+ mapc_repl_kv(root_map, strdup((char *)dir), strdup(str));
+}
+
+
+int
+mapc_keyiter(mnt_map *m, void (*fn) (char *, voidp), voidp arg)
+{
+ int i;
+ int c = 0;
+
+ for (i = 0; i < NKVHASH; i++) {
+ kv *k = m->kvhash[i];
+ while (k) {
+ (*fn) (k->key, arg);
+ k = k->next;
+ c++;
+ }
+ }
+
+ return c;
+}
+
+
+/*
+ * Iterate on the root map and call (*fn)() on the key of all the nodes.
+ * Finally throw away the root map.
+ */
+int
+root_keyiter(void (*fn)(char *, voidp), voidp arg)
+{
+ if (root_map) {
+ int c = mapc_keyiter(root_map, fn, arg);
+ return c;
+ }
+
+ return 0;
+}
+
+
+/*
+ * Was: NEW_TOPLVL_READDIR
+ * Search a chain for an entry with some name.
+ * -Erez Zadok <ezk@cs.columbia.edu>
+ */
+static int
+key_already_in_chain(char *keyname, const nfsentry *chain)
+{
+ const nfsentry *tmpchain = chain;
+
+ while (tmpchain) {
+ if (keyname && tmpchain->ne_name && STREQ(keyname, tmpchain->ne_name))
+ return 1;
+ tmpchain = tmpchain->ne_nextentry;
+ }
+
+ return 0;
+}
+
+
+/*
+ * Create a chain of entries which are not linked.
+ * -Erez Zadok <ezk@cs.columbia.edu>
+ */
+nfsentry *
+make_entry_chain(am_node *mp, const nfsentry *current_chain, int fully_browsable)
+{
+ static u_int last_cookie = ~(u_int) 0 - 1;
+ static nfsentry chain[MAX_CHAIN];
+ static int max_entries = MAX_CHAIN;
+ char *key;
+ int num_entries = 0, preflen = 0, i;
+ nfsentry *retval = (nfsentry *) NULL;
+ mntfs *mf;
+ mnt_map *mmp;
+
+ if (!mp) {
+ plog(XLOG_DEBUG, "make_entry_chain: mp is (NULL)");
+ return retval;
+ }
+ mf = mp->am_mnt;
+ if (!mf) {
+ plog(XLOG_DEBUG, "make_entry_chain: mp->am_mnt is (NULL)");
+ return retval;
+ }
+ mmp = (mnt_map *) mf->mf_private;
+ if (!mmp) {
+ plog(XLOG_DEBUG, "make_entry_chain: mp->am_mnt->mf_private is (NULL)");
+ return retval;
+ }
+
+ /* iterate over keys */
+ for (i = 0; i < NKVHASH; i++) {
+ kv *k;
+ for (k = mmp->kvhash[i]; k ; k = k->next) {
+
+ /*
+ * Skip unwanted entries which are either not real entries or
+ * very difficult to interpret (wildcards...) This test needs
+ * lots of improvement. Any takers?
+ */
+ key = k->key;
+ if (!key)
+ continue;
+
+ /* Skip '*' */
+ if (!fully_browsable && strchr(key, '*'))
+ continue;
+
+ /*
+ * If the map has a prefix-string then check if the key starts with
+ * this * string, and if it does, skip over this prefix.
+ */
+ if (preflen) {
+ if (!NSTREQ(key, mp->am_pref, preflen))
+ continue;
+ key += preflen;
+ }
+
+ /* no more '/' are allowed, unless browsable_dirs=full was used */
+ if (!fully_browsable && strchr(key, '/'))
+ continue;
+
+ /* no duplicates allowed */
+ if (key_already_in_chain(key, current_chain))
+ continue;
+
+ /* fill in a cell and link the entry */
+ if (num_entries >= max_entries) {
+ /* out of space */
+ plog(XLOG_DEBUG, "make_entry_chain: no more space in chain");
+ if (num_entries > 0) {
+ chain[num_entries - 1].ne_nextentry = 0;
+ retval = &chain[0];
+ }
+ return retval;
+ }
+
+ /* we have space. put entry in next cell */
+ --last_cookie;
+ chain[num_entries].ne_fileid = (u_int) last_cookie;
+ *(u_int *) chain[num_entries].ne_cookie =
+ (u_int) last_cookie;
+ chain[num_entries].ne_name = key;
+ if (num_entries < max_entries - 1) { /* link to next one */
+ chain[num_entries].ne_nextentry = &chain[num_entries + 1];
+ }
+ ++num_entries;
+ } /* end of "while (k)" */
+ } /* end of "for (i ... NKVHASH ..." */
+
+ /* terminate chain */
+ if (num_entries > 0) {
+ chain[num_entries - 1].ne_nextentry = 0;
+ retval = &chain[0];
+ }
+
+ return retval;
+}
+
+
+/*
+ * Error map
+ */
+static int
+error_init(mnt_map *m, char *map, time_t *tp)
+{
+ plog(XLOG_USER, "No source data for map %s", map);
+ *tp = 0;
+
+ return 0;
+}
+
+
+static int
+error_search(mnt_map *m, char *map, char *key, char **pval, time_t *tp)
+{
+ return ENOENT;
+}
+
+
+static int
+error_reload(mnt_map *m, char *map, add_fn *fn)
+{
+ return ENOENT;
+}
+
+
+static int
+error_mtime(mnt_map *m, char *map, time_t *tp)
+{
+ *tp = 0;
+
+ return 0;
+}
+
+
+/*
+ * Return absolute path of map, searched in a type-specific path.
+ * Note: uses a static buffer for returned data.
+ */
+static const char *
+get_full_path(const char *map, const char *path, const char *type)
+{
+ char component[MAXPATHLEN], *str;
+ static char full_path[MAXPATHLEN];
+ int len;
+
+ /* for now, only file-type search paths are implemented */
+ if (type && !STREQ(type, "file"))
+ return map;
+
+ /* if null map, return it */
+ if (!map)
+ return map;
+
+ /* if map includes a '/', return it (absolute or relative path) */
+ if (strchr(map, '/'))
+ return map;
+
+ /* if path is empty, return map */
+ if (!path)
+ return map;
+
+ /* now break path into components, and search in each */
+ strcpy(component, path);
+
+ str = strtok(component, ":");
+ do {
+ strcpy(full_path, str);
+ len = strlen(full_path);
+ if (full_path[len - 1] != '/') /* add trailing "/" if needed */
+ strcat(full_path, "/");
+ strcat(full_path, map);
+ if (access(full_path, R_OK) == 0)
+ return full_path;
+ str = strtok(NULL, ":");
+ } while (str);
+
+ return map; /* if found nothing, return map */
+}
diff --git a/contrib/amd/amd/mntfs.c b/contrib/amd/amd/mntfs.c
new file mode 100644
index 000000000000..31fa331d8d69
--- /dev/null
+++ b/contrib/amd/amd/mntfs.c
@@ -0,0 +1,335 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: mntfs.c,v 5.2.2.2 1992/08/02 10:42:21 jsp Exp $
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+qelem mfhead = {&mfhead, &mfhead};
+
+int mntfs_allocated;
+
+
+mntfs *
+dup_mntfs(mntfs *mf)
+{
+ if (mf->mf_refc == 0) {
+ if (mf->mf_cid)
+ untimeout(mf->mf_cid);
+ mf->mf_cid = 0;
+ }
+ mf->mf_refc++;
+
+ return mf;
+}
+
+
+static void
+init_mntfs(mntfs *mf, am_ops *ops, am_opts *mo, char *mp, char *info, char *auto_opts, char *mopts, char *remopts)
+{
+ mf->mf_ops = ops;
+ mf->mf_fo = mo;
+ mf->mf_mount = strdup(mp);
+ mf->mf_info = strdup(info);
+ mf->mf_auto = strdup(auto_opts);
+ mf->mf_mopts = strdup(mopts);
+ mf->mf_remopts = strdup(remopts);
+ mf->mf_refc = 1;
+ mf->mf_flags = 0;
+ mf->mf_error = -1;
+ mf->mf_cid = 0;
+ mf->mf_private = 0;
+ mf->mf_prfree = 0;
+
+ if (ops->ffserver)
+ mf->mf_server = (*ops->ffserver) (mf);
+ else
+ mf->mf_server = 0;
+}
+
+
+static mntfs *
+alloc_mntfs(am_ops *ops, am_opts *mo, char *mp, char *info, char *auto_opts, char *mopts, char *remopts)
+{
+ mntfs *mf = ALLOC(struct mntfs);
+
+ init_mntfs(mf, ops, mo, mp, info, auto_opts, mopts, remopts);
+ ins_que(&mf->mf_q, &mfhead);
+ mntfs_allocated++;
+
+ return mf;
+}
+
+
+mntfs *
+find_mntfs(am_ops *ops, am_opts *mo, char *mp, char *info, char *auto_opts, char *mopts, char *remopts)
+{
+ mntfs *mf;
+
+#ifdef DEBUG
+ dlog("Locating mntfs reference to %s", mp);
+#endif /* DEBUG */
+
+ ITER(mf, mntfs, &mfhead) {
+ if (STREQ(mf->mf_mount, mp)) {
+ /*
+ * Handle cases where error ops are involved
+ */
+ if (ops == &amfs_error_ops) {
+ /*
+ * If the existing ops are not amfs_error_ops
+ * then continue...
+ */
+ if (mf->mf_ops != &amfs_error_ops)
+ continue;
+ else
+ return dup_mntfs(mf);
+ } else { /* ops != &amfs_error_ops */
+ /*
+ * If the existing ops are amfs_error_ops
+ * then continue...
+ */
+ if (mf->mf_ops == &amfs_error_ops)
+ continue;
+ }
+
+ if ((mf->mf_flags & MFF_RESTART) && amd_state == Run) {
+ /*
+ * Restart a previously mounted filesystem.
+ */
+ mntfs *mf2 = alloc_mntfs(&amfs_inherit_ops, mo, mp, info, auto_opts, mopts, remopts);
+#ifdef DEBUG
+ dlog("Restarting filesystem %s", mf->mf_mount);
+#endif /* DEBUG */
+
+ /*
+ * Remember who we are restarting
+ */
+ mf2->mf_private = (voidp) dup_mntfs(mf);
+ mf2->mf_prfree = free_mntfs;
+ return mf2;
+ }
+
+ mf->mf_fo = mo;
+ if (!(mf->mf_flags & (MFF_MOUNTED | MFF_MOUNTING | MFF_UNMOUNTING))) {
+ fserver *fs;
+ mf->mf_flags &= ~MFF_ERROR;
+ mf->mf_error = -1;
+ mf->mf_auto = strealloc(mf->mf_auto, auto_opts);
+ mf->mf_mopts = strealloc(mf->mf_mopts, mopts);
+ mf->mf_remopts = strealloc(mf->mf_remopts, remopts);
+ mf->mf_info = strealloc(mf->mf_info, info);
+
+ if (mf->mf_private && mf->mf_prfree) {
+ (*mf->mf_prfree) (mf->mf_private);
+ mf->mf_private = 0;
+ }
+
+ fs = ops->ffserver ? (*ops->ffserver) (mf) : (fserver *) NULL;
+ if (mf->mf_server)
+ free_srvr(mf->mf_server);
+ mf->mf_server = fs;
+ }
+ return dup_mntfs(mf);
+ }
+ }
+
+ return alloc_mntfs(ops, mo, mp, info, auto_opts, mopts, remopts);
+}
+
+
+mntfs *
+new_mntfs(void)
+{
+ return alloc_mntfs(&amfs_error_ops, (am_opts *) 0, "//nil//", ".", "", "", "");
+}
+
+
+static void
+uninit_mntfs(mntfs *mf, int rmd)
+{
+ if (mf->mf_auto)
+ XFREE(mf->mf_auto);
+ if (mf->mf_mopts)
+ XFREE(mf->mf_mopts);
+ if (mf->mf_remopts)
+ XFREE(mf->mf_remopts);
+ if (mf->mf_info)
+ XFREE(mf->mf_info);
+ if (mf->mf_private && mf->mf_prfree)
+ (*mf->mf_prfree) (mf->mf_private);
+
+ /*
+ * Clean up any directories that were made
+ */
+ if (rmd && (mf->mf_flags & MFF_MKMNT))
+ rmdirs(mf->mf_mount);
+ /* free mf_mount _AFTER_ removing the directories */
+ if (mf->mf_mount)
+ XFREE(mf->mf_mount);
+
+ /*
+ * Clean up the file server
+ */
+ if (mf->mf_server)
+ free_srvr(mf->mf_server);
+
+ /*
+ * Don't do a callback on this mount
+ */
+ if (mf->mf_cid) {
+ untimeout(mf->mf_cid);
+ mf->mf_cid = 0;
+ }
+}
+
+
+static void
+discard_mntfs(voidp v)
+{
+ mntfs *mf = v;
+
+ rem_que(&mf->mf_q);
+
+ /*
+ * Free memory
+ */
+ uninit_mntfs(mf, TRUE);
+ XFREE(mf);
+
+ --mntfs_allocated;
+}
+
+
+void
+flush_mntfs(void)
+{
+ mntfs *mf;
+
+ mf = AM_FIRST(mntfs, &mfhead);
+ while (mf != HEAD(mntfs, &mfhead)) {
+ mntfs *mf2 = mf;
+ mf = NEXT(mntfs, mf);
+ if (mf2->mf_refc == 0 && mf2->mf_cid)
+ discard_mntfs(mf2);
+ }
+}
+
+
+void
+free_mntfs(voidp v)
+{
+ mntfs *mf = v;
+
+ if (--mf->mf_refc == 0) {
+ if (mf->mf_flags & MFF_MOUNTED) {
+ int quoted;
+ mf->mf_flags &= ~MFF_MOUNTED;
+
+ /*
+ * Record for posterity
+ */
+ quoted = strchr(mf->mf_info, ' ') != 0; /* cheap */
+ plog(XLOG_INFO, "%s%s%s %sed fstype %s from %s",
+ quoted ? "\"" : "",
+ mf->mf_info,
+ quoted ? "\"" : "",
+ mf->mf_error ? "discard" : "unmount",
+ mf->mf_ops->fs_type, mf->mf_mount);
+ }
+
+ if (mf->mf_ops->fs_flags & FS_DISCARD) {
+#ifdef DEBUG
+ dlog("Immediately discarding mntfs for %s", mf->mf_mount);
+#endif /* DEBUG */
+ discard_mntfs(mf);
+
+ } else {
+
+#ifdef DEBUG
+ if (mf->mf_flags & MFF_RESTART) {
+ dlog("Discarding remount hook for %s", mf->mf_mount);
+ } else {
+ dlog("Discarding last mntfs reference to %s fstype %s",
+ mf->mf_mount, mf->mf_ops->fs_type);
+ }
+ if (mf->mf_flags & (MFF_MOUNTED | MFF_MOUNTING | MFF_UNMOUNTING))
+ dlog("mntfs reference for %s still active", mf->mf_mount);
+#endif /* DEBUG */
+ mf->mf_cid = timeout(ALLOWED_MOUNT_TIME, discard_mntfs, (voidp) mf);
+ }
+ }
+}
+
+
+mntfs *
+realloc_mntfs(mntfs *mf, am_ops *ops, am_opts *mo, char *mp, char *info, char *auto_opts, char *mopts, char *remopts)
+{
+ mntfs *mf2;
+
+ if (mf->mf_refc == 1 && mf->mf_ops == &amfs_inherit_ops && STREQ(mf->mf_mount, mp)) {
+ /*
+ * If we are inheriting then just return
+ * the same node...
+ */
+ return mf;
+ }
+
+ /*
+ * Re-use the existing mntfs if it is mounted.
+ * This traps a race in nfsx.
+ */
+ if (mf->mf_ops != &amfs_error_ops &&
+ (mf->mf_flags & MFF_MOUNTED) &&
+ !FSRV_ISDOWN(mf->mf_server)) {
+ mf->mf_fo = mo;
+ return mf;
+ }
+
+ mf2 = find_mntfs(ops, mo, mp, info, auto_opts, mopts, remopts);
+ free_mntfs(mf);
+ return mf2;
+}
diff --git a/contrib/amd/amd/nfs_prot_svc.c b/contrib/amd/amd/nfs_prot_svc.c
new file mode 100644
index 000000000000..e2b1c70cf208
--- /dev/null
+++ b/contrib/amd/amd/nfs_prot_svc.c
@@ -0,0 +1,250 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: nfs_prot_svc.c,v 5.2.2.1 1992/02/09 15:09:30 jsp beta $
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+/* external definitions */
+extern voidp nfsproc_null_2_svc(voidp, struct svc_req *);
+extern nfsattrstat * nfsproc_getattr_2_svc(am_nfs_fh *, struct svc_req *);
+extern nfsattrstat * nfsproc_setattr_2_svc(nfssattrargs *, struct svc_req *);
+extern voidp nfsproc_root_2_svc(voidp, struct svc_req *);
+extern nfsdiropres * nfsproc_lookup_2_svc(nfsdiropargs *, struct svc_req *);
+extern nfsreadlinkres * nfsproc_readlink_2_svc(am_nfs_fh *, struct svc_req *);
+extern nfsreadres * nfsproc_read_2_svc(nfsreadargs *, struct svc_req *);
+extern voidp nfsproc_writecache_2_svc(voidp, struct svc_req *);
+extern nfsattrstat * nfsproc_write_2_svc(nfswriteargs *, struct svc_req *);
+extern nfsdiropres * nfsproc_create_2_svc(nfscreateargs *, struct svc_req *);
+extern nfsstat * nfsproc_remove_2_svc(nfsdiropargs *, struct svc_req *);
+extern nfsstat * nfsproc_rename_2_svc(nfsrenameargs *, struct svc_req *);
+extern nfsstat * nfsproc_link_2_svc(nfslinkargs *, struct svc_req *);
+extern nfsstat * nfsproc_symlink_2_svc(nfssymlinkargs *, struct svc_req *);
+extern nfsdiropres * nfsproc_mkdir_2_svc(nfscreateargs *, struct svc_req *);
+extern nfsstat * nfsproc_rmdir_2_svc(nfsdiropargs *, struct svc_req *);
+extern nfsreaddirres * nfsproc_readdir_2_svc(nfsreaddirargs *, struct svc_req *);
+extern nfsstatfsres * nfsproc_statfs_2_svc(am_nfs_fh *, struct svc_req *);
+
+/* global variables */
+SVCXPRT *nfs_program_2_transp;
+
+/* typedefs */
+typedef char *(*nfssvcproc_t)(voidp, struct svc_req *);
+
+
+void
+nfs_program_2(struct svc_req *rqstp, SVCXPRT *transp)
+{
+ union {
+ am_nfs_fh nfsproc_getattr_2_arg;
+ nfssattrargs nfsproc_setattr_2_arg;
+ nfsdiropargs nfsproc_lookup_2_arg;
+ am_nfs_fh nfsproc_readlink_2_arg;
+ nfsreadargs nfsproc_read_2_arg;
+ nfswriteargs nfsproc_write_2_arg;
+ nfscreateargs nfsproc_create_2_arg;
+ nfsdiropargs nfsproc_remove_2_arg;
+ nfsrenameargs nfsproc_rename_2_arg;
+ nfslinkargs nfsproc_link_2_arg;
+ nfssymlinkargs nfsproc_symlink_2_arg;
+ nfscreateargs nfsproc_mkdir_2_arg;
+ nfsdiropargs fsproc_rmdir_2_arg;
+ nfsreaddirargs nfsproc_readdir_2_arg;
+ am_nfs_fh nfsproc_statfs_2_arg;
+ } argument;
+ char *result;
+ xdrproc_t xdr_argument, xdr_result;
+ nfssvcproc_t local;
+
+ nfs_program_2_transp = NULL;
+
+ switch (rqstp->rq_proc) {
+
+ case NFSPROC_NULL:
+ xdr_argument = (xdrproc_t) xdr_void;
+ xdr_result = (xdrproc_t) xdr_void;
+ local = (nfssvcproc_t) nfsproc_null_2_svc;
+ break;
+
+ case NFSPROC_GETATTR:
+ xdr_argument = (xdrproc_t) xdr_nfs_fh;
+ xdr_result = (xdrproc_t) xdr_attrstat;
+ local = (nfssvcproc_t) nfsproc_getattr_2_svc;
+ break;
+
+ case NFSPROC_SETATTR:
+ xdr_argument = (xdrproc_t) xdr_sattrargs;
+ xdr_result = (xdrproc_t) xdr_attrstat;
+ local = (nfssvcproc_t) nfsproc_setattr_2_svc;
+ break;
+
+ case NFSPROC_ROOT:
+ xdr_argument = (xdrproc_t) xdr_void;
+ xdr_result = (xdrproc_t) xdr_void;
+ local = (nfssvcproc_t) nfsproc_root_2_svc;
+ break;
+
+ case NFSPROC_LOOKUP:
+ xdr_argument = (xdrproc_t) xdr_diropargs;
+ xdr_result = (xdrproc_t) xdr_diropres;
+ local = (nfssvcproc_t) nfsproc_lookup_2_svc;
+ /*
+ * Cheap way to pass transp down to amfs_auto_lookuppn so it can
+ * be stored in the am_node structure and later used for
+ * quick_reply().
+ */
+ nfs_program_2_transp = transp;
+ break;
+
+ case NFSPROC_READLINK:
+ xdr_argument = (xdrproc_t) xdr_nfs_fh;
+ xdr_result = (xdrproc_t) xdr_readlinkres;
+ local = (nfssvcproc_t) nfsproc_readlink_2_svc;
+ break;
+
+ case NFSPROC_READ:
+ xdr_argument = (xdrproc_t) xdr_readargs;
+ xdr_result = (xdrproc_t) xdr_readres;
+ local = (nfssvcproc_t) nfsproc_read_2_svc;
+ break;
+
+ case NFSPROC_WRITECACHE:
+ xdr_argument = (xdrproc_t) xdr_void;
+ xdr_result = (xdrproc_t) xdr_void;
+ local = (nfssvcproc_t) nfsproc_writecache_2_svc;
+ break;
+
+ case NFSPROC_WRITE:
+ xdr_argument = (xdrproc_t) xdr_writeargs;
+ xdr_result = (xdrproc_t) xdr_attrstat;
+ local = (nfssvcproc_t) nfsproc_write_2_svc;
+ break;
+
+ case NFSPROC_CREATE:
+ xdr_argument = (xdrproc_t) xdr_createargs;
+ xdr_result = (xdrproc_t) xdr_diropres;
+ local = (nfssvcproc_t) nfsproc_create_2_svc;
+ break;
+
+ case NFSPROC_REMOVE:
+ xdr_argument = (xdrproc_t) xdr_diropargs;
+ xdr_result = (xdrproc_t) xdr_nfsstat;
+ local = (nfssvcproc_t) nfsproc_remove_2_svc;
+ break;
+
+ case NFSPROC_RENAME:
+ xdr_argument = (xdrproc_t) xdr_renameargs;
+ xdr_result = (xdrproc_t) xdr_nfsstat;
+ local = (nfssvcproc_t) nfsproc_rename_2_svc;
+ break;
+
+ case NFSPROC_LINK:
+ xdr_argument = (xdrproc_t) xdr_linkargs;
+ xdr_result = (xdrproc_t) xdr_nfsstat;
+ local = (nfssvcproc_t) nfsproc_link_2_svc;
+ break;
+
+ case NFSPROC_SYMLINK:
+ xdr_argument = (xdrproc_t) xdr_symlinkargs;
+ xdr_result = (xdrproc_t) xdr_nfsstat;
+ local = (nfssvcproc_t) nfsproc_symlink_2_svc;
+ break;
+
+ case NFSPROC_MKDIR:
+ xdr_argument = (xdrproc_t) xdr_createargs;
+ xdr_result = (xdrproc_t) xdr_diropres;
+ local = (nfssvcproc_t) nfsproc_mkdir_2_svc;
+ break;
+
+ case NFSPROC_RMDIR:
+ xdr_argument = (xdrproc_t) xdr_diropargs;
+ xdr_result = (xdrproc_t) xdr_nfsstat;
+ local = (nfssvcproc_t) nfsproc_rmdir_2_svc;
+ break;
+
+ case NFSPROC_READDIR:
+ xdr_argument = (xdrproc_t) xdr_readdirargs;
+ xdr_result = (xdrproc_t) xdr_readdirres;
+ local = (nfssvcproc_t) nfsproc_readdir_2_svc;
+ break;
+
+ case NFSPROC_STATFS:
+ xdr_argument = (xdrproc_t) xdr_nfs_fh;
+ xdr_result = (xdrproc_t) xdr_statfsres;
+ local = (nfssvcproc_t) nfsproc_statfs_2_svc;
+ break;
+
+ default:
+ svcerr_noproc(transp);
+ return;
+ }
+
+ memset((char *) &argument, 0, sizeof(argument));
+ if (!svc_getargs(transp,
+ (XDRPROC_T_TYPE) xdr_argument,
+ (SVC_IN_ARG_TYPE) &argument)) {
+ plog(XLOG_ERROR,
+ "NFS xdr decode failed for %d %d %d",
+ rqstp->rq_prog, rqstp->rq_vers, rqstp->rq_proc);
+ svcerr_decode(transp);
+ return;
+ }
+ result = (*local) (&argument, rqstp);
+
+ nfs_program_2_transp = NULL;
+
+ if (result != NULL && !svc_sendreply(transp,
+ (XDRPROC_T_TYPE) xdr_result,
+ result)) {
+ svcerr_systemerr(transp);
+ }
+ if (!svc_freeargs(transp,
+ (XDRPROC_T_TYPE) xdr_argument,
+ (SVC_IN_ARG_TYPE) & argument)) {
+ plog(XLOG_FATAL, "unable to free rpc arguments in nfs_program_2");
+ going_down(1);
+ }
+}
diff --git a/contrib/amd/amd/nfs_start.c b/contrib/amd/amd/nfs_start.c
new file mode 100644
index 000000000000..e5740f69a956
--- /dev/null
+++ b/contrib/amd/amd/nfs_start.c
@@ -0,0 +1,472 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: nfs_start.c,v 5.2.2.1 1992/02/09 15:08:51 jsp beta $
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+#ifndef SELECT_MAXWAIT
+# define SELECT_MAXWAIT 16
+#endif /* not SELECT_MAXWAIT */
+
+SVCXPRT *nfsxprt;
+u_short nfs_port;
+
+#ifdef HAVE_FS_AUTOFS
+SVCXPRT *autofsxprt = NULL;
+u_short autofs_port = 0;
+#endif /* HAVE_FS_AUTOFS */
+
+#ifndef HAVE_SIGACTION
+# define MASKED_SIGS (sigmask(SIGINT)|sigmask(SIGTERM)|sigmask(SIGCHLD)|sigmask(SIGHUP))
+#endif /* not HAVE_SIGACTION */
+
+#ifdef DEBUG
+/*
+ * Check that we are not burning resources
+ */
+static void
+checkup(void)
+{
+
+ static int max_fd = 0;
+ static char *max_mem = 0;
+
+ int next_fd = dup(0);
+ caddr_t next_mem = sbrk(0);
+ close(next_fd);
+
+ if (max_fd < next_fd) {
+ dlog("%d new fds allocated; total is %d",
+ next_fd - max_fd, next_fd);
+ max_fd = next_fd;
+ }
+ if (max_mem < next_mem) {
+#ifdef HAVE_GETPAGESIZE
+ dlog("%#x bytes of memory allocated; total is %#x (%ld pages)",
+ next_mem - max_mem, next_mem,
+ ((long) next_mem + getpagesize() - 1) / getpagesize());
+#else /* not HAVE_GETPAGESIZE */
+ dlog("%#x bytes of memory allocated; total is %#x",
+ next_mem - max_mem, next_mem);
+#endif /* not HAVE_GETPAGESIZE */
+ max_mem = next_mem;
+
+ }
+}
+#endif /* DEBUG */
+
+
+static int
+#ifdef HAVE_SIGACTION
+do_select(sigset_t smask, int fds, fd_set *fdp, struct timeval *tvp)
+#else /* not HAVE_SIGACTION */
+do_select(int smask, int fds, fd_set *fdp, struct timeval *tvp)
+#endif /* not HAVE_SIGACTION */
+{
+
+ int sig;
+ int nsel;
+
+ if ((sig = setjmp(select_intr))) {
+ select_intr_valid = 0;
+ /* Got a signal */
+ switch (sig) {
+ case SIGINT:
+ case SIGTERM:
+ amd_state = Finishing;
+ reschedule_timeout_mp();
+ break;
+ }
+ nsel = -1;
+ errno = EINTR;
+ } else {
+ select_intr_valid = 1;
+ /*
+ * Invalidate the current clock value
+ */
+ clock_valid = 0;
+ /*
+ * Allow interrupts. If a signal
+ * occurs, then it will cause a longjmp
+ * up above.
+ */
+#ifdef HAVE_SIGACTION
+ sigprocmask(SIG_SETMASK, &smask, NULL);
+#else /* not HAVE_SIGACTION */
+ (void) sigsetmask(smask);
+#endif /* not HAVE_SIGACTION */
+
+ /*
+ * Wait for input
+ */
+ nsel = select(fds, fdp, (fd_set *) 0, (fd_set *) 0,
+ tvp->tv_sec ? tvp : (struct timeval *) 0);
+ }
+
+#ifdef HAVE_SIGACTION
+ sigprocmask(SIG_BLOCK, &masked_sigs, NULL);
+#else /* not HAVE_SIGACTION */
+ (void) sigblock(MASKED_SIGS);
+#endif /* not HAVE_SIGACTION */
+
+ /*
+ * Perhaps reload the cache?
+ */
+ if (do_mapc_reload < clocktime()) {
+ mapc_reload();
+ do_mapc_reload = clocktime() + ONE_HOUR;
+ }
+ return nsel;
+}
+
+
+/*
+ * Determine whether anything is left in
+ * the RPC input queue.
+ */
+static int
+rpc_pending_now(void)
+{
+ struct timeval tvv;
+ int nsel;
+#ifdef FD_SET
+ fd_set readfds;
+
+ FD_ZERO(&readfds);
+ FD_SET(fwd_sock, &readfds);
+#else /* not FD_SET */
+ int readfds = (1 << fwd_sock);
+#endif /* not FD_SET */
+
+ tvv.tv_sec = tvv.tv_usec = 0;
+ nsel = select(FD_SETSIZE, &readfds, (fd_set *) 0, (fd_set *) 0, &tvv);
+ if (nsel < 1)
+ return (0);
+#ifdef FD_SET
+ if (FD_ISSET(fwd_sock, &readfds))
+ return (1);
+#else /* not FD_SET */
+ if (readfds & (1 << fwd_sock))
+ return (1);
+#endif /* not FD_SET */
+
+ return (0);
+}
+
+
+static serv_state
+run_rpc(void)
+{
+#ifdef HAVE_SIGACTION
+ sigset_t smask;
+ sigprocmask(SIG_BLOCK, &masked_sigs, &smask);
+#else /* not HAVE_SIGACTION */
+ int smask = sigblock(MASKED_SIGS);
+#endif /* not HAVE_SIGACTION */
+
+ next_softclock = clocktime();
+
+ amd_state = Run;
+
+ /*
+ * Keep on trucking while we are in Run mode. This state
+ * is switched to Quit after all the file systems have
+ * been unmounted.
+ */
+ while ((int) amd_state <= (int) Finishing) {
+ struct timeval tvv;
+ int nsel;
+ time_t now;
+#ifdef HAVE_SVC_GETREQSET
+ fd_set readfds;
+
+ memmove(&readfds, &svc_fdset, sizeof(svc_fdset));
+ FD_SET(fwd_sock, &readfds);
+#else /* not HAVE_SVC_GETREQSET */
+# ifdef FD_SET
+ fd_set readfds;
+ FD_ZERO(&readfds);
+ readfds.fds_bits[0] = svc_fds;
+ FD_SET(fwd_sock, &readfds);
+# else /* not FD_SET */
+ int readfds = svc_fds | (1 << fwd_sock);
+# endif /* not FD_SET */
+#endif /* not HAVE_SVC_GETREQSET */
+
+#ifdef DEBUG
+ checkup();
+#endif /* DEBUG */
+
+ /*
+ * If the full timeout code is not called,
+ * then recompute the time delta manually.
+ */
+ now = clocktime();
+
+ if (next_softclock <= now) {
+ if (amd_state == Finishing)
+ umount_exported();
+ tvv.tv_sec = softclock();
+ } else {
+ tvv.tv_sec = next_softclock - now;
+ }
+ tvv.tv_usec = 0;
+
+ if (amd_state == Finishing && last_used_map < 0) {
+ flush_mntfs();
+ amd_state = Quit;
+ break;
+ }
+ if (tvv.tv_sec <= 0)
+ tvv.tv_sec = SELECT_MAXWAIT;
+#ifdef DEBUG
+ if (tvv.tv_sec) {
+ dlog("Select waits for %ds", tvv.tv_sec);
+ } else {
+ dlog("Select waits for Godot");
+ }
+#endif /* DEBUG */
+
+ nsel = do_select(smask, FD_SETSIZE, &readfds, &tvv);
+
+ switch (nsel) {
+ case -1:
+ if (errno == EINTR) {
+#ifdef DEBUG
+ dlog("select interrupted");
+#endif /* DEBUG */
+ continue;
+ }
+ perror("select");
+ break;
+
+ case 0:
+ break;
+
+ default:
+ /*
+ * Read all pending NFS responses at once to avoid having responses.
+ * queue up as a consequence of retransmissions.
+ */
+#ifdef FD_SET
+ if (FD_ISSET(fwd_sock, &readfds)) {
+ FD_CLR(fwd_sock, &readfds);
+#else /* not FD_SET */
+ if (readfds & (1 << fwd_sock)) {
+ readfds &= ~(1 << fwd_sock);
+#endif /* not FD_SET */
+ --nsel;
+ do {
+ fwd_reply();
+ } while (rpc_pending_now() > 0);
+ }
+
+ if (nsel) {
+ /*
+ * Anything left must be a normal
+ * RPC request.
+ */
+#ifdef HAVE_SVC_GETREQSET
+ svc_getreqset(&readfds);
+#else /* not HAVE_SVC_GETREQSET */
+# ifdef FD_SET
+ svc_getreq(readfds.fds_bits[0]);
+# else /* not FD_SET */
+ svc_getreq(readfds);
+# endif /* not FD_SET */
+#endif /* not HAVE_SVC_GETREQSET */
+ }
+ break;
+ }
+ }
+
+#ifdef HAVE_SIGACTION
+ sigprocmask(SIG_SETMASK, &smask, NULL);
+#else /* not HAVE_SIGACTION */
+ (void) sigsetmask(smask);
+#endif /* not HAVE_SIGACTION */
+
+ if (amd_state == Quit)
+ amd_state = Done;
+
+ return amd_state;
+}
+
+
+int
+mount_automounter(int ppid)
+{
+ /*
+ * Old code replaced by rpc-trash patch.
+ * Erez Zadok <ezk@cs.columbia.edu>
+ int so = socket(AF_INET, SOCK_DGRAM, 0);
+ */
+ SVCXPRT *udp_amqp = NULL, *tcp_amqp = NULL;
+ int nmount, ret;
+ int soNFS;
+ int udp_soAMQ, tcp_soAMQ;
+#ifdef HAVE_TRANSPORT_TYPE_TLI
+ struct netconfig *udp_amqncp, *tcp_amqncp;
+#endif /* HAVE_TRANSPORT_TYPE_TLI */
+#ifdef HAVE_FS_AUTOFS
+ int soAUTOFS;
+#endif /* HAVE_FS_AUTOFS */
+
+ /*
+ * Create the nfs service for amd
+ */
+#ifdef HAVE_TRANSPORT_TYPE_TLI
+ ret = create_nfs_service(&soNFS, &nfs_port, &nfsxprt, nfs_program_2);
+ if (ret != 0)
+ return ret;
+ ret = create_amq_service(&udp_soAMQ, &udp_amqp, &udp_amqncp, &tcp_soAMQ, &tcp_amqp, &tcp_amqncp);
+#else /* not HAVE_TRANSPORT_TYPE_TLI */
+ ret = create_nfs_service(&soNFS, &nfs_port, &nfsxprt, nfs_program_2);
+ if (ret != 0)
+ return ret;
+ ret = create_amq_service(&udp_soAMQ, &udp_amqp, &tcp_soAMQ, &tcp_amqp);
+#endif /* not HAVE_TRANSPORT_TYPE_TLI */
+ if (ret != 0)
+ return ret;
+
+#ifdef HAVE_FS_AUTOFS
+ /*
+ * Create the autofs service for amd.
+ */
+ plog(XLOG_INFO, "creating autofs service listener");
+ ret = create_autofs_service(&soAUTOFS, &autofs_port, &autofsxprt, autofs_program_1);
+ /* if autofs service fails it is OK if using a test amd */
+ if (ret != 0 && gopt.portmap_program == AMQ_PROGRAM)
+ return ret;
+#endif /* HAVE_FS_AUTOFS */
+
+ /*
+ * Start RPC forwarding
+ */
+ if (fwd_init() != 0)
+ return 3;
+
+ /*
+ * Construct the root automount node
+ */
+ make_root_node();
+
+ /*
+ * Pick up the pieces from a previous run
+ * This is likely to (indirectly) need the rpc_fwd package
+ * so it *must* come after the call to fwd_init().
+ */
+ if (gopt.flags & CFM_RESTART_EXISTING_MOUNTS)
+ restart();
+
+ /*
+ * Mount the top-level auto-mountpoints
+ */
+ nmount = mount_exported();
+
+ /*
+ * Now safe to tell parent that we are up and running
+ */
+ if (ppid)
+ kill(ppid, SIGQUIT);
+
+ if (nmount == 0) {
+ plog(XLOG_FATAL, "No work to do - quitting");
+ amd_state = Done;
+ return 0;
+ }
+
+#ifdef DEBUG
+ amuDebug(D_AMQ) {
+#endif /* DEBUG */
+ /*
+ * Complete registration of amq (first TCP service then UDP)
+ */
+ unregister_amq();
+
+#ifdef HAVE_TRANSPORT_TYPE_TLI
+ ret = svc_reg(tcp_amqp, get_amd_program_number(), AMQ_VERSION,
+ amq_program_1, tcp_amqncp);
+#else /* not HAVE_TRANSPORT_TYPE_TLI */
+ ret = svc_register(tcp_amqp, get_amd_program_number(), AMQ_VERSION,
+ amq_program_1, IPPROTO_TCP);
+#endif /* not HAVE_TRANSPORT_TYPE_TLI */
+ if (ret != 1) {
+ plog(XLOG_FATAL, "unable to register (AMQ_PROGRAM=%d, AMQ_VERSION, tcp)", get_amd_program_number());
+ return 3;
+ }
+
+#ifdef HAVE_TRANSPORT_TYPE_TLI
+ ret = svc_reg(udp_amqp, get_amd_program_number(), AMQ_VERSION,
+ amq_program_1, udp_amqncp);
+#else /* not HAVE_TRANSPORT_TYPE_TLI */
+ ret = svc_register(udp_amqp, get_amd_program_number(), AMQ_VERSION,
+ amq_program_1, IPPROTO_UDP);
+#endif /* not HAVE_TRANSPORT_TYPE_TLI */
+ if (ret != 1) {
+ plog(XLOG_FATAL, "unable to register (AMQ_PROGRAM=%d, AMQ_VERSION, udp)", get_amd_program_number());
+ return 4;
+ }
+
+#ifdef DEBUG
+ }
+#endif /* DEBUG */
+
+ /*
+ * Start timeout_mp rolling
+ */
+ reschedule_timeout_mp();
+
+ /*
+ * Start the server
+ */
+ if (run_rpc() != Done) {
+ plog(XLOG_FATAL, "run_rpc failed");
+ amd_state = Done;
+ }
+ return 0;
+}
diff --git a/contrib/amd/amd/nfs_subr.c b/contrib/amd/amd/nfs_subr.c
new file mode 100644
index 000000000000..3de086192209
--- /dev/null
+++ b/contrib/amd/amd/nfs_subr.c
@@ -0,0 +1,610 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: nfs_subr.c,v 5.2.2.1 1992/02/09 15:08:53 jsp beta $
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+/*
+ * Convert from UN*X to NFS error code.
+ * Some systems like linux define their own (see
+ * conf/mount/mount_linux.h).
+ */
+#ifndef nfs_error
+# define nfs_error(e) ((nfsstat)(e))
+#endif /* nfs_error */
+
+/* forward declarations */
+static void count_map_entries(const am_node *mp, u_int *out_blocks, u_int *out_bfree, u_int *out_bavail);
+
+
+static char *
+do_readlink(am_node *mp, int *error_return, nfsattrstat **attrpp)
+{
+ char *ln;
+
+ /*
+ * If there is a readlink method, then use
+ * that, otherwise if a link exists use
+ * that, otherwise use the mount point.
+ */
+ if (mp->am_mnt->mf_ops->readlink) {
+ int retry = 0;
+ mp = (*mp->am_mnt->mf_ops->readlink) (mp, &retry);
+ if (mp == 0) {
+ *error_return = retry;
+ return 0;
+ }
+ /* reschedule_timeout_mp(); */
+ }
+
+ if (mp->am_link) {
+ ln = mp->am_link;
+ } else {
+ ln = mp->am_mnt->mf_mount;
+ }
+ if (attrpp)
+ *attrpp = &mp->am_attr;
+
+ return ln;
+}
+
+
+voidp
+nfsproc_null_2_svc(voidp argp, struct svc_req *rqstp)
+{
+ static char res;
+
+ return (voidp) &res;
+}
+
+
+nfsattrstat *
+nfsproc_getattr_2_svc(am_nfs_fh *argp, struct svc_req *rqstp)
+{
+ static nfsattrstat res;
+ am_node *mp;
+ int retry;
+
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "getattr:");
+#endif /* DEBUG */
+
+ mp = fh_to_mp2(argp, &retry);
+ if (mp == 0) {
+
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "\tretry=%d", retry);
+#endif /* DEBUG */
+
+ if (retry < 0)
+ return 0;
+ res.ns_status = nfs_error(retry);
+ } else {
+ nfsattrstat *attrp = &mp->am_attr;
+
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "\tstat(%s), size = %d", mp->am_path,
+ attrp->ns_u.ns_attr_u.na_size);
+#endif /* DEBUG */
+
+ mp->am_stats.s_getattr++;
+ return attrp;
+ }
+
+#ifndef MNT2_NFS_OPT_SYMTTL
+ /*
+ * This code is needed to defeat Solaris 2.4's (and newer) symlink values
+ * cache. It forces the last-modifed time of the symlink to be current.
+ * It is not needed if the O/S has an nfs flag to turn off the
+ * symlink-cache at mount time (such as Irix 5.x and 6.x). -Erez.
+ */
+ if (++res.ns_u.ns_attr_u.na_mtime.nt_useconds == 0)
+ ++res.ns_u.ns_attr_u.na_mtime.nt_seconds;
+#endif /* not MNT2_NFS_OPT_SYMTTL */
+
+ return &res;
+}
+
+
+nfsattrstat *
+nfsproc_setattr_2_svc(nfssattrargs *argp, struct svc_req *rqstp)
+{
+ static nfsattrstat res;
+
+ if (!fh_to_mp(&argp->sag_fhandle))
+ res.ns_status = nfs_error(ESTALE);
+ else
+ res.ns_status = nfs_error(EROFS);
+
+ return &res;
+}
+
+
+voidp
+nfsproc_root_2_svc(voidp argp, struct svc_req *rqstp)
+{
+ static char res;
+
+ return (voidp) &res;
+}
+
+
+nfsdiropres *
+nfsproc_lookup_2_svc(nfsdiropargs *argp, struct svc_req *rqstp)
+{
+ static nfsdiropres res;
+ am_node *mp;
+ int retry;
+
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "lookup:");
+#endif /* DEBUG */
+
+ mp = fh_to_mp2(&argp->da_fhandle, &retry);
+ if (mp == 0) {
+ if (retry < 0)
+ return 0;
+ res.dr_status = nfs_error(retry);
+ } else {
+ int error;
+ am_node *ap;
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "\tlookuppn(%s, %s)", mp->am_path, argp->da_name);
+#endif /* DEBUG */
+ ap = (*mp->am_mnt->mf_ops->lookuppn) (mp, argp->da_name, &error, VLOOK_CREATE);
+ if (ap == 0) {
+ if (error < 0) {
+#ifdef DEBUG
+ dlog("Not sending RPC reply");
+#endif /* DEBUG */
+ amd_stats.d_drops++;
+ return 0;
+ }
+ res.dr_status = nfs_error(error);
+ } else {
+ mp_to_fh(ap, &res.dr_u.dr_drok_u.drok_fhandle);
+ res.dr_u.dr_drok_u.drok_attributes = ap->am_fattr;
+ res.dr_status = NFS_OK;
+ }
+ mp->am_stats.s_lookup++;
+ /* reschedule_timeout_mp(); */
+ }
+
+ return &res;
+}
+
+
+void
+quick_reply(am_node *mp, int error)
+{
+ SVCXPRT *transp = mp->am_transp;
+ nfsdiropres res;
+ xdrproc_t xdr_result = (xdrproc_t) xdr_diropres;
+
+ /*
+ * If there's a transp structure then we can reply to the client's
+ * nfs lookup request.
+ */
+ if (transp) {
+ if (error == 0) {
+ /*
+ * Construct a valid reply to a lookup request. Same
+ * code as in nfsproc_lookup_2_svc() above.
+ */
+ mp_to_fh(mp, &res.dr_u.dr_drok_u.drok_fhandle);
+ res.dr_u.dr_drok_u.drok_attributes = mp->am_fattr;
+ res.dr_status = NFS_OK;
+ } else
+ /*
+ * Return the error that was passed to us.
+ */
+ res.dr_status = nfs_error(error);
+
+ /*
+ * Send off our reply
+ */
+ if (!svc_sendreply(transp, (XDRPROC_T_TYPE) xdr_result, (SVC_IN_ARG_TYPE) & res))
+ svcerr_systemerr(transp);
+
+ /*
+ * Free up transp. It's only used for one reply.
+ */
+ XFREE(transp);
+ mp->am_transp = NULL;
+#ifdef DEBUG
+ dlog("Quick reply sent for %s", mp->am_mnt->mf_mount);
+#endif /* DEBUG */
+ }
+}
+
+
+nfsreadlinkres *
+nfsproc_readlink_2_svc(am_nfs_fh *argp, struct svc_req *rqstp)
+{
+ static nfsreadlinkres res;
+ am_node *mp;
+ int retry;
+
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "readlink:");
+#endif /* DEBUG */
+
+ mp = fh_to_mp2(argp, &retry);
+ if (mp == 0) {
+ readlink_retry:
+ if (retry < 0)
+ return 0;
+ res.rlr_status = nfs_error(retry);
+ } else {
+ char *ln = do_readlink(mp, &retry, (nfsattrstat **) 0);
+ if (ln == 0)
+ goto readlink_retry;
+ res.rlr_status = NFS_OK;
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ if (ln)
+ plog(XLOG_DEBUG, "\treadlink(%s) = %s", mp->am_path, ln);
+#endif /* DEBUG */
+ res.rlr_u.rlr_data_u = ln;
+ mp->am_stats.s_readlink++;
+ }
+
+ return &res;
+}
+
+
+nfsreadres *
+nfsproc_read_2_svc(nfsreadargs *argp, struct svc_req *rqstp)
+{
+ static nfsreadres res;
+
+ memset((char *) &res, 0, sizeof(res));
+ res.rr_status = nfs_error(EACCES);
+
+ return &res;
+}
+
+
+voidp
+nfsproc_writecache_2_svc(voidp argp, struct svc_req *rqstp)
+{
+ static char res;
+
+ return (voidp) &res;
+}
+
+
+nfsattrstat *
+nfsproc_write_2_svc(nfswriteargs *argp, struct svc_req *rqstp)
+{
+ static nfsattrstat res;
+
+ if (!fh_to_mp(&argp->wra_fhandle))
+ res.ns_status = nfs_error(ESTALE);
+ else
+ res.ns_status = nfs_error(EROFS);
+
+ return &res;
+}
+
+
+nfsdiropres *
+nfsproc_create_2_svc(nfscreateargs *argp, struct svc_req *rqstp)
+{
+ static nfsdiropres res;
+
+ if (!fh_to_mp(&argp->ca_where.da_fhandle))
+ res.dr_status = nfs_error(ESTALE);
+ else
+ res.dr_status = nfs_error(EROFS);
+
+ return &res;
+}
+
+
+static nfsstat *
+unlink_or_rmdir(nfsdiropargs *argp, struct svc_req *rqstp, int unlinkp)
+{
+ static nfsstat res;
+ int retry;
+
+ am_node *mp = fh_to_mp3(&argp->da_fhandle, &retry, VLOOK_DELETE);
+ if (mp == 0) {
+ if (retry < 0)
+ return 0;
+ res = nfs_error(retry);
+ goto out;
+ }
+
+ if (mp->am_fattr.na_type != NFDIR) {
+ res = nfs_error(ENOTDIR);
+ goto out;
+ }
+
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "\tremove(%s, %s)", mp->am_path, argp->da_name);
+#endif /* DEBUG */
+
+ mp = (*mp->am_mnt->mf_ops->lookuppn) (mp, argp->da_name, &retry, VLOOK_DELETE);
+ if (mp == 0) {
+ /*
+ * Ignore retries...
+ */
+ if (retry < 0)
+ retry = 0;
+ /*
+ * Usual NFS workaround...
+ */
+ else if (retry == ENOENT)
+ retry = 0;
+ res = nfs_error(retry);
+ } else {
+ forcibly_timeout_mp(mp);
+ res = NFS_OK;
+ }
+
+out:
+ return &res;
+}
+
+
+nfsstat *
+nfsproc_remove_2_svc(nfsdiropargs *argp, struct svc_req *rqstp)
+{
+ return unlink_or_rmdir(argp, rqstp, TRUE);
+}
+
+
+nfsstat *
+nfsproc_rename_2_svc(nfsrenameargs *argp, struct svc_req *rqstp)
+{
+ static nfsstat res;
+
+ if (!fh_to_mp(&argp->rna_from.da_fhandle) || !fh_to_mp(&argp->rna_to.da_fhandle))
+ res = nfs_error(ESTALE);
+ /*
+ * If the kernel is doing clever things with referenced files
+ * then let it pretend...
+ */
+ else if (NSTREQ(argp->rna_to.da_name, ".nfs", 4))
+ res = NFS_OK;
+ /*
+ * otherwise a failure
+ */
+ else
+ res = nfs_error(EROFS);
+
+ return &res;
+}
+
+
+nfsstat *
+nfsproc_link_2_svc(nfslinkargs *argp, struct svc_req *rqstp)
+{
+ static nfsstat res;
+
+ if (!fh_to_mp(&argp->la_fhandle) || !fh_to_mp(&argp->la_to.da_fhandle))
+ res = nfs_error(ESTALE);
+ else
+ res = nfs_error(EROFS);
+
+ return &res;
+}
+
+
+nfsstat *
+nfsproc_symlink_2_svc(nfssymlinkargs *argp, struct svc_req *rqstp)
+{
+ static nfsstat res;
+
+ if (!fh_to_mp(&argp->sla_from.da_fhandle))
+ res = nfs_error(ESTALE);
+ else
+ res = nfs_error(EROFS);
+
+ return &res;
+}
+
+
+nfsdiropres *
+nfsproc_mkdir_2_svc(nfscreateargs *argp, struct svc_req *rqstp)
+{
+ static nfsdiropres res;
+
+ if (!fh_to_mp(&argp->ca_where.da_fhandle))
+ res.dr_status = nfs_error(ESTALE);
+ else
+ res.dr_status = nfs_error(EROFS);
+
+ return &res;
+}
+
+
+nfsstat *
+nfsproc_rmdir_2_svc(nfsdiropargs *argp, struct svc_req *rqstp)
+{
+ return unlink_or_rmdir(argp, rqstp, FALSE);
+}
+
+
+nfsreaddirres *
+nfsproc_readdir_2_svc(nfsreaddirargs *argp, struct svc_req *rqstp)
+{
+ static nfsreaddirres res;
+ static nfsentry e_res[MAX_READDIR_ENTRIES];
+ am_node *mp;
+ int retry;
+
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "readdir:");
+#endif /* DEBUG */
+
+ mp = fh_to_mp2(&argp->rda_fhandle, &retry);
+ if (mp == 0) {
+ if (retry < 0)
+ return 0;
+ res.rdr_status = nfs_error(retry);
+ } else {
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "\treaddir(%s)", mp->am_path);
+#endif /* DEBUG */
+ res.rdr_status = nfs_error((*mp->am_mnt->mf_ops->readdir)
+ (mp, argp->rda_cookie,
+ &res.rdr_u.rdr_reply_u, e_res, argp->rda_count));
+ mp->am_stats.s_readdir++;
+ }
+
+ return &res;
+}
+
+
+nfsstatfsres *
+nfsproc_statfs_2_svc(am_nfs_fh *argp, struct svc_req *rqstp)
+{
+ static nfsstatfsres res;
+ am_node *mp;
+ int retry;
+ mntent_t mnt;
+
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "statfs:");
+#endif /* DEBUG */
+
+ mp = fh_to_mp2(argp, &retry);
+ if (mp == 0) {
+ if (retry < 0)
+ return 0;
+ res.sfr_status = nfs_error(retry);
+ } else {
+ nfsstatfsokres *fp;
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "\tstat_fs(%s)", mp->am_path);
+#endif /* DEBUG */
+
+ /*
+ * just return faked up file system information
+ */
+ fp = &res.sfr_u.sfr_reply_u;
+
+ fp->sfrok_tsize = 1024;
+ fp->sfrok_bsize = 1024;
+
+ /* check if map is browsable and show_statfs_entries=yes */
+ if ((gopt.flags & CFM_SHOW_STATFS_ENTRIES) &&
+ mp->am_mnt && mp->am_mnt->mf_mopts) {
+ mnt.mnt_opts = mp->am_mnt->mf_mopts;
+ if (hasmntopt(&mnt, "browsable")) {
+ count_map_entries(mp,
+ &fp->sfrok_blocks,
+ &fp->sfrok_bfree,
+ &fp->sfrok_bavail);
+ }
+ } else {
+ fp->sfrok_blocks = 0; /* set to 1 if you don't want empty automounts */
+ fp->sfrok_bfree = 0;
+ fp->sfrok_bavail = 0;
+ }
+
+ res.sfr_status = NFS_OK;
+ mp->am_stats.s_statfs++;
+ }
+
+ return &res;
+}
+
+
+/*
+ * count how many total entries there are in a map, and how many
+ * of them are in use.
+ */
+static void
+count_map_entries(const am_node *mp, u_int *out_blocks, u_int *out_bfree, u_int *out_bavail)
+{
+ u_int blocks, bfree, bavail, i;
+ mntfs *mf;
+ mnt_map *mmp;
+ kv *k;
+
+ blocks = bfree = bavail = 0;
+ if (!mp)
+ goto out;
+ mf = mp->am_mnt;
+ if (!mf)
+ goto out;
+ mmp = (mnt_map *) mf->mf_private;
+ if (!mmp)
+ goto out;
+
+ /* iterate over keys */
+ for (i = 0; i < NKVHASH; i++) {
+ for (k = mmp->kvhash[i]; k ; k = k->next) {
+ if (!k->key)
+ continue;
+ blocks++;
+ /*
+ * XXX: Need to count how many are actively in use and recompute
+ * bfree and bavail based on it.
+ */
+ }
+ }
+
+out:
+ *out_blocks = blocks;
+ *out_bfree = bfree;
+ *out_bavail = bavail;
+}
diff --git a/contrib/amd/amd/ops_TEMPLATE.c b/contrib/amd/amd/ops_TEMPLATE.c
new file mode 100644
index 000000000000..7a60206b2ec1
--- /dev/null
+++ b/contrib/amd/amd/ops_TEMPLATE.c
@@ -0,0 +1,293 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: ops_TEMPLATE.c,v 5.2.2.3 1992/08/02 10:42:21 jsp Exp $
+ *
+ */
+
+/*
+ * An empty template for an amd pseudo filesystem "foofs".
+ */
+
+/*
+ * NOTE: if this is an Amd file system, prepend "amfs_" to all foofs symbols
+ * and renamed the file name to amfs_foofs.c. If it is a native file system
+ * (such as pcfs, isofs, or ffs), then you can keep the names as is, and
+ * just rename the file to ops_foofs.c.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+/* forward declarations */
+static char * foofs_match(am_opts *fo);
+static int foofs_init(mntfs *mf);
+static int foofs_mount(am_node *mp);
+static int foofs_fmount(mntfs *mf);
+static int foofs_umount(am_node *mp);
+static int foofs_fumount(mntfs *mf);
+static am_node * foofs_lookuppn(am_node *mp, char *fname, int *error_return, int op);
+static int foofs_readdir(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, int count);
+static am_node * foofs_readlink(am_node *mp, int *error_return);
+static void foofs_mounted(mntfs *mf);
+static void foofs_umounted(am_node *mp);
+fserver * foofs_ffserver(mntfs *mf);
+
+
+/*
+ * Foofs operations.
+ * Define only those you need, others set to 0 (NULL)
+ */
+am_ops foofs_ops =
+{
+ "foofs", /* name of file system */
+ foofs_match, /* match */
+ foofs_init, /* initialize */
+ foofs_mount, /* mount vnode */
+ foofs_fmount, /* mount vfs */
+ foofs_umount, /* unmount vnode */
+ foofs_fumount, /* unmount VFS */
+ foofs_lookuppn, /* lookup path-name */
+ foofs_readdir, /* read directory */
+ foofs_readlink, /* read link */
+ foofs_mounted, /* after-mount extra actions */
+ foofs_umounted, /* after-umount extra actions */
+ foofs_ffserver, /* find a file server */
+ FS_MKMNT | FS_BACKGROUND | FS_AMQINFO /* flags */
+};
+
+
+/*
+ * Check that f/s has all needed fields.
+ * Returns: matched string if found, NULL otherwise.
+ */
+static char *
+foofs_match(am_opts *fo)
+{
+ char *cp = "fill this with a way to find the match";
+
+ plog(XLOG_INFO, "entering foofs_match...");
+
+ if (cp)
+ return cp; /* OK */
+
+ return NULL; /* not OK */
+}
+
+
+/*
+ * Initialize.
+ * Returns: 0 if OK, non-zero (errno) if failed.
+ */
+static int
+foofs_init(mntfs *mf)
+{
+ int error = 0;
+
+ plog(XLOG_INFO, "entering foofs_init...");
+
+ error = EPERM; /* XXX: fixme */
+ return error;
+}
+
+
+/*
+ * Mount vnode.
+ * Returns: 0 if OK, non-zero (errno) if failed.
+ */
+static int
+foofs_mount(am_node *mp)
+{
+ int error = 0;
+
+ plog(XLOG_INFO, "entering foofs_mount...");
+
+ error = EPERM; /* XXX: fixme */
+ return error;
+}
+
+
+/*
+ * Mount vfs.
+ * Returns: 0 if OK, non-zero (errno) if failed.
+ */
+static int
+foofs_fmount(mntfs *mf)
+{
+ int error = 0;
+
+ plog(XLOG_INFO, "entering foofs_fmount...");
+
+ error = EPERM; /* XXX: fixme */
+ return error;
+}
+
+
+/*
+ * Unmount vnode.
+ * Returns: 0 if OK, non-zero (errno) if failed.
+ */
+static int
+foofs_umount(am_node *mp)
+{
+ int error = 0;
+
+ plog(XLOG_INFO, "entering foofs_umount...");
+
+ error = EPERM; /* XXX: fixme */
+ return error;
+}
+
+
+/*
+ * Unmount VFS.
+ * Returns: 0 if OK, non-zero (errno) if failed.
+ */
+static int
+foofs_fumount(mntfs *mf)
+{
+ int error = 0;
+
+ plog(XLOG_INFO, "entering foofs_fumount...");
+
+ error = EPERM; /* XXX: fixme */
+ return error;
+}
+
+
+/*
+ * Lookup path-name.
+ * Returns: the am_node that was found, or NULL if failed.
+ * If failed, also fills in errno in error_return.
+ */
+static am_node *
+foofs_lookuppn(am_node *mp, char *fname, int *error_return, int op)
+{
+ int error = 0;
+
+ plog(XLOG_INFO, "entering foofs_lookuppn...");
+
+ error = EPERM; /* XXX: fixme */
+
+ *error_return = error;
+ return NULL;
+}
+
+
+/*
+ * Read directory.
+ * Returns: 0 if OK, non-zero (errno) if failed.
+ * If OK, fills in ep with chain of directory entries.
+ */
+static int
+foofs_readdir(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, int count)
+{
+ int error = 0;
+
+ plog(XLOG_INFO, "entering foofs_readdir...");
+
+ error = EPERM; /* XXX: fixme */
+ return error;
+}
+
+
+/*
+ * Read link.
+ * Returns: am_node found, or NULL if not found.
+ * If failed, fills in errno in error_return.
+ */
+static am_node *
+foofs_readlink(am_node *mp, int *error_return)
+{
+ int error = 0;
+
+ plog(XLOG_INFO, "entering foofs_readlink...");
+
+ error = EPERM; /* XXX: fixme */
+
+ *error_return = error;
+ return NULL;
+}
+
+
+/*
+ * Async mount callback function.
+ * After the base mount went OK, sometimes
+ * there are additional actions that are needed. See union_mounted() and
+ * toplvl_mounted().
+ */
+static void
+foofs_mounted(mntfs *mf)
+{
+ plog(XLOG_INFO, "entering foofs_mounted...");
+
+ return;
+}
+
+
+/*
+ * Async unmount callback function.
+ * After the base umount() succeeds, we may want to take extra actions,
+ * such as informing remote mount daemons that we've unmounted them.
+ * See amfs_auto_umounted(), host_umounted(), nfs_umounted().
+ */
+static void
+foofs_umounted(am_node *mp)
+{
+ plog(XLOG_INFO, "entering foofs_umounted...");
+
+ return;
+}
+
+
+/*
+ * Find a file server.
+ * Returns: fserver of found server, or NULL if not found.
+ */
+fserver *
+foofs_ffserver(mntfs *mf)
+{
+ plog(XLOG_INFO, "entering foofs_ffserver...");
+
+ return NULL;
+}
diff --git a/contrib/amd/amd/ops_autofs.c b/contrib/amd/amd/ops_autofs.c
new file mode 100644
index 000000000000..a566fc42ffb6
--- /dev/null
+++ b/contrib/amd/amd/ops_autofs.c
@@ -0,0 +1,1275 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: ops_autofs.c,v 5.2.2.3 1992/08/02 10:42:21 jsp Exp $
+ *
+ */
+
+/*
+ * Automounter filesystem
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+/*
+ * CLUDGE: wrap whole file in HAVE_FS_AUTOFS, becasue
+ * not all systems with an automounter file system are supported
+ * by am-utils yet...
+ */
+
+#ifdef HAVE_FS_AUTOFS
+
+/*
+ * MACROS:
+ */
+#ifndef AUTOFS_NULL
+# define AUTOFS_NULL ((u_long)0)
+#endif /* not AUTOFS_NULL */
+
+/*
+ * VARIABLES:
+ */
+
+/* forward declarations */
+static int mount_autofs(char *dir, char *opts);
+static int autofs_mount_1_svc(struct mntrequest *mr, struct mntres *result, struct authunix_parms *cred);
+static int autofs_unmount_1_svc(struct umntrequest *ur, struct umntres *result, struct authunix_parms *cred);
+
+/* externam declarations */
+extern bool_t xdr_mntrequest(XDR *, mntrequest *);
+extern bool_t xdr_mntres(XDR *, mntres *);
+extern bool_t xdr_umntrequest(XDR *, umntrequest *);
+extern bool_t xdr_umntres(XDR *, umntres *);
+
+/*
+ * STRUCTURES:
+ */
+
+/* Sun's kernel-based automounter-supporting file system */
+am_ops autofs_ops =
+{
+ "autofs",
+ amfs_auto_match,
+ 0, /* amfs_auto_init */
+ autofs_mount,
+ 0,
+ autofs_umount,
+ 0,
+ amfs_auto_lookuppn,
+ amfs_auto_readdir, /* browsable version of readdir() */
+ 0, /* autofs_readlink */
+ autofs_mounted,
+ 0, /* autofs_umounted */
+ find_amfs_auto_srvr,
+ FS_MKMNT | FS_NOTIMEOUT | FS_BACKGROUND | FS_AMQINFO | FS_DIRECTORY
+};
+
+
+/****************************************************************************
+ *** FUNCTIONS ***
+ ****************************************************************************/
+
+/*
+ * Mount the top-level using autofs
+ */
+int
+autofs_mount(am_node *mp)
+{
+ mntfs *mf = mp->am_mnt;
+ struct stat stb;
+ char opts[256], preopts[256];
+ int error;
+ char *mnttype;
+
+ /*
+ * Mounting the automounter.
+ * Make sure the mount directory exists, construct
+ * the mount options and call the mount_autofs routine.
+ */
+
+ if (stat(mp->am_path, &stb) < 0) {
+ return errno;
+ } else if ((stb.st_mode & S_IFMT) != S_IFDIR) {
+ plog(XLOG_WARNING, "%s is not a directory", mp->am_path);
+ return ENOTDIR;
+ }
+ if (mf->mf_ops == &autofs_ops)
+ mnttype = "indirect";
+ else if (mf->mf_ops == &amfs_direct_ops)
+ mnttype = "direct";
+#ifdef HAVE_AM_FS_UNION
+ else if (mf->mf_ops == &amfs_union_ops)
+ mnttype = "union";
+#endif /* HAVE_AM_FS_UNION */
+ else
+ mnttype = "auto";
+
+ /*
+ * Construct some mount options:
+ *
+ * Tack on magic map=<mapname> option in mtab to emulate
+ * SunOS automounter behavior.
+ */
+ preopts[0] = '\0';
+#ifdef MNTTAB_OPT_INTR
+ strcat(preopts, MNTTAB_OPT_INTR);
+ strcat(preopts, ",");
+#endif /* MNTTAB_OPT_INTR */
+#ifdef MNTTAB_OPT_IGNORE
+ strcat(preopts, MNTTAB_OPT_IGNORE);
+ strcat(preopts, ",");
+#endif /* MNTTAB_OPT_IGNORE */
+ sprintf(opts, "%s%s,%s=%d,%s=%d,%s=%d,%s,map=%s",
+ preopts,
+ MNTTAB_OPT_RW,
+ MNTTAB_OPT_PORT, nfs_port,
+ MNTTAB_OPT_TIMEO, gopt.amfs_auto_timeo,
+ MNTTAB_OPT_RETRANS, gopt.amfs_auto_retrans,
+ mnttype, mf->mf_info);
+
+ /* now do the mount */
+ error = mount_autofs(mf->mf_mount, opts);
+ if (error) {
+ errno = error;
+ plog(XLOG_FATAL, "mount_autofs: %m");
+ return error;
+ }
+ return 0;
+}
+
+
+void
+autofs_mounted(mntfs *mf)
+{
+ amfs_auto_mkcacheref(mf);
+}
+
+
+/*
+ * Unmount a top-level automount node
+ */
+int
+autofs_umount(am_node *mp)
+{
+ int error;
+ struct stat stb;
+
+ /*
+ * The lstat is needed if this mount is type=direct. When that happens,
+ * the kernel cache gets confused between the underlying type (dir) and
+ * the mounted type (link) and so needs to be re-synced before the
+ * unmount. This is all because the unmount system call follows links and
+ * so can't actually unmount a link (stupid!). It was noted that doing an
+ * ls -ld of the mount point to see why things were not working actually
+ * fixed the problem - so simulate an ls -ld here.
+ */
+ if (lstat(mp->am_path, &stb) < 0) {
+#ifdef DEBUG
+ dlog("lstat(%s): %m", mp->am_path);
+#endif /* DEBUG */
+ }
+ error = UMOUNT_FS(mp->am_path, mnttab_file_name);
+ if (error == EBUSY && mp->am_flags & AMF_AUTOFS) {
+ plog(XLOG_WARNING, "autofs_unmount of %s busy (autofs). exit", mp->am_path);
+ error = 0; /* fake unmount was ok */
+ }
+ return error;
+}
+
+
+/*
+ * Mount an automounter directory.
+ * The automounter is connected into the system
+ * as a user-level NFS server. mount_autofs constructs
+ * the necessary NFS parameters to be given to the
+ * kernel so that it will talk back to us.
+ */
+static int
+mount_autofs(char *dir, char *opts)
+{
+ char fs_hostname[MAXHOSTNAMELEN + MAXPATHLEN + 1];
+ char *map_opt, buf[MAXHOSTNAMELEN];
+ int retry, error, flags;
+ struct utsname utsname;
+ mntent_t mnt;
+ autofs_args_t autofs_args;
+ MTYPE_TYPE type = MOUNT_TYPE_AUTOFS;
+
+ memset((voidp) &autofs_args, 0, sizeof(autofs_args)); /* Paranoid */
+
+ memset((voidp) &mnt, 0, sizeof(mnt));
+ mnt.mnt_dir = dir;
+ mnt.mnt_fsname = pid_fsname;
+ mnt.mnt_opts = opts;
+ mnt.mnt_type = type;
+
+ retry = hasmntval(&mnt, "retry");
+ if (retry <= 0)
+ retry = 2; /* XXX */
+
+ /*
+ * SET MOUNT ARGS
+ */
+ if (uname(&utsname) < 0) {
+ strcpy(buf, "localhost.autofs");
+ } else {
+ strcpy(buf, utsname.nodename);
+ strcat(buf, ".autofs");
+ }
+#ifdef HAVE_FIELD_AUTOFS_ARGS_T_ADDR
+ autofs_args.addr.buf = buf;
+ autofs_args.addr.len = strlen(autofs_args.addr.buf);
+ autofs_args.addr.maxlen = autofs_args.addr.len;
+#endif /* HAVE_FIELD_AUTOFS_ARGS_T_ADDR */
+
+ autofs_args.path = dir;
+ autofs_args.opts = opts;
+
+ map_opt = hasmntopt(&mnt, "map");
+ if (map_opt) {
+ map_opt += sizeof("map="); /* skip the "map=" */
+ if (map_opt == NULL) {
+ plog(XLOG_WARNING, "map= has a null map name. reset to amd.unknown");
+ map_opt = "amd.unknown";
+ }
+ }
+ autofs_args.map = map_opt;
+
+ /* XXX: these I set arbitrarily... */
+ autofs_args.mount_to = 300;
+ autofs_args.rpc_to = 60;
+ autofs_args.direct = 0;
+
+ /*
+ * Make a ``hostname'' string for the kernel
+ */
+ sprintf(fs_hostname, "pid%ld@%s:%s", foreground ? mypid : getppid(),
+ hostname, dir);
+
+ /*
+ * Most kernels have a name length restriction.
+ */
+ if (strlen(fs_hostname) >= MAXHOSTNAMELEN)
+ strcpy(fs_hostname + MAXHOSTNAMELEN - 3, "..");
+
+ /*
+ * Finally we can compute the mount flags set above.
+ */
+ flags = compute_mount_flags(&mnt);
+
+ /*
+ * This is it! Here we try to mount amd on its mount points.
+ */
+ error = mount_fs(&mnt, flags, (caddr_t) &autofs_args, retry, type, 0, NULL, mnttab_file_name);
+ return error;
+}
+
+
+/****************************************************************************/
+/* autofs program dispatcher */
+void
+autofs_program_1(struct svc_req *rqstp, SVCXPRT *transp)
+{
+ int ret;
+ union {
+ mntrequest autofs_mount_1_arg;
+ umntrequest autofs_umount_1_arg;
+ } argument;
+ union {
+ mntres mount_res;
+ umntres umount_res;
+ } result;
+
+ bool_t (*xdr_argument)(), (*xdr_result)();
+ int (*local)();
+
+ switch (rqstp->rq_proc) {
+
+ case AUTOFS_NULL:
+ svc_sendreply(transp,
+ (XDRPROC_T_TYPE) xdr_void,
+ (SVC_IN_ARG_TYPE) NULL);
+ return;
+
+ case AUTOFS_MOUNT:
+ xdr_argument = xdr_mntrequest;
+ xdr_result = xdr_mntres;
+ local = (int (*)()) autofs_mount_1_svc;
+ break;
+
+ case AUTOFS_UNMOUNT:
+ xdr_argument = xdr_umntrequest;
+ xdr_result = xdr_umntres;
+ local = (int (*)()) autofs_unmount_1_svc;
+ break;
+
+ default:
+ svcerr_noproc(transp);
+ return;
+ }
+
+ memset((char *) &argument, 0, sizeof(argument));
+ if (!svc_getargs(transp,
+ (XDRPROC_T_TYPE) xdr_argument,
+ (SVC_IN_ARG_TYPE) &argument)) {
+ plog(XLOG_ERROR,
+ "AUTOFS xdr decode failed for %d %d %d",
+ rqstp->rq_prog, rqstp->rq_vers, rqstp->rq_proc);
+ svcerr_decode(transp);
+ return;
+ }
+
+ ret = (*local) (&argument, &result, rqstp);
+ if (!svc_sendreply(transp,
+ (XDRPROC_T_TYPE) xdr_result,
+ (SVC_IN_ARG_TYPE) &result)) {
+ svcerr_systemerr(transp);
+ }
+
+ if (!svc_freeargs(transp,
+ (XDRPROC_T_TYPE) xdr_argument,
+ (SVC_IN_ARG_TYPE) &argument)) {
+ plog(XLOG_FATAL, "unable to free rpc arguments in autofs_program_1");
+ going_down(1);
+ }
+}
+
+
+static int
+autofs_mount_1_svc(struct mntrequest *mr, struct mntres *result, struct authunix_parms *cred)
+{
+ int err = 0;
+ am_node *anp, *anp2;
+
+ plog(XLOG_INFO, "XXX: autofs_mount_1_svc: %s:%s:%s:%s",
+ mr->map, mr->name, mr->opts, mr->path);
+
+ /* look for map (eg. "/home") */
+ anp = find_ap(mr->path);
+ if (!anp) {
+ plog(XLOG_ERROR, "map %s not found", mr->path);
+ err = ENOENT;
+ goto out;
+ }
+ /* turn on autofs in map flags */
+ if (!(anp->am_flags & AMF_AUTOFS)) {
+ plog(XLOG_INFO, "turning on AMF_AUTOFS for node %s", mr->path);
+ anp->am_flags |= AMF_AUTOFS;
+ }
+
+ /*
+ * Look for (and create if needed) the new node.
+ *
+ * If an error occurred, return it. If a -1 was returned, that indicates
+ * that a mount is in progress, so sleep a while (while the backgrounded
+ * mount is happening), and then signal the autofs to retry the mount.
+ *
+ * There's something I don't understand. I was thinking that this code
+ * here is the one which will succeed eventually and will send an RPC
+ * reply to the kernel, but apparently that happens somewhere else, not
+ * here. It works though, just that I don't know how. Arg. -Erez.
+ * */
+ err = 0;
+ anp2 = autofs_lookuppn(anp, mr->name, &err, VLOOK_CREATE);
+ if (!anp2) {
+ if (err == -1) { /* then tell autofs to retry */
+ sleep(1);
+ err = EAGAIN;
+ }
+ goto out;
+ }
+
+out:
+ result->status = err;
+ return err;
+}
+
+
+static int
+autofs_unmount_1_svc(struct umntrequest *ur, struct umntres *result, struct authunix_parms *cred)
+{
+ int err = 0;
+
+#ifdef HAVE_FIELD_UMNTREQUEST_RDEVID
+ plog(XLOG_INFO, "XXX: autofs_unmount_1_svc: %d:%u:%lu:0x%x",
+ ur->isdirect, ur->devid, ur->rdevid, ur->next);
+#else /* HAVE_FIELD_UMNTREQUEST_RDEVID */
+ plog(XLOG_INFO, "XXX: autofs_unmount_1_svc: %d:%u:0x%x",
+ ur->isdirect, ur->devid, ur->next);
+#endif /* HAVE_FIELD_UMNTREQUEST_RDEVID */
+
+ err = EINVAL; /* XXX: not implemented yet */
+ goto out;
+
+out:
+ result->status = err;
+ return err;
+}
+
+
+/*
+ * Pick a file system to try mounting and
+ * do that in the background if necessary
+ *
+ For each location:
+ if it is new -defaults then
+ extract and process
+ continue;
+ fi
+ if it is a cut then
+ if a location has been tried then
+ break;
+ fi
+ continue;
+ fi
+ parse mount location
+ discard previous mount location if required
+ find matching mounted filesystem
+ if not applicable then
+ this_error = No such file or directory
+ continue
+ fi
+ if the filesystem failed to be mounted then
+ this_error = error from filesystem
+ elif the filesystem is mounting or unmounting then
+ this_error = -1
+ elif the fileserver is down then
+ this_error = -1
+ elif the filesystem is already mounted
+ this_error = 0
+ break
+ fi
+ if no error on this mount then
+ this_error = initialise mount point
+ fi
+ if no error on this mount and mount is delayed then
+ this_error = -1
+ fi
+ if this_error < 0 then
+ retry = true
+ fi
+ if no error on this mount then
+ make mount point if required
+ fi
+ if no error on this mount then
+ if mount in background then
+ run mount in background
+ return -1
+ else
+ this_error = mount in foreground
+ fi
+ fi
+ if an error occured on this mount then
+ update stats
+ save error in mount point
+ fi
+ endfor
+ */
+static int
+autofs_bgmount(struct continuation * cp, int mpe)
+{
+ mntfs *mf = cp->mp->am_mnt; /* Current mntfs */
+ mntfs *mf_retry = 0; /* First mntfs which needed retrying */
+ int this_error = -1; /* Per-mount error */
+ int hard_error = -1;
+ int mp_error = mpe;
+
+ /*
+ * Try to mount each location.
+ * At the end:
+ * hard_error == 0 indicates something was mounted.
+ * hard_error > 0 indicates everything failed with a hard error
+ * hard_error < 0 indicates nothing could be mounted now
+ */
+ for (; this_error && *cp->ivec; cp->ivec++) {
+ am_ops *p;
+ am_node *mp = cp->mp;
+ char *link_dir;
+ int dont_retry;
+
+ if (hard_error < 0)
+ hard_error = this_error;
+
+ this_error = -1;
+
+ if (**cp->ivec == '-') {
+ /*
+ * Pick up new defaults
+ */
+ if (cp->auto_opts && *cp->auto_opts)
+ cp->def_opts = str3cat(cp->def_opts, cp->auto_opts, ";", *cp->ivec + 1);
+ else
+ cp->def_opts = strealloc(cp->def_opts, *cp->ivec + 1);
+#ifdef DEBUG
+ dlog("Setting def_opts to \"%s\"", cp->def_opts);
+#endif /* DEBUG */
+ continue;
+ }
+ /*
+ * If a mount has been attempted, and we find
+ * a cut then don't try any more locations.
+ */
+ if (STREQ(*cp->ivec, "/") || STREQ(*cp->ivec, "||")) {
+ if (cp->tried) {
+#ifdef DEBUG
+ dlog("Cut: not trying any more locations for %s",
+ mp->am_path);
+#endif /* DEBUG */
+ break;
+ }
+ continue;
+ }
+
+ /* match the operators */
+ p = ops_match(&cp->fs_opts, *cp->ivec, cp->def_opts, mp->am_path, cp->key, mp->am_parent->am_mnt->mf_info);
+
+ /*
+ * Find a mounted filesystem for this node.
+ */
+ mp->am_mnt = mf = realloc_mntfs(mf, p, &cp->fs_opts,
+ cp->fs_opts.opt_fs,
+ cp->fs_opts.fs_mtab,
+ cp->auto_opts,
+ cp->fs_opts.opt_opts,
+ cp->fs_opts.opt_remopts);
+
+ p = mf->mf_ops;
+#ifdef DEBUG
+ dlog("Got a hit with %s", p->fs_type);
+#endif /* DEBUG */
+
+ /*
+ * Note whether this is a real mount attempt
+ */
+ if (p == &amfs_error_ops) {
+ plog(XLOG_MAP, "Map entry %s for %s failed to match", *cp->ivec, mp->am_path);
+ if (this_error <= 0)
+ this_error = ENOENT;
+ continue;
+ } else {
+ if (cp->fs_opts.fs_mtab) {
+ plog(XLOG_MAP, "Trying mount of %s on \"%s\" fstype %s",
+ cp->fs_opts.fs_mtab, mp->am_path, p->fs_type);
+ }
+ cp->tried = TRUE;
+ }
+
+ this_error = 0;
+ dont_retry = FALSE;
+
+ if (mp->am_link) {
+ XFREE(mp->am_link);
+ mp->am_link = 0;
+ }
+ link_dir = mf->mf_fo->opt_sublink;
+
+ if (link_dir && *link_dir) {
+ if (*link_dir == '/') {
+ mp->am_link = strdup(link_dir);
+ } else {
+ /*
+ * try getting fs option from continuation, not mountpoint!
+ * Don't try logging the string from mf, since it may be bad!
+ */
+ if (cp->fs_opts.opt_fs != mf->mf_fo->opt_fs)
+ plog(XLOG_ERROR, "use %s instead of 0x%x",
+ cp->fs_opts.opt_fs, mf->mf_fo->opt_fs);
+
+ mp->am_link = str3cat((char *) 0,
+ cp->fs_opts.opt_fs, "/", link_dir);
+
+ normalize_slash(mp->am_link);
+ }
+ }
+
+ if (mf->mf_error > 0) {
+ this_error = mf->mf_error;
+ } else if (mf->mf_flags & (MFF_MOUNTING | MFF_UNMOUNTING)) {
+ /*
+ * Still mounting - retry later
+ */
+#ifdef DEBUG
+ dlog("Duplicate pending mount fstype %s", p->fs_type);
+#endif /* DEBUG */
+ this_error = -1;
+ } else if (FSRV_ISDOWN(mf->mf_server)) {
+ /*
+ * Would just mount from the same place
+ * as a hung mount - so give up
+ */
+#ifdef DEBUG
+ dlog("%s is already hung - giving up", mf->mf_mount);
+#endif /* DEBUG */
+ mp_error = EWOULDBLOCK;
+ dont_retry = TRUE;
+ this_error = -1;
+ } else if (mf->mf_flags & MFF_MOUNTED) {
+#ifdef DEBUG
+ dlog("duplicate mount of \"%s\" ...", mf->mf_info);
+#endif /* DEBUG */
+
+ /*
+ * Just call mounted()
+ */
+ am_mounted(mp);
+
+ this_error = 0;
+ break;
+ }
+
+ /*
+ * Will usually need to play around with the mount nodes
+ * file attribute structure. This must be done here.
+ * Try and get things initialised, even if the fileserver
+ * is not known to be up. In the common case this will
+ * progress things faster.
+ */
+ if (!this_error) {
+ /*
+ * Fill in attribute fields.
+ */
+ if (mf->mf_ops->fs_flags & FS_DIRECTORY)
+ mk_fattr(mp, NFDIR);
+ else
+ mk_fattr(mp, NFLNK);
+
+ mp->am_fattr.na_fileid = mp->am_gen;
+
+ if (p->fs_init)
+ this_error = (*p->fs_init) (mf);
+ }
+
+ /*
+ * Make sure the fileserver is UP before doing any more work
+ */
+ if (!FSRV_ISUP(mf->mf_server)) {
+#ifdef DEBUG
+ dlog("waiting for server %s to become available", mf->mf_server->fs_host);
+#endif /* DEBUG */
+ this_error = -1;
+ }
+
+ if (!this_error && mf->mf_fo->opt_delay) {
+ /*
+ * If there is a delay timer on the mount
+ * then don't try to mount if the timer
+ * has not expired.
+ */
+ int i = atoi(mf->mf_fo->opt_delay);
+ if (i > 0 && clocktime() < (cp->start + i)) {
+#ifdef DEBUG
+ dlog("Mount of %s delayed by %ds", mf->mf_mount, i - clocktime() + cp->start);
+#endif /* DEBUG */
+ this_error = -1;
+ }
+ }
+
+ if (this_error < 0 && !dont_retry) {
+ if (!mf_retry)
+ mf_retry = dup_mntfs(mf);
+ cp->retry = TRUE;
+ }
+
+ if (!this_error)
+ if (p->fs_flags & FS_MBACKGROUND) {
+ mf->mf_flags |= MFF_MOUNTING; /* XXX */
+#ifdef DEBUG
+ dlog("backgrounding mount of \"%s\"", mf->mf_mount);
+#endif /* DEBUG */
+ if (cp->callout) {
+ untimeout(cp->callout);
+ cp->callout = 0;
+ }
+ run_task(try_mount, (voidp) mp, amfs_auto_cont, (voidp) cp);
+ mf->mf_flags |= MFF_MKMNT; /* XXX */
+ if (mf_retry)
+ free_mntfs(mf_retry);
+ return -1;
+ } else {
+#ifdef DEBUG
+ dlog("foreground mount of \"%s\" ...", mf->mf_info);
+#endif /* DEBUG */
+ this_error = try_mount((voidp) mp);
+ if (this_error < 0) {
+ if (!mf_retry)
+ mf_retry = dup_mntfs(mf);
+ cp->retry = TRUE;
+ }
+ }
+
+ if (this_error >= 0) {
+ if (this_error > 0) {
+ amd_stats.d_merr++;
+ if (mf != mf_retry) {
+ mf->mf_error = this_error;
+ mf->mf_flags |= MFF_ERROR;
+ }
+ }
+
+ /*
+ * Wakeup anything waiting for this mount
+ */
+ wakeup((voidp) mf);
+ }
+ }
+
+ if (this_error && cp->retry) {
+ free_mntfs(mf);
+ mf = cp->mp->am_mnt = mf_retry;
+ /*
+ * Not retrying again (so far)
+ */
+ cp->retry = FALSE;
+ cp->tried = FALSE;
+ /*
+ * Start at the beginning.
+ * Rewind the location vector and
+ * reset the default options.
+ */
+ cp->ivec = cp->xivec;
+ cp->def_opts = strealloc(cp->def_opts, cp->auto_opts);
+ /*
+ * Arrange that autofs_bgmount is called
+ * after anything else happens.
+ */
+#ifdef DEBUG
+ dlog("Arranging to retry mount of %s", cp->mp->am_path);
+#endif /* DEBUG */
+ sched_task(amfs_auto_retry, (voidp) cp, (voidp) mf);
+ if (cp->callout)
+ untimeout(cp->callout);
+ cp->callout = timeout(RETRY_INTERVAL, wakeup, (voidp) mf);
+
+ cp->mp->am_ttl = clocktime() + RETRY_INTERVAL;
+
+ /*
+ * Not done yet - so don't return anything
+ */
+ return -1;
+ }
+
+ if (hard_error < 0 || this_error == 0)
+ hard_error = this_error;
+
+ /*
+ * Discard handle on duff filesystem.
+ * This should never happen since it
+ * should be caught by the case above.
+ */
+ if (mf_retry) {
+ if (hard_error)
+ plog(XLOG_ERROR, "discarding a retry mntfs for %s", mf_retry->mf_mount);
+ free_mntfs(mf_retry);
+ }
+
+ /*
+ * If we get here, then either the mount succeeded or
+ * there is no more mount information available.
+ */
+ if (hard_error < 0 && mp_error)
+ hard_error = cp->mp->am_error = mp_error;
+ if (hard_error > 0) {
+ /*
+ * Set a small(ish) timeout on an error node if
+ * the error was not a time out.
+ */
+ switch (hard_error) {
+ case ETIMEDOUT:
+ case EWOULDBLOCK:
+ cp->mp->am_timeo = 17;
+ break;
+ default:
+ cp->mp->am_timeo = 5;
+ break;
+ }
+ new_ttl(cp->mp);
+ }
+
+ /*
+ * Make sure that the error value in the mntfs has a
+ * reasonable value.
+ */
+ if (mf->mf_error < 0) {
+ mf->mf_error = hard_error;
+ if (hard_error)
+ mf->mf_flags |= MFF_ERROR;
+ }
+
+ /*
+ * In any case we don't need the continuation any more
+ */
+ free_continuation(cp);
+
+ return hard_error;
+}
+
+
+/*
+ * Automount interface to RPC lookup routine
+ * Find the corresponding entry and return
+ * the file handle for it.
+ */
+am_node *
+autofs_lookuppn(am_node *mp, char *fname, int *error_return, int op)
+{
+ am_node *ap, *new_mp, *ap_hung;
+ char *info; /* Mount info - where to get the file system */
+ char **ivec, **xivec; /* Split version of info */
+ char *auto_opts; /* Automount options */
+ int error = 0; /* Error so far */
+ char path_name[MAXPATHLEN]; /* General path name buffer */
+ char apath[MAXPATHLEN]; /* authofs path (added space) */
+ char *pfname; /* Path for database lookup */
+ struct continuation *cp; /* Continuation structure if need to mount */
+ int in_progress = 0; /* # of (un)mount in progress */
+ char *dflts;
+ mntfs *mf;
+
+#ifdef DEBUG
+ dlog("in autofs_lookuppn");
+#endif /* DEBUG */
+
+ /*
+ * If the server is shutting down
+ * then don't return information
+ * about the mount point.
+ */
+ if (amd_state == Finishing) {
+#ifdef DEBUG
+ if ((mf = mp->am_mnt) == 0 || mf->mf_ops == &amfs_direct_ops)
+ dlog("%s mount ignored - going down", fname);
+ else
+ dlog("%s/%s mount ignored - going down", mp->am_path, fname);
+#endif /* DEBUG */
+ ereturn(ENOENT);
+ }
+
+ /*
+ * Handle special case of "." and ".."
+ */
+ if (fname[0] == '.') {
+ if (fname[1] == '\0')
+ return mp; /* "." is the current node */
+ if (fname[1] == '.' && fname[2] == '\0') {
+ if (mp->am_parent) {
+#ifdef DEBUG
+ dlog(".. in %s gives %s", mp->am_path, mp->am_parent->am_path);
+#endif /* DEBUG */
+ return mp->am_parent; /* ".." is the parent node */
+ }
+ ereturn(ESTALE);
+ }
+ }
+
+ /*
+ * Check for valid key name.
+ * If it is invalid then pretend it doesn't exist.
+ */
+ if (!valid_key(fname)) {
+ plog(XLOG_WARNING, "Key \"%s\" contains a disallowed character", fname);
+ ereturn(ENOENT);
+ }
+
+ /*
+ * Expand key name.
+ * fname is now a private copy.
+ */
+ fname = expand_key(fname);
+
+ for (ap_hung = 0, ap = mp->am_child; ap; ap = ap->am_osib) {
+ /*
+ * Otherwise search children of this node
+ */
+ if (FSTREQ(ap->am_name, fname)) {
+ mf = ap->am_mnt;
+ if (ap->am_error) {
+ error = ap->am_error;
+ continue;
+ }
+ /*
+ * If the error code is undefined then it must be
+ * in progress.
+ */
+ if (mf->mf_error < 0)
+ goto in_progrss;
+
+ /*
+ * Check for a hung node
+ */
+ if (FSRV_ISDOWN(mf->mf_server)) {
+#ifdef DEBUG
+ dlog("server hung");
+#endif /* DEBUG */
+ error = ap->am_error;
+ ap_hung = ap;
+ continue;
+ }
+ /*
+ * If there was a previous error with this node
+ * then return that error code.
+ */
+ if (mf->mf_flags & MFF_ERROR) {
+ error = mf->mf_error;
+ continue;
+ }
+ if (!(mf->mf_flags & MFF_MOUNTED) || (mf->mf_flags & MFF_UNMOUNTING)) {
+ in_progrss:
+ /*
+ * If the fs is not mounted or it is unmounting then there
+ * is a background (un)mount in progress. In this case
+ * we just drop the RPC request (return nil) and
+ * wait for a retry, by which time the (un)mount may
+ * have completed.
+ */
+#ifdef DEBUG
+ dlog("ignoring mount of %s in %s -- flags (%x) in progress",
+ fname, mf->mf_mount, mf->mf_flags);
+#endif /* DEBUG */
+ in_progress++;
+ continue;
+ }
+
+ /*
+ * Otherwise we have a hit: return the current mount point.
+ */
+#ifdef DEBUG
+ dlog("matched %s in %s", fname, ap->am_path);
+#endif /* DEBUG */
+ XFREE(fname);
+ return ap;
+ }
+ }
+
+ if (in_progress) {
+#ifdef DEBUG
+ dlog("Waiting while %d mount(s) in progress", in_progress);
+#endif /* DEBUG */
+ XFREE(fname);
+ ereturn(-1);
+ }
+
+ /*
+ * If an error occured then return it.
+ */
+ if (error) {
+#ifdef DEBUG
+ errno = error; /* XXX */
+ dlog("Returning error: %m", error);
+#endif /* DEBUG */
+ XFREE(fname);
+ ereturn(error);
+ }
+
+ /*
+ * If doing a delete then don't create again!
+ */
+ switch (op) {
+ case VLOOK_DELETE:
+ ereturn(ENOENT);
+
+ case VLOOK_CREATE:
+ break;
+
+ default:
+ plog(XLOG_FATAL, "Unknown op to autofs_lookuppn: 0x%x", op);
+ ereturn(EINVAL);
+ }
+
+ /*
+ * If the server is going down then just return,
+ * don't try to mount any more file systems
+ */
+ if ((int) amd_state >= (int) Finishing) {
+#ifdef DEBUG
+ dlog("not found - server going down anyway");
+#endif /* DEBUG */
+ XFREE(fname);
+ ereturn(ENOENT);
+ }
+
+ /*
+ * If we get there then this is a reference to an,
+ * as yet, unknown name so we need to search the mount
+ * map for it.
+ */
+ if (mp->am_pref) {
+ sprintf(path_name, "%s%s", mp->am_pref, fname);
+ pfname = path_name;
+ } else {
+ pfname = fname;
+ }
+
+ mf = mp->am_mnt;
+
+#ifdef DEBUG
+ dlog("will search map info in %s to find %s", mf->mf_info, pfname);
+#endif /* DEBUG */
+ /*
+ * Consult the oracle for some mount information.
+ * info is malloc'ed and belongs to this routine.
+ * It ends up being free'd in free_continuation().
+ *
+ * Note that this may return -1 indicating that information
+ * is not yet available.
+ */
+ error = mapc_search((mnt_map *) mf->mf_private, pfname, &info);
+ if (error) {
+ if (error > 0)
+ plog(XLOG_MAP, "No map entry for %s", pfname);
+ else
+ plog(XLOG_MAP, "Waiting on map entry for %s", pfname);
+ XFREE(fname);
+ ereturn(error);
+ }
+#ifdef DEBUG
+ dlog("mount info is %s", info);
+#endif /* DEBUG */
+
+ /*
+ * Split info into an argument vector.
+ * The vector is malloc'ed and belongs to
+ * this routine. It is free'd in free_continuation()
+ */
+ xivec = ivec = strsplit(info, ' ', '\"');
+
+ /*
+ * Default error code...
+ */
+ if (ap_hung)
+ error = EWOULDBLOCK;
+ else
+ error = ENOENT;
+
+ /*
+ * Allocate a new map
+ */
+ new_mp = exported_ap_alloc();
+ if (new_mp == 0) {
+ XFREE(xivec);
+ XFREE(info);
+ XFREE(fname);
+ ereturn(ENOSPC);
+ }
+ if (mf->mf_auto)
+ auto_opts = mf->mf_auto;
+ else
+ auto_opts = "";
+
+ auto_opts = strdup(auto_opts);
+
+#ifdef DEBUG
+ dlog("searching for /defaults entry");
+#endif /* DEBUG */
+ if (mapc_search((mnt_map *) mf->mf_private, "/defaults", &dflts) == 0) {
+ char *dfl;
+ char **rvec;
+#ifdef DEBUG
+ dlog("/defaults gave %s", dflts);
+#endif /* DEBUG */
+ if (*dflts == '-')
+ dfl = dflts + 1;
+ else
+ dfl = dflts;
+
+ /*
+ * Chop the defaults up
+ */
+ rvec = strsplit(dfl, ' ', '\"');
+
+ if (gopt.flags & CFM_ENABLE_DEFAULT_SELECTORS) {
+ /*
+ * Pick whichever first entry matched the list of selectors.
+ * Strip the selectors from the string, and assign to dfl the
+ * rest of the string.
+ */
+ if (rvec) {
+ am_opts ap;
+ am_ops *pt;
+ char **sp = rvec;
+ while (*sp) { /* loop until you find something, if any */
+ memset((char *) &ap, 0, sizeof(am_opts));
+ pt = ops_match(&ap, *sp, "", mp->am_path, "/defaults",
+ mp->am_parent->am_mnt->mf_info);
+ if (pt == &amfs_error_ops) {
+ plog(XLOG_MAP, "failed to match defaults for \"%s\"", *sp);
+ } else {
+ dfl = strip_selectors(*sp, "/defaults");
+ plog(XLOG_MAP, "matched default selectors \"%s\"", dfl);
+ break;
+ }
+ ++sp;
+ }
+ }
+ } else { /* not enable_default_selectors */
+ /*
+ * Extract first value
+ */
+ dfl = rvec[0];
+ }
+
+ /*
+ * If there were any values at all...
+ */
+ if (dfl) {
+ /*
+ * Log error if there were other values
+ */
+ if (!(gopt.flags & CFM_ENABLE_DEFAULT_SELECTORS) && rvec[1]) {
+# ifdef DEBUG
+ dlog("/defaults chopped into %s", dfl);
+# endif /* DEBUG */
+ plog(XLOG_USER, "More than a single value for /defaults in %s", mf->mf_info);
+ }
+
+ /*
+ * Prepend to existing defaults if they exist,
+ * otherwise just use these defaults.
+ */
+ if (*auto_opts && *dfl) {
+ char *nopts = (char *) xmalloc(strlen(auto_opts) + strlen(dfl) + 2);
+ sprintf(nopts, "%s;%s", dfl, auto_opts);
+ XFREE(auto_opts);
+ auto_opts = nopts;
+ } else if (*dfl) {
+ auto_opts = strealloc(auto_opts, dfl);
+ }
+ }
+ XFREE(dflts);
+ /*
+ * Don't need info vector any more
+ */
+ XFREE(rvec);
+ }
+
+ /*
+ * Fill it in
+ */
+ init_map(new_mp, fname);
+
+ /*
+ * Turn on autofs flag if needed.
+ */
+ if (mp->am_flags & AMF_AUTOFS) {
+ new_mp->am_flags |= AMF_AUTOFS;
+ }
+
+ /*
+ * Put it in the table
+ */
+ insert_am(new_mp, mp);
+
+ /*
+ * Fill in some other fields,
+ * path and mount point.
+ *
+ * bugfix: do not prepend old am_path if direct map
+ * <wls@astro.umd.edu> William Sebok
+ */
+
+ strcpy(apath, fname);
+ strcat(apath, " ");
+ new_mp->am_path = str3cat(new_mp->am_path,
+ mf->mf_ops == &amfs_direct_ops ? "" : mp->am_path,
+ *fname == '/' ? "" : "/",
+ apath);
+
+#ifdef DEBUG
+ dlog("setting path to \"%s\"", new_mp->am_path);
+#endif /* DEBUG */
+
+ /*
+ * Take private copy of pfname
+ */
+ pfname = strdup(pfname);
+
+ /*
+ * Construct a continuation
+ */
+ cp = ALLOC(struct continuation);
+ cp->callout = 0;
+ cp->mp = new_mp;
+ cp->xivec = xivec;
+ cp->ivec = ivec;
+ cp->info = info;
+ cp->key = pfname;
+ cp->auto_opts = auto_opts;
+ cp->retry = FALSE;
+ cp->tried = FALSE;
+ cp->start = clocktime();
+ cp->def_opts = strdup(auto_opts);
+ memset((voidp) &cp->fs_opts, 0, sizeof(cp->fs_opts));
+
+ /*
+ * Try and mount the file system. If this succeeds immediately (possible
+ * for a ufs file system) then return the attributes, otherwise just
+ * return an error.
+ */
+ error = autofs_bgmount(cp, error);
+ reschedule_timeout_mp();
+ if (!error) {
+ XFREE(fname);
+ return new_mp;
+ }
+
+ /*
+ * Code for quick reply. If nfs_program_2_transp is set, then
+ * its the transp that's been passed down from nfs_program_2().
+ * If new_mp->am_transp is not already set, set it by copying in
+ * nfs_program_2_transp. Once am_transp is set, quick_reply() can
+ * use it to send a reply to the client that requested this mount.
+ */
+ if (nfs_program_2_transp && !new_mp->am_transp) {
+ new_mp->am_transp = (SVCXPRT *) xmalloc(sizeof(SVCXPRT));
+ *(new_mp->am_transp) = *nfs_program_2_transp;
+ }
+ if (error && (new_mp->am_mnt->mf_ops == &amfs_error_ops))
+ new_mp->am_error = error;
+
+ assign_error_mntfs(new_mp);
+
+ XFREE(fname);
+
+ ereturn(error);
+}
+#endif /* HAVE_FS_AUTOFS */
diff --git a/contrib/amd/amd/ops_cachefs.c b/contrib/amd/amd/ops_cachefs.c
new file mode 100644
index 000000000000..0c40085ada6b
--- /dev/null
+++ b/contrib/amd/amd/ops_cachefs.c
@@ -0,0 +1,247 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: ops_cachefs.c,v 5.2.2.3 1992/08/02 10:42:21 jsp Exp $
+ *
+ */
+
+/*
+ * Caching filesystem (Solaris 2.x)
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+/* forward declarations */
+static char *cachefs_match(am_opts *fo);
+static int cachefs_init(mntfs *mf);
+static int cachefs_fmount(mntfs *mf);
+static int cachefs_fumount(mntfs *mf);
+
+
+/*
+ * Ops structure
+ */
+am_ops cachefs_ops =
+{
+ "cachefs",
+ cachefs_match,
+ cachefs_init,
+ amfs_auto_fmount,
+ cachefs_fmount,
+ amfs_auto_fumount,
+ cachefs_fumount,
+ amfs_error_lookuppn,
+ amfs_error_readdir,
+ 0, /* cachefs_readlink */
+ 0, /* post-mount actions */
+ 0, /* post-umount actions */
+ find_amfs_auto_srvr,
+ FS_MKMNT | FS_NOTIMEOUT | FS_UBACKGROUND | FS_AMQINFO
+};
+
+
+/*
+ * Check that f/s has all needed fields.
+ * Returns: matched string if found, NULL otherwise.
+ */
+static char *
+cachefs_match(am_opts *fo)
+{
+ /* sanity check */
+ if (!fo->opt_rfs || !fo->opt_fs || !fo->opt_cachedir) {
+ plog(XLOG_USER, "cachefs: must specify cachedir, rfs, and fs");
+ return NULL;
+ }
+
+#ifdef DEBUG
+ dlog("CACHEFS: using cache directory \"%s\"", fo->opt_cachedir);
+#endif /* DEBUG */
+
+ /* determine magic cookie to put in mtab */
+ return strdup(fo->opt_cachedir);
+}
+
+
+/*
+ * Initialize.
+ * Returns: 0 if OK, non-zero (errno) if failed.
+ */
+static int
+cachefs_init(mntfs *mf)
+{
+ /*
+ * Save cache directory name
+ */
+ if (mf->mf_refc == 1) {
+ mf->mf_private = (voidp) strdup(mf->mf_fo->opt_cachedir);
+ mf->mf_prfree = (void (*)(voidp)) free;
+ }
+
+ return 0;
+}
+
+
+/*
+ * mntpt is the mount point ($fs) [XXX: was 'dir']
+ * backdir is the mounted pathname ($rfs) [XXX: was 'fs_name']
+ * cachedir is the cache directory ($cachedir)
+ */
+static int
+mount_cachefs(char *mntpt, char *backdir, char *cachedir, char *opts)
+{
+ cachefs_args_t ca;
+ mntent_t mnt;
+ int flags;
+ char *cp;
+ MTYPE_TYPE type = MOUNT_TYPE_CACHEFS; /* F/S mount type */
+
+ memset((voidp) &ca, 0, sizeof(ca)); /* Paranoid */
+
+ /*
+ * Fill in the mount structure
+ */
+ memset((voidp) &mnt, 0, sizeof(mnt));
+ mnt.mnt_dir = mntpt;
+ mnt.mnt_fsname = backdir;
+ mnt.mnt_type = MNTTAB_TYPE_CACHEFS;
+ mnt.mnt_opts = opts;
+
+ flags = compute_mount_flags(&mnt);
+
+ /* Fill in cachefs mount arguments */
+
+ /*
+ * XXX: Caveats
+ * (1) cache directory is NOT checked for sanity beforehand, nor is it
+ * purged. Maybe it should be purged first?
+ * (2) cache directory is NOT locked. Should we?
+ */
+
+ /* mount flags */
+ ca.cfs_options.opt_flags = CFS_WRITE_AROUND | CFS_ACCESS_BACKFS;
+ /* cache population size */
+ ca.cfs_options.opt_popsize = DEF_POP_SIZE; /* default: 64K */
+ /* filegrp size */
+ ca.cfs_options.opt_fgsize = DEF_FILEGRP_SIZE; /* default: 256 */
+
+ /* CFS ID for file system (must be unique) */
+ ca.cfs_fsid = cachedir;
+
+ /* CFS fscdir name */
+ memset(ca.cfs_cacheid, 0, sizeof(ca.cfs_cacheid));
+ /* append cacheid and mountpoint */
+ sprintf(ca.cfs_cacheid, "%s:%s", ca.cfs_fsid, mntpt);
+ /* convert '/' to '_' (Solaris does that...) */
+ cp = ca.cfs_cacheid;
+ while ((cp = strpbrk(cp, "/")) != NULL)
+ *cp = '_';
+
+ /* path for this cache dir */
+ ca.cfs_cachedir = cachedir;
+
+ /* back filesystem dir */
+ ca.cfs_backfs = backdir;
+
+ /* same as nfs values (XXX: need to handle these options) */
+ ca.cfs_acregmin = 0;
+ ca.cfs_acregmax = 0;
+ ca.cfs_acdirmin = 0;
+ ca.cfs_acdirmax = 0;
+
+ /*
+ * Call generic mount routine
+ */
+ return mount_fs(&mnt, flags, (caddr_t) &ca, 0, type, 0, NULL, mnttab_file_name);
+}
+
+
+static int
+cachefs_fmount(mntfs *mf)
+{
+ int error;
+
+ error = mount_cachefs(mf->mf_mount,
+ mf->mf_fo->opt_rfs,
+ mf->mf_fo->opt_cachedir,
+ mf->mf_mopts);
+ if (error) {
+ errno = error;
+ /* according to Solaris, if errno==ESRCH, "options to not match" */
+ if (error == ESRCH)
+ plog(XLOG_ERROR, "mount_cachefs: options to no match: %m");
+ else
+ plog(XLOG_ERROR, "mount_cachefs: %m");
+ return error;
+ }
+
+ return 0;
+}
+
+
+static int
+cachefs_fumount(mntfs *mf)
+{
+ int error;
+
+ error = UMOUNT_FS(mf->mf_mount, mnttab_file_name);
+
+ /*
+ * In the case of cachefs, we must fsck the cache directory. Otherwise,
+ * it will remain inconsistent, and the next cachefs mount will fail
+ * with the error "no space left on device" (ENOSPC).
+ *
+ * XXX: this is hacky! use fork/exec/wait instead...
+ */
+ if (!error) {
+ char *cachedir = NULL;
+ char cmd[128];
+
+ cachedir = (char *) mf->mf_private;
+ plog(XLOG_INFO, "running fsck on cache directory \"%s\"", cachedir);
+ sprintf(cmd, "fsck -F cachefs %s", cachedir);
+ system(cmd);
+ }
+
+ return error;
+}
diff --git a/contrib/amd/amd/ops_cdfs.c b/contrib/amd/amd/ops_cdfs.c
new file mode 100644
index 000000000000..3a143e21a4bd
--- /dev/null
+++ b/contrib/amd/amd/ops_cdfs.c
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: ops_cdfs.c,v 5.2.2.1 1992/02/09 15:09:08 jsp beta $
+ *
+ */
+
+/*
+ * High Sierra (CD-ROM) file system
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+/* forward declarations */
+static char *cdfs_match(am_opts *fo);
+static int cdfs_fmount(mntfs *mf);
+static int cdfs_fumount(mntfs *mf);
+
+/*
+ * Ops structure
+ */
+am_ops cdfs_ops =
+{
+ "cdfs",
+ cdfs_match,
+ 0, /* cdfs_init */
+ amfs_auto_fmount,
+ cdfs_fmount,
+ amfs_auto_fumount,
+ cdfs_fumount,
+ amfs_error_lookuppn,
+ amfs_error_readdir,
+ 0, /* cdfs_readlink */
+ 0, /* cdfs_mounted */
+ 0, /* cdfs_umounted */
+ find_amfs_auto_srvr,
+ FS_MKMNT | FS_UBACKGROUND | FS_AMQINFO
+};
+
+
+/*
+ * CDFS needs remote filesystem.
+ */
+static char *
+cdfs_match(am_opts *fo)
+{
+ if (!fo->opt_dev) {
+ plog(XLOG_USER, "cdfs: no source device specified");
+ return 0;
+ }
+#ifdef DEBUG
+ dlog("CDFS: mounting device \"%s\" on \"%s\"",
+ fo->opt_dev, fo->opt_fs);
+#endif /* DEBUG */
+
+ /*
+ * Determine magic cookie to put in mtab
+ */
+ return strdup(fo->opt_dev);
+}
+
+
+static int
+mount_cdfs(char *dir, char *fs_name, char *opts)
+{
+ cdfs_args_t cdfs_args;
+ mntent_t mnt;
+ int genflags, cdfs_flags;
+
+ /*
+ * Figure out the name of the file system type.
+ */
+ MTYPE_TYPE type = MOUNT_TYPE_CDFS;
+
+ memset((voidp) &cdfs_args, 0, sizeof(cdfs_args)); /* Paranoid */
+ cdfs_flags = 0;
+
+ /*
+ * Fill in the mount structure
+ */
+ memset((voidp) &mnt, 0, sizeof(mnt));
+ mnt.mnt_dir = dir;
+ mnt.mnt_fsname = fs_name;
+ mnt.mnt_type = MNTTAB_TYPE_CDFS;
+ mnt.mnt_opts = opts;
+
+#if defined(MNT2_CDFS_OPT_DEFPERM) && defined(MNTTAB_OPT_DEFPERM)
+ if (hasmntopt(&mnt, MNTTAB_OPT_DEFPERM))
+# ifdef MNT2_CDFS_OPT_DEFPERM
+ cdfs_flags |= MNT2_CDFS_OPT_DEFPERM;
+# else /* not MNT2_CDFS_OPT_DEFPERM */
+ cdfs_flags &= ~MNT2_CDFS_OPT_NODEFPERM;
+# endif /* not MNT2_CDFS_OPT_DEFPERM */
+#endif /* defined(MNT2_CDFS_OPT_DEFPERM) && defined(MNTTAB_OPT_DEFPERM) */
+
+#if defined(MNT2_CDFS_OPT_NODEFPERM) && defined(MNTTAB_OPT_NODEFPERM)
+ if (hasmntopt(&mnt, MNTTAB_OPT_NODEFPERM))
+ cdfs_flags |= MNT2_CDFS_OPT_NODEFPERM;
+#endif /* MNTTAB_OPT_NODEFPERM */
+
+#if defined(MNT2_CDFS_OPT_NOVERSION) && defined(MNTTAB_OPT_NOVERSION)
+ if (hasmntopt(&mnt, MNTTAB_OPT_NOVERSION))
+ cdfs_flags |= MNT2_CDFS_OPT_NOVERSION;
+#endif /* defined(MNT2_CDFS_OPT_NOVERSION) && defined(MNTTAB_OPT_NOVERSION) */
+
+#if defined(MNT2_CDFS_OPT_RRIP) && defined(MNTTAB_OPT_RRIP)
+ if (hasmntopt(&mnt, MNTTAB_OPT_RRIP))
+ cdfs_flags |= MNT2_CDFS_OPT_RRIP;
+#endif /* defined(MNT2_CDFS_OPT_RRIP) && defined(MNTTAB_OPT_RRIP) */
+
+ genflags = compute_mount_flags(&mnt);
+
+#ifdef HAVE_FIELD_CDFS_ARGS_T_FLAGS
+ cdfs_args.flags = cdfs_flags;
+#endif /* HAVE_FIELD_CDFS_ARGS_T_FLAGS */
+
+#ifdef HAVE_FIELD_CDFS_ARGS_T_ISO_FLAGS
+ cdfs_args.iso_flags = genflags | cdfs_flags;
+#endif /* HAVE_FIELD_CDFS_ARGS_T_ISO_FLAGS */
+
+#ifdef HAVE_FIELD_CDFS_ARGS_T_ISO_PGTHRESH
+ cdfs_args.iso_pgthresh = hasmntval(&mnt, MNTTAB_OPT_PGTHRESH);
+#endif /* HAVE_FIELD_CDFS_ARGS_T_ISO_PGTHRESH */
+
+#ifdef HAVE_FIELD_CDFS_ARGS_T_FSPEC
+ cdfs_args.fspec = fs_name;
+#endif /* HAVE_FIELD_CDFS_ARGS_T_FSPEC */
+
+#ifdef HAVE_FIELD_CDFS_ARGS_T_NORRIP
+ /* XXX: need to provide norrip mount opt */
+ cdfs_args.norrip = 0; /* use Rock-Ridge Protocol extensions */
+#endif /* HAVE_FIELD_CDFS_ARGS_T_NORRIP */
+
+#ifdef HAVE_FIELD_CDFS_ARGS_T_SSECTOR
+ /* XXX: need to provide ssector mount option */
+ cdfs_args.ssector = 0; /* use 1st session on disk */
+#endif /* HAVE_FIELD_CDFS_ARGS_T_SSECTOR */
+
+ /*
+ * Call generic mount routine
+ */
+ return mount_fs(&mnt, genflags, (caddr_t) &cdfs_args, 0, type, 0, NULL, mnttab_file_name);
+}
+
+
+static int
+cdfs_fmount(mntfs *mf)
+{
+ int error;
+
+ error = mount_cdfs(mf->mf_mount, mf->mf_info, mf->mf_mopts);
+ if (error) {
+ errno = error;
+ plog(XLOG_ERROR, "mount_cdfs: %m");
+ return error;
+ }
+ return 0;
+}
+
+
+static int
+cdfs_fumount(mntfs *mf)
+{
+ return UMOUNT_FS(mf->mf_mount, mnttab_file_name);
+}
diff --git a/contrib/amd/amd/ops_efs.c b/contrib/amd/amd/ops_efs.c
new file mode 100644
index 000000000000..4f915f764554
--- /dev/null
+++ b/contrib/amd/amd/ops_efs.c
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: ops_efs.c,v 5.2.2.1 1992/02/09 15:09:08 jsp beta $
+ *
+ */
+
+/*
+ * Irix UN*X file system: EFS (Extent File System)
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+/* forward declarations */
+static char *efs_match(am_opts *fo);
+static int efs_fmount(mntfs *mf);
+static int efs_fumount(mntfs *mf);
+
+/*
+ * Ops structure
+ */
+am_ops efs_ops =
+{
+ "efs",
+ efs_match,
+ 0, /* efs_init */
+ amfs_auto_fmount,
+ efs_fmount,
+ amfs_auto_fumount,
+ efs_fumount,
+ amfs_error_lookuppn,
+ amfs_error_readdir,
+ 0, /* efs_readlink */
+ 0, /* efs_mounted */
+ 0, /* efs_umounted */
+ find_amfs_auto_srvr,
+ FS_MKMNT | FS_NOTIMEOUT | FS_UBACKGROUND | FS_AMQINFO
+};
+
+
+/*
+ * EFS needs local filesystem and device.
+ */
+static char *
+efs_match(am_opts *fo)
+{
+
+ if (!fo->opt_dev) {
+ plog(XLOG_USER, "efs: no device specified");
+ return 0;
+ }
+
+#ifdef DEBUG
+ dlog("EFS: mounting device \"%s\" on \"%s\"", fo->opt_dev, fo->opt_fs);
+#endif /* DEBUG */
+
+ /*
+ * Determine magic cookie to put in mtab
+ */
+ return strdup(fo->opt_dev);
+}
+
+
+static int
+mount_efs(char *dir, char *fs_name, char *opts)
+{
+ efs_args_t efs_args;
+ mntent_t mnt;
+ int flags;
+
+ /*
+ * Figure out the name of the file system type.
+ */
+ MTYPE_TYPE type = MOUNT_TYPE_EFS;
+
+ memset((voidp) &efs_args, 0, sizeof(efs_args)); /* Paranoid */
+
+ /*
+ * Fill in the mount structure
+ */
+ memset((voidp) &mnt, 0, sizeof(mnt));
+ mnt.mnt_dir = dir;
+ mnt.mnt_fsname = fs_name;
+ mnt.mnt_type = MNTTAB_TYPE_EFS;
+ mnt.mnt_opts = opts;
+
+ flags = compute_mount_flags(&mnt);
+
+#ifdef HAVE_FIELD_EFS_ARGS_T_FLAGS
+ efs_args.flags = 0; /* XXX: fix this to correct flags */
+#endif /* HAVE_FIELD_EFS_ARGS_T_FLAGS */
+#ifdef HAVE_FIELD_EFS_ARGS_T_FSPEC
+ efs_args.fspec = fs_name;
+#endif /* HAVE_FIELD_EFS_ARGS_T_FSPEC */
+
+ /*
+ * Call generic mount routine
+ */
+ return mount_fs(&mnt, flags, (caddr_t) &efs_args, 0, type, 0, NULL, mnttab_file_name);
+}
+
+
+static int
+efs_fmount(mntfs *mf)
+{
+ int error;
+
+ error = mount_efs(mf->mf_mount, mf->mf_info, mf->mf_mopts);
+ if (error) {
+ errno = error;
+ plog(XLOG_ERROR, "mount_efs: %m");
+ return error;
+ }
+
+ return 0;
+}
+
+
+static int
+efs_fumount(mntfs *mf)
+{
+ return UMOUNT_FS(mf->mf_mount, mnttab_file_name);
+}
diff --git a/contrib/amd/amd/ops_lofs.c b/contrib/amd/amd/ops_lofs.c
new file mode 100644
index 000000000000..6555db59c1c4
--- /dev/null
+++ b/contrib/amd/amd/ops_lofs.c
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: ops_lofs.c,v 5.2.2.1 1992/02/09 15:09:08 jsp beta $
+ *
+ */
+
+/*
+ * Loopback file system
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+/* forward definitions */
+static char * lofs_match(am_opts *fo);
+static int lofs_fmount(mntfs *mf);
+static int lofs_fumount(mntfs *mf);
+static int mount_lofs(char *dir, char *fs_name, char *opts);
+
+
+/*
+ * Ops structure
+ */
+am_ops lofs_ops =
+{
+ "lofs",
+ lofs_match,
+ 0, /* lofs_init */
+ amfs_auto_fmount,
+ lofs_fmount,
+ amfs_auto_fumount,
+ lofs_fumount,
+ amfs_error_lookuppn,
+ amfs_error_readdir,
+ 0, /* lofs_readlink */
+ 0, /* lofs_mounted */
+ 0, /* lofs_umounted */
+ find_amfs_auto_srvr,
+ FS_MKMNT | FS_NOTIMEOUT | FS_UBACKGROUND | FS_AMQINFO
+};
+
+
+/*
+ * LOFS needs remote filesystem.
+ */
+static char *
+lofs_match(am_opts *fo)
+{
+ if (!fo->opt_rfs) {
+ plog(XLOG_USER, "lofs: no source filesystem specified");
+ return 0;
+ }
+#ifdef DEBUG
+ dlog("LOFS: mounting fs \"%s\" on \"%s\"",
+ fo->opt_rfs, fo->opt_fs);
+#endif /* DEBUG */
+
+ /*
+ * Determine magic cookie to put in mtab
+ */
+ return strdup(fo->opt_rfs);
+}
+
+
+static int
+mount_lofs(char *dir, char *fs_name, char *opts)
+{
+ mntent_t mnt;
+ int flags;
+
+ /*
+ * Figure out the name of the file system type.
+ */
+ MTYPE_TYPE type = MOUNT_TYPE_LOFS;
+
+ /*
+ * Fill in the mount structure
+ */
+ memset((voidp) &mnt, 0, sizeof(mnt));
+ mnt.mnt_dir = dir;
+ mnt.mnt_fsname = fs_name;
+ mnt.mnt_type = MNTTAB_TYPE_LOFS;
+ mnt.mnt_opts = opts;
+
+ flags = compute_mount_flags(&mnt);
+
+ /*
+ * Call generic mount routine
+ */
+ return mount_fs(&mnt, flags, NULL, 0, type, 0, NULL, mnttab_file_name);
+}
+
+
+static int
+lofs_fmount(mntfs *mf)
+{
+ int error;
+
+ error = mount_lofs(mf->mf_mount, mf->mf_info, mf->mf_mopts);
+ if (error) {
+ errno = error;
+ plog(XLOG_ERROR, "mount_lofs: %m");
+ return error;
+ }
+ return 0;
+}
+
+
+static int
+lofs_fumount(mntfs *mf)
+{
+ return UMOUNT_FS(mf->mf_mount, mnttab_file_name);
+}
diff --git a/contrib/amd/amd/ops_mfs.c b/contrib/amd/amd/ops_mfs.c
new file mode 100644
index 000000000000..f93c30b1ed78
--- /dev/null
+++ b/contrib/amd/amd/ops_mfs.c
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: ops_mfs.c,v 5.2.2.3 1992/08/02 10:42:21 jsp Exp $
+ *
+ */
+
+/*
+ * Memory file system (RAM filesystem)
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+/* FEEL FREE TO INPLEMENT THIS... :-) */
diff --git a/contrib/amd/amd/ops_nfs.c b/contrib/amd/amd/ops_nfs.c
new file mode 100644
index 000000000000..a7006b4869cd
--- /dev/null
+++ b/contrib/amd/amd/ops_nfs.c
@@ -0,0 +1,799 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: ops_nfs.c,v 5.2.2.3 1992/08/02 10:42:21 jsp Exp $
+ *
+ */
+
+/*
+ * Network file system
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+/*
+ * Convert from nfsstat to UN*X error code
+ */
+#define unx_error(e) ((int)(e))
+
+/*
+ * FH_TTL is the time a file handle will remain in the cache since
+ * last being used. If the file handle becomes invalid, then it
+ * will be flushed anyway.
+ */
+#define FH_TTL (5 * 60) /* five minutes */
+#define FH_TTL_ERROR (30) /* 30 seconds */
+#define FHID_ALLOC(struct) (++fh_id)
+
+/*
+ * The NFS layer maintains a cache of file handles.
+ * This is *fundamental* to the implementation and
+ * also allows quick remounting when a filesystem
+ * is accessed soon after timing out.
+ *
+ * The NFS server layer knows to flush this cache
+ * when a server goes down so avoiding stale handles.
+ *
+ * Each cache entry keeps a hard reference to
+ * the corresponding server. This ensures that
+ * the server keepalive information is maintained.
+ *
+ * The copy of the sockaddr_in here is taken so
+ * that the port can be twiddled to talk to mountd
+ * instead of portmap or the NFS server as used
+ * elsewhere.
+ * The port# is flushed if a server goes down.
+ * The IP address is never flushed - we assume
+ * that the address of a mounted machine never
+ * changes. If it does, then you have other
+ * problems...
+ */
+typedef struct fh_cache fh_cache;
+struct fh_cache {
+ qelem fh_q; /* List header */
+ voidp fh_wchan; /* Wait channel */
+ int fh_error; /* Valid data? */
+ int fh_id; /* Unique id */
+ int fh_cid; /* Callout id */
+ u_long fh_nfs_version; /* highest NFS version on host */
+ am_nfs_handle_t fh_nfs_handle; /* Handle on filesystem */
+ struct sockaddr_in fh_sin; /* Address of mountd */
+ fserver *fh_fs; /* Server holding filesystem */
+ char *fh_path; /* Filesystem on host */
+};
+
+/* forward definitions */
+static int call_mountd(fh_cache *fp, u_long proc, fwd_fun f, voidp wchan);
+static int fh_id = 0;
+
+/* globals */
+AUTH *nfs_auth;
+qelem fh_head = {&fh_head, &fh_head};
+
+/*
+ * Network file system operations
+ */
+am_ops nfs_ops =
+{
+ "nfs",
+ nfs_match,
+ nfs_init,
+ amfs_auto_fmount,
+ nfs_fmount,
+ amfs_auto_fumount,
+ nfs_fumount,
+ amfs_error_lookuppn,
+ amfs_error_readdir,
+ 0, /* nfs_readlink */
+ 0, /* nfs_mounted */
+ nfs_umounted,
+ find_nfs_srvr,
+ FS_MKMNT | FS_BACKGROUND | FS_AMQINFO
+};
+
+
+static fh_cache *
+find_nfs_fhandle_cache(voidp idv, int done)
+{
+ fh_cache *fp, *fp2 = 0;
+ int id = (long) idv; /* for 64-bit archs */
+
+ ITER(fp, fh_cache, &fh_head) {
+ if (fp->fh_id == id) {
+ fp2 = fp;
+ break;
+ }
+ }
+
+#ifdef DEBUG
+ if (fp2) {
+ dlog("fh cache gives fp %#x, fs %s", fp2, fp2->fh_path);
+ } else {
+ dlog("fh cache search failed");
+ }
+#endif /* DEBUG */
+
+ if (fp2 && !done) {
+ fp2->fh_error = ETIMEDOUT;
+ return 0;
+ }
+
+ return fp2;
+}
+
+
+/*
+ * Called when a filehandle appears
+ */
+static void
+got_nfs_fh(voidp pkt, int len, struct sockaddr_in * sa, struct sockaddr_in * ia, voidp idv, int done)
+{
+ fh_cache *fp;
+
+ fp = find_nfs_fhandle_cache(idv, done);
+ if (!fp)
+ return;
+
+ /*
+ * retrieve the correct RPC reply for the file handle, based on the
+ * NFS protocol version.
+ */
+#ifdef HAVE_FS_NFS3
+ if (fp->fh_nfs_version == NFS_VERSION3)
+ fp->fh_error = pickup_rpc_reply(pkt, len, (voidp) &fp->fh_nfs_handle.v3,
+ (XDRPROC_T_TYPE) xdr_mountres3);
+ else
+#endif /* HAVE_FS_NFS3 */
+ fp->fh_error = pickup_rpc_reply(pkt, len, (voidp) &fp->fh_nfs_handle.v2,
+ (XDRPROC_T_TYPE) xdr_fhstatus);
+
+ if (!fp->fh_error) {
+#ifdef DEBUG
+ dlog("got filehandle for %s:%s", fp->fh_fs->fs_host, fp->fh_path);
+#endif /* DEBUG */
+
+ /*
+ * Wakeup anything sleeping on this filehandle
+ */
+ if (fp->fh_wchan) {
+#ifdef DEBUG
+ dlog("Calling wakeup on %#x", fp->fh_wchan);
+#endif /* DEBUG */
+ wakeup(fp->fh_wchan);
+ }
+ }
+}
+
+
+void
+flush_nfs_fhandle_cache(fserver *fs)
+{
+ fh_cache *fp;
+
+ ITER(fp, fh_cache, &fh_head) {
+ if (fp->fh_fs == fs || fs == 0) {
+ fp->fh_sin.sin_port = (u_short) 0;
+ fp->fh_error = -1;
+ }
+ }
+}
+
+
+static void
+discard_fh(voidp v)
+{
+ fh_cache *fp = v;
+
+ rem_que(&fp->fh_q);
+ if (fp->fh_fs) {
+#ifdef DEBUG
+ dlog("Discarding filehandle for %s:%s", fp->fh_fs->fs_host, fp->fh_path);
+#endif /* DEBUG */
+ free_srvr(fp->fh_fs);
+ }
+ if (fp->fh_path)
+ XFREE(fp->fh_path);
+ XFREE(fp);
+}
+
+
+/*
+ * Determine the file handle for a node
+ */
+static int
+prime_nfs_fhandle_cache(char *path, fserver *fs, am_nfs_handle_t *fhbuf, voidp wchan)
+{
+ fh_cache *fp, *fp_save = 0;
+ int error;
+ int reuse_id = FALSE;
+
+#ifdef DEBUG
+ dlog("Searching cache for %s:%s", fs->fs_host, path);
+#endif /* DEBUG */
+
+ /*
+ * First search the cache
+ */
+ ITER(fp, fh_cache, &fh_head) {
+ if (fs == fp->fh_fs && STREQ(path, fp->fh_path)) {
+ switch (fp->fh_error) {
+ case 0:
+ plog(XLOG_INFO, "prime_nfs_fhandle_cache: NFS version %d", fp->fh_nfs_version);
+#ifdef HAVE_FS_NFS3
+ if (fp->fh_nfs_version == NFS_VERSION3)
+ error = fp->fh_error = unx_error(fp->fh_nfs_handle.v3.fhs_status);
+ else
+#endif /* HAVE_FS_NFS3 */
+ error = fp->fh_error = unx_error(fp->fh_nfs_handle.v2.fhs_status);
+ if (error == 0) {
+ if (fhbuf) {
+#ifdef HAVE_FS_NFS3
+ if (fp->fh_nfs_version == NFS_VERSION3)
+ memmove((voidp) &(fhbuf->v3), (voidp) &(fp->fh_nfs_handle.v3),
+ sizeof(fp->fh_nfs_handle.v3));
+ else
+#endif /* HAVE_FS_NFS3 */
+ memmove((voidp) &(fhbuf->v2), (voidp) &(fp->fh_nfs_handle.v2),
+ sizeof(fp->fh_nfs_handle.v2));
+ }
+ if (fp->fh_cid)
+ untimeout(fp->fh_cid);
+ fp->fh_cid = timeout(FH_TTL, discard_fh, (voidp) fp);
+ } else if (error == EACCES) {
+ /*
+ * Now decode the file handle return code.
+ */
+ plog(XLOG_INFO, "Filehandle denied for \"%s:%s\"",
+ fs->fs_host, path);
+ } else {
+ errno = error; /* XXX */
+ plog(XLOG_INFO, "Filehandle error for \"%s:%s\": %m",
+ fs->fs_host, path);
+ }
+
+ /*
+ * The error was returned from the remote mount daemon.
+ * Policy: this error will be cached for now...
+ */
+ return error;
+
+ case -1:
+ /*
+ * Still thinking about it, but we can re-use.
+ */
+ fp_save = fp;
+ reuse_id = TRUE;
+ break;
+
+ default:
+ /*
+ * Return the error.
+ * Policy: make sure we recompute if required again
+ * in case this was caused by a network failure.
+ * This can thrash mountd's though... If you find
+ * your mountd going slowly then:
+ * 1. Add a fork() loop to main.
+ * 2. Remove the call to innetgr() and don't use
+ * netgroups, especially if you don't use YP.
+ */
+ error = fp->fh_error;
+ fp->fh_error = -1;
+ return error;
+ }
+ break;
+ }
+ }
+
+ /*
+ * Not in cache
+ */
+ if (fp_save) {
+ fp = fp_save;
+ /*
+ * Re-use existing slot
+ */
+ untimeout(fp->fh_cid);
+ free_srvr(fp->fh_fs);
+ XFREE(fp->fh_path);
+ } else {
+ fp = ALLOC(struct fh_cache);
+ memset((voidp) fp, 0, sizeof(struct fh_cache));
+ ins_que(&fp->fh_q, &fh_head);
+ }
+ if (!reuse_id)
+ fp->fh_id = FHID_ALLOC(struct );
+ fp->fh_wchan = wchan;
+ fp->fh_error = -1;
+ fp->fh_cid = timeout(FH_TTL, discard_fh, (voidp) fp);
+
+ /*
+ * if fs->fs_ip is null, remote server is probably down.
+ */
+ if (!fs->fs_ip) {
+ /* Mark the fileserver down and invalid again */
+ fs->fs_flags &= ~FSF_VALID;
+ fs->fs_flags |= FSF_DOWN;
+ error = AM_ERRNO_HOST_DOWN;
+ return error;
+ }
+
+ /*
+ * If the address has changed then don't try to re-use the
+ * port information
+ */
+ if (fp->fh_sin.sin_addr.s_addr != fs->fs_ip->sin_addr.s_addr) {
+ fp->fh_sin = *fs->fs_ip;
+ fp->fh_sin.sin_port = 0;
+ fp->fh_nfs_version = fs->fs_version;
+ }
+ fp->fh_fs = dup_srvr(fs);
+ fp->fh_path = strdup(path);
+
+ error = call_mountd(fp, MOUNTPROC_MNT, got_nfs_fh, wchan);
+ if (error) {
+ /*
+ * Local error - cache for a short period
+ * just to prevent thrashing.
+ */
+ untimeout(fp->fh_cid);
+ fp->fh_cid = timeout(error < 0 ? 2 * ALLOWED_MOUNT_TIME : FH_TTL_ERROR,
+ discard_fh, (voidp) fp);
+ fp->fh_error = error;
+ } else {
+ error = fp->fh_error;
+ }
+
+ return error;
+}
+
+
+int
+make_nfs_auth(void)
+{
+ AUTH_CREATE_GIDLIST_TYPE group_wheel = 0;
+
+ /* Some NFS mounts (particularly cross-domain) require FQDNs to succeed */
+
+#ifdef HAVE_TRANSPORT_TYPE_TLI
+ if (gopt.flags & CFM_FULLY_QUALIFIED_HOSTS) {
+ plog(XLOG_INFO, "Using NFS auth for fqhn \"%s\"", hostd);
+ nfs_auth = authsys_create(hostd, 0, 0, 1, &group_wheel);
+ } else {
+ nfs_auth = authsys_create_default();
+ }
+#else /* not HAVE_TRANSPORT_TYPE_TLI */
+ if (gopt.flags & CFM_FULLY_QUALIFIED_HOSTS) {
+ plog(XLOG_INFO, "Using NFS auth for fqhn \"%s\"", hostd);
+ nfs_auth = authunix_create(hostd, 0, 0, 1, &group_wheel);
+ } else {
+ nfs_auth = authunix_create_default();
+ }
+#endif /* not HAVE_TRANSPORT_TYPE_TLI */
+
+ if (!nfs_auth)
+ return ENOBUFS;
+
+ return 0;
+}
+
+
+static int
+call_mountd(fh_cache *fp, u_long proc, fwd_fun f, voidp wchan)
+{
+ struct rpc_msg mnt_msg;
+ int len;
+ char iobuf[8192];
+ int error;
+ u_long mnt_version;
+
+ if (!nfs_auth) {
+ error = make_nfs_auth();
+ if (error)
+ return error;
+ }
+
+ if (fp->fh_sin.sin_port == 0) {
+ u_short port;
+ error = nfs_srvr_port(fp->fh_fs, &port, wchan);
+ if (error)
+ return error;
+ fp->fh_sin.sin_port = port;
+ }
+
+ /* find the right version of the mount protocol */
+#ifdef HAVE_FS_NFS3
+ if (fp->fh_nfs_version == NFS_VERSION3)
+ mnt_version = MOUNTVERS3;
+ else
+#endif /* HAVE_FS_NFS3 */
+ mnt_version = MOUNTVERS;
+ plog(XLOG_INFO, "call_mountd: NFS version %d, mount version %d",
+ fp->fh_nfs_version, mnt_version);
+
+ rpc_msg_init(&mnt_msg, MOUNTPROG, mnt_version, MOUNTPROC_NULL);
+ len = make_rpc_packet(iobuf,
+ sizeof(iobuf),
+ proc,
+ &mnt_msg,
+ (voidp) &fp->fh_path,
+ (XDRPROC_T_TYPE) xdr_nfspath,
+ nfs_auth);
+
+ if (len > 0) {
+ error = fwd_packet(MK_RPC_XID(RPC_XID_MOUNTD, fp->fh_id),
+ (voidp) iobuf,
+ len,
+ &fp->fh_sin,
+ &fp->fh_sin,
+ (voidp) ((long) fp->fh_id), /* for 64-bit archs */
+ f);
+ } else {
+ error = -len;
+ }
+
+/*
+ * It may be the case that we're sending to the wrong MOUNTD port. This
+ * occurs if mountd is restarted on the server after the port has been
+ * looked up and stored in the filehandle cache somewhere. The correct
+ * solution, if we're going to cache port numbers is to catch the ICMP
+ * port unreachable reply from the server and cause the portmap request
+ * to be redone. The quick solution here is to invalidate the MOUNTD
+ * port.
+ */
+ fp->fh_sin.sin_port = 0;
+
+ return error;
+}
+
+
+/*
+ * NFS needs the local filesystem, remote filesystem
+ * remote hostname.
+ * Local filesystem defaults to remote and vice-versa.
+ */
+char *
+nfs_match(am_opts *fo)
+{
+ char *xmtab;
+
+ if (fo->opt_fs && !fo->opt_rfs)
+ fo->opt_rfs = fo->opt_fs;
+ if (!fo->opt_rfs) {
+ plog(XLOG_USER, "nfs: no remote filesystem specified");
+ return NULL;
+ }
+ if (!fo->opt_rhost) {
+ plog(XLOG_USER, "nfs: no remote host specified");
+ return NULL;
+ }
+
+ /*
+ * Determine magic cookie to put in mtab
+ */
+ xmtab = (char *) xmalloc(strlen(fo->opt_rhost) + strlen(fo->opt_rfs) + 2);
+ sprintf(xmtab, "%s:%s", fo->opt_rhost, fo->opt_rfs);
+#ifdef DEBUG
+ dlog("NFS: mounting remote server \"%s\", remote fs \"%s\" on \"%s\"",
+ fo->opt_rhost, fo->opt_rfs, fo->opt_fs);
+#endif /* DEBUG */
+
+ return xmtab;
+}
+
+
+/*
+ * Initialize am structure for nfs
+ */
+int
+nfs_init(mntfs *mf)
+{
+ int error;
+ am_nfs_handle_t fhs;
+ char *colon;
+
+ if (mf->mf_private)
+ return 0;
+
+ colon = strchr(mf->mf_info, ':');
+ if (colon == 0)
+ return ENOENT;
+
+ error = prime_nfs_fhandle_cache(colon + 1, mf->mf_server, &fhs, (voidp) mf);
+ if (!error) {
+ mf->mf_private = (voidp) ALLOC(am_nfs_handle_t);
+ mf->mf_prfree = (void (*)(voidp)) free;
+ memmove(mf->mf_private, (voidp) &fhs, sizeof(fhs));
+ }
+ return error;
+}
+
+
+int
+mount_nfs_fh(am_nfs_handle_t *fhp, char *dir, char *fs_name, char *opts, mntfs *mf)
+{
+ MTYPE_TYPE type;
+ char *colon;
+ char *xopts;
+ char host[MAXHOSTNAMELEN + MAXPATHLEN + 2];
+ fserver *fs = mf->mf_server;
+ u_long nfs_version = fs->fs_version;
+ char *nfs_proto = fs->fs_proto; /* "tcp" or "udp" */
+ int error;
+ int genflags;
+ int retry;
+ mntent_t mnt;
+ nfs_args_t nfs_args;
+
+ /*
+ * Extract HOST name to give to kernel.
+ * Some systems like osf1/aix3/bsd44 variants may need old code
+ * for NFS_ARGS_NEEDS_PATH.
+ */
+ if (!(colon = strchr(fs_name, ':')))
+ return ENOENT;
+#ifdef MOUNT_TABLE_ON_FILE
+ *colon = '\0';
+#endif /* MOUNT_TABLE_ON_FILE */
+ strncpy(host, fs_name, sizeof(host));
+#ifdef MOUNT_TABLE_ON_FILE
+ *colon = ':';
+#endif /* MOUNT_TABLE_ON_FILE */
+#ifdef MAXHOSTNAMELEN
+ /* most kernels have a name length restriction */
+ if (strlen(host) >= MAXHOSTNAMELEN)
+ strcpy(host + MAXHOSTNAMELEN - 3, "..");
+#endif /* MAXHOSTNAMELEN */
+
+ if (mf->mf_remopts && *mf->mf_remopts && !islocalnet(fs->fs_ip->sin_addr.s_addr))
+ xopts = strdup(mf->mf_remopts);
+ else
+ xopts = strdup(opts);
+
+ memset((voidp) &mnt, 0, sizeof(mnt));
+ mnt.mnt_dir = dir;
+ mnt.mnt_fsname = fs_name;
+ mnt.mnt_opts = xopts;
+
+ /*
+ * Set mount types accordingly
+ */
+#ifndef HAVE_FS_NFS3
+ type = MOUNT_TYPE_NFS;
+ mnt.mnt_type = MNTTAB_TYPE_NFS;
+#else /* HAVE_FS_NFS3 */
+ if (nfs_version == NFS_VERSION3) {
+ type = MOUNT_TYPE_NFS3;
+ /*
+ * Systems that include the mount table "vers" option generally do not
+ * set the mnttab entry to "nfs3", but to "nfs" and then they set
+ * "vers=3". Setting it to "nfs3" works, but it may break some things
+ * like "df -t nfs" and the "quota" program (esp. on Solaris and Irix).
+ * So on those systems, set it to "nfs".
+ * Note: MNTTAB_OPT_VERS is always set for NFS3 (see am_compat.h).
+ */
+# if defined(MNTTAB_OPT_VERS) && defined(MOUNT_TABLE_ON_FILE)
+ mnt.mnt_type = MNTTAB_TYPE_NFS;
+# else /* defined(MNTTAB_OPT_VERS) && defined(MOUNT_TABLE_ON_FILE) */
+ mnt.mnt_type = MNTTAB_TYPE_NFS3;
+# endif /* defined(MNTTAB_OPT_VERS) && defined(MOUNT_TABLE_ON_FILE) */
+ } else {
+ type = MOUNT_TYPE_NFS;
+ mnt.mnt_type = MNTTAB_TYPE_NFS;
+ }
+#endif /* HAVE_FS_NFS3 */
+ plog(XLOG_INFO, "mount_nfs_fh: NFS version %d", nfs_version);
+#if defined(HAVE_FS_NFS3) || defined(HAVE_TRANSPORT_TYPE_TLI)
+ plog(XLOG_INFO, "mount_nfs_fh: using NFS transport %s", nfs_proto);
+#endif /* defined(HAVE_FS_NFS3) || defined(HAVE_TRANSPORT_TYPE_TLI) */
+
+ retry = hasmntval(&mnt, MNTTAB_OPT_RETRY);
+ if (retry <= 0)
+ retry = 1; /* XXX */
+
+ genflags = compute_mount_flags(&mnt);
+
+ /* setup the many fields and flags within nfs_args */
+#ifdef HAVE_TRANSPORT_TYPE_TLI
+ compute_nfs_args(&nfs_args,
+ &mnt,
+ genflags,
+ NULL, /* struct netconfig *nfsncp */
+ fs->fs_ip,
+ nfs_version,
+ nfs_proto,
+ fhp,
+ host,
+ fs_name);
+#else /* not HAVE_TRANSPORT_TYPE_TLI */
+ compute_nfs_args(&nfs_args,
+ &mnt,
+ genflags,
+ fs->fs_ip,
+ nfs_version,
+ nfs_proto,
+ fhp,
+ host,
+ fs_name);
+#endif /* not HAVE_TRANSPORT_TYPE_TLI */
+
+ /* finally call the mounting function */
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ print_nfs_args(&nfs_args, nfs_version);
+#endif /* DEBUG */
+ error = mount_fs(&mnt, genflags, (caddr_t) &nfs_args, retry, type,
+ nfs_version, nfs_proto, mnttab_file_name);
+ XFREE(xopts);
+
+#ifdef HAVE_TRANSPORT_TYPE_TLI
+ free_knetconfig(nfs_args.knconf);
+ if (nfs_args.addr)
+ XFREE(nfs_args.addr); /* allocated in compute_nfs_args() */
+#endif /* HAVE_TRANSPORT_TYPE_TLI */
+
+ return error;
+}
+
+
+static int
+mount_nfs(char *dir, char *fs_name, char *opts, mntfs *mf)
+{
+ if (!mf->mf_private) {
+ plog(XLOG_ERROR, "Missing filehandle for %s", fs_name);
+ return EINVAL;
+ }
+
+ return mount_nfs_fh((am_nfs_handle_t *) mf->mf_private, dir, fs_name, opts, mf);
+}
+
+
+int
+nfs_fmount(mntfs *mf)
+{
+ int error = 0;
+
+ error = mount_nfs(mf->mf_mount, mf->mf_info, mf->mf_mopts, mf);
+
+#ifdef DEBUG
+ if (error) {
+ errno = error;
+ dlog("mount_nfs: %m");
+ }
+#endif /* DEBUG */
+
+ return error;
+}
+
+
+int
+nfs_fumount(mntfs *mf)
+{
+ int error = UMOUNT_FS(mf->mf_mount, mnttab_file_name);
+
+ /*
+ * Here is some code to unmount 'restarted' file systems.
+ * The restarted file systems are marked as 'nfs', not
+ * 'host', so we only have the map information for the
+ * the top-level mount. The unmount will fail (EBUSY)
+ * if there are anything else from the NFS server mounted
+ * below the mount-point. This code checks to see if there
+ * is anything mounted with the same prefix as the
+ * file system to be unmounted ("/a/b/c" when unmounting "/a/b").
+ * If there is, and it is a 'restarted' file system, we unmount
+ * it.
+ * Added by Mike Mitchell, mcm@unx.sas.com, 09/08/93
+ */
+ if (error == EBUSY) {
+ mntfs *new_mf;
+ int len = strlen(mf->mf_mount);
+ int didsome = 0;
+
+ ITER(new_mf, mntfs, &mfhead) {
+ if (new_mf->mf_ops != mf->mf_ops ||
+ new_mf->mf_refc > 1 ||
+ mf == new_mf ||
+ ((new_mf->mf_flags & (MFF_MOUNTED | MFF_UNMOUNTING | MFF_RESTART)) == (MFF_MOUNTED | MFF_RESTART)))
+ continue;
+
+ if (NSTREQ(mf->mf_mount, new_mf->mf_mount, len) &&
+ new_mf->mf_mount[len] == '/') {
+ UMOUNT_FS(new_mf->mf_mount, mnttab_file_name);
+ didsome = 1;
+ }
+ }
+ if (didsome)
+ error = UMOUNT_FS(mf->mf_mount, mnttab_file_name);
+ }
+ if (error)
+ return error;
+
+ return 0;
+}
+
+
+void
+nfs_umounted(am_node *mp)
+{
+ /*
+ * Don't bother to inform remote mountd that we are finished. Until a
+ * full track of filehandles is maintained the mountd unmount callback
+ * cannot be done correctly anyway...
+ */
+ mntfs *mf = mp->am_mnt;
+ fserver *fs;
+ char *colon, *path;
+
+ if (mf->mf_error || mf->mf_refc > 1)
+ return;
+
+ fs = mf->mf_server;
+
+ /*
+ * Call the mount daemon on the server to announce that we are not using
+ * the fs any more.
+ *
+ * This is *wrong*. The mountd should be called when the fhandle is
+ * flushed from the cache, and a reference held to the cached entry while
+ * the fs is mounted...
+ */
+ colon = path = strchr(mf->mf_info, ':');
+ if (fs && colon) {
+ fh_cache f;
+
+#ifdef DEBUG
+ dlog("calling mountd for %s", mf->mf_info);
+#endif /* DEBUG */
+ *path++ = '\0';
+ f.fh_path = path;
+ f.fh_sin = *fs->fs_ip;
+ f.fh_sin.sin_port = (u_short) 0;
+ f.fh_nfs_version = fs->fs_version;
+ f.fh_fs = fs;
+ f.fh_id = 0;
+ f.fh_error = 0;
+ prime_nfs_fhandle_cache(colon + 1, mf->mf_server, (am_nfs_handle_t *) 0, (voidp) mf);
+ call_mountd(&f, MOUNTPROC_UMNT, (fwd_fun) 0, (voidp) 0);
+ *colon = ':';
+ }
+}
diff --git a/contrib/amd/amd/ops_nfs3.c b/contrib/amd/amd/ops_nfs3.c
new file mode 100644
index 000000000000..5db0713dbd49
--- /dev/null
+++ b/contrib/amd/amd/ops_nfs3.c
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: ops_nfs3.c,v 5.2.2.3 1992/08/02 10:42:21 jsp Exp $
+ *
+ */
+
+/*
+ * Network file system version 3.0
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+/* FEEL FREE TO INPLEMENT THIS... :-) */
diff --git a/contrib/amd/amd/ops_nullfs.c b/contrib/amd/amd/ops_nullfs.c
new file mode 100644
index 000000000000..bf2009f87b46
--- /dev/null
+++ b/contrib/amd/amd/ops_nullfs.c
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: ops_nullfs.c,v 5.2.2.3 1992/08/02 10:42:21 jsp Exp $
+ *
+ */
+
+/*
+ * The null filesystem in BSD-4.4 is similar to the loopback one.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+/* FEEL FREE TO INPLEMENT THIS... :-) */
diff --git a/contrib/amd/amd/ops_pcfs.c b/contrib/amd/amd/ops_pcfs.c
new file mode 100644
index 000000000000..e46b7112aff8
--- /dev/null
+++ b/contrib/amd/amd/ops_pcfs.c
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: ops_pcfs.c,v 5.2.2.1 1992/02/09 15:09:08 jsp beta $
+ *
+ */
+
+/*
+ * PC (MS-DOS) file system
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+/* forward definitions */
+static char *pcfs_match(am_opts *fo);
+static int pcfs_fmount(mntfs *mf);
+static int pcfs_fumount(mntfs *mf);
+
+/*
+ * Ops structure
+ */
+am_ops pcfs_ops =
+{
+ "pcfs",
+ pcfs_match,
+ 0, /* pcfs_init */
+ amfs_auto_fmount,
+ pcfs_fmount,
+ amfs_auto_fumount,
+ pcfs_fumount,
+ amfs_error_lookuppn,
+ amfs_error_readdir,
+ 0, /* pcfs_readlink */
+ 0, /* pcfs_mounted */
+ 0, /* pcfs_umounted */
+ find_amfs_auto_srvr,
+ FS_MKMNT | FS_UBACKGROUND | FS_AMQINFO
+};
+
+
+
+/*
+ * PCFS needs remote filesystem.
+ */
+static char *
+pcfs_match(am_opts *fo)
+{
+ if (!fo->opt_dev) {
+ plog(XLOG_USER, "pcfs: no source device specified");
+ return 0;
+ }
+#ifdef DEBUG
+ dlog("PCFS: mounting device \"%s\" on \"%s\"", fo->opt_dev, fo->opt_fs);
+#endif /* DEBUG */
+
+ /*
+ * Determine magic cookie to put in mtab
+ */
+ return strdup(fo->opt_dev);
+}
+
+
+static int
+mount_pcfs(char *dir, char *fs_name, char *opts)
+{
+ pcfs_args_t pcfs_args;
+ mntent_t mnt;
+ int flags;
+
+ /*
+ * Figure out the name of the file system type.
+ */
+ MTYPE_TYPE type = MOUNT_TYPE_PCFS;
+
+ memset((voidp) &pcfs_args, 0, sizeof(pcfs_args)); /* Paranoid */
+
+ /*
+ * Fill in the mount structure
+ */
+ memset((voidp) &mnt, 0, sizeof(mnt));
+ mnt.mnt_dir = dir;
+ mnt.mnt_fsname = fs_name;
+ mnt.mnt_type = MNTTAB_TYPE_PCFS;
+ mnt.mnt_opts = opts;
+
+ flags = compute_mount_flags(&mnt);
+
+#ifdef HAVE_FIELD_PCFS_ARGS_T_FSPEC
+ pcfs_args.fspec = fs_name;
+#endif /* HAVE_FIELD_PCFS_ARGS_T_FSPEC */
+
+#ifdef HAVE_FIELD_PCFS_ARGS_T_MASK
+ pcfs_args.mask = 0777; /* this may be the msdos file modes */
+#endif /* HAVE_FIELD_PCFS_ARGS_T_MASK */
+
+#ifdef HAVE_FIELD_PCFS_ARGS_T_UID
+ pcfs_args.uid = 0; /* root */
+#endif /* HAVE_FIELD_PCFS_ARGS_T_UID */
+
+#ifdef HAVE_FIELD_PCFS_ARGS_T_GID
+ pcfs_args.gid = 0; /* wheel */
+#endif /* HAVE_FIELD_PCFS_ARGS_T_GID */
+
+#ifdef HAVE_FIELD_PCFS_ARGS_T_SECONDSWEST
+ pcfs_args.secondswest = 0; /* XXX: fill in correct values */
+#endif /* HAVE_FIELD_PCFS_ARGS_T_SECONDSWEST */
+#ifdef HAVE_FIELD_PCFS_ARGS_T_DSTTIME
+ pcfs_args.dsttime = 0; /* XXX: fill in correct values */
+#endif /* HAVE_FIELD_PCFS_ARGS_T_DSTTIME */
+
+ /*
+ * Call generic mount routine
+ */
+ return mount_fs(&mnt, flags, (caddr_t) & pcfs_args, 0, type, 0, NULL, mnttab_file_name);
+}
+
+
+static int
+pcfs_fmount(mntfs *mf)
+{
+ int error;
+
+ error = mount_pcfs(mf->mf_mount, mf->mf_info, mf->mf_mopts);
+ if (error) {
+ errno = error;
+ plog(XLOG_ERROR, "mount_pcfs: %m");
+ return error;
+ }
+
+ return 0;
+}
+
+
+static int
+pcfs_fumount(mntfs *mf)
+{
+ return UMOUNT_FS(mf->mf_mount, mnttab_file_name);
+}
diff --git a/contrib/amd/amd/ops_tfs.c b/contrib/amd/amd/ops_tfs.c
new file mode 100644
index 000000000000..97cd18c23217
--- /dev/null
+++ b/contrib/amd/amd/ops_tfs.c
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: ops_tfs.c,v 5.2.2.3 1992/08/02 10:42:21 jsp Exp $
+ *
+ */
+
+/*
+ * Translucent file system
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+/* FEEL FREE TO INPLEMENT THIS... :-) */
diff --git a/contrib/amd/amd/ops_tmpfs.c b/contrib/amd/amd/ops_tmpfs.c
new file mode 100644
index 000000000000..ce1b4fd3948f
--- /dev/null
+++ b/contrib/amd/amd/ops_tmpfs.c
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: ops_tmpfs.c,v 5.2.2.3 1992/08/02 10:42:21 jsp Exp $
+ *
+ */
+
+/*
+ * TMPFS file system (combines RAM-fs and swap-fs)
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+/* FEEL FREE TO INPLEMENT THIS... :-) */
diff --git a/contrib/amd/amd/ops_ufs.c b/contrib/amd/amd/ops_ufs.c
new file mode 100644
index 000000000000..9883af156c35
--- /dev/null
+++ b/contrib/amd/amd/ops_ufs.c
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: ops_ufs.c,v 5.2.2.1 1992/02/09 15:09:08 jsp beta $
+ *
+ */
+
+/*
+ * UN*X file system
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+/* forward declarations */
+static char *ufs_match(am_opts *fo);
+static int ufs_fmount(mntfs *mf);
+static int ufs_fumount(mntfs *mf);
+
+/*
+ * Ops structure
+ */
+am_ops ufs_ops =
+{
+ "ufs",
+ ufs_match,
+ 0, /* ufs_init */
+ amfs_auto_fmount,
+ ufs_fmount,
+ amfs_auto_fumount,
+ ufs_fumount,
+ amfs_error_lookuppn,
+ amfs_error_readdir,
+ 0, /* ufs_readlink */
+ 0, /* ufs_mounted */
+ 0, /* ufs_umounted */
+ find_amfs_auto_srvr,
+ FS_MKMNT | FS_NOTIMEOUT | FS_UBACKGROUND | FS_AMQINFO
+};
+
+
+/*
+ * UFS needs local filesystem and device.
+ */
+static char *
+ufs_match(am_opts *fo)
+{
+
+ if (!fo->opt_dev) {
+ plog(XLOG_USER, "ufs: no device specified");
+ return 0;
+ }
+
+#ifdef DEBUG
+ dlog("UFS: mounting device \"%s\" on \"%s\"", fo->opt_dev, fo->opt_fs);
+#endif /* DEBUG */
+
+ /*
+ * Determine magic cookie to put in mtab
+ */
+ return strdup(fo->opt_dev);
+}
+
+
+static int
+mount_ufs(char *dir, char *fs_name, char *opts)
+{
+ ufs_args_t ufs_args;
+ mntent_t mnt;
+ int genflags;
+
+ /*
+ * Figure out the name of the file system type.
+ */
+ MTYPE_TYPE type = MOUNT_TYPE_UFS;
+
+ memset((voidp) &ufs_args, 0, sizeof(ufs_args)); /* Paranoid */
+
+ /*
+ * Fill in the mount structure
+ */
+ memset((voidp) &mnt, 0, sizeof(mnt));
+ mnt.mnt_dir = dir;
+ mnt.mnt_fsname = fs_name;
+ mnt.mnt_type = MNTTAB_TYPE_UFS;
+ mnt.mnt_opts = opts;
+
+ genflags = compute_mount_flags(&mnt);
+
+#ifdef HAVE_FIELD_UFS_ARGS_T_FLAGS
+ ufs_args.flags = genflags; /* XXX: is this correct? */
+#endif /* HAVE_FIELD_UFS_ARGS_T_FLAGS */
+
+#ifdef HAVE_FIELD_UFS_ARGS_T_UFS_FLAGS
+ ufs_args.ufs_flags = genflags; /* XXX: is this correct? */
+#endif /* HAVE_FIELD_UFS_ARGS_T_UFS_FLAGS */
+
+#ifdef HAVE_FIELD_UFS_ARGS_T_FSPEC
+ ufs_args.fspec = fs_name;
+#endif /* HAVE_FIELD_UFS_ARGS_T_FSPEC */
+
+#ifdef HAVE_FIELD_UFS_ARGS_T_UFS_PGTHRESH
+ ufs_args.ufs_pgthresh = hasmntval(&mnt, MNTTAB_OPT_PGTHRESH);
+#endif /* HAVE_FIELD_UFS_ARGS_T_UFS_PGTHRESH */
+
+ /*
+ * Call generic mount routine
+ */
+ return mount_fs(&mnt, genflags, (caddr_t) &ufs_args, 0, type, 0, NULL, mnttab_file_name);
+}
+
+
+static int
+ufs_fmount(mntfs *mf)
+{
+ int error;
+
+ error = mount_ufs(mf->mf_mount, mf->mf_info, mf->mf_mopts);
+ if (error) {
+ errno = error;
+ plog(XLOG_ERROR, "mount_ufs: %m");
+ return error;
+ }
+
+ return 0;
+}
+
+
+static int
+ufs_fumount(mntfs *mf)
+{
+ return UMOUNT_FS(mf->mf_mount, mnttab_file_name);
+}
diff --git a/contrib/amd/amd/ops_umapfs.c b/contrib/amd/amd/ops_umapfs.c
new file mode 100644
index 000000000000..b2dcd7258b63
--- /dev/null
+++ b/contrib/amd/amd/ops_umapfs.c
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: ops_umapfs.c,v 5.2.2.3 1992/08/02 10:42:21 jsp Exp $
+ *
+ */
+
+/*
+ * uid/gid mapping filesystem.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+/* FEEL FREE TO INPLEMENT THIS... :-) */
diff --git a/contrib/amd/amd/ops_unionfs.c b/contrib/amd/amd/ops_unionfs.c
new file mode 100644
index 000000000000..24f7e1f1cdd8
--- /dev/null
+++ b/contrib/amd/amd/ops_unionfs.c
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: ops_unionfs.c,v 5.2.2.3 1992/08/02 10:42:21 jsp Exp $
+ *
+ */
+
+/*
+ * Union filesystem (ala BSD-4.4)
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+/* FEEL FREE TO INPLEMENT THIS... :-) */
diff --git a/contrib/amd/amd/ops_xfs.c b/contrib/amd/amd/ops_xfs.c
new file mode 100644
index 000000000000..1b4aab4d89a2
--- /dev/null
+++ b/contrib/amd/amd/ops_xfs.c
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: ops_xfs.c,v 5.2.2.1 1992/02/09 15:09:08 jsp beta $
+ *
+ */
+
+/*
+ * Irix UN*X file system: XFS (Extended File System)
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+/* forward declarations */
+static char * xfs_match(am_opts *fo);
+static int xfs_fmount(mntfs *mf);
+static int xfs_fumount(mntfs *mf);
+
+/*
+ * Ops structure
+ */
+am_ops xfs_ops =
+{
+ "xfs",
+ xfs_match,
+ 0, /* xfs_init */
+ amfs_auto_fmount,
+ xfs_fmount,
+ amfs_auto_fumount,
+ xfs_fumount,
+ amfs_error_lookuppn,
+ amfs_error_readdir,
+ 0, /* xfs_readlink */
+ 0, /* xfs_mounted */
+ 0, /* xfs_umounted */
+ find_amfs_auto_srvr,
+ FS_MKMNT | FS_NOTIMEOUT | FS_UBACKGROUND | FS_AMQINFO
+};
+
+
+/*
+ * XFS needs local filesystem and device.
+ */
+static char *
+xfs_match(am_opts *fo)
+{
+
+ if (!fo->opt_dev) {
+ plog(XLOG_USER, "xfs: no device specified");
+ return 0;
+ }
+
+#ifdef DEBUG
+ dlog("XFS: mounting device \"%s\" on \"%s\"", fo->opt_dev, fo->opt_fs);
+#endif /* DEBUG */
+
+ /*
+ * Determine magic cookie to put in mtab
+ */
+ return strdup(fo->opt_dev);
+}
+
+
+static int
+mount_xfs(char *dir, char *fs_name, char *opts)
+{
+ xfs_args_t xfs_args;
+ mntent_t mnt;
+ int flags;
+
+ /*
+ * Figure out the name of the file system type.
+ */
+ MTYPE_TYPE type = MOUNT_TYPE_XFS;
+
+ memset((voidp) &xfs_args, 0, sizeof(xfs_args)); /* Paranoid */
+
+ /*
+ * Fill in the mount structure
+ */
+ memset((voidp) &mnt, 0, sizeof(mnt));
+ mnt.mnt_dir = dir;
+ mnt.mnt_fsname = fs_name;
+ mnt.mnt_type = MNTTAB_TYPE_XFS;
+ mnt.mnt_opts = opts;
+
+ flags = compute_mount_flags(&mnt);
+
+#ifdef HAVE_FIELD_XFS_ARGS_T_FLAGS
+ xfs_args.flags = 0; /* XXX: fix this to correct flags */
+#endif /* HAVE_FIELD_XFS_ARGS_T_FLAGS */
+#ifdef HAVE_FIELD_XFS_ARGS_T_FSPEC
+ xfs_args.fspec = fs_name;
+#endif /* HAVE_FIELD_XFS_ARGS_T_FSPEC */
+
+ /*
+ * Call generic mount routine
+ */
+ return mount_fs(&mnt, flags, (caddr_t) &xfs_args, 0, type, 0, NULL, mnttab_file_name);
+}
+
+
+static int
+xfs_fmount(mntfs *mf)
+{
+ int error;
+
+ error = mount_xfs(mf->mf_mount, mf->mf_info, mf->mf_mopts);
+ if (error) {
+ errno = error;
+ plog(XLOG_ERROR, "mount_xfs: %m");
+ return error;
+ }
+
+ return 0;
+}
+
+
+static int
+xfs_fumount(mntfs *mf)
+{
+ return UMOUNT_FS(mf->mf_mount, mnttab_file_name);
+}
diff --git a/contrib/amd/amd/opts.c b/contrib/amd/amd/opts.c
new file mode 100644
index 000000000000..294cdb799a4a
--- /dev/null
+++ b/contrib/amd/amd/opts.c
@@ -0,0 +1,1304 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: opts.c,v 5.2.2.4 1992/08/02 10:42:21 jsp Exp $
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+/*
+ * MACROS:
+ */
+#define NLEN 16 /* Length of longest option name (conservative) */
+#define S(x) (x) , (sizeof(x)-1)
+/*
+ * The BUFSPACE macros checks that there is enough space
+ * left in the expansion buffer. If there isn't then we
+ * give up completely. This is done to avoid crashing the
+ * automounter itself (which would be a bad thing to do).
+ */
+#define BUFSPACE(ep, len) (((ep) + (len)) < expbuf+MAXPATHLEN)
+
+/*
+ * TYPEDEFS:
+ */
+typedef int (*IntFuncPtr) (char *);
+typedef struct opt_apply opt_apply;
+enum vs_opt { SelEQ, SelNE, VarAss };
+
+/*
+ * STRUCTURES
+ */
+struct opt {
+ char *name; /* Name of the option */
+ int nlen; /* Length of option name */
+ char **optp; /* Pointer to option value string */
+ char **sel_p; /* Pointer to selector value string */
+ int (*fxn_p)(char *); /* Pointer to boolean function */
+ int case_insensitive; /* How to do selector comparisons */
+};
+
+struct opt_apply {
+ char **opt;
+ char *val;
+};
+
+struct functable {
+ char *name;
+ IntFuncPtr func;
+};
+
+/*
+ * FORWARD DEFINITSION:
+ */
+static int f_in_network(char *);
+static int f_netgrp(char *);
+static int f_exists(char *);
+static int f_false(char *);
+static int f_true(char *);
+
+/*
+ * STATICS:
+ */
+static struct am_opts fs_static; /* copy of the options to play with */
+static char NullStr[] = "<NULL>";
+static char nullstr[] = "";
+static char *opt_dkey = NullStr;
+static char *opt_host = hostname;
+static char *opt_hostd = hostd;
+static char *opt_key = nullstr;
+static char *opt_keyd = nullstr;
+static char *opt_map = nullstr;
+static char *opt_path = nullstr;
+static char *vars[8];
+
+
+/*
+ * Options in something corresponding to frequency of use so that
+ * first-match algorithm is sped up.
+ */
+static struct opt opt_fields[] = {
+ /* Name and length.
+ Option str. Selector str. boolean fxn. flags */
+ { S("opts"),
+ &fs_static.opt_opts, 0, 0, FALSE },
+ { S("host"),
+ 0, &opt_host, 0, TRUE },
+ { S("hostd"),
+ 0, &opt_hostd, 0, TRUE },
+ { S("type"),
+ &fs_static.opt_type, 0, 0, FALSE },
+ { S("rhost"),
+ &fs_static.opt_rhost, 0, 0, TRUE },
+ { S("rfs"),
+ &fs_static.opt_rfs, 0, 0, FALSE },
+ { S("fs"),
+ &fs_static.opt_fs, 0, 0, FALSE },
+ { S("key"),
+ 0, &opt_key, 0, FALSE },
+ { S("map"),
+ 0, &opt_map, 0, FALSE },
+ { S("sublink"),
+ &fs_static.opt_sublink, 0, 0, FALSE },
+ { S("arch"),
+ 0, &gopt.arch, 0, TRUE },
+ { S("dev"),
+ &fs_static.opt_dev, 0, 0, FALSE },
+ { S("pref"),
+ &fs_static.opt_pref, 0, 0, FALSE },
+ { S("autopref"),
+ &fs_static.opt_autopref,0, 0, FALSE },
+ { S("path"),
+ 0, &opt_path, 0, FALSE },
+ { S("autodir"),
+ 0, &gopt.auto_dir, 0, FALSE },
+ { S("delay"),
+ &fs_static.opt_delay, 0, 0, FALSE },
+ { S("domain"),
+ 0, &hostdomain, 0, TRUE },
+ { S("karch"),
+ 0, &gopt.karch, 0, TRUE },
+ { S("cluster"),
+ 0, &gopt.cluster, 0, TRUE },
+ { S("wire"),
+ 0, 0, f_in_network, TRUE },
+ { S("network"),
+ 0, 0, f_in_network, TRUE },
+ { S("netnumber"),
+ 0, 0, f_in_network, TRUE },
+ { S("byte"),
+ 0, &endian, 0, TRUE },
+ { S("os"),
+ 0, &gopt.op_sys, 0, TRUE },
+ { S("osver"),
+ 0, &gopt.op_sys_ver, 0, TRUE },
+ { S("remopts"),
+ &fs_static.opt_remopts, 0, 0, FALSE },
+ { S("mount"),
+ &fs_static.opt_mount, 0, 0, FALSE },
+ { S("unmount"),
+ &fs_static.opt_unmount, 0, 0, FALSE },
+ { S("cache"),
+ &fs_static.opt_cache, 0, 0, FALSE },
+ { S("user"),
+ &fs_static.opt_user, 0, 0, FALSE },
+ { S("group"),
+ &fs_static.opt_group, 0, 0, FALSE },
+ { S(".key"),
+ 0, &opt_dkey, 0, FALSE },
+ { S("key."),
+ 0, &opt_keyd, 0, FALSE },
+ { S("maptype"),
+ &fs_static.opt_maptype, 0, 0, FALSE },
+ { S("cachedir"),
+ &fs_static.opt_cachedir, 0, 0, FALSE },
+ { S("addopts"),
+ &fs_static.opt_addopts, 0, 0, FALSE },
+ { S("var0"),
+ &vars[0], 0, 0, FALSE },
+ { S("var1"),
+ &vars[1], 0, 0, FALSE },
+ { S("var2"),
+ &vars[2], 0, 0, FALSE },
+ { S("var3"),
+ &vars[3], 0, 0, FALSE },
+ { S("var4"),
+ &vars[4], 0, 0, FALSE },
+ { S("var5"),
+ &vars[5], 0, 0, FALSE },
+ { S("var6"),
+ &vars[6], 0, 0, FALSE },
+ { S("var7"),
+ &vars[7], 0, 0, FALSE },
+ { 0, 0, 0, 0, 0, FALSE },
+};
+
+static struct functable functable[] = {
+ { "in_network", f_in_network },
+ { "netgrp", f_netgrp },
+ { "exists", f_exists },
+ { "false", f_false },
+ { "true", f_true },
+ { 0, 0 },
+};
+
+/*
+ * Specially expand the remote host name first
+ */
+static opt_apply rhost_expansion[] =
+{
+ {&fs_static.opt_rhost, "${host}"},
+ {0, 0},
+};
+
+/*
+ * List of options which need to be expanded
+ * Note that the order here _may_ be important.
+ */
+static opt_apply expansions[] =
+{
+ {&fs_static.opt_sublink, 0},
+ {&fs_static.opt_rfs, "${path}"},
+ {&fs_static.opt_fs, "${autodir}/${rhost}${rfs}"},
+ {&fs_static.opt_opts, "rw"},
+ {&fs_static.opt_remopts, "${opts}"},
+ {&fs_static.opt_mount, 0},
+ {&fs_static.opt_unmount, 0},
+ {&fs_static.opt_cachedir, 0},
+ {&fs_static.opt_addopts, 0},
+ {0, 0},
+};
+
+/*
+ * List of options which need to be free'ed before re-use
+ */
+static opt_apply to_free[] =
+{
+ {&fs_static.fs_glob, 0},
+ {&fs_static.fs_local, 0},
+ {&fs_static.fs_mtab, 0},
+ {&fs_static.opt_sublink, 0},
+ {&fs_static.opt_rfs, 0},
+ {&fs_static.opt_fs, 0},
+ {&fs_static.opt_rhost, 0},
+ {&fs_static.opt_opts, 0},
+ {&fs_static.opt_remopts, 0},
+ {&fs_static.opt_mount, 0},
+ {&fs_static.opt_unmount, 0},
+ {&fs_static.opt_cachedir, 0},
+ {&fs_static.opt_addopts, 0},
+ {&vars[0], 0},
+ {&vars[1], 0},
+ {&vars[2], 0},
+ {&vars[3], 0},
+ {&vars[4], 0},
+ {&vars[5], 0},
+ {&vars[6], 0},
+ {&vars[7], 0},
+ {0, 0},
+};
+
+
+/*
+ * expand backslash escape sequences
+ */
+static char
+backslash(char **p)
+{
+ char c;
+
+ if ((*p)[1] == '\0') {
+ plog(XLOG_USER, "Empty backslash escape");
+ return **p;
+ }
+
+ if (**p == '\\') {
+ (*p)++;
+ switch (**p) {
+ case 'a':
+ c = '\007'; /* Bell */
+ break;
+ case 'b':
+ c = '\010'; /* Backspace */
+ break;
+ case 't':
+ c = '\011'; /* Horizontal Tab */
+ break;
+ case 'n':
+ c = '\012'; /* New Line */
+ break;
+ case 'v':
+ c = '\013'; /* Vertical Tab */
+ break;
+ case 'f':
+ c = '\014'; /* Form Feed */
+ break;
+ case 'r':
+ c = '\015'; /* Carriage Return */
+ break;
+ case 'e':
+ c = '\033'; /* Escape */
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ {
+ int cnt, val, ch;
+
+ for (cnt = 0, val = 0; cnt < 3; cnt++) {
+ ch = *(*p)++;
+ if (ch < '0' || ch > '7') {
+ (*p)--;
+ break;
+ }
+ val = (val << 3) | (ch - '0');
+ }
+
+ if ((val & 0xffffff00) != 0)
+ plog(XLOG_USER,
+ "Too large character constant %u\n",
+ val);
+ c = (char) val;
+ --(*p);
+ }
+ break;
+
+ default:
+ c = **p;
+ break;
+ }
+ } else
+ c = **p;
+
+ return c;
+}
+
+
+/*
+ * Skip to next option in the string
+ */
+static char *
+opt(char **p)
+{
+ char *cp = *p;
+ char *dp = cp;
+ char *s = cp;
+
+top:
+ while (*cp && *cp != ';') {
+ if (*cp == '"') {
+ /*
+ * Skip past string
+ */
+ for (cp++; *cp && *cp != '"'; cp++)
+ if (*cp == '\\')
+ *dp++ = backslash(&cp);
+ else
+ *dp++ = *cp;
+ if (*cp)
+ cp++;
+ } else {
+ *dp++ = *cp++;
+ }
+ }
+
+ /*
+ * Skip past any remaining ';'s
+ */
+ while (*cp == ';')
+ cp++;
+
+ /*
+ * If we have a zero length string
+ * and there are more fields, then
+ * parse the next one. This allows
+ * sequences of empty fields.
+ */
+ if (*cp && dp == s)
+ goto top;
+
+ *dp = '\0';
+
+ *p = cp;
+ return s;
+}
+
+
+/*
+ * These routines add a new style of selector; function-style boolean
+ * operators. To add new ones, just define functions as in true, false,
+ * exists (below) and add them to the functable, above.
+ *
+ * Usage example: Some people have X11R5 local, some go to a server. I do
+ * this:
+ *
+ * * exists(/usr/pkg/${key});type:=link;fs:=/usr/pkg/${key} || \
+ * -type:=nfs;rfs=/usr/pkg/${key} \
+ * rhost:=server1 \
+ * rhost:=server2
+ *
+ * -Rens Troost <rens@imsi.com>
+ */
+static IntFuncPtr
+functable_lookup(char *key)
+{
+ struct functable *fp;
+
+ for (fp = functable; fp->name; fp++)
+ if (FSTREQ(fp->name, key))
+ return (fp->func);
+ return (IntFuncPtr) NULL;
+}
+
+
+static int
+eval_opts(char *opts, char *mapkey)
+{
+ /*
+ * Fill in the global structure fs_static by
+ * cracking the string opts. opts may be
+ * scribbled on at will.
+ */
+ char *o = opts;
+ char *f;
+
+ /*
+ * For each user-specified option
+ */
+ while (*(f = opt(&o))) {
+ struct opt *op;
+ enum vs_opt vs_opt = VarAss;
+ char *eq = strchr(f, '=');
+ char *fx;
+ IntFuncPtr func;
+ char *opt = NULL;
+
+ if (!eq || eq[1] == '\0' || eq == f) {
+ /*
+ * No value, is it a function call?
+ */
+ char *arg = strchr(f, '(');
+
+ if (!arg || arg[1] == '\0' || arg == f) {
+ /*
+ * No, just continue
+ */
+ plog(XLOG_USER, "key %s: No value component in \"%s\"", mapkey, f);
+ continue;
+ }
+
+ /* null-terminate the argument */
+ *arg++ = '\0';
+ fx = strchr(arg, ')');
+ if (!arg || fx == arg) {
+ plog(XLOG_USER, "key %s: Malformed function in \"%s\"", mapkey, f);
+ continue;
+ }
+ *fx = '\0';
+
+ /*
+ * look up f in functable and pass it arg.
+ * func must return 0 on failure, and 1 on success.
+ */
+ if ((func = functable_lookup(f))) {
+ if (!(*func) (arg)) {
+ return (0);
+ }
+ continue;
+ } else if (NSTREQ(f, "!", 1) && (func = functable_lookup(&f[1]))) {
+ /* then this is a negated prefixed function such as "!exists" */
+ plog(XLOG_USER, "executing negated function %s", &f[1]);
+ if ((*func) (arg)) {
+ return (0);
+ }
+ continue;
+ } else {
+ plog(XLOG_USER, "key %s: unknown function \"%s\"", mapkey, f);
+ return (0);
+ }
+
+ }
+
+ /*
+ * Check what type of operation is happening
+ * !=, =! is SelNE
+ * == is SelEQ
+ * := is VarAss
+ */
+ if (eq[-1] == '!') { /* != */
+ vs_opt = SelNE;
+ eq[-1] = '\0';
+ opt = eq + 1;
+ } else if (eq[-1] == ':') { /* := */
+ vs_opt = VarAss;
+ eq[-1] = '\0';
+ opt = eq + 1;
+ } else if (eq[1] == '=') { /* == */
+ vs_opt = SelEQ;
+ eq[0] = '\0';
+ opt = eq + 2;
+ } else if (eq[1] == '!') { /* =! */
+ vs_opt = SelNE;
+ eq[0] = '\0';
+ opt = eq + 2;
+ }
+
+ /*
+ * For each recognized option
+ */
+ for (op = opt_fields; op->name; op++) {
+ /*
+ * Check whether they match
+ */
+ if (FSTREQ(op->name, f)) {
+ int selok;
+ switch (vs_opt) {
+ case SelEQ:
+ case SelNE:
+ if ((selok = (op->sel_p != NULL))) {
+ if (op->case_insensitive) {
+ selok = (STRCEQ(*op->sel_p, opt) == (vs_opt == SelNE));
+ } else {
+ selok = (STREQ(*op->sel_p, opt) == (vs_opt == SelNE));
+ }
+ }
+ if (selok) {
+ plog(XLOG_MAP, "key %s: map selector %s (=%s) did not %smatch %s",
+ mapkey,
+ op->name,
+ *op->sel_p,
+ vs_opt == SelNE ? "mis" : "",
+ opt);
+ return 0;
+ }
+ /* check if to apply a function */
+ if (op->fxn_p &&
+ ((*op->fxn_p)(opt) == (vs_opt == SelNE))) {
+ plog(XLOG_MAP, "key %s: map function %s did not %smatch %s",
+ mapkey,
+ op->name,
+ vs_opt == SelNE ? "mis" : "",
+ opt);
+ return 0;
+ }
+ break;
+
+ case VarAss:
+ if (op->sel_p) {
+ plog(XLOG_USER, "key %s: Can't assign to a selector (%s)",
+ mapkey, op->name);
+ return 0;
+ }
+ *op->optp = opt;
+ break;
+
+ } /* end of "switch (vs_opt)" statement */
+ break; /* break out of for loop */
+ }
+ }
+
+ if (!op->name)
+ plog(XLOG_USER, "key %s: Unrecognized key/option \"%s\"", mapkey, f);
+ }
+
+ return 1;
+}
+
+
+/*
+ * Skip to next option in the string, but don't scribble over the string.
+ * However, *p gets repointed to the start of the next string past ';'.
+ */
+static char *
+opt_no_scribble(char **p)
+{
+ char *cp = *p;
+ char *dp = cp;
+ char *s = cp;
+
+top:
+ while (*cp && *cp != ';') {
+ if (*cp == '\"') {
+ /*
+ * Skip past string
+ */
+ cp++;
+ while (*cp && *cp != '\"')
+ *dp++ = *cp++;
+ if (*cp)
+ cp++;
+ } else {
+ *dp++ = *cp++;
+ }
+ }
+
+ /*
+ * Skip past any remaining ';'s
+ */
+ while (*cp == ';')
+ cp++;
+
+ /*
+ * If we have a zero length string
+ * and there are more fields, then
+ * parse the next one. This allows
+ * sequences of empty fields.
+ */
+ if (*cp && dp == s)
+ goto top;
+
+ *p = cp;
+ return s;
+}
+
+
+/*
+ * Strip any selectors from a string. Selectors are all assumed to be
+ * first in the string. This is used for the new /defaults method which will
+ * use selectors as well.
+ */
+char *
+strip_selectors(char *opts, char *mapkey)
+{
+ /*
+ * Fill in the global structure fs_static by
+ * cracking the string opts. opts may be
+ * scribbled on at will.
+ */
+ char *o = opts;
+ char *oo = opts;
+ char *f;
+
+ /*
+ * Scan options. Note that the opt() function scribbles on the opt string.
+ */
+ while (*(f = opt_no_scribble(&o))) {
+ enum vs_opt vs_opt = VarAss;
+ char *eq = strchr(f, '=');
+
+ if (!eq || eq[1] == '\0' || eq == f) {
+ /*
+ * No option or assignment? Return as is.
+ */
+ plog(XLOG_USER, "key %s: No option or assignment in \"%s\"", mapkey, f);
+ return o;
+ }
+ /*
+ * Check what type of operation is happening
+ * !=, =! is SelNE
+ * == is SelEQ
+ * := is VarAss
+ */
+ if (eq[-1] == '!') { /* != */
+ vs_opt = SelNE;
+ } else if (eq[-1] == ':') { /* := */
+ vs_opt = VarAss;
+ } else if (eq[1] == '=') { /* == */
+ vs_opt = SelEQ;
+ } else if (eq[1] == '!') { /* =! */
+ vs_opt = SelNE;
+ }
+ switch (vs_opt) {
+ case SelEQ:
+ case SelNE:
+ /* Skip this selector, maybe there's another one following it */
+ plog(XLOG_USER, "skipping selector to \"%s\"", o);
+ /* store previous match. it may have been the first assignment */
+ oo = o;
+ break;
+
+ case VarAss:
+ /* found the first assignment, return the string starting with it */
+#ifdef DEBUG
+ dlog("found first assignment past selectors \"%s\"", o);
+#endif /* DEBUG */
+ return oo;
+ }
+ }
+
+ /* return the same string by default. should not happen. */
+ return oo;
+}
+
+
+/*****************************************************************************
+ *** BOOLEAN FUNCTIONS (return 0 if false, 1 if true): ***
+ *****************************************************************************/
+
+/* test if arg is any of this host's network names or numbers */
+static int
+f_in_network(char *arg)
+{
+ int status;
+
+ if (!arg)
+ return FALSE;
+
+ status = is_network_member(arg);
+#ifdef DEBUG
+ plog(XLOG_USER, "%s is %son a local network",
+ arg, (status ? "" : "not "));
+#endif /* DEBUG */
+ return status;
+}
+
+
+/* test if this host is in netgroup (arg) */
+static int
+f_netgrp(char *arg)
+{
+ int status;
+
+ status = innetgr(arg, opt_host, NULL, NULL);
+#ifdef DEBUG
+ plog(XLOG_USER, "netgrp = %s status = %d host = %s", arg, status, opt_host);
+#endif /* DEBUG */
+ return status;
+}
+
+
+/* test if file (arg) exists via lstat */
+static int
+f_exists(char *arg)
+{
+ struct stat buf;
+
+ if (lstat(arg, &buf) < 0)
+ return (0);
+ else
+ return (1);
+}
+
+
+/* always false */
+static int
+f_false(char *arg)
+{
+ return (0);
+}
+
+
+/* always true */
+static int
+f_true(char *arg)
+{
+ return (1);
+}
+
+
+/*
+ * Free an option
+ */
+static void
+free_op(opt_apply *p, int b)
+{
+ if (*p->opt) {
+ XFREE(*p->opt);
+ *p->opt = 0;
+ }
+}
+
+
+/*
+ * Normalize slashes in the string.
+ */
+void
+normalize_slash(char *p)
+{
+ char *f = strchr(p, '/');
+ char *f0 = f;
+
+ if (f) {
+ char *t = f;
+ do {
+ /* assert(*f == '/'); */
+ if (f == f0 && f[0] == '/' && f[1] == '/') {
+ /* copy double slash iff first */
+ *t++ = *f++;
+ *t++ = *f++;
+ } else {
+ /* copy a single / across */
+ *t++ = *f++;
+ }
+
+ /* assert(f[-1] == '/'); */
+ /* skip past more /'s */
+ while (*f == '/')
+ f++;
+
+ /* assert(*f != '/'); */
+ /* keep copying up to next / */
+ while (*f && *f != '/') {
+ *t++ = *f++;
+ }
+
+ /* assert(*f == 0 || *f == '/'); */
+
+ } while (*f);
+ *t = 0; /* derived from fix by Steven Glassman */
+ }
+}
+
+
+/*
+ * Macro-expand an option. Note that this does not
+ * handle recursive expansions. They will go badly wrong.
+ * If sel is true then old expand selectors, otherwise
+ * don't expand selectors.
+ */
+static void
+expand_op(opt_apply *p, int sel_p)
+{
+ static char expand_error[] = "No space to expand \"%s\"";
+ char expbuf[MAXPATHLEN + 1];
+ char nbuf[NLEN + 1];
+ char *ep = expbuf;
+ char *cp = *p->opt;
+ char *dp;
+ struct opt *op;
+#ifdef DEBUG
+ char *cp_orig = *p->opt;
+#endif /* DEBUG */
+
+ while ((dp = strchr(cp, '$'))) {
+ char ch;
+ /*
+ * First copy up to the $
+ */
+ {
+ int len = dp - cp;
+
+ if (BUFSPACE(ep, len)) {
+ strncpy(ep, cp, len);
+ ep += len;
+ } else {
+ plog(XLOG_ERROR, expand_error, *p->opt);
+ goto out;
+ }
+ }
+
+ cp = dp + 1;
+ ch = *cp++;
+ if (ch == '$') {
+ if (BUFSPACE(ep, 1)) {
+ *ep++ = '$';
+ } else {
+ plog(XLOG_ERROR, expand_error, *p->opt);
+ goto out;
+ }
+ } else if (ch == '{') {
+ /* Expansion... */
+ enum {
+ E_All, E_Dir, E_File, E_Domain, E_Host
+ } todo;
+ /*
+ * Find closing brace
+ */
+ char *br_p = strchr(cp, '}');
+ int len;
+
+ /*
+ * Check we found it
+ */
+ if (!br_p) {
+ /*
+ * Just give up
+ */
+ plog(XLOG_USER, "No closing '}' in \"%s\"", *p->opt);
+ goto out;
+ }
+ len = br_p - cp;
+
+ /*
+ * Figure out which part of the variable to grab.
+ */
+ if (*cp == '/') {
+ /*
+ * Just take the last component
+ */
+ todo = E_File;
+ cp++;
+ --len;
+ } else if (br_p[-1] == '/') {
+ /*
+ * Take all but the last component
+ */
+ todo = E_Dir;
+ --len;
+ } else if (*cp == '.') {
+ /*
+ * Take domain name
+ */
+ todo = E_Domain;
+ cp++;
+ --len;
+ } else if (br_p[-1] == '.') {
+ /*
+ * Take host name
+ */
+ todo = E_Host;
+ --len;
+ } else {
+ /*
+ * Take the whole lot
+ */
+ todo = E_All;
+ }
+
+ /*
+ * Truncate if too long. Since it won't
+ * match anyway it doesn't matter that
+ * it has been cut short.
+ */
+ if (len > NLEN)
+ len = NLEN;
+
+ /*
+ * Put the string into another buffer so
+ * we can do comparisons.
+ */
+ strncpy(nbuf, cp, len);
+ nbuf[len] = '\0';
+
+ /*
+ * Advance cp
+ */
+ cp = br_p + 1;
+
+ /*
+ * Search the option array
+ */
+ for (op = opt_fields; op->name; op++) {
+ /*
+ * Check for match
+ */
+ if (len == op->nlen && STREQ(op->name, nbuf)) {
+ char xbuf[NLEN + 3];
+ char *val;
+ /*
+ * Found expansion. Copy
+ * the correct value field.
+ */
+ if (!(!op->sel_p == !sel_p)) {
+ /*
+ * Copy the string across unexpanded
+ */
+ sprintf(xbuf, "${%s%s%s}",
+ todo == E_File ? "/" :
+ todo == E_Domain ? "." : "",
+ nbuf,
+ todo == E_Dir ? "/" :
+ todo == E_Host ? "." : "");
+ val = xbuf;
+ /*
+ * Make sure expansion doesn't
+ * munge the value!
+ */
+ todo = E_All;
+ } else if (op->sel_p) {
+ val = *op->sel_p;
+ } else {
+ val = *op->optp;
+ }
+
+ if (val) {
+ /*
+ * Do expansion:
+ * ${/var} means take just the last part
+ * ${var/} means take all but the last part
+ * ${.var} means take all but first part
+ * ${var.} means take just the first part
+ * ${var} means take the whole lot
+ */
+ int vlen = strlen(val);
+ char *vptr = val;
+ switch (todo) {
+ case E_Dir:
+ vptr = strrchr(val, '/');
+ if (vptr)
+ vlen = vptr - val;
+ vptr = val;
+ break;
+ case E_File:
+ vptr = strrchr(val, '/');
+ if (vptr) {
+ vptr++;
+ vlen = strlen(vptr);
+ } else
+ vptr = val;
+ break;
+ case E_Domain:
+ vptr = strchr(val, '.');
+ if (vptr) {
+ vptr++;
+ vlen = strlen(vptr);
+ } else {
+ vptr = "";
+ vlen = 0;
+ }
+ break;
+ case E_Host:
+ vptr = strchr(val, '.');
+ if (vptr)
+ vlen = vptr - val;
+ vptr = val;
+ break;
+ case E_All:
+ break;
+ }
+
+ if (BUFSPACE(ep, vlen)) {
+ strcpy(ep, vptr);
+ ep += vlen;
+ } else {
+ plog(XLOG_ERROR, expand_error, *p->opt);
+ goto out;
+ }
+ }
+ /*
+ * Done with this variable
+ */
+ break;
+ }
+ }
+
+ /*
+ * Check that the search was succesful
+ */
+ if (!op->name) {
+ /*
+ * If it wasn't then scan the
+ * environment for that name
+ * and use any value found
+ */
+ char *env = getenv(nbuf);
+
+ if (env) {
+ int vlen = strlen(env);
+
+ if (BUFSPACE(ep, vlen)) {
+ strcpy(ep, env);
+ ep += vlen;
+ } else {
+ plog(XLOG_ERROR, expand_error, *p->opt);
+ goto out;
+ }
+#ifdef DEBUG
+ amuDebug(D_STR)
+ plog(XLOG_DEBUG, "Environment gave \"%s\" -> \"%s\"", nbuf, env);
+#endif /* DEBUG */
+ } else {
+ plog(XLOG_USER, "Unknown sequence \"${%s}\"", nbuf);
+ }
+ }
+ } else {
+ /*
+ * Error, error
+ */
+ plog(XLOG_USER, "Unknown $ sequence in \"%s\"", *p->opt);
+ }
+ }
+
+out:
+ /*
+ * Handle common case - no expansion
+ */
+ if (cp == *p->opt) {
+ *p->opt = strdup(cp);
+ } else {
+ /*
+ * Finish off the expansion
+ */
+ if (BUFSPACE(ep, strlen(cp))) {
+ strcpy(ep, cp);
+ /* ep += strlen(ep); */
+ } else {
+ plog(XLOG_ERROR, expand_error, *p->opt);
+ }
+
+ /*
+ * Save the exansion
+ */
+ *p->opt = strdup(expbuf);
+ }
+
+ normalize_slash(*p->opt);
+
+#ifdef DEBUG
+ amuDebug(D_STR) {
+ plog(XLOG_DEBUG, "Expansion of \"%s\"...", cp_orig);
+ plog(XLOG_DEBUG, "... is \"%s\"", *p->opt);
+ }
+#endif /* DEBUG */
+}
+
+
+/*
+ * Wrapper for expand_op
+ */
+static void
+expand_opts(opt_apply *p, int sel_p)
+{
+ if (*p->opt) {
+ expand_op(p, sel_p);
+ } else if (p->val) {
+ /*
+ * Do double expansion, remembering
+ * to free the string from the first
+ * expansion...
+ */
+ char *s = *p->opt = expand_key(p->val);
+ expand_op(p, sel_p);
+ XFREE(s);
+ }
+}
+
+
+/*
+ * Apply a function to a list of options
+ */
+static void
+apply_opts(void (*op) (opt_apply *, int), opt_apply ppp[], int b)
+{
+ opt_apply *pp;
+
+ for (pp = ppp; pp->opt; pp++)
+ (*op) (pp, b);
+}
+
+
+/*
+ * Free the option table
+ */
+void
+free_opts(am_opts *fo)
+{
+ /*
+ * Copy in the structure we are playing with
+ */
+ fs_static = *fo;
+
+ /*
+ * Free previously allocated memory
+ */
+ apply_opts(free_op, to_free, FALSE);
+}
+
+
+/*
+ * Expand lookup key
+ */
+char *
+expand_key(char *key)
+{
+ opt_apply oa;
+
+ oa.opt = &key;
+ oa.val = 0;
+ expand_opts(&oa, TRUE);
+
+ return key;
+}
+
+
+/*
+ * Remove trailing /'s from a string
+ * unless the string is a single / (Steven Glassman)
+ * or unless it is two slashes // (Kevin D. Bond)
+ */
+void
+deslashify(char *s)
+{
+ if (s && *s) {
+ char *sl = s + strlen(s);
+
+ while (*--sl == '/' && sl > s)
+ *sl = '\0';
+ }
+}
+
+
+int
+eval_fs_opts(am_opts *fo, char *opts, char *g_opts, char *path, char *key, char *map)
+{
+ int ok = TRUE;
+
+ free_opts(fo);
+
+ /*
+ * Clear out the option table
+ */
+ memset((voidp) &fs_static, 0, sizeof(fs_static));
+ memset((voidp) vars, 0, sizeof(vars));
+ memset((voidp) fo, 0, sizeof(*fo));
+
+ /*
+ * Set key, map & path before expansion
+ */
+ opt_key = key;
+ opt_map = map;
+ opt_path = path;
+
+ opt_dkey = strchr(key, '.');
+ if (!opt_dkey) {
+ opt_dkey = NullStr;
+ opt_keyd = key;
+ } else {
+ opt_keyd = strnsave(key, opt_dkey - key);
+ opt_dkey++;
+ if (*opt_dkey == '\0') /* check for 'host.' */
+ opt_dkey = NullStr;
+ }
+
+ /*
+ * Expand global options
+ */
+ fs_static.fs_glob = expand_key(g_opts);
+
+ /*
+ * Expand local options
+ */
+ fs_static.fs_local = expand_key(opts);
+
+ /*
+ * Expand default (global) options
+ */
+ if (!eval_opts(fs_static.fs_glob, key))
+ ok = FALSE;
+
+ /*
+ * Expand local options
+ */
+ if (ok && !eval_opts(fs_static.fs_local, key))
+ ok = FALSE;
+
+ /*
+ * Normalise remote host name.
+ * 1. Expand variables
+ * 2. Normalize relative to host tables
+ * 3. Strip local domains from the remote host
+ * name before using it in other expansions.
+ * This makes mount point names and other things
+ * much shorter, while allowing cross domain
+ * sharing of mount maps.
+ */
+ apply_opts(expand_opts, rhost_expansion, FALSE);
+ if (ok && fs_static.opt_rhost && *fs_static.opt_rhost)
+ host_normalize(&fs_static.opt_rhost);
+
+ /*
+ * Macro expand the options.
+ * Do this regardless of whether we are accepting
+ * this mount - otherwise nasty things happen
+ * with memory allocation.
+ */
+ apply_opts(expand_opts, expansions, FALSE);
+
+ /*
+ * Strip trailing slashes from local pathname...
+ */
+ deslashify(fs_static.opt_fs);
+
+ /*
+ * ok... copy the data back out.
+ */
+ *fo = fs_static;
+
+ /*
+ * Clear defined options
+ */
+ if (opt_keyd != key && opt_keyd != nullstr)
+ XFREE(opt_keyd);
+ opt_keyd = nullstr;
+ opt_dkey = NullStr;
+ opt_key = opt_map = opt_path = nullstr;
+
+ return ok;
+}
diff --git a/contrib/amd/amd/restart.c b/contrib/amd/amd/restart.c
new file mode 100644
index 000000000000..572df86b06d1
--- /dev/null
+++ b/contrib/amd/amd/restart.c
@@ -0,0 +1,208 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: restart.c,v 5.2.2.2 1992/08/02 10:42:21 jsp Exp $
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+
+/*
+ * Handle an amd restart.
+ *
+ * Scan through the mount list finding all "interesting" mount points.
+ * Next hack up partial data structures and add the mounted file
+ * system to the list of known filesystems. This will leave a
+ * dangling reference to that filesystems, so when the filesystem is
+ * finally inherited, an extra "free" must be done on it.
+ *
+ * This module relies on internal details of other components. If
+ * you change something else make *sure* restart() still works.
+ */
+void
+restart(void)
+{
+ /*
+ * Read the existing mount table
+ */
+ mntlist *ml, *mlp;
+
+ /*
+ * For each entry, find nfs, ufs or auto mounts
+ * and create a partial am_node to represent it.
+ */
+ for (mlp = ml = read_mtab("restart", mnttab_file_name);
+ mlp;
+ mlp = mlp->mnext) {
+ mntent_t *me = mlp->mnt;
+ am_ops *fs_ops = 0;
+ if (STREQ(me->mnt_type, MNTTAB_TYPE_UFS)) {
+ /*
+ * UFS entry
+ */
+ fs_ops = &ufs_ops;
+ } else if (STREQ(me->mnt_type, MNTTAB_TYPE_NFS)) {
+ /*
+ * NFS entry, or possibly an Amd entry...
+ * The mnt_fsname for daemon mount points is
+ * host:(pidXXX)
+ * or (seen on Solaris)
+ * host:daemon(pidXXX)
+ */
+ char *colon = strchr(me->mnt_fsname, ':');
+
+ if (colon && strstr(colon, "(pid")) {
+ plog(XLOG_WARNING, "%s is an existing automount point", me->mnt_dir);
+ fs_ops = &amfs_link_ops;
+ } else {
+ fs_ops = &nfs_ops;
+ }
+#ifdef MNTTAB_TYPE_NFS3
+ } else if (STREQ(me->mnt_type, MNTTAB_TYPE_NFS3)) {
+ fs_ops = &nfs_ops;
+#endif /* MNTTAB_TYPE_NFS3 */
+#ifdef MNTTAB_TYPE_LOFS
+ } else if (STREQ(me->mnt_type, MNTTAB_TYPE_LOFS)) {
+ fs_ops = &lofs_ops;
+#endif /* MNTTAB_TYPE_LOFS */
+#ifdef MNTTAB_TYPE_CDFS
+ } else if (STREQ(me->mnt_type, MNTTAB_TYPE_CDFS)) {
+ fs_ops = &cdfs_ops;
+#endif /* MNTTAB_TYPE_CDFS */
+#ifdef MNTTAB_TYPE_PCFS
+ } else if (STREQ(me->mnt_type, MNTTAB_TYPE_PCFS)) {
+ fs_ops = &pcfs_ops;
+#endif /* MNTTAB_TYPE_PCFS */
+#ifdef MNTTAB_TYPE_MFS
+ } else if (STREQ(me->mnt_type, MNTTAB_TYPE_MFS)) {
+ /*
+ * MFS entry. Fake with a symlink.
+ */
+ fs_ops = &amfs_link_ops;
+#endif /* MNTTAB_TYPE_MFS */
+ } else {
+ /*
+ * Catch everything else with symlinks to
+ * avoid recursive mounts. This is debatable...
+ */
+ fs_ops = &amfs_link_ops;
+ }
+
+ /*
+ * If we found something to do
+ */
+ if (fs_ops) {
+ mntfs *mf;
+ am_opts mo;
+ char *cp;
+ cp = strchr(me->mnt_fsname, ':');
+
+ /*
+ * Partially fake up an opts structure
+ */
+ mo.opt_rhost = 0;
+ mo.opt_rfs = 0;
+ if (cp) {
+ *cp = '\0';
+ mo.opt_rhost = strdup(me->mnt_fsname);
+ mo.opt_rfs = strdup(cp + 1);
+ *cp = ':';
+ } else if (fs_ops->ffserver == find_nfs_srvr) {
+ /*
+ * Prototype 4.4 BSD used to end up here -
+ * might as well keep the workaround for now
+ */
+ plog(XLOG_WARNING, "NFS server entry assumed to be %s:/", me->mnt_fsname);
+ mo.opt_rhost = strdup(me->mnt_fsname);
+ mo.opt_rfs = strdup("/");
+ me->mnt_fsname = str3cat(me->mnt_fsname, mo.opt_rhost, ":", "/");
+ }
+ mo.opt_fs = me->mnt_dir;
+ mo.opt_opts = me->mnt_opts;
+
+ /*
+ * Make a new mounted filesystem
+ */
+ mf = find_mntfs(fs_ops, &mo, me->mnt_dir,
+ me->mnt_fsname, "", me->mnt_opts, "");
+ if (mf->mf_refc == 1) {
+ mf->mf_flags |= MFF_RESTART | MFF_MOUNTED;
+ mf->mf_error = 0; /* Already mounted correctly */
+ mf->mf_fo = 0;
+ /*
+ * If the restarted type is a link then
+ * don't time out.
+ */
+ if (fs_ops == &amfs_link_ops || fs_ops == &ufs_ops)
+ mf->mf_flags |= MFF_RSTKEEP;
+ if (fs_ops->fs_init) {
+ /*
+ * Don't care whether this worked since
+ * it is checked again when the fs is
+ * inherited.
+ */
+ (void) (*fs_ops->fs_init) (mf);
+ }
+ plog(XLOG_INFO, "%s restarted fstype %s on %s",
+ me->mnt_fsname, fs_ops->fs_type, me->mnt_dir);
+ } else {
+ /* Something strange happened - two mounts at the same place! */
+ free_mntfs(mf);
+ }
+ /*
+ * Clean up mo
+ */
+ if (mo.opt_rhost)
+ XFREE(mo.opt_rhost);
+ if (mo.opt_rfs)
+ XFREE(mo.opt_rfs);
+ }
+ }
+
+ /*
+ * Free the mount list
+ */
+ free_mntlist(ml);
+}
diff --git a/contrib/amd/amd/rpc_fwd.c b/contrib/amd/amd/rpc_fwd.c
new file mode 100644
index 000000000000..7f3c59b81d78
--- /dev/null
+++ b/contrib/amd/amd/rpc_fwd.c
@@ -0,0 +1,476 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: rpc_fwd.c,v 5.2.2.1 1992/02/09 15:09:01 jsp beta $
+ *
+ */
+
+/*
+ * RPC packet forwarding
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+/*
+ * Note that the ID field in the external packet is only
+ * ever treated as a 32 bit opaque data object, so there
+ * is no need to convert to and from network byte ordering.
+ */
+
+#define XID_ALLOC(struct ) (xid++)
+#define MAX_PACKET_SIZE 8192 /* Maximum UDP packet size */
+
+/*
+ * Each pending reply has an rpc_forward structure
+ * associated with it. These have a 15 second lifespan.
+ * If a new structure is required, then an expired
+ * one will be re-allocated if available, otherwise a fresh
+ * one is allocated. Whenever a reply is received the
+ * structure is discarded.
+ */
+typedef struct rpc_forward rpc_forward;
+struct rpc_forward {
+ qelem rf_q; /* Linked list */
+ time_t rf_ttl; /* Time to live */
+ u_int rf_xid; /* Packet id */
+ u_int rf_oldid; /* Original packet id */
+ fwd_fun rf_fwd; /* Forwarding function */
+ voidp rf_ptr;
+ struct sockaddr_in rf_sin;
+};
+
+/*
+ * Head of list of pending replies
+ */
+qelem rpc_head = {&rpc_head, &rpc_head};
+int fwd_sock;
+static u_int xid;
+
+
+/*
+ * Allocate a rely structure
+ */
+static rpc_forward *
+fwd_alloc(void)
+{
+ time_t now = clocktime();
+ rpc_forward *p = 0, *p2;
+
+ /*
+ * First search for an existing expired one.
+ */
+ ITER(p2, rpc_forward, &rpc_head) {
+ if (p2->rf_ttl <= now) {
+ p = p2;
+ break;
+ }
+ }
+
+ /*
+ * If one couldn't be found then allocate
+ * a new structure and link it at the
+ * head of the list.
+ */
+ if (p) {
+ /*
+ * Call forwarding function to say that
+ * this message was junked.
+ */
+#ifdef DEBUG
+ dlog("Re-using packet forwarding slot - id %#x", p->rf_xid);
+#endif /* DEBUG */
+ if (p->rf_fwd)
+ (*p->rf_fwd) (0, 0, 0, &p->rf_sin, p->rf_ptr, FALSE);
+ rem_que(&p->rf_q);
+ } else {
+ p = ALLOC(struct rpc_forward);
+ }
+ ins_que(&p->rf_q, &rpc_head);
+
+ /*
+ * Set the time to live field
+ * Timeout in 43 seconds
+ */
+ p->rf_ttl = now + 43;
+
+ return p;
+}
+
+
+/*
+ * Free an allocated reply structure.
+ * First unlink it from the list, then
+ * discard it.
+ */
+static void
+fwd_free(rpc_forward *p)
+{
+ rem_que(&p->rf_q);
+ XFREE(p);
+}
+
+
+/*
+ * Initialize the RPC forwarder
+ */
+int
+fwd_init(void)
+{
+#ifdef FIONBIO
+ int on = 1;
+#endif /* FIONBIO */
+
+#ifdef HAVE_TRANSPORT_TYPE_TLI
+ /*
+ * Create ping TLI socket (/dev/tcp and /dev/ticlts did not work)
+ * (HPUX-11 does not like using O_NDELAY in flags)
+ */
+ fwd_sock = t_open("/dev/udp", O_RDWR|O_NONBLOCK, 0);
+ if (fwd_sock < 0) {
+ plog(XLOG_ERROR, "unable to create RPC forwarding TLI socket: %s",
+ t_errlist[t_errno]);
+ return errno;
+ }
+#else /* not HAVE_TRANSPORT_TYPE_TLI */
+ /*
+ * Create ping socket
+ */
+ fwd_sock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (fwd_sock < 0) {
+ plog(XLOG_ERROR, "unable to create RPC forwarding socket: %m");
+ return errno;
+ }
+#endif /* not HAVE_TRANSPORT_TYPE_TLI */
+
+ /*
+ * Some things we talk to require a priv port - so make one here
+ */
+ if (bind_resv_port(fwd_sock, (u_short *) 0) < 0)
+ plog(XLOG_ERROR, "can't bind privileged port");
+
+ if (fcntl(fwd_sock, F_SETFL, FNDELAY) < 0
+#ifdef FIONBIO
+ && ioctl(fwd_sock, FIONBIO, &on) < 0
+#endif /* FIONBIO */
+ ) {
+ plog(XLOG_ERROR, "Can't set non-block on forwarding socket: %m");
+ return errno;
+ }
+
+ return 0;
+}
+
+
+/*
+ * Locate a packet in the forwarding list
+ */
+static rpc_forward *
+fwd_locate(u_int id)
+{
+ rpc_forward *p;
+
+ ITER(p, rpc_forward, &rpc_head) {
+ if (p->rf_xid == id)
+ return p;
+ }
+
+ return 0;
+}
+
+
+/*
+ * This is called to forward a packet to another
+ * RPC server. The message id is changed and noted
+ * so that when a reply appears we can tie it up
+ * correctly. Just matching the reply's source address
+ * would not work because it might come from a
+ * different address.
+ */
+int
+fwd_packet(int type_id, voidp pkt, int len, struct sockaddr_in *fwdto, struct sockaddr_in *replyto, voidp i, fwd_fun cb)
+{
+ rpc_forward *p;
+ u_int *pkt_int;
+ int error;
+#ifdef HAVE_TRANSPORT_TYPE_TLI
+ struct t_unitdata ud;
+#endif /* HAVE_TRANSPORT_TYPE_TLI */
+
+ if ((int) amd_state >= (int) Finishing)
+ return ENOENT;
+
+ /*
+ * See if the type_id is fully specified.
+ * If so, then discard any old entries
+ * for this id.
+ * Otherwise make sure the type_id is
+ * fully qualified by allocating an id here.
+ */
+#ifdef DEBUG
+ switch (type_id & RPC_XID_MASK) {
+ case RPC_XID_PORTMAP:
+ dlog("Sending PORTMAP request");
+ break;
+ case RPC_XID_MOUNTD:
+ dlog("Sending MOUNTD request %#x", type_id);
+ break;
+ case RPC_XID_NFSPING:
+ dlog("Sending NFS ping");
+ break;
+ default:
+ dlog("UNKNOWN RPC XID");
+ break;
+ }
+#endif /* DEBUG */
+
+ if (type_id & ~RPC_XID_MASK) {
+ p = fwd_locate(type_id);
+ if (p) {
+#ifdef DEBUG
+ dlog("Discarding earlier rpc fwd handle");
+#endif /* DEBUG */
+ fwd_free(p);
+ }
+ } else {
+#ifdef DEBUG
+ dlog("Allocating a new xid...");
+#endif /* DEBUG */
+ type_id = MK_RPC_XID(type_id, XID_ALLOC(struct ));
+ }
+
+ p = fwd_alloc();
+ if (!p)
+ return ENOBUFS;
+
+ error = 0;
+
+ pkt_int = (u_int *) pkt;
+
+ /*
+ * Get the original packet id
+ */
+ p->rf_oldid = *pkt_int;
+
+ /*
+ * Replace with newly allocated id
+ */
+ p->rf_xid = *pkt_int = type_id;
+
+ /*
+ * The sendto may fail if, for example, the route
+ * to a remote host is lost because an intermediate
+ * gateway has gone down. Important to fill in the
+ * rest of "p" otherwise nasty things happen later...
+ */
+#ifdef DEBUG
+ {
+ char dq[20];
+ if (p && fwdto)
+ dlog("Sending packet id %#x to %s.%d",
+ p->rf_xid,
+ inet_dquad(dq, fwdto->sin_addr.s_addr),
+ ntohs(fwdto->sin_port));
+ }
+#endif /* DEBUG */
+
+ /* if NULL, remote server probably down */
+ if (!fwdto) {
+ error = AM_ERRNO_HOST_DOWN;
+ goto out;
+ }
+
+#ifdef HAVE_TRANSPORT_TYPE_TLI
+ ud.addr.buf = (char *) fwdto;
+ if (fwdto) /* if NULL, set sizes to zero */
+ ud.addr.maxlen = ud.addr.len = sizeof(struct sockaddr_in);
+ else
+ ud.addr.maxlen = ud.addr.len = 0;
+ ud.opt.buf = (char *) NULL;
+ ud.opt.maxlen = ud.opt.len = 0;
+ ud.udata.buf = pkt;
+ ud.udata.maxlen = ud.udata.len = len;
+ if (t_sndudata(fwd_sock, &ud) < 0) {
+ plog(XLOG_ERROR,"fwd_packet failed: t_errno=%d, errno=%d",t_errno,errno);
+ error = errno;
+ }
+#else /* not HAVE_TRANSPORT_TYPE_TLI */
+ if (sendto(fwd_sock, (char *) pkt, len, 0,
+ (struct sockaddr *) fwdto, sizeof(*fwdto)) < 0)
+ error = errno;
+#endif /* not HAVE_TRANSPORT_TYPE_TLI */
+
+ /*
+ * Save callback function and return address
+ */
+out:
+ p->rf_fwd = cb;
+ if (replyto)
+ p->rf_sin = *replyto;
+ else
+ memset((voidp) &p->rf_sin, 0, sizeof(p->rf_sin));
+ p->rf_ptr = i;
+
+ return error;
+}
+
+
+/*
+ * Called when some data arrives on the forwarding socket
+ */
+void
+fwd_reply(void)
+{
+ int len;
+ u_int pkt[MAX_PACKET_SIZE / sizeof(u_int) + 1];
+ u_int *pkt_int;
+ int rc;
+ rpc_forward *p;
+ struct sockaddr_in src_addr;
+ RECVFROM_FROMLEN_TYPE src_addr_len;
+#ifdef HAVE_TRANSPORT_TYPE_TLI
+ struct t_unitdata ud;
+ int flags = 0;
+#endif /* HAVE_TRANSPORT_TYPE_TLI */
+
+ /*
+ * Determine the length of the packet
+ */
+ len = MAX_PACKET_SIZE;
+
+ /*
+ * Read the packet and check for validity
+ */
+again:
+ src_addr_len = sizeof(src_addr);
+#ifdef HAVE_TRANSPORT_TYPE_TLI
+ ud.addr.buf = (char *) &src_addr;
+ ud.addr.maxlen = ud.addr.len = src_addr_len;
+ ud.opt.buf = (char *) NULL;
+ ud.opt.maxlen = ud.opt.len = 0;
+ ud.udata.buf = (char *) pkt;
+ ud.udata.maxlen = ud.udata.len = len;
+ /* XXX: use flags accordingly such as if T_MORE set */
+ rc = t_rcvudata(fwd_sock, &ud, &flags);
+ if (rc == 0) /* success, reset rc to length */
+ rc = ud.udata.len;
+ else {
+ plog(XLOG_ERROR,"fwd_reply failed: t_errno=%d, errno=%d, flags=%d",t_errno,errno, flags);
+ }
+#else /* not HAVE_TRANSPORT_TYPE_TLI */
+ rc = recvfrom(fwd_sock,
+ (char *) pkt,
+ len,
+ 0,
+ (struct sockaddr *) &src_addr,
+ &src_addr_len);
+#endif /* not HAVE_TRANSPORT_TYPE_TLI */
+
+ /*
+ * XXX: in svr4, if the T_MORE bit of flags is set, what do
+ * we then do? -Erez
+ */
+ if (rc < 0 || src_addr_len != sizeof(src_addr) ||
+ src_addr.sin_family != AF_INET) {
+ if (rc < 0 && errno == EINTR)
+ goto again;
+ plog(XLOG_ERROR, "Error reading RPC reply: %m");
+ goto out;
+ }
+
+ /*
+ * Do no more work if finishing soon
+ */
+ if ((int) amd_state >= (int) Finishing)
+ goto out;
+
+ /*
+ * Find packet reference
+ */
+ pkt_int = (u_int *) pkt;
+
+#ifdef DEBUG
+ switch (*pkt_int & RPC_XID_MASK) {
+ case RPC_XID_PORTMAP:
+ dlog("Receiving PORTMAP reply");
+ break;
+ case RPC_XID_MOUNTD:
+ dlog("Receiving MOUNTD reply %#x", *pkt_int);
+ break;
+ case RPC_XID_NFSPING:
+ dlog("Receiving NFS ping %#x", *pkt_int);
+ break;
+ default:
+ dlog("UNKNOWN RPC XID");
+ break;
+ }
+#endif /* DEBUG */
+
+ p = fwd_locate(*pkt_int);
+ if (!p) {
+#ifdef DEBUG
+ dlog("Can't forward reply id %#x", *pkt_int);
+#endif /* DEBUG */
+ goto out;
+ }
+
+ if (p->rf_fwd) {
+ /*
+ * Put the original message id back
+ * into the packet.
+ */
+ *pkt_int = p->rf_oldid;
+
+ /*
+ * Call forwarding function
+ */
+ (*p->rf_fwd) ((voidp) pkt, rc, &src_addr, &p->rf_sin, p->rf_ptr, TRUE);
+ }
+
+ /*
+ * Free forwarding info
+ */
+ fwd_free(p);
+
+out:;
+}
diff --git a/contrib/amd/amd/sched.c b/contrib/amd/amd/sched.c
new file mode 100644
index 000000000000..ffbe2c810de9
--- /dev/null
+++ b/contrib/amd/amd/sched.c
@@ -0,0 +1,300 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: sched.c,v 5.2.2.1 1992/02/09 15:09:02 jsp beta $
+ *
+ */
+
+/*
+ * Process scheduler
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+
+typedef struct pjob pjob;
+
+struct pjob {
+ qelem hdr; /* Linked list */
+ int pid; /* Process ID of job */
+ cb_fun cb_fun; /* Callback function */
+ voidp cb_closure; /* Closure for callback */
+ int w; /* everyone these days uses int, not a "union wait" */
+ voidp wchan; /* Wait channel */
+};
+
+/* globals */
+qelem proc_list_head = {&proc_list_head, &proc_list_head};
+qelem proc_wait_list = {&proc_wait_list, &proc_wait_list};
+int task_notify_todo;
+
+
+void
+ins_que(qelem *elem, qelem *pred)
+{
+ qelem *p = pred->q_forw;
+
+ elem->q_back = pred;
+ elem->q_forw = p;
+ pred->q_forw = elem;
+ p->q_back = elem;
+}
+
+
+void
+rem_que(qelem *elem)
+{
+ qelem *p = elem->q_forw;
+ qelem *p2 = elem->q_back;
+
+ p2->q_forw = p;
+ p->q_back = p2;
+}
+
+
+static pjob *
+sched_job(cb_fun cf, voidp ca)
+{
+ pjob *p = ALLOC(struct pjob);
+
+ p->cb_fun = cf;
+ p->cb_closure = ca;
+
+ /*
+ * Now place on wait queue
+ */
+ ins_que(&p->hdr, &proc_wait_list);
+
+ return p;
+}
+
+
+/*
+ * tf: The task to execute (ta is its arguments)
+ * cf: Continuation function (ca is its arguments)
+ */
+void
+run_task(task_fun tf, voidp ta, cb_fun cf, voidp ca)
+{
+ pjob *p = sched_job(cf, ca);
+#ifdef HAVE_SIGACTION
+ sigset_t new, mask;
+#else /* not HAVE_SIGACTION */
+ int mask;
+#endif /* not HAVE_SIGACTION */
+
+ p->wchan = (voidp) p;
+
+#ifdef HAVE_SIGACTION
+ sigemptyset(&new); /* initialise signal set we wish to block */
+ sigaddset(&new, SIGCHLD); /* only block on SIGCHLD */
+ sigprocmask(SIG_BLOCK, &new, &mask);
+#else /* not HAVE_SIGACTION */
+ mask = sigblock(sigmask(SIGCHLD));
+#endif /* not HAVE_SIGACTION */
+
+ if ((p->pid = background())) {
+#ifdef HAVE_SIGACTION
+ sigprocmask(SIG_SETMASK, &mask, NULL);
+#else /* not HAVE_SIGACTION */
+ sigsetmask(mask);
+#endif /* not HAVE_SIGACTION */
+ return;
+ }
+
+ /* child code runs here, parent have returned to caller */
+
+ exit((*tf) (ta));
+ /* firewall... */
+ abort();
+}
+
+
+/*
+ * Schedule a task to be run when woken up
+ */
+void
+sched_task(cb_fun cf, voidp ca, voidp wchan)
+{
+ /*
+ * Allocate a new task
+ */
+ pjob *p = sched_job(cf, ca);
+
+#ifdef DEBUG
+ dlog("SLEEP on %#x", wchan);
+#endif /* DEBUG */
+ p->wchan = wchan;
+ p->pid = 0;
+ memset((voidp) &p->w, 0, sizeof(p->w));
+}
+
+
+static void
+wakeupjob(pjob *p)
+{
+ rem_que(&p->hdr);
+ ins_que(&p->hdr, &proc_list_head);
+ task_notify_todo++;
+}
+
+
+void
+wakeup(voidp wchan)
+{
+ pjob *p, *p2;
+
+ if (!foreground)
+ return;
+
+ /*
+ * Can't user ITER() here because
+ * wakeupjob() juggles the list.
+ */
+ for (p = AM_FIRST(pjob, &proc_wait_list);
+ p2 = NEXT(pjob, p), p != HEAD(pjob, &proc_wait_list);
+ p = p2) {
+ if (p->wchan == wchan) {
+ wakeupjob(p);
+ }
+ }
+}
+
+
+void
+wakeup_task(int rc, int term, voidp cl)
+{
+ wakeup(cl);
+}
+
+
+/*
+ * Run any pending tasks.
+ * This must be called with SIGCHLD disabled
+ */
+void
+do_task_notify(void)
+{
+ /*
+ * Keep taking the first item off the list and processing it.
+ *
+ * Done this way because the the callback can, quite reasonably,
+ * queue a new task, so no local reference into the list can be
+ * held here.
+ */
+ while (AM_FIRST(pjob, &proc_list_head) != HEAD(pjob, &proc_list_head)) {
+ pjob *p = AM_FIRST(pjob, &proc_list_head);
+ rem_que(&p->hdr);
+ /*
+ * This job has completed
+ */
+ --task_notify_todo;
+
+ /*
+ * Do callback if it exists
+ */
+ if (p->cb_fun) {
+ /* these two trigraphs will ensure compatibity with strict POSIX.1 */
+ (*p->cb_fun) (WIFEXITED(p->w) ? WEXITSTATUS(p->w) : 0,
+ WIFSIGNALED(p->w) ? WTERMSIG(p->w) : 0,
+ p->cb_closure);
+ }
+ XFREE(p);
+ }
+}
+
+
+RETSIGTYPE
+sigchld(int sig)
+{
+ int w; /* everyone these days uses int, not a "union wait" */
+ int pid;
+
+#ifdef HAVE_WAITPID
+ while ((pid = waitpid((pid_t) -1, &w, WNOHANG)) > 0) {
+#else /* not HAVE_WAITPID */
+ while ((pid = wait3( &w, WNOHANG, (struct rusage *) 0)) > 0) {
+#endif /* not HAVE_WAITPID */
+ pjob *p, *p2;
+
+ if (WIFSIGNALED(w))
+ plog(XLOG_ERROR, "Process %d exited with signal %d",
+ pid, WTERMSIG(w));
+#ifdef DEBUG
+ else
+ dlog("Process %d exited with status %d",
+ pid, WEXITSTATUS(w));
+#endif /* DEBUG */
+
+ for (p = AM_FIRST(pjob, &proc_wait_list);
+ p2 = NEXT(pjob, p), p != HEAD(pjob, &proc_wait_list);
+ p = p2) {
+ if (p->pid == pid) {
+ p->w = w;
+ wakeupjob(p);
+ break;
+ }
+ } /* end of for loop */
+
+#ifdef DEBUG
+ if (!p)
+ dlog("can't locate task block for pid %d", pid);
+#endif /* DEBUG */
+
+ /*
+ * Must count down children inside the while loop, otherwise we won't
+ * count them all, and NumChild (and later backoff) will be set
+ * incorrectly. SH/RUNIT 940519.
+ */
+ if (--NumChild < 0)
+ NumChild = 0;
+ } /* end of "while wait..." loop */
+
+#ifdef REINSTALL_SIGNAL_HANDLER
+ signal(sig, sigchld);
+#endif /* REINSTALL_SIGNAL_HANDLER */
+
+ if (select_intr_valid)
+ longjmp(select_intr, sig);
+}
diff --git a/contrib/amd/amd/srvr_amfs_auto.c b/contrib/amd/amd/srvr_amfs_auto.c
new file mode 100644
index 000000000000..e77c59181157
--- /dev/null
+++ b/contrib/amd/amd/srvr_amfs_auto.c
@@ -0,0 +1,214 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: srvr_amfs_auto.c,v 5.2.2.1 1992/02/09 15:09:05 jsp beta $
+ *
+ */
+
+/*
+ * Automount FS server ("localhost") modeling
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+/* globals */
+qelem amfs_auto_srvr_list = {&amfs_auto_srvr_list, &amfs_auto_srvr_list};
+
+/* statics */
+static fserver *localhost;
+
+
+/*
+ * Find an nfs server for the local host
+ */
+fserver *
+find_amfs_auto_srvr(mntfs *mf)
+{
+ fserver *fs = localhost;
+
+ if (!fs) {
+ fs = ALLOC(struct fserver);
+ fs->fs_refc = 0;
+ fs->fs_host = strdup("localhost");
+ fs->fs_ip = 0;
+ fs->fs_cid = 0;
+ fs->fs_pinger = 0;
+ fs->fs_flags = FSF_VALID;
+ fs->fs_type = "local";
+ fs->fs_private = 0;
+ fs->fs_prfree = 0;
+
+ ins_que(&fs->fs_q, &amfs_auto_srvr_list);
+
+ srvrlog(fs, "starts up");
+
+ localhost = fs;
+ }
+ fs->fs_refc++;
+
+ return fs;
+}
+
+
+/*****************************************************************************
+ *** GENERIC ROUTINES FOLLOW
+ *****************************************************************************/
+
+/*
+ * Wakeup anything waiting for this server
+ */
+void
+wakeup_srvr(fserver *fs)
+{
+ fs->fs_flags &= ~FSF_WANT;
+ wakeup((voidp) fs);
+}
+
+
+/*
+ * Called when final ttl of server has expired
+ */
+static void
+timeout_srvr(voidp v)
+{
+ fserver *fs = v;
+
+ /*
+ * If the reference count is still zero then
+ * we are free to remove this node
+ */
+ if (fs->fs_refc == 0) {
+#ifdef DEBUG
+ dlog("Deleting file server %s", fs->fs_host);
+#endif /* DEBUG */
+ if (fs->fs_flags & FSF_WANT)
+ wakeup_srvr(fs);
+
+ /*
+ * Remove from queue.
+ */
+ rem_que(&fs->fs_q);
+ /*
+ * (Possibly) call the private free routine.
+ */
+ if (fs->fs_private && fs->fs_prfree)
+ (*fs->fs_prfree) (fs->fs_private);
+
+ /*
+ * Free the net address
+ */
+ if (fs->fs_ip)
+ XFREE(fs->fs_ip);
+
+ /*
+ * Free the host name.
+ */
+ XFREE(fs->fs_host);
+
+ /*
+ * Discard the fserver object.
+ */
+ XFREE(fs);
+ }
+}
+
+
+/*
+ * Free a file server
+ */
+void
+free_srvr(fserver *fs)
+{
+ if (--fs->fs_refc == 0) {
+ /*
+ * The reference count is now zero,
+ * so arrange for this node to be
+ * removed in AM_TTL seconds if no
+ * other mntfs is referencing it.
+ */
+ int ttl = (fs->fs_flags & (FSF_DOWN | FSF_ERROR)) ? 19 : AM_TTL;
+
+#ifdef DEBUG
+ dlog("Last hard reference to file server %s - will timeout in %ds", fs->fs_host, ttl);
+#endif /* DEBUG */
+ if (fs->fs_cid) {
+ untimeout(fs->fs_cid);
+ /*
+ * Turn off pinging - XXX
+ */
+ fs->fs_flags &= ~FSF_PINGING;
+ }
+
+ /*
+ * Keep structure lying around for a while
+ */
+ fs->fs_cid = timeout(ttl, timeout_srvr, (voidp) fs);
+
+ /*
+ * Mark the fileserver down and invalid again
+ */
+ fs->fs_flags &= ~FSF_VALID;
+ fs->fs_flags |= FSF_DOWN;
+ }
+}
+
+
+/*
+ * Make a duplicate fserver reference
+ */
+fserver *
+dup_srvr(fserver *fs)
+{
+ fs->fs_refc++;
+ return fs;
+}
+
+
+/*
+ * Log state change
+ */
+void srvrlog(fserver *fs, char *state)
+{
+ plog(XLOG_INFO, "file server %s type %s %s", fs->fs_host, fs->fs_type, state);
+}
diff --git a/contrib/amd/amd/srvr_nfs.c b/contrib/amd/amd/srvr_nfs.c
new file mode 100644
index 000000000000..22a264021882
--- /dev/null
+++ b/contrib/amd/amd/srvr_nfs.c
@@ -0,0 +1,851 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: srvr_nfs.c,v 5.2.2.1 1992/02/09 15:09:06 jsp beta $
+ *
+ */
+
+/*
+ * NFS server modeling
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+/*
+ * Number of pings allowed to fail before host is declared down
+ * - three-fifths of the allowed mount time...
+ */
+#define MAX_ALLOWED_PINGS (3 + /* for luck ... */ 1)
+
+/*
+ * How often to ping when starting a new server
+ */
+#define FAST_NFS_PING 3
+
+#if (FAST_NFS_PING * MAX_ALLOWED_PINGS) >= ALLOWED_MOUNT_TIME
+# error: sanity check failed in srvr_nfs.c
+/*
+ * you cannot do things this way...
+ * sufficient fast pings must be given the chance to fail
+ * within the allowed mount time
+ */
+#endif /* (FAST_NFS_PING * MAX_ALLOWED_PINGS) >= ALLOWED_MOUNT_TIME */
+
+#define NPXID_ALLOC(struct ) (++np_xid)
+
+/* structures and typedefs */
+typedef struct nfs_private {
+ u_short np_mountd; /* Mount daemon port number */
+ char np_mountd_inval; /* Port *may* be invalid */
+ int np_ping; /* Number of failed ping attempts */
+ time_t np_ttl; /* Time when server is thought dead */
+ int np_xid; /* RPC transaction id for pings */
+ int np_error; /* Error during portmap request */
+} nfs_private;
+
+/* globals */
+qelem nfs_srvr_list = {&nfs_srvr_list, &nfs_srvr_list};
+
+/* statics */
+static int np_xid; /* For NFS pings */
+static int ping_len;
+static char ping_buf[sizeof(struct rpc_msg) + 32];
+
+#if defined(MNTTAB_OPT_PROTO) || defined(HAVE_FS_NFS3)
+/* protocols we know about, in order of preference */
+static char *protocols[] = { "tcp", "udp", NULL };
+#endif /* defined(MNTTAB_OPT_PROTO) || defined(HAVE_FS_NFS3) */
+
+/* forward definitions */
+static void nfs_keepalive(voidp);
+
+
+
+/*
+ * Flush any cached data
+ */
+void
+flush_srvr_nfs_cache(void)
+{
+ fserver *fs = 0;
+
+ ITER(fs, fserver, &nfs_srvr_list) {
+ nfs_private *np = (nfs_private *) fs->fs_private;
+ if (np) {
+ np->np_mountd_inval = TRUE;
+ np->np_error = -1;
+ }
+ }
+}
+
+
+/*
+ * Startup the NFS ping for a particular version.
+ */
+static void
+start_ping(u_long nfs_version)
+{
+ XDR ping_xdr;
+ struct rpc_msg ping_msg;
+
+ /*
+ * Non nfs mounts like /afs/glue.umd.edu have ended up here.
+ */
+ if (nfs_version == 0) {
+ nfs_version = NFS_VERSION;
+ plog(XLOG_WARNING, "start_ping: nfs_version = 0 fixed");
+ }
+ plog(XLOG_INFO, "start_ping: nfs_version: %d", nfs_version);
+
+ rpc_msg_init(&ping_msg, NFS_PROGRAM, nfs_version, NFSPROC_NULL);
+
+ /*
+ * Create an XDR endpoint
+ */
+ xdrmem_create(&ping_xdr, ping_buf, sizeof(ping_buf), XDR_ENCODE);
+
+ /*
+ * Create the NFS ping message
+ */
+ if (!xdr_callmsg(&ping_xdr, &ping_msg)) {
+ plog(XLOG_ERROR, "Couldn't create ping RPC message");
+ going_down(3);
+ }
+ /*
+ * Find out how long it is
+ */
+ ping_len = xdr_getpos(&ping_xdr);
+
+ /*
+ * Destroy the XDR endpoint - we don't need it anymore
+ */
+ xdr_destroy(&ping_xdr);
+}
+
+
+/*
+ * Called when a portmap reply arrives
+ */
+static void
+got_portmap(voidp pkt, int len, struct sockaddr_in * sa, struct sockaddr_in * ia, voidp idv, int done)
+{
+ fserver *fs2 = (fserver *) idv;
+ fserver *fs = 0;
+
+ /*
+ * Find which fileserver we are talking about
+ */
+ ITER(fs, fserver, &nfs_srvr_list)
+ if (fs == fs2)
+ break;
+
+ if (fs == fs2) {
+ u_long port = 0; /* XXX - should be short but protocol is naff */
+ int error = done ? pickup_rpc_reply(pkt, len, (voidp) &port, (XDRPROC_T_TYPE) xdr_u_long) : -1;
+ nfs_private *np = (nfs_private *) fs->fs_private;
+
+ if (!error && port) {
+#ifdef DEBUG
+ dlog("got port (%d) for mountd on %s", port, fs->fs_host);
+#endif /* DEBUG */
+ /*
+ * Grab the port number. Portmap sends back
+ * an u_long in native ordering, so it
+ * needs converting to a u_short in
+ * network ordering.
+ */
+ np->np_mountd = htons((u_short) port);
+ np->np_mountd_inval = FALSE;
+ np->np_error = 0;
+ } else {
+#ifdef DEBUG
+ dlog("Error fetching port for mountd on %s", fs->fs_host);
+ dlog("\t error=%d, port=%d", error, port);
+#endif /* DEBUG */
+ /*
+ * Almost certainly no mountd running on remote host
+ */
+ np->np_error = error ? error : ETIMEDOUT;
+ }
+
+ if (fs->fs_flags & FSF_WANT)
+ wakeup_srvr(fs);
+ } else if (done) {
+#ifdef DEBUG
+ dlog("Got portmap for old port request");
+#endif /* DEBUG */
+ } else {
+#ifdef DEBUG
+ dlog("portmap request timed out");
+#endif /* DEBUG */
+ }
+}
+
+
+/*
+ * Obtain portmap information
+ */
+static int
+call_portmap(fserver *fs, AUTH * auth, u_long prog, u_long vers, u_long prot)
+{
+ struct rpc_msg pmap_msg;
+ int len;
+ char iobuf[UDPMSGSIZE];
+ int error;
+ struct pmap pmap;
+
+ rpc_msg_init(&pmap_msg, PMAPPROG, PMAPVERS, PMAPPROC_NULL);
+ pmap.pm_prog = prog;
+ pmap.pm_vers = vers;
+ pmap.pm_prot = prot;
+ pmap.pm_port = 0;
+ len = make_rpc_packet(iobuf,
+ sizeof(iobuf),
+ PMAPPROC_GETPORT,
+ &pmap_msg,
+ (voidp) &pmap,
+ (XDRPROC_T_TYPE) xdr_pmap,
+ auth);
+ if (len > 0) {
+ struct sockaddr_in sin;
+ memset((voidp) &sin, 0, sizeof(sin));
+ sin = *fs->fs_ip;
+ sin.sin_port = htons(PMAPPORT);
+ error = fwd_packet(RPC_XID_PORTMAP, (voidp) iobuf, len,
+ &sin, &sin, (voidp) fs, got_portmap);
+ } else {
+ error = -len;
+ }
+
+ return error;
+}
+
+
+static void
+recompute_portmap(fserver *fs)
+{
+ int error;
+ u_long mnt_version;
+
+ if (nfs_auth)
+ error = 0;
+ else
+ error = make_nfs_auth();
+
+ if (error) {
+ nfs_private *np = (nfs_private *) fs->fs_private;
+ np->np_error = error;
+ return;
+ }
+
+ if (fs->fs_version == 0)
+ plog(XLOG_WARNING, "recompute_portmap: nfs_version = 0 fixed");
+
+ plog(XLOG_INFO, "recompute_portmap: NFS version %d", fs->fs_version);
+#ifdef HAVE_FS_NFS3
+ if (fs->fs_version == NFS_VERSION3)
+ mnt_version = MOUNTVERS3;
+ else
+#endif /* HAVE_FS_NFS3 */
+ mnt_version = MOUNTVERS;
+
+ plog(XLOG_INFO, "Using MOUNT version: %d", mnt_version);
+ call_portmap(fs, nfs_auth, MOUNTPROG, mnt_version, (u_long) IPPROTO_UDP);
+}
+
+
+/*
+ * This is called when we get a reply to an RPC ping.
+ * The value of id was taken from the nfs_private
+ * structure when the ping was transmitted.
+ */
+static void
+nfs_pinged(voidp pkt, int len, struct sockaddr_in * sp, struct sockaddr_in * tsp, voidp idv, int done)
+{
+ int xid = (long) idv; /* for 64-bit archs */
+ fserver *fs;
+#ifdef DEBUG
+ int found_map = 0;
+#endif /* DEBUG */
+
+ if (!done)
+ return;
+
+ /*
+ * For each node...
+ */
+ ITER(fs, fserver, &nfs_srvr_list) {
+ nfs_private *np = (nfs_private *) fs->fs_private;
+ if (np->np_xid == xid && (fs->fs_flags & FSF_PINGING)) {
+ /*
+ * Reset the ping counter.
+ * Update the keepalive timer.
+ * Log what happened.
+ */
+ if (fs->fs_flags & FSF_DOWN) {
+ fs->fs_flags &= ~FSF_DOWN;
+ if (fs->fs_flags & FSF_VALID) {
+ srvrlog(fs, "is up");
+ } else {
+ if (np->np_ping > 1)
+ srvrlog(fs, "ok");
+#ifdef DEBUG
+ else
+ srvrlog(fs, "starts up");
+#endif /* DEBUG */
+ fs->fs_flags |= FSF_VALID;
+ }
+
+ map_flush_srvr(fs);
+ } else {
+ if (fs->fs_flags & FSF_VALID) {
+#ifdef DEBUG
+ dlog("file server %s type nfs is still up", fs->fs_host);
+#endif /* DEBUG */
+ } else {
+ if (np->np_ping > 1)
+ srvrlog(fs, "ok");
+ fs->fs_flags |= FSF_VALID;
+ }
+ }
+
+ /*
+ * Adjust ping interval
+ */
+ untimeout(fs->fs_cid);
+ fs->fs_cid = timeout(fs->fs_pinger, nfs_keepalive, (voidp) fs);
+
+ /*
+ * Update ttl for this server
+ */
+ np->np_ttl = clocktime() +
+ (MAX_ALLOWED_PINGS - 1) * FAST_NFS_PING + fs->fs_pinger - 1;
+
+ /*
+ * New RPC xid...
+ */
+ np->np_xid = NPXID_ALLOC(struct );
+
+ /*
+ * Failed pings is zero...
+ */
+ np->np_ping = 0;
+
+ /*
+ * Recompute portmap information if not known
+ */
+ if (np->np_mountd_inval)
+ recompute_portmap(fs);
+
+#ifdef DEBUG
+ found_map++;
+#endif /* DEBUG */
+ break;
+ }
+ }
+
+#ifdef DEBUG
+ if (found_map == 0)
+ dlog("Spurious ping packet");
+#endif /* DEBUG */
+}
+
+
+/*
+ * Called when no ping-reply received
+ */
+static void
+nfs_timed_out(voidp v)
+{
+ fserver *fs = v;
+ nfs_private *np = (nfs_private *) fs->fs_private;
+
+ /*
+ * Another ping has failed
+ */
+ np->np_ping++;
+
+ /*
+ * Not known to be up any longer
+ */
+ if (FSRV_ISUP(fs)) {
+ fs->fs_flags &= ~FSF_VALID;
+ if (np->np_ping > 1)
+ srvrlog(fs, "not responding");
+ }
+
+ /*
+ * If ttl has expired then guess that it is dead
+ */
+ if (np->np_ttl < clocktime()) {
+ int oflags = fs->fs_flags;
+ if ((fs->fs_flags & FSF_DOWN) == 0) {
+ /*
+ * Server was up, but is now down.
+ */
+ srvrlog(fs, "is down");
+ fs->fs_flags |= FSF_DOWN | FSF_VALID;
+ /*
+ * Since the server is down, the portmap
+ * information may now be wrong, so it
+ * must be flushed from the local cache
+ */
+ flush_nfs_fhandle_cache(fs);
+ np->np_error = -1;
+ } else {
+ /*
+ * Known to be down
+ */
+#ifdef DEBUG
+ if ((fs->fs_flags & FSF_VALID) == 0)
+ srvrlog(fs, "starts down");
+#endif /* DEBUG */
+ fs->fs_flags |= FSF_VALID;
+ }
+ if (oflags != fs->fs_flags && (fs->fs_flags & FSF_WANT))
+ wakeup_srvr(fs);
+ } else {
+#ifdef DEBUG
+ if (np->np_ping > 1)
+ dlog("%d pings to %s failed - at most %d allowed", np->np_ping, fs->fs_host, MAX_ALLOWED_PINGS);
+#endif /* DEBUG */
+ }
+
+ /*
+ * Run keepalive again
+ */
+ nfs_keepalive(fs);
+}
+
+
+/*
+ * Keep track of whether a server is alive
+ */
+static void
+nfs_keepalive(voidp v)
+{
+ fserver *fs = v;
+ int error;
+ nfs_private *np = (nfs_private *) fs->fs_private;
+ int fstimeo = -1;
+
+ /*
+ * Send an NFS ping to this node
+ */
+
+ if (ping_len == 0)
+ start_ping(fs->fs_version);
+
+ /*
+ * Queue the packet...
+ */
+ error = fwd_packet(MK_RPC_XID(RPC_XID_NFSPING, np->np_xid),
+ (voidp) ping_buf,
+ ping_len,
+ fs->fs_ip,
+ (struct sockaddr_in *) 0,
+ (voidp) ((long) np->np_xid), /* for 64-bit archs */
+ nfs_pinged);
+
+ /*
+ * See if a hard error occured
+ */
+ switch (error) {
+ case ENETDOWN:
+ case ENETUNREACH:
+ case EHOSTDOWN:
+ case EHOSTUNREACH:
+ np->np_ping = MAX_ALLOWED_PINGS; /* immediately down */
+ np->np_ttl = (time_t) 0;
+ /*
+ * This causes an immediate call to nfs_timed_out
+ * whenever the server was thought to be up.
+ * See +++ below.
+ */
+ fstimeo = 0;
+ break;
+
+ case 0:
+#ifdef DEBUG
+ dlog("Sent NFS ping to %s", fs->fs_host);
+#endif /* DEBUG */
+ break;
+ }
+
+ /*
+ * Back off the ping interval if we are not getting replies and
+ * the remote system is know to be down.
+ */
+ switch (fs->fs_flags & (FSF_DOWN | FSF_VALID)) {
+ case FSF_VALID: /* Up */
+ if (fstimeo < 0) /* +++ see above */
+ fstimeo = FAST_NFS_PING;
+ break;
+
+ case FSF_VALID | FSF_DOWN: /* Down */
+ fstimeo = fs->fs_pinger;
+ break;
+
+ default: /* Unknown */
+ fstimeo = FAST_NFS_PING;
+ break;
+ }
+
+#ifdef DEBUG
+ dlog("NFS timeout in %d seconds", fstimeo);
+#endif /* DEBUG */
+
+ fs->fs_cid = timeout(fstimeo, nfs_timed_out, (voidp) fs);
+}
+
+
+int
+nfs_srvr_port(fserver *fs, u_short * port, voidp wchan)
+{
+ int error = -1;
+ if ((fs->fs_flags & FSF_VALID) == FSF_VALID) {
+ if ((fs->fs_flags & FSF_DOWN) == 0) {
+ nfs_private *np = (nfs_private *) fs->fs_private;
+ if (np->np_error == 0) {
+ *port = np->np_mountd;
+ error = 0;
+ } else {
+ error = np->np_error;
+ }
+ /*
+ * Now go get the port mapping again in case it changed.
+ * Note that it is used even if (np_mountd_inval)
+ * is True. The flag is used simply as an
+ * indication that the mountd may be invalid, not
+ * that it is known to be invalid.
+ */
+ if (np->np_mountd_inval)
+ recompute_portmap(fs);
+ else
+ np->np_mountd_inval = TRUE;
+ } else {
+ error = EWOULDBLOCK;
+ }
+ }
+ if (error < 0 && wchan && !(fs->fs_flags & FSF_WANT)) {
+ /*
+ * If a wait channel is supplied, and no
+ * error has yet occured, then arrange
+ * that a wakeup is done on the wait channel,
+ * whenever a wakeup is done on this fs node.
+ * Wakeup's are done on the fs node whenever
+ * it changes state - thus causing control to
+ * come back here and new, better things to happen.
+ */
+ fs->fs_flags |= FSF_WANT;
+ sched_task(wakeup_task, wchan, (voidp) fs);
+ }
+ return error;
+}
+
+
+static void
+start_nfs_pings(fserver *fs, int pingval)
+{
+ if (!(fs->fs_flags & FSF_PINGING)) {
+ fs->fs_flags |= FSF_PINGING;
+ if (fs->fs_cid)
+ untimeout(fs->fs_cid);
+ if (pingval < 0) {
+ srvrlog(fs, "wired up");
+ fs->fs_flags |= FSF_VALID;
+ fs->fs_flags &= ~FSF_DOWN;
+ } else {
+ nfs_keepalive(fs);
+ }
+ } else {
+#ifdef DEBUG
+ dlog("Already running pings to %s", fs->fs_host);
+#endif /* DEBUG */
+ }
+}
+
+
+/*
+ * Find an nfs server for a host.
+ */
+fserver *
+find_nfs_srvr(mntfs *mf)
+{
+ char *host = mf->mf_fo->opt_rhost;
+ char *nfs_proto = NULL;
+ fserver *fs;
+ int pingval;
+ mntent_t mnt;
+ nfs_private *np;
+ struct hostent *hp = 0;
+ struct sockaddr_in *ip;
+ u_long nfs_version = 0; /* default is no version specified */
+#ifdef MNTTAB_OPT_PROTO
+ char *rfsname = mf->mf_fo->opt_rfs;
+#endif /* MNTTAB_OPT_PROTO */
+
+ /*
+ * Get ping interval from mount options.
+ * Current only used to decide whether pings
+ * are required or not. < 0 = no pings.
+ */
+ mnt.mnt_opts = mf->mf_mopts;
+ pingval = hasmntval(&mnt, "ping");
+
+ /*
+ * Get the NFS version from the mount options. This is used
+ * to decide the highest NFS version to try.
+ */
+#ifdef MNTTAB_OPT_VERS
+ nfs_version = hasmntval(&mnt, MNTTAB_OPT_VERS);
+#endif /* MNTTAB_OPT_VERS */
+
+#ifdef MNTTAB_OPT_PROTO
+ {
+ char *proto_opt = hasmntopt(&mnt, MNTTAB_OPT_PROTO);
+ if (proto_opt) {
+ char **p;
+
+ proto_opt += sizeof(MNTTAB_OPT_PROTO) - 1; /* skip the "proto" */
+
+ for (p = protocols; *p; p ++)
+ if (proto_opt[0] == '=' &&
+ NSTREQ(&proto_opt[1], *p, strlen(*p))) {
+ nfs_proto = *p;
+ break;
+ }
+ if (*p == NULL)
+ plog(XLOG_WARNING, "ignoring unknown protocol option for %s:%s",
+ host, rfsname);
+ }
+ }
+#endif /* MNTTAB_OPT_PROTO */
+
+ /*
+ * lookup host address and canonical name
+ */
+ hp = gethostbyname(host);
+
+ /*
+ * New code from Bob Harris <harris@basil-rathbone.mit.edu>
+ * Use canonical name to keep track of file server
+ * information. This way aliases do not generate
+ * multiple NFS pingers. (Except when we're normalizing
+ * hosts.)
+ */
+ if (hp && !(gopt.flags & CFM_NORMALIZE_HOSTNAMES))
+ host = (char *) hp->h_name;
+
+ if (hp) {
+ switch (hp->h_addrtype) {
+ case AF_INET:
+ ip = ALLOC(struct sockaddr_in);
+ memset((voidp) ip, 0, sizeof(*ip));
+ ip->sin_family = AF_INET;
+ memmove((voidp) &ip->sin_addr, (voidp) hp->h_addr, sizeof(ip->sin_addr));
+
+ ip->sin_port = htons(NFS_PORT);
+ break;
+
+ default:
+ ip = 0;
+ break;
+ }
+ } else {
+ plog(XLOG_USER, "Unknown host: %s", host);
+ ip = 0;
+ }
+
+ /*
+ * Get the NFS Version, and verify server is up. Probably no
+ * longer need to start server down below.
+ */
+ if (ip) {
+#ifdef HAVE_FS_NFS3
+ /*
+ * Find the best combination of NFS version and protocol.
+ * When given a choice, use the highest available version,
+ * and use TCP over UDP if available.
+ */
+ if (nfs_proto)
+ nfs_version = get_nfs_version(host, ip, nfs_version, nfs_proto);
+ else {
+ int best_nfs_version = 0;
+ int proto_nfs_version;
+ char **p;
+
+ for (p = protocols; *p; p ++) {
+ proto_nfs_version = get_nfs_version(host, ip, nfs_version, *p);
+
+ if (proto_nfs_version > best_nfs_version) {
+ best_nfs_version = proto_nfs_version;
+ nfs_proto = *p;
+ }
+ }
+ nfs_version = best_nfs_version;
+ }
+
+ if (!nfs_version) {
+ /*
+ * If the NFS server is down or does not support the portmapper call
+ * (such as certain Novell NFS servers) we mark it as version 2 and we
+ * let the nfs code deal with the case that is down. If when the
+ * server comes back up, it can support NFS V.3 and/or TCP, it will
+ * use those.
+ */
+ nfs_version = NFS_VERSION;
+ nfs_proto = "udp";
+ }
+#else /* not HAVE_FS_NFS3 */
+ nfs_version = NFS_VERSION;
+#endif /* not HAVE_FS_NFS3 */
+ }
+
+ if (!nfs_proto)
+ nfs_proto = "udp";
+
+ plog(XLOG_INFO, "Using NFS version %d, protocol %s on host %s",
+ nfs_version, nfs_proto, host);
+
+ /*
+ * Try to find an existing fs server stucture for this host.
+ * Note that differing versions or protocols have their own structures.
+ * XXX: Need to fix the ping mechanism to actually use the NFS protocol
+ * chosen here (right now it always uses datagram sockets).
+ */
+ ITER(fs, fserver, &nfs_srvr_list) {
+ if (STREQ(host, fs->fs_host) &&
+ nfs_version == fs->fs_version &&
+ STREQ(nfs_proto, fs->fs_proto)) {
+ /*
+ * following if statement from Mike Mitchell
+ * <mcm@unx.sas.com>
+ * Initialize the ping data if we aren't pinging
+ * now. The np_ttl and np_ping fields are
+ * especially important.
+ */
+ if (!(fs->fs_flags & FSF_PINGING)) {
+ np = (nfs_private *) fs->fs_private;
+ np->np_mountd_inval = TRUE;
+ np->np_xid = NPXID_ALLOC(struct );
+ np->np_error = -1;
+ np->np_ping = 0;
+ /*
+ * Initially the server will be deemed dead
+ * after MAX_ALLOWED_PINGS of the fast variety
+ * have failed.
+ */
+ np->np_ttl = MAX_ALLOWED_PINGS * FAST_NFS_PING + clocktime() - 1;
+ }
+ /*
+ * fill in the IP address -- this is only needed
+ * if there is a chance an IP address will change
+ * between mounts.
+ * Mike Mitchell, mcm@unx.sas.com, 09/08/93
+ */
+ if (hp && fs->fs_ip)
+ memmove((voidp) &fs->fs_ip->sin_addr, (voidp) hp->h_addr, sizeof(fs->fs_ip->sin_addr));
+
+ start_nfs_pings(fs, pingval);
+ fs->fs_refc++;
+ if (ip)
+ XFREE(ip);
+ return fs;
+ }
+ }
+
+ /*
+ * Get here if we can't find an entry
+ */
+
+ /*
+ * Allocate a new server
+ */
+ fs = ALLOC(struct fserver);
+ fs->fs_refc = 1;
+ fs->fs_host = strdup(hp ? hp->h_name : "unknown_hostname");
+ if (gopt.flags & CFM_NORMALIZE_HOSTNAMES)
+ host_normalize(&fs->fs_host);
+ fs->fs_ip = ip;
+ fs->fs_cid = 0;
+ if (ip) {
+ fs->fs_flags = FSF_DOWN; /* Starts off down */
+ } else {
+ fs->fs_flags = FSF_ERROR | FSF_VALID;
+ mf->mf_flags |= MFF_ERROR;
+ mf->mf_error = ENOENT;
+ }
+ fs->fs_version = nfs_version;
+ fs->fs_proto = nfs_proto;
+ fs->fs_type = MNTTAB_TYPE_NFS;
+ fs->fs_pinger = AM_PINGER;
+ np = ALLOC(struct nfs_private);
+ memset((voidp) np, 0, sizeof(*np));
+ np->np_mountd_inval = TRUE;
+ np->np_xid = NPXID_ALLOC(struct );
+ np->np_error = -1;
+
+ /*
+ * Initially the server will be deemed dead after
+ * MAX_ALLOWED_PINGS of the fast variety have failed.
+ */
+ np->np_ttl = clocktime() + MAX_ALLOWED_PINGS * FAST_NFS_PING - 1;
+ fs->fs_private = (voidp) np;
+ fs->fs_prfree = (void (*)(voidp)) free;
+
+ if (!(fs->fs_flags & FSF_ERROR)) {
+ /*
+ * Start of keepalive timer
+ */
+ start_nfs_pings(fs, pingval);
+ }
+
+ /*
+ * Add to list of servers
+ */
+ ins_que(&fs->fs_q, &nfs_srvr_list);
+
+ return fs;
+}
diff --git a/contrib/amd/amq/amq.8 b/contrib/amd/amq/amq.8
new file mode 100644
index 000000000000..4084cdb0558a
--- /dev/null
+++ b/contrib/amd/amq/amq.8
@@ -0,0 +1,214 @@
+.\"
+.\" Copyright (c) 1997-1998 Erez Zadok
+.\" Copyright (c) 1990 Jan-Simon Pendry
+.\" Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+.\" Copyright (c) 1990 The Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Jan-Simon Pendry at Imperial College, London.
+.\"
+.\" 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.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgment:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+.\"
+.\" %W% (Berkeley) %G%
+.\"
+.\" $Id: amq.8,v 5.2.2.1 1992/02/09 15:11:15 jsp beta $
+.\"
+.TH AMQ 8 "25 April 1989"
+.SH NAME
+amq \- automounter query tool
+.SH SYNOPSIS
+.B amq
+[
+.BI \-fmpsuvTU
+] [
+.BI \-h " hostname"
+] [
+.BI \-l " log_file"
+] [
+.BI \-x " log_options"
+] [
+.BI \-D " debug_options"
+] [
+.BI \-M " mountmap entry"
+] [
+.BI \-P " program_number"
+] [
+.I directory
+] .\|.\|.
+.SH DESCRIPTION
+.B Amq
+provides a simple way of determining the current state of
+.B amd
+program.
+Communication is by
+.SM RPC.
+Three modes of operation are supported by the current protocol. By default
+a list of mount points and auto-mounted filesystems is output. An
+alternative host can be specified using the
+.I \-h
+option.
+.LP
+If
+.I directory
+names are given, as output by default, then per-filesystem
+information is displayed.
+.SH OPTIONS
+
+.TP
+.B \-f
+Ask the automounter to flush the internal caches.
+
+.TP
+.BI \-h " hostname"
+Specify an alternate host to query. By default the local host is used. In
+an
+.SM HP-UX
+cluster, the root server is queried by default, since that is the system on
+which the automounter is normally run.
+
+.TP
+.BI \-l " log_file"
+Tell amd to use
+.I log_file
+as the log file name. For security reasons, this must be the same log file
+which amd used when started. This option is therefore only useful to
+refresh amd's open file handle on the log file, so that it can be rotated
+and compressed via daily cron jobs.
+
+.TP
+.B \-m
+Ask the automounter to provide a list of mounted filesystems, including the
+number of references to each filesystem and any error which occurred while
+mounting.
+
+.TP
+.B \-p
+Return the process ID of the remote or locally running amd. Useful when you
+need to send a signal to the local amd process, and would rather not have to
+search through the process table. This option is used in the
+.I ctl-amd
+script.
+
+.TP
+.B \-s
+Ask the automounter to provide system-wide mount statistics.
+
+.TP
+.B \-u
+Ask the automounter to unmount the filesystems named in
+.I directory
+instead of providing
+information about them. Unmounts are requested, not forced. They merely
+cause the mounted filesystem to timeout, which will be picked up by
+.BR amd 's
+main scheduler thus causing the normal timeout action to be taken.
+
+.TP
+.B \-v
+Ask the automounter for its version information. This is a subset of the
+information output by
+.BR amd 's
+.I -v
+option.
+
+.TP
+.BI \-x " log_options"
+Ask the automounter to use the logging options specified in
+.I log_options
+from now on.
+
+.TP
+.BI \-D " log_options"
+Ask the automounter to use the debugging options specified in
+.I debug_options
+from now on.
+
+.TP
+.BI \-M " map_ent"
+Pass a mount map entry to
+.B amd
+and wait for it to be evaluated, possible causing a mount. This option is
+highly insecure. By default, amd and amq do not support it. You have to
+configure am-utils with
+.I \-\-enable\-amq\-mount
+to enable this option.
+
+.TP
+.BI \-P " program_number"
+Contact an alternate running amd that had registered itself on a different
+RPC
+.I program_number
+and apply all other operations to that instance of the automounter. This is
+useful when you run multiple copies of amd, and need to manage each
+one separately. If not specified, amq will use the default program number
+for amd, 300019. For security reasons, the only alternate program numbers
+amd can use range from 300019 to 300029, inclusive.
+
+.TP
+.B \-T
+Contact
+.B amd
+using the TCP transport only. Normally
+.B amq
+will try TCP, and if that failed, will try UDP.
+
+.TP
+.B \-U
+Contact
+.B amd
+using UDP (connectionless) transport only. Normally
+.B amq
+will try TCP, and if that failed, will try UDP.
+
+.SH FILES
+.PD 0
+.TP 20
+.B amq.x
+.SM RPC
+protocol description.
+.SH CAVEATS
+.B Amq
+uses a Sun registered
+.SM RPC
+program number (300019 decimal) which may not
+be in the /etc/rpc database.
+.SH "SEE ALSO"
+.BR amd (8),
+.BR ctl-amd (8),
+.BR amd.conf (5).
+.SH AUTHORS
+Jan-Simon Pendry <jsp@doc.ic.ac.uk>, Department of Computing, Imperial College, London, UK.
+.P
+Erez Zadok <ezk@cs.columbia.edu>, Department of Computer Science, Columbia
+University, New York, USA.
+.P
+Other authors and contributors to am-utils are listed in the
+.B AUTHORS
+file distributed with am-utils.
diff --git a/contrib/amd/amq/amq.c b/contrib/amd/amq/amq.c
new file mode 100644
index 000000000000..7fba9b1c0d1d
--- /dev/null
+++ b/contrib/amd/amq/amq.c
@@ -0,0 +1,938 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: amq.c,v 5.2.2.1 1992/02/09 15:09:16 jsp beta $
+ *
+ */
+
+/*
+ * Automounter query tool
+ */
+
+#ifndef lint
+char copyright[] = "\
+@(#)Copyright (c) 1997-1998 Erez Zadok\n\
+@(#)Copyright (c) 1990 Jan-Simon Pendry\n\
+@(#)Copyright (c) 1990 Imperial College of Science, Technology & Medicine\n\
+@(#)Copyright (c) 1990 The Regents of the University of California.\n\
+@(#)All rights reserved.\n";
+#if __GNUC__ < 2
+static char rcsid[] = "$Id: amq.c,v 6.0 1997-1998/01/01 15:09:16 ezk $";
+static char sccsid[] = "%W% (Berkeley) %G%";
+#endif /* __GNUC__ < 2 */
+#endif /* not lint */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amq.h>
+
+/* locals */
+char *progname;
+static int flush_flag;
+static int minfo_flag;
+static int getpid_flag;
+static int unmount_flag;
+static int stats_flag;
+static int getvers_flag;
+static int amd_program_number = AMQ_PROGRAM;
+static int use_tcp_flag, use_udp_flag;
+static char *debug_opts;
+static char *amq_logfile;
+static char *mount_map;
+static char *xlog_optstr;
+static char localhost[] = "localhost";
+static char *def_server = localhost;
+
+/* externals */
+extern int optind;
+extern char *optarg;
+
+/* forward decalrations */
+#ifdef HAVE_TRANSPORT_TYPE_TLI
+static CLIENT *get_secure_amd_client(char *host, struct timeval *tv, int *sock);
+static int amq_bind_resv_port(int td, u_short *pp);
+#else /* not HAVE_TRANSPORT_TYPE_TLI */
+static int privsock(int ty);
+#endif /* not HAVE_TRANSPORT_TYPE_TLI */
+
+/* dummy variables */
+char hostname[MAXHOSTNAMELEN];
+int orig_umask, foreground, debug_flags;
+pid_t mypid;
+serv_state amd_state;
+
+/* structures */
+enum show_opt {
+ Full, Stats, Calc, Short, ShowDone
+};
+
+
+/*
+ * If (e) is Calc then just calculate the sizes
+ * Otherwise display the mount node on stdout
+ */
+static void
+show_mti(amq_mount_tree *mt, enum show_opt e, int *mwid, int *dwid, int *twid)
+{
+ switch (e) {
+ case Calc:
+ {
+ int mw = strlen(mt->mt_mountinfo);
+ int dw = strlen(mt->mt_directory);
+ int tw = strlen(mt->mt_type);
+ if (mw > *mwid)
+ *mwid = mw;
+ if (dw > *dwid)
+ *dwid = dw;
+ if (tw > *twid)
+ *twid = tw;
+ }
+ break;
+
+ case Full:
+ {
+ struct tm *tp = localtime((time_t *) &mt->mt_mounttime);
+ printf("%-*.*s %-*.*s %-*.*s %s\n\t%-5d %-7d %-6d %-7d %-7d %-6d %02d/%02d/%02d %02d:%02d:%02d\n",
+ *dwid, *dwid,
+ *mt->mt_directory ? mt->mt_directory : "/", /* XXX */
+ *twid, *twid,
+ mt->mt_type,
+ *mwid, *mwid,
+ mt->mt_mountinfo,
+ mt->mt_mountpoint,
+
+ mt->mt_mountuid,
+ mt->mt_getattr,
+ mt->mt_lookup,
+ mt->mt_readdir,
+ mt->mt_readlink,
+ mt->mt_statfs,
+
+ tp->tm_year > 99 ? tp->tm_year - 100 : tp->tm_year,
+ tp->tm_mon + 1, tp->tm_mday,
+ tp->tm_hour, tp->tm_min, tp->tm_sec);
+ }
+ break;
+
+ case Stats:
+ {
+ struct tm *tp = localtime((time_t *) &mt->mt_mounttime);
+ printf("%-*.*s %-5d %-7d %-6d %-7d %-7d %-6d %02d/%02d/%02d %02d:%02d:%02d\n",
+ *dwid, *dwid,
+ *mt->mt_directory ? mt->mt_directory : "/", /* XXX */
+
+ mt->mt_mountuid,
+ mt->mt_getattr,
+ mt->mt_lookup,
+ mt->mt_readdir,
+ mt->mt_readlink,
+ mt->mt_statfs,
+
+ tp->tm_year > 99 ? tp->tm_year - 100 : tp->tm_year,
+ tp->tm_mon + 1, tp->tm_mday,
+ tp->tm_hour, tp->tm_min, tp->tm_sec);
+ }
+ break;
+
+ case Short:
+ {
+ printf("%-*.*s %-*.*s %-*.*s %s\n",
+ *dwid, *dwid,
+ *mt->mt_directory ? mt->mt_directory : "/",
+ *twid, *twid,
+ mt->mt_type,
+ *mwid, *mwid,
+ mt->mt_mountinfo,
+ mt->mt_mountpoint);
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+/*
+ * Display a mount tree.
+ */
+static void
+show_mt(amq_mount_tree *mt, enum show_opt e, int *mwid, int *dwid, int *pwid)
+{
+ while (mt) {
+ show_mti(mt, e, mwid, dwid, pwid);
+ show_mt(mt->mt_next, e, mwid, dwid, pwid);
+ mt = mt->mt_child;
+ }
+}
+
+static void
+show_mi(amq_mount_info_list *ml, enum show_opt e, int *mwid, int *dwid, int *twid)
+{
+ int i;
+
+ switch (e) {
+
+ case Calc:
+ {
+ for (i = 0; i < ml->amq_mount_info_list_len; i++) {
+ amq_mount_info *mi = &ml->amq_mount_info_list_val[i];
+ int mw = strlen(mi->mi_mountinfo);
+ int dw = strlen(mi->mi_mountpt);
+ int tw = strlen(mi->mi_type);
+ if (mw > *mwid)
+ *mwid = mw;
+ if (dw > *dwid)
+ *dwid = dw;
+ if (tw > *twid)
+ *twid = tw;
+ }
+ }
+ break;
+
+ case Full:
+ {
+ for (i = 0; i < ml->amq_mount_info_list_len; i++) {
+ amq_mount_info *mi = &ml->amq_mount_info_list_val[i];
+ printf("%-*.*s %-*.*s %-*.*s %-3d %s is %s",
+ *mwid, *mwid, mi->mi_mountinfo,
+ *dwid, *dwid, mi->mi_mountpt,
+ *twid, *twid, mi->mi_type,
+ mi->mi_refc, mi->mi_fserver,
+ mi->mi_up > 0 ? "up" :
+ mi->mi_up < 0 ? "starting" : "down");
+ if (mi->mi_error > 0) {
+ extern int sys_nerr;
+ if (mi->mi_error < sys_nerr)
+ printf(" (%s)", sys_errlist[mi->mi_error]);
+ else
+ printf(" (Error %d)", mi->mi_error);
+ } else if (mi->mi_error < 0) {
+ fputs(" (in progress)", stdout);
+ }
+ fputc('\n', stdout);
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+/*
+ * Display general mount statistics
+ */
+static void
+show_ms(amq_mount_stats *ms)
+{
+ printf("\
+requests stale mount mount unmount\n\
+deferred fhandles ok failed failed\n\
+%-9d %-9d %-9d %-9d %-9d\n",
+ ms->as_drops, ms->as_stale, ms->as_mok, ms->as_merr, ms->as_uerr);
+}
+
+
+#if defined(HAVE_CLUSTER_H) && defined(HAVE_CNODEID) && defined(HAVE_GETCCENT)
+static char *
+cluster_server(void)
+{
+ struct cct_entry *cp;
+
+ if (cnodeid() == 0) {
+ /*
+ * Not clustered
+ */
+ return def_server;
+ }
+ while (cp = getccent())
+ if (cp->cnode_type == 'r')
+ return cp->cnode_name;
+
+ return def_server;
+}
+#endif /* defined(HAVE_CLUSTER_H) && defined(HAVE_CNODEID) && defined(HAVE_GETCCENT) */
+
+
+/*
+ * MAIN
+ */
+int
+main(int argc, char *argv[])
+{
+ int opt_ch;
+ int errs = 0;
+ char *server;
+ struct sockaddr_in server_addr;
+ int s; /* to pass the Amd security check, we must use a priv port */
+ CLIENT *clnt = NULL;
+ struct hostent *hp;
+ int nodefault = 0;
+ struct timeval tv;
+#ifndef HAVE_TRANSPORT_TYPE_TLI
+ enum clnt_stat cs;
+#endif /* not HAVE_TRANSPORT_TYPE_TLI */
+
+
+ /*
+ * Compute program name
+ */
+ if (argv[0]) {
+ progname = strrchr(argv[0], '/');
+ if (progname && progname[1])
+ progname++;
+ else
+ progname = argv[0];
+ }
+ if (!progname)
+ progname = "amq";
+
+ /*
+ * Parse arguments
+ */
+ while ((opt_ch = getopt(argc, argv, "fh:l:msuvx:D:M:pP:TU")) != EOF)
+ switch (opt_ch) {
+ case 'f':
+ flush_flag = 1;
+ nodefault = 1;
+ break;
+
+ case 'h':
+ def_server = optarg;
+ break;
+
+ case 'l':
+ amq_logfile = optarg;
+ nodefault = 1;
+ break;
+
+ case 'm':
+ minfo_flag = 1;
+ nodefault = 1;
+ break;
+
+ case 'p':
+ getpid_flag = 1;
+ nodefault = 1;
+ break;
+
+ case 's':
+ stats_flag = 1;
+ nodefault = 1;
+ break;
+
+ case 'u':
+ unmount_flag = 1;
+ nodefault = 1;
+ break;
+
+ case 'v':
+ getvers_flag = 1;
+ nodefault = 1;
+ break;
+
+ case 'x':
+ xlog_optstr = optarg;
+ nodefault = 1;
+ break;
+
+ case 'D':
+ debug_opts = optarg;
+ nodefault = 1;
+ break;
+
+ case 'M':
+ mount_map = optarg;
+ nodefault = 1;
+ break;
+
+ case 'P':
+ amd_program_number = atoi(optarg);
+ break;
+
+ case 'T':
+ use_tcp_flag = 1;
+ break;
+
+ case 'U':
+ use_udp_flag = 1;
+ break;
+
+ default:
+ errs = 1;
+ break;
+ }
+
+ if (optind == argc) {
+ if (unmount_flag)
+ errs = 1;
+ }
+ if (errs) {
+ show_usage:
+ fprintf(stderr, "\
+Usage: %s [-h host] [[-f] [-m] [-p] [-v] [-s]] | [[-u] directory ...]]\n\
+\t[-l logfile|\"syslog\"] [-x log_flags] [-D dbg_opts] [-M mapent]\n\
+\t[-P prognum] [-T] [-U]\n", progname);
+ exit(1);
+ }
+
+ /* set use_udp and use_tcp flags both to on if none are defined */
+ if (!use_tcp_flag && !use_udp_flag)
+ use_tcp_flag = use_udp_flag = 1;
+
+#if defined(HAVE_CLUSTER_H) && defined(HAVE_CNODEID) && defined(HAVE_GETCCENT)
+ /*
+ * Figure out root server of cluster
+ */
+ if (def_server == localhost)
+ server = cluster_server();
+ else
+#endif /* defined(HAVE_CLUSTER_H) && defined(HAVE_CNODEID) && defined(HAVE_GETCCENT) */
+ server = def_server;
+
+ /*
+ * Get address of server
+ */
+ if ((hp = gethostbyname(server)) == 0 && !STREQ(server, localhost)) {
+ fprintf(stderr, "%s: Can't get address of %s\n", progname, server);
+ exit(1);
+ }
+ memset(&server_addr, 0, sizeof server_addr);
+ server_addr.sin_family = AF_INET;
+ if (hp) {
+ memmove((voidp) &server_addr.sin_addr, (voidp) hp->h_addr,
+ sizeof(server_addr.sin_addr));
+ } else {
+ /* fake "localhost" */
+ server_addr.sin_addr.s_addr = htonl(0x7f000001);
+ }
+
+ /*
+ * Create RPC endpoint
+ */
+ tv.tv_sec = 5; /* 5 seconds for timeout or per retry */
+ tv.tv_usec = 0;
+
+#ifdef HAVE_TRANSPORT_TYPE_TLI
+ clnt = get_secure_amd_client(server, &tv, &s);
+ if (!clnt && use_tcp_flag) /* try tcp first */
+ clnt = clnt_create(server, amd_program_number, AMQ_VERSION, "tcp");
+ if (!clnt && use_udp_flag) { /* try udp next */
+ clnt = clnt_create(server, amd_program_number, AMQ_VERSION, "udp");
+ /* if ok, set timeout (valid for connectionless transports only) */
+ if (clnt)
+ clnt_control(clnt, CLSET_RETRY_TIMEOUT, (char *) &tv);
+ }
+#else /* not HAVE_TRANSPORT_TYPE_TLI */
+
+ /* first check if remote portmapper is up */
+ cs = pmap_ping(&server_addr);
+ if (cs == RPC_TIMEDOUT) {
+ fprintf(stderr, "%s: failed to contact portmapper on host \"%s\". %s\n",
+ progname, server, clnt_sperrno(cs));
+ exit(1);
+ }
+
+ /* portmapper exists: get remote amd info from it */
+ if (!clnt && use_tcp_flag) { /* try tcp first */
+ s = RPC_ANYSOCK;
+ clnt = clnttcp_create(&server_addr, amd_program_number,
+ AMQ_VERSION, &s, 0, 0);
+ }
+ if (!clnt && use_udp_flag) { /* try udp next */
+ /* XXX: do we need to close(s) ? */
+ s = privsock(SOCK_DGRAM);
+ clnt = clntudp_create(&server_addr, amd_program_number,
+ AMQ_VERSION, tv, &s);
+ }
+#endif /* not HAVE_TRANSPORT_TYPE_TLI */
+ if (!clnt) {
+ fprintf(stderr, "%s: ", progname);
+ clnt_pcreateerror(server);
+ exit(1);
+ }
+
+ /*
+ * Control debugging
+ */
+ if (debug_opts) {
+ int *rc;
+ amq_setopt opt;
+ opt.as_opt = AMOPT_DEBUG;
+ opt.as_str = debug_opts;
+ rc = amqproc_setopt_1(&opt, clnt);
+ if (rc && *rc < 0) {
+ fprintf(stderr, "%s: daemon not compiled for debug\n", progname);
+ errs = 1;
+ } else if (!rc || *rc > 0) {
+ fprintf(stderr, "%s: debug setting for \"%s\" failed\n", progname, debug_opts);
+ errs = 1;
+ }
+ }
+
+ /*
+ * Control logging
+ */
+ if (xlog_optstr) {
+ int *rc;
+ amq_setopt opt;
+ opt.as_opt = AMOPT_XLOG;
+ opt.as_str = xlog_optstr;
+ rc = amqproc_setopt_1(&opt, clnt);
+ if (!rc || *rc) {
+ fprintf(stderr, "%s: setting log level to \"%s\" failed\n", progname, xlog_optstr);
+ errs = 1;
+ }
+ }
+
+ /*
+ * Control log file
+ */
+ if (amq_logfile) {
+ int *rc;
+ amq_setopt opt;
+ opt.as_opt = AMOPT_LOGFILE;
+ opt.as_str = amq_logfile;
+ rc = amqproc_setopt_1(&opt, clnt);
+ if (!rc || *rc) {
+ fprintf(stderr, "%s: setting logfile to \"%s\" failed\n", progname, amq_logfile);
+ errs = 1;
+ }
+ }
+
+ /*
+ * Flush map cache
+ */
+ if (flush_flag) {
+ int *rc;
+ amq_setopt opt;
+ opt.as_opt = AMOPT_FLUSHMAPC;
+ opt.as_str = "";
+ rc = amqproc_setopt_1(&opt, clnt);
+ if (!rc || *rc) {
+ fprintf(stderr, "%s: amd on %s cannot flush the map cache\n", progname, server);
+ errs = 1;
+ }
+ }
+
+ /*
+ * Mount info
+ */
+ if (minfo_flag) {
+ int dummy;
+ amq_mount_info_list *ml = amqproc_getmntfs_1(&dummy, clnt);
+ if (ml) {
+ int mwid = 0, dwid = 0, twid = 0;
+ show_mi(ml, Calc, &mwid, &dwid, &twid);
+ mwid++;
+ dwid++;
+ twid++;
+ show_mi(ml, Full, &mwid, &dwid, &twid);
+
+ } else {
+ fprintf(stderr, "%s: amd on %s cannot provide mount info\n", progname, server);
+ }
+ }
+
+ /*
+ * Mount map
+ */
+ if (mount_map) {
+ int *rc;
+ do {
+ rc = amqproc_mount_1(&mount_map, clnt);
+ } while (rc && *rc < 0);
+ if (!rc || *rc > 0) {
+ if (rc)
+ errno = *rc;
+ else
+ errno = ETIMEDOUT;
+ fprintf(stderr, "%s: could not start new ", progname);
+ perror("autmount point");
+ }
+ }
+
+ /*
+ * Get Version
+ */
+ if (getvers_flag) {
+ amq_string *spp = amqproc_getvers_1((voidp) 0, clnt);
+ if (spp && *spp) {
+ fputs(*spp, stdout);
+ XFREE(*spp);
+ } else {
+ fprintf(stderr, "%s: failed to get version information\n", progname);
+ errs = 1;
+ }
+ }
+
+ /*
+ * Get PID of amd
+ */
+ if (getpid_flag) {
+ int *ip = amqproc_getpid_1((voidp) 0, clnt);
+ if (ip && *ip) {
+ printf("%d\n", *ip);
+ } else {
+ fprintf(stderr, "%s: failed to get PID of amd\n", progname);
+ errs = 1;
+ }
+ }
+
+ /*
+ * Apply required operation to all remaining arguments
+ */
+ if (optind < argc) {
+ do {
+ char *fs = argv[optind++];
+ if (unmount_flag) {
+ /*
+ * Unmount request
+ */
+ amqproc_umnt_1(&fs, clnt);
+ } else {
+ /*
+ * Stats request
+ */
+ amq_mount_tree_p *mtp = amqproc_mnttree_1(&fs, clnt);
+ if (mtp) {
+ amq_mount_tree *mt = *mtp;
+ if (mt) {
+ int mwid = 0, dwid = 0, twid = 0;
+ show_mt(mt, Calc, &mwid, &dwid, &twid);
+ mwid++;
+ dwid++, twid++;
+ printf("%-*.*s Uid Getattr Lookup RdDir RdLnk Statfs Mounted@\n",
+ dwid, dwid, "What");
+ show_mt(mt, Stats, &mwid, &dwid, &twid);
+ } else {
+ fprintf(stderr, "%s: %s not automounted\n", progname, fs);
+ }
+ xdr_pri_free((XDRPROC_T_TYPE) xdr_amq_mount_tree_p, (caddr_t) mtp);
+ } else {
+ fprintf(stderr, "%s: ", progname);
+ clnt_perror(clnt, server);
+ errs = 1;
+ }
+ }
+ } while (optind < argc);
+
+ } else if (unmount_flag) {
+ goto show_usage;
+
+ } else if (stats_flag) {
+ amq_mount_stats *ms = amqproc_stats_1((voidp) 0, clnt);
+ if (ms) {
+ show_ms(ms);
+ } else {
+ fprintf(stderr, "%s: ", progname);
+ clnt_perror(clnt, server);
+ errs = 1;
+ }
+
+ } else if (!nodefault) {
+ amq_mount_tree_list *mlp = amqproc_export_1((voidp) 0, clnt);
+ if (mlp) {
+ enum show_opt e = Calc;
+ int mwid = 0, dwid = 0, pwid = 0;
+ while (e != ShowDone) {
+ int i;
+ for (i = 0; i < mlp->amq_mount_tree_list_len; i++) {
+ show_mt(mlp->amq_mount_tree_list_val[i],
+ e, &mwid, &dwid, &pwid);
+ }
+ mwid++;
+ dwid++, pwid++;
+ if (e == Calc)
+ e = Short;
+ else if (e == Short)
+ e = ShowDone;
+ }
+
+ } else {
+ fprintf(stderr, "%s: ", progname);
+ clnt_perror(clnt, server);
+ errs = 1;
+ }
+ }
+ exit(errs);
+ return errs; /* should never reache here */
+}
+
+
+#ifdef HAVE_TRANSPORT_TYPE_TLI
+
+/*
+ * How to bind to reserved ports.
+ * TLI handle (socket) and port version.
+ */
+/* defined here so that it does not have to resolve it with libamu.a */
+static int
+amq_bind_resv_port(int td, u_short *pp)
+{
+ int rc = -1, port;
+ struct t_bind *treq, *tret;
+ struct sockaddr_in *sin;
+
+ treq = (struct t_bind *) t_alloc(td, T_BIND, T_ADDR);
+ if (!treq) {
+ plog(XLOG_ERROR, "t_alloc 1");
+ return -1;
+ }
+ tret = (struct t_bind *) t_alloc(td, T_BIND, T_ADDR);
+ if (!tret) {
+ t_free((char *) treq, T_BIND);
+ plog(XLOG_ERROR, "t_alloc 2");
+ return -1;
+ }
+ memset((char *) treq->addr.buf, 0, treq->addr.len);
+ sin = (struct sockaddr_in *) treq->addr.buf;
+ sin->sin_family = AF_INET;
+ treq->qlen = 0;
+ treq->addr.len = treq->addr.maxlen;
+ errno = EADDRINUSE;
+ port = IPPORT_RESERVED;
+
+ do {
+ --port;
+ sin->sin_port = htons(port);
+ rc = t_bind(td, treq, tret);
+ if (rc < 0) {
+ } else {
+ if (memcmp(treq->addr.buf, tret->addr.buf, tret->addr.len) == 0)
+ break;
+ else
+ t_unbind(td);
+ }
+ } while ((rc < 0 || errno == EADDRINUSE) && (int) port > IPPORT_RESERVED / 2);
+
+ if (pp) {
+ if (rc == 0)
+ *pp = port;
+ else
+ plog(XLOG_ERROR, "could not t_bind to any reserved port");
+ }
+ t_free((char *) tret, T_BIND);
+ t_free((char *) treq, T_BIND);
+ return rc;
+}
+
+
+/*
+ * Create a secure rpc client attached to the amd daemon.
+ */
+static CLIENT *
+get_secure_amd_client(char *host, struct timeval *tv, int *sock)
+{
+ CLIENT *client;
+ struct netbuf nb;
+ struct netconfig *nc, *pm_nc;
+ struct sockaddr_in sin;
+
+
+ nb.maxlen = sizeof(sin);
+ nb.buf = (char *) &sin;
+
+ /*
+ * Ensure that remote portmapper is alive
+ * (must use connectionless netconfig).
+ */
+ if ((pm_nc = getnetconfigent(NC_UDP)) != NULL) {
+ enum clnt_stat cs;
+
+ cs = rpcb_rmtcall(pm_nc,
+ host,
+ amd_program_number,
+ AMQ_VERSION,
+ AMQPROC_NULL,
+ (XDRPROC_T_TYPE) xdr_void,
+ NULL,
+ (XDRPROC_T_TYPE) xdr_void,
+ NULL,
+ *tv,
+ NULL);
+ if (cs == RPC_TIMEDOUT) {
+ fprintf(stderr, "%s: failed to contact portmapper on host \"%s\". %s\n",
+ progname, host, clnt_sperrno(cs));
+ exit(1);
+ }
+ }
+
+ /*
+ * First transport type to try: TCP
+ */
+ if (use_tcp_flag) {
+ /* Find amd address on TCP */
+ nc = getnetconfigent(NC_TCP);
+ if (!nc) {
+ fprintf(stderr, "getnetconfig for tcp failed: %s\n", nc_sperror());
+ goto tryudp;
+ }
+
+ if (!rpcb_getaddr(amd_program_number, AMQ_VERSION, nc, &nb, host)) {
+ /*
+ * don't pring error messages here, since amd might legitimately
+ * serve udp only
+ */
+ goto tryudp;
+ }
+ /* Create priviledged TCP socket */
+ *sock = t_open(nc->nc_device, O_RDWR, 0);
+
+ if (*sock < 0) {
+ fprintf(stderr, "t_open %s: %m\n", nc->nc_device);
+ goto tryudp;
+ }
+ if (amq_bind_resv_port(*sock, (u_short *) 0) < 0)
+ goto tryudp;
+
+ client = clnt_vc_create(*sock, &nb, amd_program_number, AMQ_VERSION, 0, 0);
+ if (!client) {
+ fprintf(stderr, "clnt_vc_create failed");
+ t_close(*sock);
+ goto tryudp;
+ }
+ /* tcp succeeded */
+ return client;
+ }
+
+tryudp:
+ /*
+ * TCP failed so try UDP
+ */
+ if (use_udp_flag) {
+ /* find amd address on UDP */
+ nc = getnetconfigent(NC_UDP);
+ if (!nc) {
+ fprintf(stderr, "getnetconfig for udp failed: %s\n", nc_sperror());
+ return NULL;
+ }
+ if (!rpcb_getaddr(amd_program_number, AMQ_VERSION, nc, &nb, host)) {
+ fprintf(stderr, "%s\n",
+ clnt_spcreateerror("couldn't get amd address on udp"));
+ return NULL;
+ }
+ /* create priviledged UDP socket */
+ *sock = t_open(nc->nc_device, O_RDWR, 0);
+
+ if (*sock < 0) {
+ fprintf(stderr, "t_open %s: %m\n", nc->nc_device);
+ return NULL; /* neither tcp not udp succeeded */
+ }
+ if (amq_bind_resv_port(*sock, (u_short *) 0) < 0)
+ return NULL;
+
+ client = clnt_dg_create(*sock, &nb, amd_program_number, AMQ_VERSION, 0, 0);
+ if (!client) {
+ fprintf(stderr, "clnt_dg_create failed\n");
+ t_close(*sock);
+ return NULL; /* neither tcp not udp succeeded */
+ }
+ if (clnt_control(client, CLSET_RETRY_TIMEOUT, (char *) tv) == FALSE) {
+ fprintf(stderr, "clnt_control CLSET_RETRY_TIMEOUT for udp failed\n");
+ clnt_destroy(client);
+ return NULL; /* neither tcp not udp succeeded */
+ }
+ /* udp succeeded */
+ return client;
+ }
+
+ /* should never get here */
+ return NULL;
+}
+
+#else /* not HAVE_TRANSPORT_TYPE_TLI */
+
+/*
+ * inetresport creates a datagram socket and attempts to bind it to a
+ * secure port.
+ * returns: The bound socket, or -1 to indicate an error.
+ */
+static int
+inetresport(int ty)
+{
+ int alport;
+ struct sockaddr_in addr;
+ int fd;
+
+ /* Use internet address family */
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = INADDR_ANY;
+ if ((fd = socket(AF_INET, ty, 0)) < 0)
+ return -1;
+
+ for (alport = IPPORT_RESERVED - 1; alport > IPPORT_RESERVED / 2 + 1; alport--) {
+ addr.sin_port = htons((u_short) alport);
+ if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) >= 0)
+ return fd;
+ if (errno != EADDRINUSE) {
+ close(fd);
+ return -1;
+ }
+ }
+ close(fd);
+ errno = EAGAIN;
+ return -1;
+}
+
+
+/*
+ * Privsock() calls inetresport() to attempt to bind a socket to a secure
+ * port. If inetresport() fails, privsock returns a magic socket number which
+ * indicates to RPC that it should make its own socket.
+ * returns: A privileged socket # or RPC_ANYSOCK.
+ */
+static int
+privsock(int ty)
+{
+ int sock = inetresport(ty);
+
+ if (sock < 0) {
+ errno = 0;
+ /* Couldn't get a secure port, let RPC make an insecure one */
+ sock = RPC_ANYSOCK;
+ }
+ return sock;
+}
+
+#endif /* not HAVE_TRANSPORT_TYPE_TLI */
diff --git a/contrib/amd/amq/amq.h b/contrib/amd/amq/amq.h
new file mode 100644
index 000000000000..98f73831a49f
--- /dev/null
+++ b/contrib/amd/amq/amq.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: amq.h,v 1.1 1996/01/13 23:23:39 ezk Exp ezk $
+ *
+ */
+
+#ifndef _AMQ_H
+#define _AMQ_H
+
+/*
+ * external definitions for building amq
+ */
+
+extern voidp amqproc_null_1(voidp argp, CLIENT *rqstp);
+extern amq_mount_tree_p *amqproc_mnttree_1(amq_string *argp, CLIENT *rqstp);
+extern voidp amqproc_umnt_1(amq_string *argp, CLIENT *rqstp);
+extern amq_mount_stats *amqproc_stats_1(voidp argp, CLIENT *rqstp);
+extern amq_mount_tree_list *amqproc_export_1(voidp argp, CLIENT *rqstp);
+extern int *amqproc_setopt_1(amq_setopt *argp, CLIENT *rqstp);
+extern amq_mount_info_list *amqproc_getmntfs_1(voidp argp, CLIENT *rqstp);
+extern int *amqproc_mount_1(voidp argp, CLIENT *rqstp);
+extern amq_string *amqproc_getvers_1(voidp argp, CLIENT *rqstp);
+extern int *amqproc_getpid_1(voidp argp, CLIENT *rqstp);
+
+#endif /* not _AMQ_H */
diff --git a/contrib/amd/amq/amq_clnt.c b/contrib/amd/amq/amq_clnt.c
new file mode 100644
index 000000000000..7220cec2fef4
--- /dev/null
+++ b/contrib/amd/amq/amq_clnt.c
@@ -0,0 +1,208 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: amq_clnt.c,v 5.2.2.1 1992/02/09 15:09:24 jsp beta $
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amq.h>
+
+
+static struct timeval TIMEOUT = {ALLOWED_MOUNT_TIME, 0};
+
+
+voidp
+amqproc_null_1(voidp argp, CLIENT *clnt)
+{
+ static char res;
+
+ memset((char *) &res, 0, sizeof(res));
+ if (clnt_call(clnt, AMQPROC_NULL,
+ (XDRPROC_T_TYPE) xdr_void, argp,
+ (XDRPROC_T_TYPE) xdr_void, &res, TIMEOUT)
+ != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return ((voidp) &res);
+}
+
+
+amq_mount_tree_p *
+amqproc_mnttree_1(amq_string *argp, CLIENT *clnt)
+{
+ static amq_mount_tree_p res;
+
+ memset((char *) &res, 0, sizeof(res));
+ if (clnt_call(clnt, AMQPROC_MNTTREE,
+ (XDRPROC_T_TYPE) xdr_amq_string, (SVC_IN_ARG_TYPE) argp,
+ (XDRPROC_T_TYPE) xdr_amq_mount_tree_p, (SVC_IN_ARG_TYPE) & res,
+ TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&res);
+}
+
+
+voidp
+amqproc_umnt_1(amq_string *argp, CLIENT *clnt)
+{
+ static char res;
+
+ memset((char *) &res, 0, sizeof(res));
+ if (clnt_call(clnt, AMQPROC_UMNT,
+ (XDRPROC_T_TYPE) xdr_amq_string, (SVC_IN_ARG_TYPE) argp,
+ (XDRPROC_T_TYPE) xdr_void, &res,
+ TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return ((voidp) &res);
+}
+
+
+amq_mount_stats *
+amqproc_stats_1(voidp argp, CLIENT *clnt)
+{
+ static amq_mount_stats res;
+
+ memset((char *) &res, 0, sizeof(res));
+ if (clnt_call(clnt, AMQPROC_STATS,
+ (XDRPROC_T_TYPE) xdr_void, argp,
+ (XDRPROC_T_TYPE) xdr_amq_mount_stats,
+ (SVC_IN_ARG_TYPE) & res,
+ TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&res);
+}
+
+
+amq_mount_tree_list *
+amqproc_export_1(voidp argp, CLIENT *clnt)
+{
+ static amq_mount_tree_list res;
+
+ memset((char *) &res, 0, sizeof(res));
+ if (clnt_call(clnt, AMQPROC_EXPORT,
+ (XDRPROC_T_TYPE) xdr_void, argp,
+ (XDRPROC_T_TYPE) xdr_amq_mount_tree_list,
+ (SVC_IN_ARG_TYPE) & res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&res);
+}
+
+
+int *
+amqproc_setopt_1(amq_setopt *argp, CLIENT *clnt)
+{
+ static int res;
+
+ memset((char *) &res, 0, sizeof(res));
+ if (clnt_call(clnt, AMQPROC_SETOPT, (XDRPROC_T_TYPE) xdr_amq_setopt,
+ (SVC_IN_ARG_TYPE) argp, (XDRPROC_T_TYPE) xdr_int,
+ (SVC_IN_ARG_TYPE) & res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&res);
+}
+
+
+amq_mount_info_list *
+amqproc_getmntfs_1(voidp argp, CLIENT *clnt)
+{
+ static amq_mount_info_list res;
+
+ memset((char *) &res, 0, sizeof(res));
+ if (clnt_call(clnt, AMQPROC_GETMNTFS, (XDRPROC_T_TYPE) xdr_void, argp,
+ (XDRPROC_T_TYPE) xdr_amq_mount_info_list,
+ (SVC_IN_ARG_TYPE) & res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&res);
+}
+
+
+int *
+amqproc_mount_1(voidp argp, CLIENT *clnt)
+{
+ static int res;
+
+ memset((char *) &res, 0, sizeof(res));
+ if (clnt_call(clnt, AMQPROC_MOUNT, (XDRPROC_T_TYPE) xdr_amq_string, argp,
+ (XDRPROC_T_TYPE) xdr_int, (SVC_IN_ARG_TYPE) & res,
+ TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&res);
+}
+
+
+amq_string *
+amqproc_getvers_1(voidp argp, CLIENT *clnt)
+{
+ static amq_string res;
+
+ memset((char *) &res, 0, sizeof(res));
+ if (clnt_call(clnt, AMQPROC_GETVERS, (XDRPROC_T_TYPE) xdr_void, argp,
+ (XDRPROC_T_TYPE) xdr_amq_string, (SVC_IN_ARG_TYPE) & res,
+ TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&res);
+}
+
+
+int *
+amqproc_getpid_1(voidp argp, CLIENT *clnt)
+{
+ static int res;
+
+ memset((char *) &res, 0, sizeof(res));
+ if (clnt_call(clnt, AMQPROC_GETPID, (XDRPROC_T_TYPE) xdr_void, argp,
+ (XDRPROC_T_TYPE) xdr_int, (SVC_IN_ARG_TYPE) & res,
+ TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&res);
+}
diff --git a/contrib/amd/amq/amq_xdr.c b/contrib/amd/amq/amq_xdr.c
new file mode 100644
index 000000000000..79a729424830
--- /dev/null
+++ b/contrib/amd/amq/amq_xdr.c
@@ -0,0 +1,259 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: amq_xdr.c,v 5.2.2.1 1992/02/09 15:09:23 jsp beta $
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amq.h>
+
+
+bool_t
+xdr_time_type(XDR *xdrs, time_type *objp)
+{
+ if (!xdr_long(xdrs, (long *) objp)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+
+bool_t
+xdr_amq_mount_tree(XDR *xdrs, amq_mount_tree *objp)
+{
+
+ if (!xdr_amq_string(xdrs, &objp->mt_mountinfo)) {
+ return (FALSE);
+ }
+
+ if (!xdr_amq_string(xdrs, &objp->mt_directory)) {
+ return (FALSE);
+ }
+
+ if (!xdr_amq_string(xdrs, &objp->mt_mountpoint)) {
+ return (FALSE);
+ }
+
+ if (!xdr_amq_string(xdrs, &objp->mt_type)) {
+ return (FALSE);
+ }
+
+ if (!xdr_time_type(xdrs, &objp->mt_mounttime)) {
+ return (FALSE);
+ }
+
+ if (!xdr_u_short(xdrs, &objp->mt_mountuid)) {
+ return (FALSE);
+ }
+
+ if (!xdr_int(xdrs, &objp->mt_getattr)) {
+ return (FALSE);
+ }
+
+ if (!xdr_int(xdrs, &objp->mt_lookup)) {
+ return (FALSE);
+ }
+
+ if (!xdr_int(xdrs, &objp->mt_readdir)) {
+ return (FALSE);
+ }
+
+ if (!xdr_int(xdrs, &objp->mt_readlink)) {
+ return (FALSE);
+ }
+
+ if (!xdr_int(xdrs, &objp->mt_statfs)) {
+ return (FALSE);
+ }
+
+ if (!xdr_pointer(xdrs, (char **) &objp->mt_next, sizeof(amq_mount_tree), (XDRPROC_T_TYPE) xdr_amq_mount_tree)) {
+ return (FALSE);
+ }
+
+ if (!xdr_pointer(xdrs, (char **) &objp->mt_child, sizeof(amq_mount_tree), (XDRPROC_T_TYPE) xdr_amq_mount_tree)) {
+ return (FALSE);
+ }
+
+ return (TRUE);
+}
+
+
+bool_t
+xdr_amq_mount_tree_p(XDR *xdrs, amq_mount_tree_p *objp)
+{
+ if (!xdr_pointer(xdrs, (char **) objp, sizeof(amq_mount_tree), (XDRPROC_T_TYPE) xdr_amq_mount_tree)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+
+bool_t
+xdr_amq_mount_info(XDR *xdrs, amq_mount_info *objp)
+{
+
+ if (!xdr_amq_string(xdrs, &objp->mi_type)) {
+ return (FALSE);
+ }
+
+ if (!xdr_amq_string(xdrs, &objp->mi_mountpt)) {
+ return (FALSE);
+ }
+
+ if (!xdr_amq_string(xdrs, &objp->mi_mountinfo)) {
+ return (FALSE);
+ }
+
+ if (!xdr_amq_string(xdrs, &objp->mi_fserver)) {
+ return (FALSE);
+ }
+
+ if (!xdr_int(xdrs, &objp->mi_error)) {
+ return (FALSE);
+ }
+
+ if (!xdr_int(xdrs, &objp->mi_refc)) {
+ return (FALSE);
+ }
+
+ if (!xdr_int(xdrs, &objp->mi_up)) {
+ return (FALSE);
+ }
+
+ return (TRUE);
+}
+
+
+bool_t
+xdr_amq_mount_info_list(XDR *xdrs, amq_mount_info_list *objp)
+{
+ if (!xdr_array(xdrs,
+ (char **) &objp->amq_mount_info_list_val,
+ (u_int *) &objp->amq_mount_info_list_len,
+ ~0,
+ sizeof(amq_mount_info),
+ (XDRPROC_T_TYPE) xdr_amq_mount_info)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+
+bool_t
+xdr_amq_mount_tree_list(XDR *xdrs, amq_mount_tree_list *objp)
+{
+ if (!xdr_array(xdrs,
+ (char **) &objp->amq_mount_tree_list_val,
+ (u_int *) &objp->amq_mount_tree_list_len,
+ ~0,
+ sizeof(amq_mount_tree_p),
+ (XDRPROC_T_TYPE) xdr_amq_mount_tree_p)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+
+bool_t
+xdr_amq_mount_stats(XDR *xdrs, amq_mount_stats *objp)
+{
+
+ if (!xdr_int(xdrs, &objp->as_drops)) {
+ return (FALSE);
+ }
+
+ if (!xdr_int(xdrs, &objp->as_stale)) {
+ return (FALSE);
+ }
+
+ if (!xdr_int(xdrs, &objp->as_mok)) {
+ return (FALSE);
+ }
+
+ if (!xdr_int(xdrs, &objp->as_merr)) {
+ return (FALSE);
+ }
+
+ if (!xdr_int(xdrs, &objp->as_uerr)) {
+ return (FALSE);
+ }
+
+ return (TRUE);
+}
+
+
+bool_t
+xdr_amq_opt(XDR *xdrs, amq_opt *objp)
+{
+ if (!xdr_enum(xdrs, (enum_t *) objp)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+
+bool_t
+xdr_amq_setopt(XDR *xdrs, amq_setopt *objp)
+{
+
+ if (!xdr_amq_opt(xdrs, &objp->as_opt)) {
+ return (FALSE);
+ }
+
+ if (!xdr_amq_string(xdrs, &objp->as_str)) {
+ return (FALSE);
+ }
+
+ return (TRUE);
+}
+
+
+bool_t
+xdr_pri_free(XDRPROC_T_TYPE xdr_args, caddr_t args_ptr)
+{
+ XDR xdr;
+
+ xdr.x_op = XDR_FREE;
+ return ((*xdr_args) (&xdr, (caddr_t *) args_ptr));
+}
diff --git a/contrib/amd/amq/pawd.1 b/contrib/amd/amq/pawd.1
new file mode 100644
index 000000000000..d04737260b9f
--- /dev/null
+++ b/contrib/amd/amq/pawd.1
@@ -0,0 +1,72 @@
+.\"
+.\" Copyright (c) 1997-1998 Erez Zadok
+.\" Copyright (c) 1990 Jan-Simon Pendry
+.\" Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+.\" Copyright (c) 1990 The Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Jan-Simon Pendry at Imperial College, London.
+.\"
+.\" 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.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgment:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+.\"
+.\" %W% (Berkeley) %G%
+.\"
+.\" $Id: pawd.1,v 5.2.2.1 1992/02/09 15:11:15 jsp beta $
+.\"
+.TH PAWD 1 "6 Jan 1998"
+.SH NAME
+pawd \- print automounter working directory
+.SH SYNOPSIS
+.B pawd
+[
+.I path ...
+]
+.SH DESCRIPTION
+.LP
+.B pawd
+is used to print the current working directory, adjusted to reflect proper
+paths that can be reused to go through the automounter for the shortest
+possible path. In particular, the path printed back does not include any
+of
+.BR Amd 's
+local mount points. Using them is unsafe, because
+.B Amd
+may unmount managed file systems from the mount points, and thus including
+them in paths may not always find the files within.
+.P
+Without any arguments,
+.B pawd
+will print the automounter adjusted current working directory. With any
+number of arguments, it will print the adjusted path of each one of the
+arguments.
+.SH "SEE ALSO"
+.BR amd (8),
+.BR amq (8),
+.BR pwd (1).
diff --git a/contrib/amd/amq/pawd.c b/contrib/amd/amq/pawd.c
new file mode 100644
index 000000000000..86ed55f118c4
--- /dev/null
+++ b/contrib/amd/amq/pawd.c
@@ -0,0 +1,296 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: pawd.c,v 5.2.2.1 1992/02/09 15:09:16 jsp beta $
+ *
+ */
+
+/*
+ * pawd is similar to pwd, except that it returns more "natural" versions of
+ * pathnames for directories automounted with the amd automounter. If any
+ * arguments are given, the "more natural" form of the given pathnames are
+ * printed.
+ *
+ * Paul Anderson (paul@ed.lfcs)
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amq.h>
+
+/* dummy variables */
+char *progname;
+char hostname[MAXHOSTNAMELEN];
+int orig_umask, foreground, debug_flags;
+pid_t mypid;
+serv_state amd_state;
+
+/* statics */
+static char *localhost="localhost";
+static char newdir[MAXPATHLEN];
+static char transform[MAXPATHLEN];
+
+static int
+find_mt(amq_mount_tree *mt, char *dir)
+{
+ while (mt) {
+ if (STREQ(mt->mt_type, "link") || STREQ(mt->mt_type, "nfs")) {
+ int len = strlen(mt->mt_mountpoint);
+ if (NSTREQ(mt->mt_mountpoint, dir, len) &&
+ ((dir[len] == '\0') || (dir[len] == '/'))) {
+ char tmp_buf[MAXPATHLEN];
+ strcpy(tmp_buf, mt->mt_directory);
+ strcat(tmp_buf, &dir[len]);
+ strcpy(newdir, tmp_buf);
+ return 1;
+ }
+ }
+ if (find_mt(mt->mt_next,dir))
+ return 1;
+ mt = mt->mt_child;
+ }
+ return 0;
+}
+
+
+static int
+find_mlp(amq_mount_tree_list *mlp, char *dir)
+{
+ int i;
+
+ for (i = 0; i < mlp->amq_mount_tree_list_len; i++) {
+ if (find_mt(mlp->amq_mount_tree_list_val[i], dir))
+ return 1;
+ }
+ return 0;
+}
+
+
+#ifdef HAVE_CNODEID
+static char *
+cluster_server(void)
+{
+# ifdef HAVE_EXTERN_GETCCENT
+ struct cct_entry *cp;
+# endif /* HAVE_EXTERN_GETCCENT */
+
+ if (cnodeid() == 0)
+ return localhost;
+
+# ifdef HAVE_EXTERN_GETCCENT
+ while ((cp = getccent()))
+ if (cp->cnode_type == 'r')
+ return cp->cnode_name;
+# endif /* HAVE_EXTERN_GETCCENT */
+
+ return localhost;
+}
+#endif /* HAVE_CNODEID */
+
+
+/* DISK_HOME_HACK added by gdmr */
+#ifdef DISK_HOME_HACK
+static char *
+hack_name(char *dir)
+{
+ char partition[MAXPATHLEN];
+ char username[MAXPATHLEN];
+ char hesiod_lookup[MAXPATHLEN];
+ char *to, *ch, *hes_name, *dot;
+ char **hes;
+
+#ifdef DEBUG
+ fprintf(stderr, "hack_name(%s)\n", dir);
+#endif /* DEBUG */
+
+ if (dir[0] == '/' && dir[1] == 'a' && dir[2] == '/') {
+ /* Could be /a/server/disk/home/partition/user... */
+ ch = dir + 3;
+ while (*ch && *ch != '/') ch++; /* Skip server */
+ if (!NSTREQ(ch, "/disk/home/", 11))
+ return NULL; /* Nope */
+ /* Looking promising, next should be the partition name */
+ ch += 11;
+ to = partition;
+ while (*ch && *ch != '/') *to++ = *ch++;
+ to = '\0';
+ if (!(*ch))
+ return NULL; /* Off the end */
+ /* Now the username */
+ ch++;
+ to = username;
+ while (*ch && *ch != '/') *to++ = *ch++;
+ to = '\0';
+#ifdef DEBUG
+ fprintf(stderr, "partition %s, username %s\n", partition, username);
+#endif /* DEBUG */
+
+ sprintf(hesiod_lookup, "%s.homes-remote", username);
+ hes = hes_resolve(hesiod_lookup, "amd");
+ if (!hes)
+ return NULL;
+#ifdef DEBUG
+ fprintf(stderr, "hesiod -> <%s>\n", *hes);
+#endif /* DEBUG */
+ hes_name = strstr(*hes, "/homes/remote/");
+ if (!hes_name) return NULL;
+ hes_name += 14;
+#ifdef DEBUG
+ fprintf(stderr, "hesiod -> <%s>\n", hes_name);
+#endif /* DEBUG */
+ dot = hes_name;
+ while (*dot && *dot != '.') dot++;
+ *dot = '\0';
+#ifdef DEBUG
+ fprintf(stderr, "hesiod -> <%s>\n", hes_name);
+#endif /* DEBUG */
+
+ if (strcmp(partition, hes_name)) return NULL;
+#ifdef DEBUG
+ fprintf(stderr, "A match, munging....\n");
+#endif /* DEBUG */
+ strcpy(transform, "/home/");
+ strcat(transform, username);
+ if (*ch) strcat(transform, ch);
+#ifdef DEBUG
+ fprintf(stderr, "Munged to <%s>\n", transform);
+#endif /* DEBUG */
+ return transform;
+ }
+ return NULL;
+}
+#endif /* DISK_HOME_HACK */
+
+
+/*
+ * The routine transform_dir(path) transforms pathnames of directories
+ * mounted with the amd automounter to produce a more "natural" version.
+ * The automount table is obtained from the local amd via the rpc interface
+ * and reverse lookups are repeatedly performed on the directory name
+ * substituting the name of the automount link for the value of the link
+ * whenever it occurs as a prefix of the directory name.
+ */
+static char *
+transform_dir(char *dir)
+{
+#ifdef DISK_HOME_HACK
+ char *ch;
+#endif /* DISK_HOME_HACK */
+ char *server;
+ struct sockaddr_in server_addr;
+ int s = RPC_ANYSOCK;
+ CLIENT *clnt;
+ struct hostent *hp;
+ amq_mount_tree_list *mlp;
+ struct timeval tmo = {10, 0};
+
+#ifdef DISK_HOME_HACK
+ if (ch = hack_name(dir))
+ return ch;
+#endif /* DISK_HOME_HACK */
+
+#ifdef HAVE_CNODEID
+ server = cluster_server();
+#else /* not HAVE_CNODEID */
+ server = localhost;
+#endif /* not HAVE_CNODEID */
+
+ if ((hp = gethostbyname(server)) == 0)
+ return dir;
+ memset(&server_addr, 0, sizeof(server_addr));
+ server_addr.sin_family = AF_INET;
+ server_addr.sin_addr = *(struct in_addr *) hp->h_addr;
+
+ clnt = clntudp_create(&server_addr, AMQ_PROGRAM, AMQ_VERSION, tmo, &s);
+ if (clnt == 0)
+ return dir;
+
+ strcpy(transform,dir);
+ while ( (mlp = amqproc_export_1((voidp)0, clnt)) &&
+ find_mlp(mlp,transform) ) {
+ strcpy(transform,newdir);
+ }
+ return transform;
+}
+
+
+/* getawd() is a substitute for getwd() which transforms the path */
+static char *
+getawd(char *path)
+{
+#ifdef HAVE_GETCWD
+ char *wd = getcwd(path, MAXPATHLEN+1);
+#else /* not HAVE_GETCWD */
+ char *wd = getwd(path);
+#endif /* not HAVE_GETCWD */
+
+ if (wd == NULL) {
+ return NULL;
+ }
+ strcpy(path, transform_dir(wd));
+ return path;
+}
+
+
+int
+main(int argc, char *argv[])
+{
+ char tmp_buf[MAXPATHLEN], *wd;
+
+ if (argc == 1) {
+ wd = getawd(tmp_buf);
+ if (wd == NULL) {
+ fprintf(stderr, "pawd: %s\n", tmp_buf);
+ exit(1);
+ } else {
+ fprintf(stdout, "%s\n", wd);
+ }
+ } else {
+ while (--argc) {
+ wd = transform_dir(*++argv);
+ fprintf(stdout, "%s\n", wd);
+ }
+ }
+ exit(0);
+}
+
diff --git a/contrib/amd/conf/checkmount/checkmount_bsd44.c b/contrib/amd/conf/checkmount/checkmount_bsd44.c
new file mode 100644
index 000000000000..80276e63a7e9
--- /dev/null
+++ b/contrib/amd/conf/checkmount/checkmount_bsd44.c
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: checkmount_bsd44.c,v 5.2.2.2 1992/05/31 16:35:45 jsp Exp $
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+
+extern int is_same_host(char *name1, char *name2, struct in_addr addr2);
+int fixmount_check_mount(char *host, struct in_addr hostaddr, char *path);
+
+int
+fixmount_check_mount(char *host, struct in_addr hostaddr, char *path)
+{
+ struct statfs *mntbufp, *mntp;
+ int nloc, i;
+ char *colon;
+
+ /* read mount table from kernel */
+ nloc = getmntinfo(&mntbufp, MNT_NOWAIT);
+ if (nloc <= 0) {
+ perror("getmntinfo");
+ exit(1);
+ }
+
+ mntp = mntbufp;
+ for (i=0; i<nloc; ++i) {
+ if ((colon = strchr(mntp->f_mntfromname, ':'))) {
+ *colon = '\0';
+ if (STREQ(colon + 1, path) &&
+ is_same_host(mntp->f_mntfromname, host, hostaddr))
+ return 1;
+ }
+ }
+
+ return 0;
+}
diff --git a/contrib/amd/conf/fh_dref/fh_dref_freebsd22.h b/contrib/amd/conf/fh_dref/fh_dref_freebsd22.h
new file mode 100644
index 000000000000..989561d267f6
--- /dev/null
+++ b/contrib/amd/conf/fh_dref/fh_dref_freebsd22.h
@@ -0,0 +1,2 @@
+/* $srcdir/conf/fh_dref/fh_dref_freebsd22.h */
+#define NFS_FH_DREF(dst, src) (dst) = (u_char *) (src)
diff --git a/contrib/amd/conf/hn_dref/hn_dref_default.h b/contrib/amd/conf/hn_dref/hn_dref_default.h
new file mode 100644
index 000000000000..d495ea8ca943
--- /dev/null
+++ b/contrib/amd/conf/hn_dref/hn_dref_default.h
@@ -0,0 +1,2 @@
+/* $srcdir/conf/hn_dref/hn_dref_default.h */
+#define NFS_HN_DREF(dst, src) (dst) = (src)
diff --git a/contrib/amd/conf/mount/mount_freebsd3.c b/contrib/amd/conf/mount/mount_freebsd3.c
new file mode 100644
index 000000000000..5888ed949d30
--- /dev/null
+++ b/contrib/amd/conf/mount/mount_freebsd3.c
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: mount_freebsd3.c,v 5.2.2.1 1992/02/09 15:10:08 jsp beta $
+ *
+ */
+
+/*
+ * FreeBSD 3.x (as of snapshot 3.0-980311-SNAP) Mount helper
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amu.h>
+
+/*
+ * Map from conventional mount arguments
+ * to FreeBSD 3.0 style arguments.
+ */
+int
+mount_freebsd3(MTYPE_TYPE type, const char *dir, int flags, voidp data)
+{
+ char const *namelist[] = INITMOUNTNAMES;
+
+ return mount(namelist[type],
+ dir,
+ flags,
+ data);
+}
diff --git a/contrib/amd/conf/mtab/mtab_bsd.c b/contrib/amd/conf/mtab/mtab_bsd.c
new file mode 100644
index 000000000000..100d7d5d6c3d
--- /dev/null
+++ b/contrib/amd/conf/mtab/mtab_bsd.c
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: mtab_bsd.c,v 5.2.2.2 1992/11/12 23:29:14 jsp Exp $
+ *
+ */
+
+/*
+ * BSD 4.4 systems don't write their mount tables on a file. Instead, they
+ * use a (better) system where the kernel keeps this state, and you access
+ * the mount tables via a known interface.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amu.h>
+
+
+static mntent_t *
+mnt_dup(struct statfs *mp)
+{
+ mntent_t *new_mp = ALLOC(mntent_t);
+ char *ty;
+
+ new_mp->mnt_fsname = strdup(mp->f_mntfromname);
+ new_mp->mnt_dir = strdup(mp->f_mntonname);
+
+#ifdef HAVE_FIELD_STRUCT_STATFS_F_FSTYPENAME
+ ty = mp->f_fstypename;
+#else /* not HAVE_FIELD_STRUCT_STATFS_F_FSTYPENAME */
+ switch (mp->f_type) {
+
+# if defined(MOUNT_UFS) && defined(MNTTAB_TYPE_UFS)
+ case MOUNT_UFS:
+ ty = MNTTAB_TYPE_UFS;
+ break;
+# endif /* defined(MOUNT_UFS) && defined(MNTTAB_TYPE_UFS) */
+
+# if defined(MOUNT_NFS) && defined(MNTTAB_TYPE_NFS)
+ case MOUNT_NFS:
+ ty = MNTTAB_TYPE_NFS;
+ break;
+# endif /* defined(MOUNT_NFS) && defined(MNTTAB_TYPE_NFS) */
+
+# if defined(MOUNT_MFS) && defined(MNTTAB_TYPE_MFS)
+ case MOUNT_MFS:
+ ty = MNTTAB_TYPE_MFS;
+ break;
+# endif /* defined(MOUNT_MFS) && defined(MNTTAB_TYPE_MFS) */
+
+ default:
+ ty = "unknown";
+
+ break;
+ }
+#endif /* not HAVE_FIELD_STRUCT_STATFS_F_FSTYPENAME */
+
+ new_mp->mnt_type = strdup(ty);
+ new_mp->mnt_opts = strdup("unset");
+ new_mp->mnt_freq = 0;
+ new_mp->mnt_passno = 0;
+
+ return new_mp;
+}
+
+
+/*
+ * Read a mount table into memory
+ */
+mntlist *
+read_mtab(char *fs, const char *mnttabname)
+{
+ mntlist **mpp, *mhp;
+ struct statfs *mntbufp, *mntp;
+
+ int nloc = getmntinfo(&mntbufp, MNT_NOWAIT);
+
+ if (nloc == 0) {
+ plog(XLOG_ERROR, "Can't read mount table");
+ return 0;
+ }
+ mpp = &mhp;
+ for (mntp = mntbufp; mntp < mntbufp + nloc; mntp++) {
+ /*
+ * Allocate a new slot
+ */
+ *mpp = ALLOC(struct mntlist);
+
+ /*
+ * Copy the data returned by getmntent
+ */
+ (*mpp)->mnt = mnt_dup(mntp);
+
+ /*
+ * Move to next pointer
+ */
+ mpp = &(*mpp)->mnext;
+ }
+
+ /*
+ * Terminate the list
+ */
+ *mpp = 0;
+
+ return mhp;
+}
diff --git a/contrib/amd/conf/nfs_prot/nfs_prot_freebsd2.h b/contrib/amd/conf/nfs_prot/nfs_prot_freebsd2.h
new file mode 100644
index 000000000000..e55e1145b28a
--- /dev/null
+++ b/contrib/amd/conf/nfs_prot/nfs_prot_freebsd2.h
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: nfs_prot_freebsd2.h,v 1.1 1996/01/13 23:23:39 ezk Exp ezk $
+ *
+ */
+
+#ifndef _AMU_NFS_PROT_H
+#define _AMU_NFS_PROT_H
+
+#ifdef HAVE_RPCSVC_NFS_PROT_H
+# include <rpcsvc/nfs_prot.h>
+#endif /* HAVE_RPCSVC_NFS_PROT_H */
+
+/*
+ * MACROS:
+ */
+#define dr_drok_u diropres
+#define ca_where where
+#define da_fhandle dir
+#define da_name name
+#define dl_entries entries
+#define dl_eof eof
+#define dr_status status
+#define dr_u diropres_u
+#define drok_attributes attributes
+#define drok_fhandle file
+#define fh_data data
+#define la_fhandle from
+#define la_to to
+#define na_atime atime
+#define na_ctime ctime
+#define na_fileid fileid
+#define na_fsid fsid
+#define na_mode mode
+#define na_mtime mtime
+#define na_nlink nlink
+#define na_size size
+#define na_type type
+#define ne_cookie cookie
+#define ne_fileid fileid
+#define ne_name name
+#define ne_nextentry nextentry
+#define ns_attr_u attributes
+#define ns_status status
+#define ns_u attrstat_u
+#define nt_seconds seconds
+#define nt_useconds useconds
+#define rda_cookie cookie
+#define rda_count count
+#define rda_fhandle dir
+#define rdr_reply_u reply
+#define rdr_status status
+#define rdr_u readdirres_u
+#define rlr_data_u data
+#define rlr_status status
+#define rlr_u readlinkres_u
+#define rna_from from
+#define rna_to to
+#define rr_status status
+#define sag_fhandle file
+#define sfr_reply_u reply
+#define sfr_status status
+#define sfr_u statfsres_u
+#define sfrok_bavail bavail
+#define sfrok_bfree bfree
+#define sfrok_blocks blocks
+#define sfrok_bsize bsize
+#define sfrok_tsize tsize
+#define sla_from from
+#define wra_fhandle file
+
+
+/*
+ * TYPEDEFS:
+ */
+typedef attrstat nfsattrstat;
+typedef createargs nfscreateargs;
+typedef dirlist nfsdirlist;
+typedef diropargs nfsdiropargs;
+typedef diropres nfsdiropres;
+typedef entry nfsentry;
+typedef fattr nfsfattr;
+typedef ftype nfsftype;
+typedef linkargs nfslinkargs;
+typedef readargs nfsreadargs;
+typedef readdirargs nfsreaddirargs;
+typedef readdirres nfsreaddirres;
+typedef readlinkres nfsreadlinkres;
+typedef readres nfsreadres;
+typedef renameargs nfsrenameargs;
+typedef sattrargs nfssattrargs;
+typedef statfsokres nfsstatfsokres;
+typedef statfsres nfsstatfsres;
+typedef symlinkargs nfssymlinkargs;
+typedef writeargs nfswriteargs;
+
+
+/*
+ * FreeBSD 2.2.x has NFS V3, but it does not define enough macros
+ * in the headers to automatically detect it.
+ * So fake it in the meant time.
+ */
+#if 0
+#define MOUNT_NFS3 MOUNT_NFS
+#endif
+
+
+#endif /* not _AMU_NFS_PROT_H */
diff --git a/contrib/amd/conf/nfs_prot/nfs_prot_freebsd3.h b/contrib/amd/conf/nfs_prot/nfs_prot_freebsd3.h
new file mode 100644
index 000000000000..915f01411a5c
--- /dev/null
+++ b/contrib/amd/conf/nfs_prot/nfs_prot_freebsd3.h
@@ -0,0 +1,211 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: nfs_prot_freebsd3.h,v 1.1 1996/01/13 23:23:39 ezk Exp ezk $
+ *
+ */
+
+#ifndef _AMU_NFS_PROT_H
+#define _AMU_NFS_PROT_H
+
+#ifdef HAVE_RPCSVC_NFS_PROT_H
+# include <rpcsvc/nfs_prot.h>
+#endif /* HAVE_RPCSVC_NFS_PROT_H */
+#ifdef HAVE_NFS_RPCV2_H
+# include <nfs/rpcv2.h>
+#endif /* HAVE_NFS_RPCV2_H */
+#ifdef HAVE_NFS_NFS_H
+# include <nfs/nfs.h>
+#endif /* HAVE_NFS_NFS_H */
+
+/*
+ * MACROS:
+ */
+#define dr_drok_u diropres
+#define ca_where where
+#define da_fhandle dir
+#define da_name name
+#define dl_entries entries
+#define dl_eof eof
+#define dr_status status
+#define dr_u diropres_u
+#define drok_attributes attributes
+#define drok_fhandle file
+#define fh_data data
+#define la_fhandle from
+#define la_to to
+#define na_atime atime
+#define na_ctime ctime
+#define na_fileid fileid
+#define na_fsid fsid
+#define na_mode mode
+#define na_mtime mtime
+#define na_nlink nlink
+#define na_size size
+#define na_type type
+#define ne_cookie cookie
+#define ne_fileid fileid
+#define ne_name name
+#define ne_nextentry nextentry
+#define ns_attr_u attributes
+#define ns_status status
+#define ns_u attrstat_u
+#define nt_seconds seconds
+#define nt_useconds useconds
+#define rda_cookie cookie
+#define rda_count count
+#define rda_fhandle dir
+#define rdr_reply_u reply
+#define rdr_status status
+#define rdr_u readdirres_u
+#define rlr_data_u data
+#define rlr_status status
+#define rlr_u readlinkres_u
+#define rna_from from
+#define rna_to to
+#define rr_status status
+#define sag_fhandle file
+#define sfr_reply_u reply
+#define sfr_status status
+#define sfr_u statfsres_u
+#define sfrok_bavail bavail
+#define sfrok_bfree bfree
+#define sfrok_blocks blocks
+#define sfrok_bsize bsize
+#define sfrok_tsize tsize
+#define sla_from from
+#define wra_fhandle file
+
+
+/*
+ * TYPEDEFS:
+ */
+typedef attrstat nfsattrstat;
+typedef createargs nfscreateargs;
+typedef dirlist nfsdirlist;
+typedef diropargs nfsdiropargs;
+typedef diropres nfsdiropres;
+typedef entry nfsentry;
+typedef fattr nfsfattr;
+typedef ftype nfsftype;
+typedef linkargs nfslinkargs;
+typedef readargs nfsreadargs;
+typedef readdirargs nfsreaddirargs;
+typedef readdirres nfsreaddirres;
+typedef readlinkres nfsreadlinkres;
+typedef readres nfsreadres;
+typedef renameargs nfsrenameargs;
+typedef sattrargs nfssattrargs;
+typedef statfsokres nfsstatfsokres;
+typedef statfsres nfsstatfsres;
+typedef symlinkargs nfssymlinkargs;
+typedef writeargs nfswriteargs;
+
+
+/*
+ *
+ * FreeBSD 3.0 has NFS V3, but you need to regenrate the rpcsc header files
+ * as follows:
+ * cd /usr/local/rpcsvc
+ * rpcgen -DWANT_NFS3 mount.x
+ * rpcgen -DWANT_NFS3 nfs_prot.x
+ * But that's not expected of everyone, plus there are additional things
+ * needed so I define everything that's neede for NFS V3 here.
+ */
+#ifdef NFSMNT_NFSV3
+
+# define MOUNT_NFS3 MOUNT_NFS
+# define MNTOPT_NFS3 "nfs"
+
+#define FHSIZE3 64 /* size in bytes of a file handle (v3) */
+#define NFS3_FHSIZE 64
+#define MOUNTVERS3 ((unsigned long)(3))
+#define NFS_V3 ((unsigned long)(3))
+
+typedef struct {
+ u_int fhandle3_len;
+ char *fhandle3_val;
+} fhandle3;
+
+enum mountstat3 {
+ MNT3_OK = 0,
+ MNT3ERR_PERM = 1,
+ MNT3ERR_NOENT = 2,
+ MNT3ERR_IO = 5,
+ MNT3ERR_ACCES = 13,
+ MNT3ERR_NOTDIR = 20,
+ MNT3ERR_INVAL = 22,
+ MNT3ERR_NAMETOOLONG = 63,
+ MNT3ERR_NOTSUPP = 10004,
+ MNT3ERR_SERVERFAULT = 10006
+};
+typedef enum mountstat3 mountstat3;
+
+struct mountres3_ok {
+ fhandle3 fhandle;
+ struct {
+ u_int auth_flavors_len;
+ int *auth_flavors_val;
+ } auth_flavors;
+};
+typedef struct mountres3_ok mountres3_ok;
+
+struct mountres3 {
+ mountstat3 fhs_status;
+ union {
+ mountres3_ok mountinfo;
+ } mountres3_u;
+};
+typedef struct mountres3 mountres3;
+
+struct nfs_fh3 {
+ u_int fh3_length;
+ union nfs_fh3_u {
+ struct nfs_fh3_i {
+ fhandle_t fh3_i;
+ } nfs_fh3_i;
+ char data[NFS3_FHSIZE];
+ } fh3_u;
+};
+typedef struct nfs_fh3 nfs_fh3;
+
+#endif /* NFSMNT_NFSV3 */
+
+#endif /* not _AMU_NFS_PROT_H */
diff --git a/contrib/amd/conf/sa_dref/sa_dref_bsd44.h b/contrib/amd/conf/sa_dref/sa_dref_bsd44.h
new file mode 100644
index 000000000000..e76c16b7b8b5
--- /dev/null
+++ b/contrib/amd/conf/sa_dref/sa_dref_bsd44.h
@@ -0,0 +1,5 @@
+/* $srcdir/conf/sa_dref/sa_dref_bsd44.h */
+#define NFS_SA_DREF(dst, src) { \
+ (dst)->addr = (struct sockaddr *) (src); \
+ (dst)->addrlen = sizeof(*src); \
+ }
diff --git a/contrib/amd/conf/transp/transp_sockets.c b/contrib/amd/conf/transp/transp_sockets.c
new file mode 100644
index 000000000000..45aa01eb782c
--- /dev/null
+++ b/contrib/amd/conf/transp/transp_sockets.c
@@ -0,0 +1,399 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: transp_sockets.c,v 5.2.2.2 1992/07/18 18:57:03 jsp Exp jsp $
+ *
+ * Socket specific utilities.
+ * -Erez Zadok <ezk@cs.columbia.edu>
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amu.h>
+
+
+/*
+ * find the IP address that can be used to connect to the local host
+ */
+void
+amu_get_myaddress(struct in_addr *iap)
+{
+ struct sockaddr_in sin;
+
+ memset((char *) &sin, 0, sizeof(sin));
+ get_myaddress(&sin);
+ iap->s_addr = sin.sin_addr.s_addr;
+}
+
+
+/*
+ * How to bind to reserved ports.
+ */
+int
+bind_resv_port(int so, u_short *pp)
+{
+ struct sockaddr_in sin;
+ int rc;
+ u_short port;
+
+ memset((voidp) &sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+
+ port = IPPORT_RESERVED;
+
+ do {
+ --port;
+ sin.sin_port = htons(port);
+ rc = bind(so, (struct sockaddr *) &sin, sizeof(sin));
+ } while (rc < 0 && (int) port > IPPORT_RESERVED / 2);
+
+ if (pp && rc == 0)
+ *pp = port;
+
+ return rc;
+}
+
+
+/*
+ * close a descriptor, Sockets style
+ */
+int
+amu_close(int fd)
+{
+ return close(fd);
+}
+
+
+/*
+ * Create an rpc client attached to the mount daemon.
+ */
+CLIENT *
+get_mount_client(char *unused_host, struct sockaddr_in *sin, struct timeval *tv, int *sock, u_long mnt_version)
+{
+ CLIENT *client;
+
+ /*
+ * First try a TCP socket
+ */
+ if ((*sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) > 0) {
+ /*
+ * Bind to a privileged port
+ */
+ if (bind_resv_port(*sock, (u_short *) 0) < 0)
+ plog(XLOG_ERROR, "can't bind privileged port");
+
+ /*
+ * Find mountd port to connect to.
+ * Connect to mountd.
+ * Create a tcp client.
+ */
+ if ((sin->sin_port = htons(pmap_getport(sin, MOUNTPROG, mnt_version, IPPROTO_TCP))) != 0) {
+ if (connect(*sock, (struct sockaddr *) sin, sizeof(*sin)) >= 0
+ && ((client = clnttcp_create(sin, MOUNTPROG, mnt_version, sock, 0, 0)) != NULL))
+ return client;
+ }
+ /*
+ * Failed so close socket
+ */
+ (void) close(*sock);
+ } /* tcp socket opened */
+ /* TCP failed so try UDP */
+ if ((*sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ plog(XLOG_ERROR, "Can't create socket to connect to mountd: %m");
+ *sock = RPC_ANYSOCK;
+ return NULL;
+ }
+ /*
+ * Bind to a privileged port
+ */
+ if (bind_resv_port(*sock, (u_short *) 0) < 0)
+ plog(XLOG_ERROR, "can't bind privileged port");
+
+ /*
+ * Zero out the port - make sure we recompute
+ */
+ sin->sin_port = 0;
+
+ /*
+ * Make a UDP client
+ */
+ if ((client = clntudp_create(sin, MOUNTPROG, mnt_version, *tv, sock)) == NULL) {
+ (void) close(*sock);
+ *sock = RPC_ANYSOCK;
+ return NULL;
+ }
+#ifdef DEBUG
+ dlog("get_mount_client: Using udp, port %d", sin->sin_port);
+#endif /* DEBUG */
+ return client;
+}
+
+
+/*
+ * find the address of the caller of an RPC procedure.
+ */
+struct sockaddr_in *
+amu_svc_getcaller(SVCXPRT *xprt)
+{
+ return svc_getcaller(xprt);
+}
+
+
+/*
+ * Bind NFS to a reserved port.
+ */
+static int
+bindnfs_port(int so, u_short *nfs_portp)
+{
+ u_short port;
+ int error = bind_resv_port(so, &port);
+
+ if (error == 0)
+ *nfs_portp = port;
+ return error;
+}
+
+
+/*
+ * Create the nfs service for amd
+ */
+int
+create_nfs_service(int *soNFSp, u_short *nfs_portp, SVCXPRT **nfs_xprtp, void (*dispatch_fxn)(struct svc_req *rqstp, SVCXPRT *transp))
+{
+
+ *soNFSp = socket(AF_INET, SOCK_DGRAM, 0);
+
+ if (*soNFSp < 0 || bindnfs_port(*soNFSp, nfs_portp) < 0) {
+ plog(XLOG_FATAL, "Can't create privileged nfs port");
+ return 1;
+ }
+
+ if ((*nfs_xprtp = svcudp_create(*soNFSp)) == NULL) {
+ plog(XLOG_FATAL, "cannot create rpc/udp service");
+ return 2;
+ }
+
+ if (!svc_register(*nfs_xprtp, NFS_PROGRAM, NFS_VERSION, dispatch_fxn, 0)) {
+ plog(XLOG_FATAL, "unable to register (NFS_PROGRAM, NFS_VERSION, 0)");
+ return 3;
+ }
+
+ return 0; /* all is well */
+}
+
+
+/*
+ * Create the amq service for amd (both TCP and UDP)
+ */
+int
+create_amq_service(int *udp_soAMQp, SVCXPRT **udp_amqpp, int *tcp_soAMQp, SVCXPRT **tcp_amqpp)
+{
+ /* first create TCP service */
+ if (tcp_soAMQp) {
+ *tcp_soAMQp = socket(AF_INET, SOCK_STREAM, 0);
+ if (*tcp_soAMQp < 0) {
+ plog(XLOG_FATAL, "cannot create tcp socket for amq service: %m");
+ return 1;
+ }
+
+ /* now create RPC service handle for amq */
+ if (tcp_amqpp &&
+ (*tcp_amqpp = svctcp_create(*tcp_soAMQp, AMQ_SIZE, AMQ_SIZE)) == NULL) {
+ plog(XLOG_FATAL, "cannot create tcp service for amq: soAMQp=%d", *tcp_soAMQp);
+ return 2;
+ }
+ }
+
+ /* next create UDP service */
+ if (udp_soAMQp) {
+ *udp_soAMQp = socket(AF_INET, SOCK_DGRAM, 0);
+ if (*udp_soAMQp < 0) {
+ plog(XLOG_FATAL, "cannot create udp socket for amq service: %m");
+ return 3;
+ }
+
+ /* now create RPC service handle for amq */
+ if (udp_amqpp &&
+ (*udp_amqpp = svcudp_bufcreate(*udp_soAMQp, AMQ_SIZE, AMQ_SIZE)) == NULL) {
+ plog(XLOG_FATAL, "cannot create udp service for amq: soAMQp=%d", *udp_soAMQp);
+ return 4;
+ }
+ }
+
+ return 0; /* all is well */
+}
+
+
+/*
+ * Ping the portmapper on a remote system by calling the nullproc
+ */
+enum clnt_stat
+pmap_ping(struct sockaddr_in *address)
+{
+ CLIENT *client;
+ enum clnt_stat clnt_stat = RPC_TIMEDOUT; /* assume failure */
+ int socket = RPC_ANYSOCK;
+ struct timeval timeout;
+
+ timeout.tv_sec = 3;
+ timeout.tv_usec = 0;
+ address->sin_port = htons(PMAPPORT);
+ client = clntudp_create(address, PMAPPROG, PMAPVERS, timeout, &socket);
+ if (client != (CLIENT *) NULL) {
+ clnt_stat = clnt_call(client,
+ PMAPPROC_NULL,
+ (XDRPROC_T_TYPE) xdr_void,
+ NULL,
+ (XDRPROC_T_TYPE) xdr_void,
+ NULL,
+ timeout);
+ clnt_destroy(client);
+ }
+ close(socket);
+ address->sin_port = 0;
+
+ return clnt_stat;
+}
+
+
+/*
+ * Find the best NFS version for a host and protocol.
+ */
+u_long
+get_nfs_version(char *host, struct sockaddr_in *sin, u_long nfs_version, const char *proto)
+{
+ CLIENT *clnt;
+ int again = 0;
+ enum clnt_stat clnt_stat;
+ struct timeval tv;
+ int sock;
+
+ /*
+ * If not set or set wrong, then try from NFS_VERS_MAX on down. If
+ * set, then try from nfs_version on down.
+ */
+ if (nfs_version <= 0 || nfs_version > NFS_VERS_MAX) {
+ nfs_version = NFS_VERS_MAX;
+ again = 1;
+ }
+ tv.tv_sec = 3; /* retry every 3 seconds, but also timeout */
+ tv.tv_usec = 0;
+
+ /*
+ * First check if remote portmapper is up (verify if remote host is up).
+ */
+ clnt_stat = pmap_ping(sin);
+ if (clnt_stat == RPC_TIMEDOUT) {
+ plog(XLOG_ERROR, "get_nfs_version: failed to contact portmapper on host \"%s\": %s", host, clnt_sperrno(clnt_stat));
+ return 0;
+ }
+
+#ifdef HAVE_FS_NFS3
+try_again:
+#endif /* HAVE_FS_NFS3 */
+
+ sock = RPC_ANYSOCK;
+ if (STREQ(proto, "tcp"))
+ clnt = clnttcp_create(sin, NFS_PROGRAM, nfs_version, &sock, 0, 0);
+ else if (STREQ(proto, "udp"))
+ clnt = clntudp_create(sin, NFS_PROGRAM, nfs_version, tv, &sock);
+ else
+ clnt = NULL;
+
+ if (clnt == NULL) {
+#ifdef HAVE_CLNT_SPCREATEERROR
+ plog(XLOG_INFO, "get_nfs_version NFS(%d,%s) failed for %s :%s",
+ nfs_version, proto, host, clnt_spcreateerror(""));
+#else /* not HAVE_CLNT_SPCREATEERROR */
+ plog(XLOG_INFO, "get_nfs_version NFS(%d,%s) failed for %s",
+ nfs_version, proto, host);
+#endif /* not HAVE_CLNT_SPCREATEERROR */
+ return 0;
+ }
+
+ /* Try a couple times to verify the CLIENT handle. */
+ tv.tv_sec = 6;
+ clnt_stat = clnt_call(clnt,
+ NFSPROC_NULL,
+ (XDRPROC_T_TYPE) xdr_void,
+ 0,
+ (XDRPROC_T_TYPE) xdr_void,
+ 0,
+ tv);
+ close(sock);
+ clnt_destroy(clnt);
+ if (clnt_stat != RPC_SUCCESS) {
+ if (again) {
+#ifdef HAVE_FS_NFS3
+ if (nfs_version == NFS_VERSION3) {
+ plog(XLOG_INFO, "get_nfs_version trying a lower version");
+ nfs_version = NFS_VERSION;
+ again = 0;
+ }
+ goto try_again;
+#endif /* HAVE_FS_NFS3 */
+ }
+ plog(XLOG_INFO, "get_nfs_version NFS(%d,%s) failed for %s",
+ nfs_version, proto, host);
+ return 0;
+ }
+
+ plog(XLOG_INFO, "get_nfs_version: returning (%d,%s) on host %s",
+ nfs_version, proto, host);
+ return nfs_version;
+}
+
+
+/*
+ * AUTOFS FUNCTIONS FOR SOCKETS:
+ */
+#ifdef HAVE_FS_AUTOFS
+/*
+ * Create the nfs service for amd
+ */
+int
+create_autofs_service(int *soAUTOFSp, u_short *autofs_portp, SVCXPRT **autofs_xprtp, void (*dispatch_fxn)(struct svc_req *rqstp, SVCXPRT *transp))
+{
+ /* NOT IMPLEMENTED! */
+ return -1;
+}
+#endif /* HAVE_FS_AUTOFS */
diff --git a/contrib/amd/conf/trap/trap_default.h b/contrib/amd/conf/trap/trap_default.h
new file mode 100644
index 000000000000..a132b0ec4931
--- /dev/null
+++ b/contrib/amd/conf/trap/trap_default.h
@@ -0,0 +1,2 @@
+/* $srcdir/conf/trap/trap_default.h */
+#define MOUNT_TRAP(type, mnt, flags, mnt_data) mount(type, mnt->mnt_dir, flags, mnt_data)
diff --git a/contrib/amd/conf/trap/trap_freebsd3.h b/contrib/amd/conf/trap/trap_freebsd3.h
new file mode 100644
index 000000000000..95c664254c05
--- /dev/null
+++ b/contrib/amd/conf/trap/trap_freebsd3.h
@@ -0,0 +1,3 @@
+/* $srcdir/conf/trap/trap_freebsd3.h */
+extern int mount_freebsd3(MTYPE_TYPE type, const char *dir, int flags, voidp data);
+#define MOUNT_TRAP(type, mnt, flags, mnt_data) mount_freebsd3(type, mnt->mnt_dir, flags, mnt_data)
diff --git a/contrib/amd/conf/umount/umount_bsd44.c b/contrib/amd/conf/umount/umount_bsd44.c
new file mode 100644
index 000000000000..4e241b388d1b
--- /dev/null
+++ b/contrib/amd/conf/umount/umount_bsd44.c
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: umount_bsd44.c,v 5.2.2.2 1993/01/27 07:32:45 jsp Exp $
+ *
+ */
+
+/*
+ * Unmounting filesystems under BSD 4.4.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amu.h>
+
+
+int
+umount_fs(char *fs_name, const char *mnttabname)
+{
+ int error;
+
+eintr:
+ error = unmount(fs_name, 0);
+ if (error < 0)
+ error = errno;
+
+ switch (error) {
+ case EINVAL:
+ case ENOTBLK:
+ case ENOENT:
+ plog(XLOG_WARNING, "unmount: %s is not mounted", fs_name);
+ error = 0; /* Not really an error */
+ break;
+
+ case EINTR:
+#ifdef DEBUG
+ /* not sure why this happens, but it does. ask kirk one day... */
+ dlog("%s: unmount: %m", fs_name);
+#endif /* DEBUG */
+ goto eintr;
+
+#ifdef DEBUG
+ default:
+ dlog("%s: unmount: %m", fs_name);
+ break;
+#endif /* DEBUG */
+ }
+
+ return error;
+}
diff --git a/contrib/amd/doc/am-utils.texi b/contrib/amd/doc/am-utils.texi
new file mode 100644
index 000000000000..ca545031e180
--- /dev/null
+++ b/contrib/amd/doc/am-utils.texi
@@ -0,0 +1,7816 @@
+\input texinfo @c -*-texinfo-*-
+@c
+@c Copyright (c) 1997-1998 Erez Zadok
+@c Copyright (c) 1989 Jan-Simon Pendry
+@c Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+@c Copyright (c) 1989 The Regents of the University of California.
+@c All rights reserved.
+@c
+@c This code is derived from software contributed to Berkeley by
+@c Jan-Simon Pendry at Imperial College, London.
+@c
+@c Redistribution and use in source and binary forms, with or without
+@c modification, are permitted provided that the following conditions
+@c are met:
+@c 1. Redistributions of source code must retain the above copyright
+@c notice, this list of conditions and the following disclaimer.
+@c 2. Redistributions in binary form must reproduce the above copyright
+@c notice, this list of conditions and the following disclaimer in the
+@c documentation and/or other materials provided with the distribution.
+@c 3. All advertising materials mentioning features or use of this software
+@c must display the following acknowledgment:
+@c This product includes software developed by the University of
+@c California, Berkeley and its contributors.
+@c 4. Neither the name of the University nor the names of its contributors
+@c may be used to endorse or promote products derived from this software
+@c without specific prior written permission.
+@c
+@c THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+@c ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+@c IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+@c ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+@c FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+@c DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+@c OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+@c HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+@c LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+@c OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+@c
+@c %W% (Berkeley) %G%
+@c
+@c $Id: am-utils.texi,v 6.0 1997/02/09 15:11:50 ezk beta $
+@c
+@setfilename am-utils.info
+
+@include version.texi
+
+@c info directory entry
+@direntry
+* Am-utils: (am-utils). The Amd automounter suite of utilities
+@end direntry
+
+@titlepage
+@title Am-utils (4.4BSD Automounter Utilities)
+@subtitle For version @value{VERSION}, @value{UPDATED}
+
+@author Erez Zadok
+(Originally by Jan-Simon Pendry and Nick Williams)
+
+@page
+Copyright @copyright{} 1997-1998 Erez Zadok
+@*
+Copyright @copyright{} 1989 Jan-Simon Pendry
+@*
+Copyright @copyright{} 1989 Imperial College of Science, Technology & Medicine
+@*
+Copyright @copyright{} 1989 The Regents of the University of California.
+@sp
+All Rights Reserved.
+@vskip 1ex
+Permission to copy this document, or any portion of it, as
+necessary for use of this software is granted provided this
+copyright notice and statement of permission are included.
+@end titlepage
+@page
+
+@c Define a new index for options.
+@syncodeindex pg cp
+@syncodeindex vr cp
+
+@ifinfo
+
+@c ################################################################
+@node Top, License, , (DIR)
+Am-utils - The 4.4BSD Automounter Tool Suite
+*********************************************
+
+Am-utils is the 4.4BSD Automounter Tool Suite, which includes the Amd
+automounter, the Amq query and control program, the Hlfsd daemon, and
+other tools. This Info file describes how to use and understand the
+tools within Am-utils.
+@end ifinfo
+
+@menu
+* License:: Explains the terms and conditions for using
+ and distributing Am-utils.
+* Distrib:: How to get the latest Am-utils distribution.
+* Intro:: An introduction to Automounting concepts.
+* History:: History of am-utils' development.
+* Overview:: An overview of Amd.
+* Supported Platforms:: Machines and Systems supported by Amd.
+* Mount Maps:: Details of mount maps
+* Amd Command Line Options:: All the Amd command line options explained.
+* Filesystem Types:: The different mount types supported by Amd.
+* Amd Configuration File:: The amd.conf file syntax and meaning.
+* Run-time Administration:: How to start, stop and control Amd.
+* FSinfo:: The FSinfo filesystem management tool.
+* Hlfsd:: The Home-Link Filesystem server.
+* Assorted Tools:: Other tools which come with am-utils.
+* Examples:: Some examples showing how Amd might be used.
+* Internals:: Implementation details.
+* Acknowledgments & Trademarks:: Legal Notes
+
+Indexes
+* Index:: An item for each concept.
+@end menu
+
+@iftex
+@unnumbered Preface
+
+This manual documents the use of the 4.4BSD automounter tool suite,
+which includes @i{Amd}, @i{Amq}, @i{Hlfsd}, and other programs. This is
+primarily a reference manual. While no tutorial exists, there are
+examples available. @xref{Examples}.
+
+This manual comes in two forms: the published form and the Info form.
+The Info form is for on-line perusal with the INFO program which is
+distributed along with GNU texinfo package (a version of which is
+available for GNU Emacs).@footnote{GNU packages can be found in
+@url{ftp://ftp.gnu.org/pub/gnu/}.} Both forms contain substantially
+the same text and are generated from a common source file, which is
+distributed with the @i{Am-utils} source.
+@end iftex
+
+@c ################################################################
+@node License, Distrib, Top, Top
+@unnumbered License
+@cindex License Information
+
+@i{Am-utils} is not in the public domain; it is copyrighted and there are
+restrictions on its distribution.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+@enumerate
+
+@item
+Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+
+@item
+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.
+
+@item
+All advertising materials mentioning features or use of this software
+must display the following acknowledgment:
+
+@cartouche
+``This product includes software developed by the University of
+California, Berkeley and its contributors, as well as the Trustees of
+Columbia University.''
+@end cartouche
+
+@item
+Neither the name of the University nor the names of its contributors may
+be used to endorse or promote products derived from this software
+without specific prior written permission.
+
+@end enumerate
+
+THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+
+@c ################################################################
+@node Distrib, Intro, License, Top
+@unnumbered Source Distribution
+@cindex Source code distribution
+@cindex Obtaining the source code
+
+The @i{Am-utils} home page is located in
+@example
+@url{http://www.cs.columbia.edu/~ezk/am-utils/}
+@end example
+
+You can get the latest distribution version of @i{Am-utils} from
+@example
+@url{ftp://shekel.mcl.cs.columbia.edu/pub/am-utils/am-utils.tar.gz}
+@end example
+
+Alpha and beta distributions are available in
+@example
+@url{ftp://shekel.mcl.cs.columbia.edu/pub/am-utils/}.
+@end example
+
+Revision 5.2 was part of the 4.3BSD Reno distribution.
+
+Revision 5.3bsdnet, a late alpha version of 5.3, was part
+of the BSD network version 2 distribution
+
+Revision 6.0 was made independently by @email{ezk@@cs.columbia.edu,Erez
+Zadok} at the @uref{http://www.cs.columbia.edu/,Computer Science
+Department} of @uref{http://www.columbia.edu/,Columbia University}, as
+part of his @uref{http://www.cs.columbia.edu/~ezk/research/tp/thesis_proposal.html,PhD thesis work}. @xref{History} for more details.
+
+@unnumberedsec Bug Reports
+@cindex Bug reports
+
+Before reporting a bug, see if it is a known one in the
+@uref{http://www.cs.columbia.edu/~ezk/am-utils/BUGS.txt,bugs} file.
+Send all bug reports to @email{amd-dev@@majordomo.cs.columbia.edu}
+quoting the details of the release and your configuration. These can be
+obtained by running the command @samp{amd -v}. It would greatly help if
+you could provide a reproducible procedure for detecting the bug you are
+reporting.
+
+Providing working patches is highly encouraged. Every patch
+incorporated, however small, will get its author an honorable mention in
+the @uref{http://www.cs.columbia.edu/~ezk/am-utils/AUTHORS.txt,authors
+file}.
+
+@unnumberedsec Mailing List
+@cindex Mailing list
+
+There are two mailing lists for people interested in keeping up-to-date
+with developments.
+
+@c ###############
+
+@enumerate
+
+@item
+The older list, @samp{amd-workers} is for general "how to" questions and
+announcements. To subscribe, send a note to
+@email{amd-workers-request@@majordomo.glue.umd.edu}.@footnote{Note that
+the older address, @email{amd-workers-request@@acl.lanl.gov}, is
+defunct.} To post a message to this list, send mail to
+@email{amd-workers@@majordomo.glue.umd.edu}.
+
+@item
+The developers only list, @samp{amd-dev} is for
+
+@itemize @minus
+@item
+announcements of alpha and beta releases of am-utils
+@item
+reporting of bugs and patches
+@item
+discussions of new features for am-utils
+@item
+implementation and porting issues
+@end itemize
+
+To subscribe, send a note to @email{majordomo@@majordomo.cs.columbia.edu}
+with the single body text line @samp{subscribe amd-dev}. To post a
+message to this list, send mail to
+@email{amd-dev@@majordomo.cs.columbia.edu}. To avoid as much spam as
+possible, only subscribers to this list may post to it.
+
+Subscribers of @samp{amd-dev} are most suitable if they have the time
+and resources to test new and buggy versions of amd, on as many
+different platforms as possible. They should also be prepared to learn
+and use the GNU Autoconf, Automake, and Libtool packages, and of course,
+be very familiar with the complex code in the am-utils package. In
+other words, subscribers on this list should be able to contribute
+meaningfully to the development of amd.
+
+@end enumerate
+
+@c ################################################################
+@node Intro, History, Distrib, Top
+@unnumbered Introduction
+@cindex Introduction
+
+An @dfn{automounter} maintains a cache of mounted filesystems.
+Filesystems are mounted on demand when they are first referenced,
+and unmounted after a period of inactivity.
+
+@i{Amd} may be used as a replacement for Sun's automounter. The choice
+of which filesystem to mount can be controlled dynamically with
+@dfn{selectors}. Selectors allow decisions of the form ``hostname is
+@var{this},'' or ``architecture is not @var{that}.'' Selectors may be
+combined arbitrarily. @i{Amd} also supports a variety of filesystem
+types, including NFS, UFS and the novel @dfn{program} filesystem. The
+combination of selectors and multiple filesystem types allows identical
+configuration files to be used on all machines thus reducing the
+administrative overhead.
+
+@i{Amd} ensures that it will not hang if a remote server goes down.
+Moreover, @i{Amd} can determine when a remote server has become
+inaccessible and then mount replacement filesystems as and when they
+become available.
+
+@i{Amd} contains no proprietary source code and has been ported to
+numerous flavors of Unix.
+
+@c ################################################################
+@node History, Overview, Intro, Top
+@unnumbered History
+@cindex History
+
+The @i{Amd} package has been without an official maintainer since 1992.
+Several people have stepped in to maintain it unofficially. Most
+notable were the `upl' (Unofficial Patch Level) releases of @i{Amd},
+created by me (@email{ezk@@cs.columbia.edu,Erez Zadok}), and available from
+@url{ftp://ftp.cs.columbia.edu/pub/amd/}. The last such unofficial
+release was `upl102'.
+
+Through the process of patching and aging, it was becoming more and more
+apparent that @i{Amd} was in much need of revitalizing. Maintaining
+@i{Amd} had become a difficult task. I took it upon myself to cleanup
+the code, so that it would be easier to port to new platforms, add new
+features, keep up with the many new feature requests, and deal with the
+never ending stream of bug reports.
+
+I have been working on such a release of @i{Amd} on and off since
+January of 1996. The new suite of tools is currently named "am-utils"
+(AutoMounter Utilities), in line with GNU naming conventions, befitting
+the contents of the package. In October of 1996 I had received enough
+offers to help me with this task that I decided to make a mailing list
+for this group of people. Around the same time, @i{Amd} had become a
+necessary part of my PhD thesis work, resulting in more work performed
+on am-utils.
+
+Am-utils version 6.0 was numbered with a major new release number to
+distinguish it from the last official release of @i{Amd} (5.x). Many
+new features have been added such as a GNU @code{configure} system, NFS
+Version 3, Autofs support, a run-time configuration file (`amd.conf'),
+many new ports, more scripts and programs, as well as numerous bug
+fixes. Another reason for the new major release number was to alert
+users of am-utils that user-visible interfaces may have changed. In
+order to make @i{Amd} work well for the next 10 years, and be easier to
+maintain, it was necessary to remove old or unused features, change
+various syntax files, etc. However, great care was taken to ensure the
+maximum possible backwards compatibility.
+
+@c ################################################################
+@node Overview, Supported Platforms, History, Top
+@chapter Overview
+
+@i{Amd} maintains a cache of mounted filesystems. Filesystems are
+@dfn{demand-mounted} when they are first referenced, and unmounted after
+a period of inactivity. @i{Amd} may be used as a replacement for Sun's
+@b{automount}(8) program. It contains no proprietary source code and
+has been ported to numerous flavors of Unix. @xref{Supported
+Platforms}.@refill
+
+@i{Amd} was designed as the basis for experimenting with filesystem
+layout and management. Although @i{Amd} has many direct applications it
+is loaded with additional features which have little practical use. At
+some point the infrequently used components may be removed to streamline
+the production system.
+
+@c @i{Amd} supports the notion of @dfn{replicated} filesystems by evaluating
+@c each member of a list of possible filesystem locations in parallel.
+@c @i{Amd} checks that each cached mapping remains valid. Should a mapping be
+@c lost -- such as happens when a fileserver goes down -- @i{Amd} automatically
+@c selects a replacement should one be available.
+@c
+@menu
+* Fundamentals::
+* Filesystems and Volumes::
+* Volume Naming::
+* Volume Binding::
+* Operational Principles::
+* Mounting a Volume::
+* Automatic Unmounting::
+* Keep-alives::
+* Non-blocking Operation::
+@end menu
+
+@node Fundamentals, Filesystems and Volumes, Overview, Overview
+@comment node-name, next, previous, up
+@section Fundamentals
+@cindex Automounter fundamentals
+
+The fundamental concept behind @i{Amd} is the ability to separate the
+name used to refer to a file from the name used to refer to its physical
+storage location. This allows the same files to be accessed with the
+same name regardless of where in the network the name is used. This is
+very different from placing @file{/n/hostname} in front of the pathname
+since that includes location dependent information which may change if
+files are moved to another machine.
+
+By placing the required mappings in a centrally administered database,
+filesystems can be re-organized without requiring changes to
+configuration files, shell scripts and so on.
+
+@node Filesystems and Volumes, Volume Naming, Fundamentals, Overview
+@comment node-name, next, previous, up
+@section Filesystems and Volumes
+@cindex Filesystem
+@cindex Volume
+@cindex Fileserver
+@cindex sublink
+
+@i{Amd} views the world as a set of fileservers, each containing one or
+more filesystems where each filesystem contains one or more
+@dfn{volumes}. Here the term @dfn{volume} is used to refer to a
+coherent set of files such as a user's home directory or a @TeX{}
+distribution.@refill
+
+In order to access the contents of a volume, @i{Amd} must be told in
+which filesystem the volume resides and which host owns the filesystem.
+By default the host is assumed to be local and the volume is assumed to
+be the entire filesystem. If a filesystem contains more than one
+volume, then a @dfn{sublink} is used to refer to the sub-directory
+within the filesystem where the volume can be found.
+
+@node Volume Naming, Volume Binding, Filesystems and Volumes, Overview
+@comment node-name, next, previous, up
+@section Volume Naming
+@cindex Volume names
+@cindex Network-wide naming
+@cindex Replicated volumes
+@cindex Duplicated volumes
+@cindex Replacement volumes
+
+Volume names are defined to be unique across the entire network. A
+volume name is the pathname to the volume's root as known by the users
+of that volume. Since this name uniquely identifies the volume
+contents, all volumes can be named and accessed from each host, subject
+to administrative controls.
+
+Volumes may be replicated or duplicated. Replicated volumes contain
+identical copies of the same data and reside at two or more locations in
+the network. Each of the replicated volumes can be used
+interchangeably. Duplicated volumes each have the same name but contain
+different, though functionally identical, data. For example,
+@samp{/vol/tex} might be the name of a @TeX{} distribution which varied
+for each machine architecture.@refill
+
+@i{Amd} provides facilities to take advantage of both replicated and
+duplicated volumes. Configuration options allow a single set of
+configuration data to be shared across an entire network by taking
+advantage of replicated and duplicated volumes.
+
+@i{Amd} can take advantage of replacement volumes by mounting them as
+required should an active fileserver become unavailable.
+
+@node Volume Binding, Operational Principles, Volume Naming, Overview
+@comment node-name, next, previous, up
+@section Volume Binding
+@cindex Volume binding
+@cindex Unix namespace
+@cindex Namespace
+@cindex Binding names to filesystems
+
+Unix implements a namespace of hierarchically mounted filesystems. Two
+forms of binding between names and files are provided. A @dfn{hard
+link} completes the binding when the name is added to the filesystem. A
+@dfn{soft link} delays the binding until the name is accessed. An
+@dfn{automounter} adds a further form in which the binding of name to
+filesystem is delayed until the name is accessed.@refill
+
+The target volume, in its general form, is a tuple (host, filesystem,
+sublink) which can be used to name the physical location of any volume
+in the network.
+
+When a target is referenced, @i{Amd} ignores the sublink element and
+determines whether the required filesystem is already mounted. This is
+done by computing the local mount point for the filesystem and checking
+for an existing filesystem mounted at the same place. If such a
+filesystem already exists then it is assumed to be functionally
+identical to the target filesystem. By default there is a one-to-one
+mapping between the pair (host, filesystem) and the local mount point so
+this assumption is valid.
+
+@node Operational Principles, Mounting a Volume, Volume Binding, Overview
+@comment node-name, next, previous, up
+@section Operational Principles
+@cindex Operational principles
+
+@i{Amd} operates by introducing new mount points into the namespace.
+These are called @dfn{automount} points. The kernel sees these
+automount points as NFS filesystems being served by @i{Amd}. Having
+attached itself to the namespace, @i{Amd} is now able to control the
+view the rest of the system has of those mount points. RPC calls are
+received from the kernel one at a time.
+
+When a @dfn{lookup} call is received @i{Amd} checks whether the name is
+already known. If it is not, the required volume is mounted. A
+symbolic link pointing to the volume root is then returned. Once the
+symbolic link is returned, the kernel will send all other requests
+direct to the mounted filesystem.
+
+If a volume is not yet mounted, @i{Amd} consults a configuration
+@dfn{mount-map} corresponding to the automount point. @i{Amd} then
+makes a runtime decision on what and where to mount a filesystem based
+on the information obtained from the map.
+
+@i{Amd} does not implement all the NFS requests; only those relevant
+to name binding such as @dfn{lookup}, @dfn{readlink} and @dfn{readdir}.
+Some other calls are also implemented but most simply return an error
+code; for example @dfn{mkdir} always returns ``read-only filesystem''.
+
+@node Mounting a Volume, Automatic Unmounting, Operational Principles, Overview
+@comment node-name, next, previous, up
+@section Mounting a Volume
+@cindex Mounting a volume
+@cindex Location lists
+@cindex Alternate locations
+@cindex Mount retries
+@cindex Background mounts
+
+Each automount point has a corresponding mount map. The mount map
+contains a list of key--value pairs. The key is the name of the volume
+to be mounted. The value is a list of locations describing where the
+filesystem is stored in the network. In the source for the map the
+value would look like
+
+@display
+location1 location2 @dots{} locationN
+@end display
+
+@i{Amd} examines each location in turn. Each location may contain
+@dfn{selectors} which control whether @i{Amd} can use that location.
+For example, the location may be restricted to use by certain hosts.
+Those locations which cannot be used are ignored.
+
+@i{Amd} attempts to mount the filesystem described by each remaining
+location until a mount succeeds or @i{Amd} can no longer proceed. The
+latter can occur in three ways:
+
+@itemize @bullet
+@item
+If none of the locations could be used, or if all of the locations
+caused an error, then the last error is returned.
+
+@item
+If a location could be used but was being mounted in the background then
+@i{Amd} marks that mount as being ``in progress'' and continues with
+the next request; no reply is sent to the kernel.
+
+@item
+Lastly, one or more of the mounts may have been @dfn{deferred}. A mount
+is deferred if extra information is required before the mount can
+proceed. When the information becomes available the mount will take
+place, but in the mean time no reply is sent to the kernel. If the
+mount is deferred, @i{Amd} continues to try any remaining locations.
+@end itemize
+
+Once a volume has been mounted, @i{Amd} establishes a @dfn{volume
+mapping} which is used to satisfy subsequent requests.@refill
+
+@node Automatic Unmounting, Keep-alives, Mounting a Volume, Overview
+@comment node-name, next, previous, up
+@section Automatic Unmounting
+
+To avoid an ever increasing number of filesystem mounts, @i{Amd} removes
+volume mappings which have not been used recently. A time-to-live
+interval is associated with each mapping and when that expires the
+mapping is removed. When the last reference to a filesystem is removed,
+that filesystem is unmounted. If the unmount fails, for example the
+filesystem is still busy, the mapping is re-instated and its
+time-to-live interval is extended. The global default for this grace
+period is controlled by the @code{-w} command-line option (@pxref{-w
+Option, -w}) or the @i{amd.conf} parameter @samp{dismount_interval}
+(@pxref{dismount_interval Parameter}). It is also possible to set this
+value on a per-mount basis (@pxref{opts Option, opts, opts}).
+
+Filesystems can be forcefully timed out using the @i{Amq} command.
+@xref{Run-time Administration}.
+
+@node Keep-alives, Non-blocking Operation, Automatic Unmounting, Overview
+@comment node-name, next, previous, up
+@section Keep-alives
+@cindex Keep-alives
+@cindex Server crashes
+@cindex NFS ping
+
+Use of some filesystem types requires the presence of a server on
+another machine. If a machine crashes then it is of no concern to
+processes on that machine that the filesystem is unavailable. However,
+to processes on a remote host using that machine as a fileserver this
+event is important. This situation is most widely recognized when an
+NFS server crashes and the behavior observed on client machines is that
+more and more processes hang. In order to provide the possibility of
+recovery, @i{Amd} implements a @dfn{keep-alive} interval timer for some
+filesystem types. Currently only NFS makes use of this service.
+
+The basis of the NFS keep-alive implementation is the observation that
+most sites maintain replicated copies of common system data such as
+manual pages, most or all programs, system source code and so on. If
+one of those servers goes down it would be reasonable to mount one of
+the others as a replacement.
+
+The first part of the process is to keep track of which fileservers are
+up and which are down. @i{Amd} does this by sending RPC requests to the
+servers' NFS @code{NullProc} and checking whether a reply is returned.
+While the server state is uncertain the requests are re-transmitted at
+three second intervals and if no reply is received after four attempts
+the server is marked down. If a reply is received the fileserver is
+marked up and stays in that state for 30 seconds at which time another
+NFS ping is sent.
+
+Once a fileserver is marked down, requests continue to be sent every 30
+seconds in order to determine when the fileserver comes back up. During
+this time any reference through @i{Amd} to the filesystems on that
+server fail with the error ``Operation would block''. If a replacement
+volume is available then it will be mounted, otherwise the error is
+returned to the user.
+
+@c @i{Amd} keeps track of which servers are up and which are down.
+@c It does this by sending RPC requests to the servers' NFS {\sc NullProc} and
+@c checking whether a reply is returned. If no replies are received after a
+@c short period, @i{Amd} marks the fileserver @dfn{down}.
+@c RPC requests continue to be sent so that it will notice when a fileserver
+@c comes back up.
+@c ICMP echo packets \cite{rfc:icmp} are not used because it is the availability
+@c of the NFS service that is important, not the existence of a base kernel.
+@c Whenever a reference to a fileserver which is down is made via @i{Amd}, an alternate
+@c filesystem is mounted if one is available.
+@c
+Although this action does not protect user files, which are unique on
+the network, or processes which do not access files via @i{Amd} or
+already have open files on the hung filesystem, it can prevent most new
+processes from hanging.
+
+By default, fileserver state is not maintained for NFS/TCP mounts. The
+remote fileserver is always assumed to be up.
+@c
+@c With a suitable combination of filesystem management and mount-maps,
+@c machines can be protected against most server downtime. This can be
+@c enhanced by allocating boot-servers dynamically which allows a diskless
+@c workstation to be quickly restarted if necessary. Once the root filesystem
+@c is mounted, @i{Amd} can be started and allowed to mount the remainder of
+@c the filesystem from whichever fileservers are available.
+
+@node Non-blocking Operation, , Keep-alives, Overview
+@comment node-name, next, previous, up
+@section Non-blocking Operation
+@cindex Non-blocking operation
+@cindex Multiple-threaded server
+@cindex RPC retries
+
+Since there is only one instance of @i{Amd} for each automount point,
+and usually only one instance on each machine, it is important that it
+is always available to service kernel calls. @i{Amd} goes to great
+lengths to ensure that it does not block in a system call. As a last
+resort @i{Amd} will fork before it attempts a system call that may block
+indefinitely, such as mounting an NFS filesystem. Other tasks such as
+obtaining filehandle information for an NFS filesystem, are done using a
+purpose built non-blocking RPC library which is integrated with
+@i{Amd}'s task scheduler. This library is also used to implement NFS
+keep-alives (@pxref{Keep-alives}).
+
+Whenever a mount is deferred or backgrounded, @i{Amd} must wait for it
+to complete before replying to the kernel. However, this would cause
+@i{Amd} to block waiting for a reply to be constructed. Rather than do
+this, @i{Amd} simply @dfn{drops} the call under the assumption that the
+kernel RPC mechanism will automatically retry the request.
+
+@c ################################################################
+@node Supported Platforms, Mount Maps, Overview, Top
+@comment node-name, next, previous, up
+@chapter Supported Platforms
+@cindex Supported Platforms
+@cindex shared libraries
+@cindex NFS V.3 support
+
+@i{Am-utils} has been ported to a wide variety of machines and operating
+systems. @i{Am-utils}'s code works for little-endian and big-endian
+machines, as well as 32 bit and 64 bit architectures. Furthermore, when
+@i{Am-utils} ports to an Operating System on one architecture, it is generally
+readily portable to the same Operating System on all platforms on which
+it is available.
+
+The table below lists those platforms supported by the latest release.
+The listing is based on the standard output from GNU's
+@code{config.guess} script. Since significant changes have been made to
+am-utils, not all systems listed here have been verified working for all
+features.
+
+@multitable {Auto-Configured System Name} {Config} {Compile} {Amd} {NFS3} {Shlib} {Hlfsd}
+
+@item @b{Auto-Configured System Name}
+@c {Config} {Compile} {Amd} {NFS V.3} {Shlib} {Hlfsd}
+@tab @b{Config} @tab @b{Compile} @tab @b{Amd} @tab @b{NFS3} @tab @b{Shlib} @tab @b{Hlfsd}
+
+@item @b{alpha-dec-osf2.1}
+@c {Config} {Compile} {Amd} {NFS V.3} {Shlib} {Hlfsd}
+@tab yes @tab yes @tab yes @tab @tab @tab
+
+@item @b{alpha-dec-osf4.0}
+@c {Config} {Compile} {Amd} {NFS V.3} {Shlib} {Hlfsd}
+@tab yes @tab yes @tab yes @tab yes @tab @tab
+
+@item @b{alphaev5-unknown-linux-gnu}
+@c {Config} {Compile} {Amd} {NFS V.3} {Shlib} {Hlfsd}
+@tab yes @tab yes @tab yes @tab n/a @tab yes @tab
+
+@item @b{hppa1.0-hp-hpux11.00}
+@c {Config} {Compile} {Amd} {NFS V.3} {Shlib} {Hlfsd}
+@tab yes @tab yes @tab yes @tab no @tab @tab
+
+@item @b{hppa1.1-hp-hpux10.10}
+@c {Config} {Compile} {Amd} {NFS V.3} {Shlib} {Hlfsd}
+@tab yes @tab yes @tab yes @tab n/a @tab no @tab
+
+@item @b{hppa1.1-hp-hpux10.20}
+@c {Config} {Compile} {Amd} {NFS V.3} {Shlib} {Hlfsd}
+@tab yes @tab yes @tab yes @tab n/a @tab no @tab
+
+@item @b{hppa1.1-hp-hpux9.01}
+@c {Config} {Compile} {Amd} {NFS V.3} {Shlib} {Hlfsd}
+@tab yes @tab yes @tab yes @tab n/a @tab @tab
+
+@item @b{hppa1.1-hp-hpux9.05}
+@c {Config} {Compile} {Amd} {NFS V.3} {Shlib} {Hlfsd}
+@tab yes @tab yes @tab yes @tab n/a @tab @tab
+
+@item @b{hppa1.1-hp-hpux9.07}
+@c {Config} {Compile} {Amd} {NFS V.3} {Shlib} {Hlfsd}
+@tab yes @tab yes @tab yes @tab n/a @tab @tab
+
+@item @b{i386-pc-bsdi2.1}
+@c {Config} {Compile} {Amd} {NFS V.3} {Shlib} {Hlfsd}
+@tab yes @tab yes @tab yes @tab n/a @tab @tab
+
+@item @b{i386-pc-bsdi3.0}
+@c {Config} {Compile} {Amd} {NFS V.3} {Shlib} {Hlfsd}
+@tab yes @tab yes @tab yes @tab n/a @tab @tab
+
+@item @b{i386-pc-bsdi3.1}
+@c {Config} {Compile} {Amd} {NFS V.3} {Shlib} {Hlfsd}
+@tab yes @tab yes @tab yes @tab n/a @tab @tab
+
+@item @b{i386-pc-solaris2.5.1}
+@c {Config} {Compile} {Amd} {NFS V.3} {Shlib} {Hlfsd}
+@tab yes @tab yes @tab yes @tab yes @tab yes @tab yes
+
+@item @b{i386-pc-solaris2.6}
+@c {Config} {Compile} {Amd} {NFS V.3} {Shlib} {Hlfsd}
+@tab yes @tab yes @tab yes @tab yes @tab yes @tab yes
+
+@item @b{i386-unknown-freebsd2.1.0}
+@c {Config} {Compile} {Amd} {NFS V.3} {Shlib} {Hlfsd}
+@tab yes @tab yes @tab yes @tab n/a @tab @tab
+
+@item @b{i386-unknown-freebsd2.2.1}
+@c {Config} {Compile} {Amd} {NFS V.3} {Shlib} {Hlfsd}
+@tab yes @tab yes @tab yes @tab n/a @tab yes @tab
+
+@item @b{i386-unknown-freebsd3.0}
+@c {Config} {Compile} {Amd} {NFS V.3} {Shlib} {Hlfsd}
+@tab yes @tab yes @tab yes @tab yes @tab yes @tab
+
+@item @b{i386-unknown-netbsd1.2.1}
+@c {Config} {Compile} {Amd} {NFS V.3} {Shlib} {Hlfsd}
+@tab yes @tab yes @tab yes @tab yes @tab yes @tab
+
+@item @b{i386-unknown-netbsd1.3}
+@c {Config} {Compile} {Amd} {NFS V.3} {Shlib} {Hlfsd}
+@tab yes @tab yes @tab yes @tab yes @tab yes @tab
+
+@item @b{i386-unknown-netbsd1.3.1}
+@c {Config} {Compile} {Amd} {NFS V.3} {Shlib} {Hlfsd}
+@tab yes @tab yes @tab yes @tab yes @tab yes @tab
+
+@item @b{i386-unknown-openbsd2.1}
+@c {Config} {Compile} {Amd} {NFS V.3} {Shlib} {Hlfsd}
+@tab yes @tab yes @tab yes @tab yes @tab yes @tab
+
+@item @b{i486-ncr-sysv4.3.03}
+@c {Config} {Compile} {Amd} {NFS V.3} {Shlib} {Hlfsd}
+@tab yes @tab yes @tab @tab yes @tab @tab
+
+@item @b{i486-pc-linux-gnulibc1}
+@c {Config} {Compile} {Amd} {NFS V.3} {Shlib} {Hlfsd}
+@tab yes @tab yes @tab yes @tab n/a @tab yes @tab
+
+@item @b{i586-pc-linux-gnulinc1}
+@c {Config} {Compile} {Amd} {NFS V.3} {Shlib} {Hlfsd}
+@tab yes @tab yes @tab yes @tab n/a @tab yes @tab
+
+@item @b{i686-pc-linux-gnu}
+@c {Config} {Compile} {Amd} {NFS V.3} {Shlib} {Hlfsd}
+@tab yes @tab yes @tab yes @tab n/a @tab yes @tab
+
+@item @b{m68k-hp-hpux9.00}
+@c {Config} {Compile} {Amd} {NFS V.3} {Shlib} {Hlfsd}
+@tab yes @tab yes @tab yes @tab n/a @tab @tab
+
+@item @b{m68k-sun-sunos4.1.1}
+@c {Config} {Compile} {Amd} {NFS V.3} {Shlib} {Hlfsd}
+@tab yes @tab yes @tab yes @tab n/a @tab @tab
+
+@item @b{m68k-next-nextstep3}
+@c {Config} {Compile} {Amd} {NFS V.3} {Shlib} {Hlfsd}
+@tab yes @tab yes @tab yes @tab n/a @tab @tab
+
+@item @b{mips-dec-ultrix4.3}
+@c {Config} {Compile} {Amd} {NFS V.3} {Shlib} {Hlfsd}
+@tab yes @tab yes @tab yes @tab n/a @tab @tab
+
+@item @b{mips-sgi-irix5.2}
+@c {Config} {Compile} {Amd} {NFS V.3} {Shlib} {Hlfsd}
+@tab @tab @tab @tab @tab @tab
+
+@item @b{mips-sgi-irix5.3}
+@c {Config} {Compile} {Amd} {NFS V.3} {Shlib} {Hlfsd}
+@tab yes @tab yes @tab yes @tab yes @tab @tab
+
+@item @b{mips-sgi-irix6.2}
+@c {Config} {Compile} {Amd} {NFS V.3} {Shlib} {Hlfsd}
+@tab yes @tab yes @tab yes @tab yes @tab @tab
+
+@item @b{mips-sgi-irix6.4}
+@c {Config} {Compile} {Amd} {NFS V.3} {Shlib} {Hlfsd}
+@tab yes @tab yes @tab yes @tab yes @tab yes @tab
+
+@item @b{powerpc-ibm-aix4.1.5.0}
+@c {Config} {Compile} {Amd} {NFS V.3} {Shlib} {Hlfsd}
+@tab yes @tab yes @tab yes @tab n/a @tab @tab
+
+@item @b{powerpc-ibm-aix4.2.1.0}
+@c {Config} {Compile} {Amd} {NFS V.3} {Shlib} {Hlfsd}
+@tab yes @tab yes @tab yes @tab yes @tab @tab
+
+@item @b{rs6000-ibm-aix3.2}
+@c {Config} {Compile} {Amd} {NFS V.3} {Shlib} {Hlfsd}
+@tab yes @tab yes @tab yes @tab n/a @tab @tab
+
+@item @b{rs6000-ibm-aix3.2.5}
+@c {Config} {Compile} {Amd} {NFS V.3} {Shlib} {Hlfsd}
+@tab yes @tab yes @tab yes @tab n/a @tab @tab
+
+@item @b{rs6000-ibm-aix4.1.4.0}
+@c {Config} {Compile} {Amd} {NFS V.3} {Shlib} {Hlfsd}
+@tab yes @tab yes @tab yes @tab n/a @tab @tab
+
+@item @b{rs6000-ibm-aix4.1.5.0}
+@c {Config} {Compile} {Amd} {NFS V.3} {Shlib} {Hlfsd}
+@tab yes @tab yes @tab yes @tab n/a @tab @tab
+
+@item @b{sparc-sun-solaris2.3}
+@c {Config} {Compile} {Amd} {NFS V.3} {Shlib} {Hlfsd}
+@tab yes @tab yes @tab yes @tab n/a @tab @tab
+
+@item @b{sparc-sun-solaris2.4}
+@c {Config} {Compile} {Amd} {NFS V.3} {Shlib} {Hlfsd}
+@tab yes @tab yes @tab yes @tab n/a @tab yes @tab
+
+@item @b{sparc-sun-solaris2.5}
+@c {Config} {Compile} {Amd} {NFS V.3} {Shlib} {Hlfsd}
+@tab yes @tab yes @tab yes @tab yes @tab yes @tab
+
+@item @b{sparc-sun-solaris2.5.1}
+@c {Config} {Compile} {Amd} {NFS V.3} {Shlib} {Hlfsd}
+@tab yes @tab yes @tab yes @tab yes @tab yes @tab yes
+
+@item @b{sparc-sun-solaris2.6}
+@c {Config} {Compile} {Amd} {NFS V.3} {Shlib} {Hlfsd}
+@tab yes @tab yes @tab yes @tab yes @tab yes @tab yes
+
+@item @b{sparc-sun-sunos4.1.1}
+@c {Config} {Compile} {Amd} {NFS V.3} {Shlib} {Hlfsd}
+@tab yes @tab yes @tab yes @tab n/a @tab yes @tab
+
+@item @b{sparc-sun-sunos4.1.3}
+@c {Config} {Compile} {Amd} {NFS V.3} {Shlib} {Hlfsd}
+@tab yes @tab yes @tab yes @tab n/a @tab yes @tab
+
+@item @b{sparc-sun-sunos4.1.3C}
+@c {Config} {Compile} {Amd} {NFS V.3} {Shlib} {Hlfsd}
+@tab yes @tab yes @tab yes @tab n/a @tab yes @tab
+
+@item @b{sparc-sun-sunos4.1.3_U1}
+@c {Config} {Compile} {Amd} {NFS V.3} {Shlib} {Hlfsd}
+@tab yes @tab yes @tab yes @tab n/a @tab yes @tab
+
+@item @b{sparc-sun-sunos4.1.4}
+@c {Config} {Compile} {Amd} {NFS V.3} {Shlib} {Hlfsd}
+@tab yes @tab yes @tab yes @tab n/a @tab yes @tab
+
+@item @b{sparc-unknown-linux-gnulibc1}
+@c {Config} {Compile} {Amd} {NFS V.3} {Shlib} {Hlfsd}
+@tab yes @tab yes @tab yes @tab n/a @tab yes @tab
+
+@item @b{sparc-unknown-netbsd1.2E}
+@c {Config} {Compile} {Amd} {NFS V.3} {Shlib} {Hlfsd}
+@tab yes @tab yes @tab yes @tab @tab @tab
+
+@end multitable
+
+See the @file{INSTALL} in the distribution for more specific details on
+building and/or configuring for some systems.
+
+@c ################################################################
+@node Mount Maps, Amd Command Line Options, Supported Platforms, Top
+@comment node-name, next, previous, up
+@chapter Mount Maps
+@cindex Mount maps
+@cindex Automounter configuration maps
+@cindex Mount information
+
+@i{Amd} has no built-in knowledge of machines or filesystems.
+External @dfn{mount-maps} are used to provide the required information.
+Specifically, @i{Amd} needs to know when and under what conditions it
+should mount filesystems.
+
+The map entry corresponding to the requested name contains a list of
+possible locations from which to resolve the request. Each location
+specifies filesystem type, information required by that filesystem (for
+example the block special device in the case of UFS), and some
+information describing where to mount the filesystem (@pxref{fs Option}). A
+location may also contain @dfn{selectors} (@pxref{Selectors}).@refill
+
+@menu
+* Map Types::
+* Key Lookup::
+* Location Format::
+@end menu
+
+@node Map Types, Key Lookup, Mount Maps, Mount Maps
+@comment node-name, next, previous, up
+@section Map Types
+@cindex Mount map types
+@cindex Map types
+@cindex Configuration map types
+@cindex Types of mount map
+@cindex Types of configuration map
+@cindex Determining the map type
+
+A mount-map provides the run-time configuration information to @i{Amd}.
+Maps can be implemented in many ways. Some of the forms supported by
+@i{Amd} are regular files, ndbm databases, NIS maps, the @dfn{Hesiod}
+name server, and even the password file.
+
+A mount-map @dfn{name} is a sequence of characters. When an automount
+point is created a handle on the mount-map is obtained. For each map
+type configured, @i{Amd} attempts to reference the map of the
+appropriate type. If a map is found, @i{Amd} notes the type for future
+use and deletes the reference, for example closing any open file
+descriptors. The available maps are configured when @i{Amd} is built
+and can be displayed by running the command @samp{amd -v}.
+
+When using an @i{Amd} configuration file (@pxref{Amd Configuration File})
+and the keyword @samp{map_type} (@pxref{map_type Parameter}), you may
+force the map used to any type.
+
+By default, @i{Amd} caches data in a mode dependent on the type of map.
+This is the same as specifying @samp{cache:=mapdefault} and selects a
+suitable default cache mode depending on the map type. The individual
+defaults are described below. The @var{cache} option can be specified
+on automount points to alter the caching behavior (@pxref{Automount
+Filesystem}).@refill
+
+The following map types have been implemented, though some are not
+available on all machines. Run the command @samp{amd -v} to obtain a
+list of map types configured on your machine.
+
+@menu
+* File maps::
+* ndbm maps::
+* NIS maps::
+* NIS+ maps::
+* Hesiod maps::
+* Password maps::
+* Union maps::
+* LDAP maps::
+@end menu
+
+@node File maps, ndbm maps, Map Types, Map Types
+@comment node-name, next, previous, up
+@subsection File maps
+@cindex File maps
+@cindex Flat file maps
+@cindex File map syntactic conventions
+
+When @i{Amd} searches a file for a map entry it does a simple scan of
+the file and supports both comments and continuation lines.
+
+Continuation lines are indicated by a backslash character (@samp{\}) as
+the last character of a line in the file. The backslash, newline character
+@emph{and any leading white space on the following line} are discarded. A maximum
+line length of 2047 characters is enforced after continuation lines are read
+but before comments are stripped. Each line must end with
+a newline character; that is newlines are terminators, not separators.
+The following examples illustrate this:
+
+@example
+key valA valB; \
+ valC
+@end example
+
+specifies @emph{three} locations, and is identical to
+
+@example
+key valA valB; valC
+@end example
+
+However,
+
+@example
+key valA valB;\
+ valC
+@end example
+
+specifies only @emph{two} locations, and is identical to
+
+@example
+key valA valB;valC
+@end example
+
+After a complete line has been read from the file, including
+continuations, @i{Amd} determines whether there is a comment on the
+line. A comment begins with a hash (``@samp{#}'') character and
+continues to the end of the line. There is no way to escape or change
+the comment lead-in character.
+
+Note that continuation lines and comment support @dfn{only} apply to
+file maps, or ndbm maps built with the @code{mk-amd-map} program.
+
+When caching is enabled, file maps have a default cache mode of
+@code{all} (@pxref{Automount Filesystem}).
+
+@node ndbm maps, NIS maps, File maps, Map Types
+@comment node-name, next, previous, up
+@subsection ndbm maps
+@cindex ndbm maps
+
+An ndbm map may be used as a fast access form of a file map. The program,
+@code{mk-amd-map}, converts a normal map file into an ndbm database.
+This program supports the same continuation and comment conventions that
+are provided for file maps. Note that ndbm format files may @emph{not}
+be sharable across machine architectures. The notion of speed generally
+only applies to large maps; a small map, less than a single disk block,
+is almost certainly better implemented as a file map.
+
+ndbm maps have a default cache mode of @samp{all} (@pxref{Automount Filesystem}).
+
+@node NIS maps, NIS+ maps, ndbm maps, Map Types
+@comment node-name, next, previous, up
+@subsection NIS maps
+@cindex NIS (YP) maps
+
+When using NIS (formerly YP), an @i{Amd} map is implemented directly
+by the underlying NIS map. Comments and continuation lines are
+@emph{not} supported in the automounter and must be stripped when
+constructing the NIS server's database.
+
+NIS maps have a default cache mode of @code{all} (@pxref{Automount
+Filesystem}).
+
+The following rule illustrates what could be added to your NIS @file{Makefile},
+in this case causing the @file{amd.home} map to be rebuilt:
+@example
+$(YPTSDIR)/amd.home.time: $(ETCDIR)/amd.home
+ -@@sed -e "s/#.*$$//" -e "/^$$/d" $(ETCDIR)/amd.home | \
+ awk '@{ \
+ for (i = 1; i <= NF; i++) \
+ if (i == NF) @{ \
+ if (substr($$i, length($$i), 1) == "\\") \
+ printf("%s", substr($$i, 1, length($$i) - 1)); \
+ else \
+ printf("%s\n", $$i); \
+ @} \
+ else \
+ printf("%s ", $$i); \
+ @}' | \
+ $(MAKEDBM) - $(YPDBDIR)/amd.home; \
+ touch $(YPTSDIR)/amd.home.time; \
+ echo "updated amd.home"; \
+ if [ ! $(NOPUSH) ]; then \
+ $(YPPUSH) amd.home; \
+ echo "pushed amd.home"; \
+ else \
+ : ; \
+ fi
+@end example
+
+Here @code{$(YPTSDIR)} contains the time stamp files, and @code{$(YPDBDIR)} contains
+the dbm format NIS files.
+
+@node NIS+ maps, Hesiod maps, NIS maps, Map Types
+@comment node-name, next, previous, up
+@subsection NIS+ maps
+@cindex NIS+ maps
+
+NIS+ maps do not support cache mode @samp{all} and, when caching is
+enabled, have a default cache mode of @samp{inc}.
+
+XXX: FILL IN WITH AN EXAMPLE.
+
+@node Hesiod maps, Password maps, NIS+ maps, Map Types
+@comment node-name, next, previous, up
+@subsection Hesiod maps
+@cindex Hesiod maps
+
+When the map name begins with the string @samp{hesiod.} lookups are made
+using the @dfn{Hesiod} name server. The string following the dot is
+used as a name qualifier and is prepended with the key being located.
+The entire string is then resolved in the @code{automount} context, or
+the @i{amd.conf} parameter @samp{hesiod_base} (@pxref{hesiod_base
+Parameter}). For example, if the the key is @samp{jsp} and map name is
+@samp{hesiod.homes} then @dfn{Hesiod} is asked to resolve
+@samp{jsp.homes.automount}.
+
+Hesiod maps do not support cache mode @samp{all} and, when caching is
+enabled, have a default cache mode of @samp{inc} (@pxref{Automount
+Filesystem}).
+
+The following is an example of a @dfn{Hesiod} map entry:
+
+@example
+jsp.homes.automount HS TXT "rfs:=/home/charm;rhost:=charm;sublink:=jsp"
+njw.homes.automount HS TXT "rfs:=/home/dylan/dk2;rhost:=dylan;sublink:=njw"
+@end example
+
+@node Password maps, Union maps, Hesiod maps, Map Types
+@comment node-name, next, previous, up
+@subsection Password maps
+@cindex Password file maps
+@cindex /etc/passwd maps
+@cindex User maps, automatic generation
+@cindex Automatic generation of user maps
+@cindex Using the password file as a map
+
+The password map support is unlike the four previous map types. When
+the map name is the string @file{/etc/passwd} @i{Amd} can lookup a user
+name in the password file and re-arrange the home directory field to
+produce a usable map entry.
+
+@i{Amd} assumes the home directory has the format
+`@t{/}@i{anydir}@t{/}@i{dom1}@t{/../}@i{domN}@t{/}@i{login}'.
+@c @footnote{This interpretation is not necessarily exactly what you want.}
+It breaks this string into a map entry where @code{$@{rfs@}} has the
+value `@t{/}@i{anydir}@t{/}@i{domN}', @code{$@{rhost@}} has the value
+`@i{domN}@t{.}@i{...}@t{.}@i{dom1}', and @code{$@{sublink@}} has the
+value @i{login}.@refill
+
+Thus if the password file entry was
+
+@example
+/home/achilles/jsp
+@end example
+
+the map entry used by @i{Amd} would be
+
+@example
+rfs:=/home/achilles;rhost:=achilles;sublink:=jsp
+@end example
+
+Similarly, if the password file entry was
+
+@example
+/home/cc/sugar/mjh
+@end example
+
+the map entry used by @i{Amd} would be
+
+@example
+rfs:=/home/sugar;rhost:=sugar.cc;sublink:=jsp
+@end example
+
+@node Union maps, LDAP maps , Password maps, Map Types
+@comment node-name, next, previous, up
+@subsection Union maps
+@cindex Union file maps
+
+The union map support is provided specifically for use with the union
+filesystem, @pxref{Union Filesystem}.
+
+It is identified by the string @samp{union:} which is followed by a
+colon separated list of directories. The directories are read in order,
+and the names of all entries are recorded in the map cache. Later
+directories take precedence over earlier ones. The union filesystem
+type then uses the map cache to determine the union of the names in all
+the directories.
+
+@node LDAP maps, , Union maps, Map Types
+@comment node-name, next, previous, up
+@subsection LDAP maps
+@cindex LDAP maps
+@cindex Lightweight Directory Access Protocol
+
+LDAP (Lightweight Directory Access Protocol) maps do not support cache
+mode @samp{all} and, when caching is enabled, have a default cache mode
+of @samp{inc}.
+
+For example, an @i{Amd} map @samp{amd.home} that looks as follows:
+
+@example
+/defaults opts:=rw,intr;type:=link
+
+zing -rhost:=shekel \
+ host==shekel \
+ host!=shekel;type:=nfs
+@end example
+@noindent
+when converted to LDAP (@pxref{amd2ldif}), will result in the following
+LDAP database:
+@example
+$ amd2ldif amd.home CUCS < amd.home
+dn: cn=amdmap timestamp, CUCS
+cn : amdmap timestamp
+objectClass : amdmapTimestamp
+amdmapTimestamp: 873071363
+
+dn: cn=amdmap amd.home[/defaults], CUCS
+cn : amdmap amd.home[/defaults]
+objectClass : amdmap
+amdmapName : amd.home
+amdmapKey : /defaults
+amdmapValue : opts:=rw,intr;type:=link
+
+dn: cn=amdmap amd.home[], CUCS
+cn : amdmap amd.home[]
+objectClass : amdmap
+amdmapName : amd.home
+amdmapKey :
+amdmapValue :
+
+dn: cn=amdmap amd.home[zing], CUCS
+cn : amdmap amd.home[zing]
+objectClass : amdmap
+amdmapName : amd.home
+amdmapKey : zing
+amdmapValue : -rhost:=shekel host==shekel host!=shekel;type:=nfs
+@end example
+
+@c subsection Gdbm
+
+@node Key Lookup, Location Format, Map Types, Mount Maps
+@comment node-name, next, previous, up
+@section How keys are looked up
+@cindex Key lookup
+@cindex Map lookup
+@cindex Looking up keys
+@cindex How keys are looked up
+@cindex Wildcards in maps
+
+The key is located in the map whose type was determined when the
+automount point was first created. In general the key is a pathname
+component. In some circumstances this may be modified by variable
+expansion (@pxref{Variable Expansion}) and prefixing. If the automount
+point has a prefix, specified by the @var{pref} option, then that is
+prepended to the search key before the map is searched.
+
+If the map cache is a @samp{regexp} cache then the key is treated as an
+egrep-style regular expression, otherwise a normal string comparison is
+made.
+
+If the key cannot be found then a @dfn{wildcard} match is attempted.
+@i{Amd} repeatedly strips the basename from the key, appends @samp{/*} and
+attempts a lookup. Finally, @i{Amd} attempts to locate the special key @samp{*}.
+
+For example, the following sequence would be checked if @file{home/dylan/dk2} was
+being located:
+
+@example
+ home/dylan/dk2
+ home/dylan/*
+ home/*
+ *
+@end example
+
+At any point when a wildcard is found, @i{Amd} proceeds as if an exact
+match had been found and the value field is then used to resolve the
+mount request, otherwise an error code is propagated back to the kernel.
+(@pxref{Filesystem Types}).@refill
+
+@node Location Format, , Key Lookup, Mount Maps
+@comment node-name, next, previous, up
+@section Location Format
+@cindex Location format
+@cindex Map entry format
+@cindex How locations are parsed
+
+The value field from the lookup provides the information required to
+mount a filesystem. The information is parsed according to the syntax
+shown below.
+
+@display
+@i{location-list}:
+ @i{location-selection}
+ @i{location-list} @i{white-space} @t{||} @i{white-space} @i{location-selection}
+@i{location-selection}:
+ @i{location}
+ @i{location-selection} @i{white-space} @i{location}
+@i{location}:
+ @i{location-info}
+ @t{-}@i{location-info}
+ @t{-}
+@i{location-info}:
+ @i{sel-or-opt}
+ @i{location-info}@t{;}@i{sel-or-opt}
+ @t{;}
+@i{sel-or-opt}:
+ @i{selection}
+ @i{opt-ass}
+@i{selection}:
+ selector@t{==}@i{value}
+ selector@t{!=}@i{value}
+@i{opt-ass}:
+ option@t{:=}@i{value}
+@i{white-space}:
+ space
+ tab
+@end display
+
+Note that unquoted whitespace is not allowed in a location description.
+White space is only allowed, and is mandatory, where shown with non-terminal
+@i{white-space}.
+
+A @dfn{location-selection} is a list of possible volumes with which to
+satisfy the request. @dfn{location-selection}s are separated by the
+@samp{||} operator. The effect of this operator is to prevent use of
+location-selections to its right if any of the location-selections on
+its left were selected whether or not any of them were successfully
+mounted (@pxref{Selectors}).@refill
+
+The location-selection, and singleton @dfn{location-list},
+@samp{type:=ufs;dev:=/dev/xd1g} would inform @i{Amd} to mount a UFS
+filesystem from the block special device @file{/dev/xd1g}.
+
+The @dfn{sel-or-opt} component is either the name of an option required
+by a specific filesystem, or it is the name of a built-in, predefined
+selector such as the architecture type. The value may be quoted with
+double quotes @samp{"}, for example
+@samp{type:="ufs";dev:="/dev/xd1g"}. These quotes are stripped when the
+value is parsed and there is no way to get a double quote into a value
+field. Double quotes are used to get white space into a value field,
+which is needed for the program filesystem (@pxref{Program Filesystem}).@refill
+
+@menu
+* Map Defaults::
+* Variable Expansion::
+* Selectors::
+* Map Options::
+@end menu
+
+@node Map Defaults, Variable Expansion, Location Format, Location Format
+@comment node-name, next, previous, up
+@subsection Map Defaults
+@cindex Map defaults
+@cindex How to set default map parameters
+@cindex Setting default map parameters
+
+A location beginning with a dash @samp{-} is used to specify default
+values for subsequent locations. Any previously specified defaults in
+the location-list are discarded. The default string can be empty in
+which case no defaults apply.
+
+The location @samp{-fs:=/mnt;opts:=ro} would set the local mount point
+to @file{/mnt} and cause mounts to be read-only by default. Defaults
+specified this way are appended to, and so override, any global map
+defaults given with @samp{/defaults}).
+
+@c
+@c A @samp{/defaults} value @dfn{gdef} and a location list
+@c \begin{quote}
+@c $@samp{-}@dfn{def}_a $\verb*+ +$ @dfn{loc}_{a_1} $\verb*+ +$ @dfn{loc}_{a_2} $\verb*+ +$ @samp{-}@dfn{def}_b $\verb*+ +$ @dfn{loc}_{b_1} \ldots$
+@c \end{quote}
+@c is equivalent to
+@c \begin{quote}
+@c $@samp{-}@dfn{gdef}@samp{;}@dfn{def}_a $\verb*+ +$ @dfn{loc}_{a_1} $\verb*+ +$ @dfn{loc}_{a_2} $\verb*+ +$ @samp{-}@dfn{gdef}@samp{;}@dfn{def}_b $\verb*+ +$ @dfn{loc}_{b_1} \ldots$
+@c \end{quote}
+@c which is equivalent to
+@c \begin{quote}
+@c $@dfn{gdef}@samp{;}@dfn{def}_a@samp{;}@dfn{loc}_{a_1} $\verb*+ +$@dfn{gdef}@samp{;}@dfn{def}_a@samp{;}@dfn{loc}_{a_2} $\verb*+ +$@dfn{gdef}@samp{;}@dfn{def}_b@samp{;}@dfn{loc}_{b_1} \ldots$
+@c \end{quote}
+
+@node Variable Expansion, Selectors, Map Defaults, Location Format
+@comment node-name, next, previous, up
+@subsection Variable Expansion
+@cindex Variable expansion
+@cindex How variables are expanded
+@cindex Pathname operators
+@cindex Domain stripping
+@cindex Domainname operators
+@cindex Stripping the local domain name
+@cindex Environment variables
+@cindex How to access environment variables in maps
+
+To allow generic location specifications @i{Amd} does variable expansion
+on each location and also on some of the option strings. Any option or
+selector appearing in the form @code{$@dfn{var}} is replaced by the
+current value of that option or selector. For example, if the value of
+@code{$@{key@}} was @samp{bin}, @code{$@{autodir@}} was @samp{/a} and
+@code{$@{fs@}} was `@t{$@{autodir@}}@t{/local/}@t{$@{key@}}' then
+after expansion @code{$@{fs@}} would have the value @samp{/a/local/bin}.
+Any environment variable can be accessed in a similar way.@refill
+
+Two pathname operators are available when expanding a variable. If the
+variable name begins with @samp{/} then only the last component of the
+pathname is substituted. For example, if @code{$@{path@}} was
+@samp{/foo/bar} then @code{$@{/path@}} would be expanded to @samp{bar}.
+Similarly, if the variable name ends with @samp{/} then all but the last
+component of the pathname is substituted. In the previous example,
+@code{$@{path/@}} would be expanded to @samp{/foo}.@refill
+
+Two domain name operators are also provided. If the variable name
+begins with @samp{.} then only the domain part of the name is
+substituted. For example, if @code{$@{rhost@}} was
+@samp{swan.doc.ic.ac.uk} then @code{$@{.rhost@}} would be expanded to
+@samp{doc.ic.ac.uk}. Similarly, if the variable name ends with @samp{.}
+then only the host component is substituted. In the previous example,
+@code{$@{rhost.@}} would be expanded to @samp{swan}.@refill
+
+Variable expansion is a two phase process. Before a location is parsed,
+all references to selectors, @i{eg} @code{$@{path@}}, are expanded. The
+location is then parsed, selections are evaluated and option assignments
+recorded. If there were no selections or they all succeeded the
+location is used and the values of the following options are expanded in
+the order given: @var{sublink}, @var{rfs}, @var{fs}, @var{opts},
+@var{remopts}, @var{mount} and @var{unmount}.
+
+Note that expansion of option values is done after @dfn{all} assignments
+have been completed and not in a purely left to right order as is done
+by the shell. This generally has the desired effect but care must be
+taken if one of the options references another, in which case the
+ordering can become significant.
+
+There are two special cases concerning variable expansion:
+
+@enumerate
+@item
+before a map is consulted, any selectors in the name received
+from the kernel are expanded. For example, if the request from the
+kernel was for `@t{$@{arch@}}@t{.bin}' and the machine architecture
+was @samp{vax}, the value given to @code{$@{key@}} would be
+@samp{vax.bin}.@refill
+
+@item
+the value of @code{$@{rhost@}} is expanded and normalized before the
+other options are expanded. The normalization process strips any local
+sub-domain components. For example, if @code{$@{domain@}} was
+@samp{Berkeley.EDU} and @code{$@{rhost@}} was initially
+@samp{snow.Berkeley.EDU}, after the normalization it would simply be
+@samp{snow}. Hostname normalization is currently done in a
+@emph{case-dependent} manner.@refill
+@end enumerate
+
+@c======================================================================
+@node Selectors, Map Options, Variable Expansion, Location Format
+@comment node-name, next, previous, up
+@subsection Selectors
+@cindex Selectors
+
+Selectors are used to control the use of a location. It is possible to
+share a mount map between many machines in such a way that filesystem
+location, architecture and operating system differences are hidden from
+the users. A selector of the form @samp{arch==sun3;os==sunos4} would only
+apply on Sun-3s running SunOS 4.x.
+
+Selectors can be negated by using @samp{!=} instead of @samp{==}. For
+example to select a location on all non-Vax machines the selector
+@samp{arch!=vax} would be used.
+
+Selectors are evaluated left to right. If a selector fails then that
+location is ignored. Thus the selectors form a conjunction and the
+locations form a disjunction. If all the locations are ignored or
+otherwise fail then @i{Amd} uses the @dfn{error} filesystem
+(@pxref{Error Filesystem}). This is equivalent to having a location
+@samp{type:=error} at the end of each mount-map entry.@refill
+
+The default value of many of the selectors listed here can be overridden
+by an @i{Amd} command line switch or in an @i{Amd} configuration file.
+@xref{Amd Configuration File}.
+
+These are the selectors currently implemented.
+
+@menu
+* arch Selector Variable::
+* autodir Selector Variable::
+* byte Selector Variable::
+* cluster Selector Variable::
+* domain Selector Variable::
+* host Selector Variable::
+* hostd Selector Variable::
+* karch Selector Variable::
+* os Selector Variable::
+* osver Selector Variable::
+
+* key Selector Variable::
+* map Selector Variable::
+* netnumber Selector Variable::
+* network Selector Variable::
+* path Selector Variable::
+* wire Selector Variable::
+
+* exists Selector Function::
+* false Selector Function::
+* netgrp Selector Function::
+* in_network Selector Function::
+* true Selector Function::
+@end menu
+
+@c ----------------------------------------------------------------
+@node arch Selector Variable, autodir Selector Variable, Selectors, Selectors
+@comment node-name, next, previous, up
+@subsubsection arch Selector Variable
+@cindex arch Selector Variable
+@cindex arch, mount selector
+@cindex Mount selector; arch
+@cindex Selector; arch
+
+The machine architecture which was automatically determined at compile
+time. The architecture type can be displayed by running the command
+@samp{amd -v}. @xref{Supported Platforms}.@refill
+
+@c ----------------------------------------------------------------
+@node autodir Selector Variable, byte Selector Variable, arch Selector Variable, Selectors
+@comment node-name, next, previous, up
+@subsubsection autodir Selector Variable
+@cindex autodir Selector Variable
+@cindex autodir, mount selector
+@cindex Mount selector; autodir
+@cindex Selector; autodir
+
+The default directory under which to mount filesystems. This may be
+changed by the @code{-a} command line option. @xref{fs Option}.
+
+@c ----------------------------------------------------------------
+@node byte Selector Variable, cluster Selector Variable, autodir Selector Variable, Selectors
+@comment node-name, next, previous, up
+@subsubsection byte Selector Variable
+@cindex byte Selector Variable
+@cindex byte, mount selector
+@cindex Mount selector; byte
+@cindex Selector; byte
+
+The machine's byte ordering. This is either @samp{little}, indicating
+little-endian, or @samp{big}, indicating big-endian. One possible use
+is to share @samp{rwho} databases (@pxref{rwho servers}). Another is to
+share ndbm databases, however this use can be considered a courageous
+juggling act.
+
+@c ----------------------------------------------------------------
+@node cluster Selector Variable, domain Selector Variable, byte Selector Variable, Selectors
+@comment node-name, next, previous, up
+@subsubsection cluster Selector Variable
+@cindex cluster Selector Variable
+@cindex cluster, mount selector
+@cindex Mount selector; cluster
+@cindex Selector; cluster
+
+This is provided as a hook for the name of the local cluster. This can
+be used to decide which servers to use for copies of replicated
+filesystems. @code{$@{cluster@}} defaults to the value of
+@code{$@{domain@}} unless a different value is set with the @code{-C}
+command line option.
+
+@c ----------------------------------------------------------------
+@node domain Selector Variable, host Selector Variable, cluster Selector Variable, Selectors
+@comment node-name, next, previous, up
+@subsubsection domain Selector Variable
+@cindex domain Selector Variable
+@cindex domain, mount selector
+@cindex Mount selector; domain
+@cindex Selector; domain
+
+The local domain name as specified by the @code{-d} command line option.
+@xref{host Selector Variable}.
+
+@c ----------------------------------------------------------------
+@node host Selector Variable, hostd Selector Variable, domain Selector Variable, Selectors
+@comment node-name, next, previous, up
+@subsubsection host Selector Variable
+@cindex host Selector Variable
+@cindex host, mount selector
+@cindex Mount selector; host
+@cindex Selector; host
+
+The local hostname as determined by @b{gethostname}(2). If no domain
+name was specified on the command line and the hostname contains a
+period @samp{.} then the string before the period is used as the host
+name, and the string after the period is assigned to @code{$@{domain@}}.
+For example, if the hostname is @samp{styx.doc.ic.ac.uk} then
+@code{host} would be @samp{styx} and @code{domain} would be
+@samp{doc.ic.ac.uk}. @code{hostd} would be
+@samp{styx.doc.ic.ac.uk}.@refill
+
+@c ----------------------------------------------------------------
+@node hostd Selector Variable, karch Selector Variable, host Selector Variable, Selectors
+@comment node-name, next, previous, up
+@subsubsection hostd Selector Variable
+@cindex hostd Selector Variable
+@cindex hostd, mount selector
+@cindex Mount selector; hostd
+@cindex Selector; hostd
+
+This resolves to the @code{$@{host@}} and @code{$@{domain@}}
+concatenated with a @samp{.} inserted between them if required. If
+@code{$@{domain@}} is an empty string then @code{$@{host@}} and
+@code{$@{hostd@}} will be identical.
+
+@c ----------------------------------------------------------------
+@node karch Selector Variable, os Selector Variable, hostd Selector Variable, Selectors
+@comment node-name, next, previous, up
+@subsubsection karch Selector Variable
+@cindex karch Selector Variable
+@cindex karch, mount selector
+@cindex Mount selector; karch
+@cindex Selector; karch
+
+This is provided as a hook for the kernel architecture. This is used on
+SunOS 4 and SunOS 5, for example, to distinguish between different
+@samp{/usr/kvm} volumes. @code{$@{karch@}} defaults to the ``machine''
+value gotten from @b{uname}(2). If the @b{uname}(2) system call is not
+available, the value of @code{$@{karch@}} defaults to that of
+@code{$@{arch@}}. Finally, a different value can be set with the @code{-k}
+command line option.
+
+@c ----------------------------------------------------------------
+@node os Selector Variable, osver Selector Variable, karch Selector Variable, Selectors
+@comment node-name, next, previous, up
+@subsubsection os Selector Variable
+@cindex os Selector Variable
+@cindex os, mount selector
+@cindex Mount selector; os
+@cindex Selector; os
+
+The operating system. Like the machine architecture, this is
+automatically determined at compile time. The operating system name can
+be displayed by running the command @samp{amd -v}. @xref{Supported
+Platforms}.@refill
+
+@c ----------------------------------------------------------------
+@node osver Selector Variable, key Selector Variable, os Selector Variable, Selectors
+@comment node-name, next, previous, up
+@subsubsection osver Selector Variable
+@cindex osver Selector Variable
+@cindex osver, mount selector
+@cindex Mount selector; osver
+@cindex Selector; osver
+
+The operating system version. Like the machine architecture, this is
+automatically determined at compile time. The operating system name can
+be displayed by running the command @samp{amd -v}. @xref{Supported
+Platforms}.@refill
+
+@c ----------------------------------------------------------------
+@ifhtml
+<HR>
+@end ifhtml
+@sp 3
+The following selectors are also provided. Unlike the other selectors,
+they vary for each lookup. Note that when the name from the kernel is
+expanded prior to a map lookup, these selectors are all defined as empty
+strings.
+
+@c ----------------------------------------------------------------
+@node key Selector Variable, map Selector Variable, osver Selector Variable, Selectors
+@comment node-name, next, previous, up
+@subsubsection key Selector Variable
+@cindex key Selector Variable
+@cindex key, mount selector
+@cindex Mount selector; key
+@cindex Selector; key
+
+The name being resolved. For example, if @file{/home} is an automount
+point, then accessing @file{/home/foo} would set @code{$@{key@}} to the
+string @samp{foo}. The key is prefixed by the @var{pref} option set in
+the parent mount point. The default prefix is an empty string. If the
+prefix was @file{blah/} then @code{$@{key@}} would be set to
+@file{blah/foo}.@refill
+
+@c ----------------------------------------------------------------
+@node map Selector Variable, netnumber Selector Variable, key Selector Variable, Selectors
+@comment node-name, next, previous, up
+@subsubsection map Selector Variable
+@cindex map Selector Variable
+@cindex map, mount selector
+@cindex Mount selector; map
+@cindex Selector; map
+
+The name of the mount map being used.
+
+@c ----------------------------------------------------------------
+@node netnumber Selector Variable, network Selector Variable, map Selector Variable, Selectors
+@comment node-name, next, previous, up
+@subsubsection netnumber Selector Variable
+@cindex netnumber Selector Variable
+@cindex netnumber, mount selector
+@cindex Mount selector; netnumber
+@cindex Selector; netnumber
+
+This selector is identical to the @samp{in_network} selector function,
+see @ref{in_network Selector Function}. It will match either the name
+or number of @i{any} network interface on which this host is connected
+to. The names and numbers of all attached interfaces are available from
+the output of @samp{amd -v}.
+
+@c ----------------------------------------------------------------
+@node network Selector Variable, path Selector Variable, netnumber Selector Variable, Selectors
+@comment node-name, next, previous, up
+@subsubsection network Selector Variable
+@cindex network Selector Variable
+@cindex network, mount selector
+@cindex Mount selector; network
+@cindex Selector; network
+
+This selector is identical to the @samp{in_network} selector function,
+see @ref{in_network Selector Function}. It will match either the name
+or number of @i{any} network interface on which this host is connected
+to. The names and numbers of all attached interfaces are available from
+the output of @samp{amd -v}.
+
+@c ----------------------------------------------------------------
+@node path Selector Variable, wire Selector Variable, network Selector Variable, Selectors
+@comment node-name, next, previous, up
+@subsubsection path Selector Variable
+@cindex path Selector Variable
+@cindex path, mount selector
+@cindex Mount selector; path
+@cindex Selector; path
+
+The full pathname of the name being resolved. For example
+@file{/home/foo} in the example above.
+
+@c ----------------------------------------------------------------
+@node wire Selector Variable, exists Selector Function, path Selector Variable, Selectors
+@comment node-name, next, previous, up
+@subsubsection wire Selector Variable
+@cindex wire Selector Variable
+@cindex wire, mount selector
+@cindex Mount selector; wire
+@cindex Selector; wire
+
+This selector is identical to the @samp{in_network} selector function,
+see @ref{in_network Selector Function}. It will match either the name
+or number of @i{any} network interface on which this host is connected
+to. The names and numbers of all attached interfaces are available from
+the output of @samp{amd -v}.
+
+@c ----------------------------------------------------------------
+@ifhtml
+<HR>
+@end ifhtml
+@sp 2
+The following boolean functions are selectors which take an argument
+@i{ARG}. They return a value of true or false, and thus do not need to
+be compared with a value. Each of these may be negated by prepending
+@samp{!} to their name.
+
+@c ----------------------------------------------------------------
+@node exists Selector Function, false Selector Function, wire Selector Variable, Selectors
+@comment node-name, next, previous, up
+@subsubsection exists Selector Function
+@cindex exists Selector Function
+@cindex exists, boolean mount selector
+@cindex !exists, boolean mount selector
+@cindex Mount selector; exists
+@cindex Selector; exists
+
+If the file listed by @i{ARG} exists (via @b{lstat}(2)), this function
+evaluates to true. Otherwise it evaluates to false.
+
+@c ----------------------------------------------------------------
+@node false Selector Function, netgrp Selector Function, exists Selector Function, Selectors
+@comment node-name, next, previous, up
+@subsubsection false Selector Function
+@cindex false Selector Function
+@cindex false, boolean mount selector
+@cindex !false, boolean mount selector
+@cindex Mount selector; false
+@cindex Selector; false
+
+Always evaluates to false. @i{ARG} is ignored.
+
+@c ----------------------------------------------------------------
+@node netgrp Selector Function, in_network Selector Function, false Selector Function, Selectors
+@comment node-name, next, previous, up
+@subsubsection netgrp Selector Function
+@cindex netgrp Selector Function
+@cindex netgrp, boolean mount selector
+@cindex !netgrp, boolean mount selector
+@cindex Mount selector; netgrp
+@cindex Selector; netgrp
+
+If the current host as determined by the value of @code{$@{host@}} is a
+member of the netgroup @i{ARG}, this selector evaluates to true.
+Otherwise it evaluates to false.
+
+For example, suppose you have a netgroup @samp{ppp-hosts}, and for
+reasons of performance, these have a local @file{/home} partition, while
+all other clients on the faster network can access a shared home
+directory. A common map to use for both might look like the following:
+
+@example
+home/* netgrp(ppp-hosts);type:=link;fs:=/local/$@{key@} \
+ !netgrp(ppp-hosts);type:=nfs;rhost=serv1;rfs:=/remote/$@{key@}
+@end example
+
+@c ----------------------------------------------------------------
+@node in_network Selector Function, true Selector Function, netgrp Selector Function, Selectors
+@comment node-name, next, previous, up
+@subsubsection in_network Selector Function
+@cindex in_network Selector Function
+@cindex in_network, boolean mount selector
+@cindex !in_network, boolean mount selector
+@cindex Mount selector; in_network
+@cindex Selector; in_network
+
+If the current host has any network interface that is locally attached
+to the network specified in @i{ARG} (either via name or number), this
+selector evaluates to true. Otherwise it evaluates to false.
+
+For example, suppose you have two servers that have an exportable
+@file{/opt} that smaller clients can NFS mount. The two servers are
+say, @samp{serv1} on network @samp{foo-net.site.com} and @samp{serv2} on
+network @samp{123.4.5.0}. You can write a map to be used by all clients
+that will attempt to mount the closest one as follows:
+
+@example
+opt in_network(foo-net.site.com);rhost:=serv1;rfs:=/opt \
+ in_network(123.4.5.0);rhost:=serv2;rfs:=/opt \
+ rhost:=fallback-server
+@end example
+
+@c ----------------------------------------------------------------
+@node true Selector Function, , in_network Selector Function, Selectors
+@comment node-name, next, previous, up
+@subsubsection true Selector Function
+@cindex true Selector Function
+@cindex true, boolean mount selector
+@cindex !true, boolean mount selector
+@cindex Mount selector; true
+@cindex Selector; true
+
+Always evaluates to true. @i{ARG} is ignored.
+
+@c ================================================================
+@node Map Options, , Selectors, Location Format
+@comment node-name, next, previous, up
+@subsection Map Options
+@cindex Map options
+@cindex Setting map options
+
+Options are parsed concurrently with selectors. The difference is that
+when an option is seen the string following the @samp{:=} is
+recorded for later use. As a minimum the @var{type} option must be
+specified. Each filesystem type has other options which must also be
+specified. @xref{Filesystem Types}, for details on the filesystem
+specific options.@refill
+
+Superfluous option specifications are ignored and are not reported
+as errors.
+
+The following options apply to more than one filesystem type.
+
+@menu
+* addopts Option::
+* delay Option::
+* fs Option::
+* opts Option::
+* remopts Option::
+* sublink Option::
+* type Option::
+@end menu
+
+@node addopts Option, delay Option, Map Options, Map Options
+@comment node-name, next, previous, up
+@subsubsection addopts Option
+@cindex Setting additional options on a mount location
+@cindex Overriding or adding options to a mount
+@cindex addopts, mount option
+@cindex Mount option; addopts
+
+This option adds additional options to default options normally
+specified in the @samp{/defaults} entry or the defaults of the key entry
+being processed (@xref{opts Option}). Normally when you specify
+@samp{opts} in both the @samp{/defaults} and the map entry, the latter
+overrides the former completely. But with @samp{addopts} it will
+append the options and override any conflicting ones.
+
+Options which start with @samp{no} will override those with the same
+name that do not start with @samp{no} and vice verse. Special handling
+is given to inverted options such as @samp{soft} and @samp{hard},
+@samp{bg} and @samp{fg}, @samp{ro} and @samp{rw}, etc.
+
+For example, if the default options specified were
+@example
+opts:=rw,nosuid,intr,rsize=1024,wsize=1024,quota,posix
+@end example
+
+and the ones specified in a map entry were
+
+@example
+addopts:=grpid,suid,ro,rsize=2048,quota,nointr
+@end example
+
+then the actual options used would be
+
+@example
+wsize=1024,posix,grpid,suid,ro,rsize=2048,quota,nointr
+@end example
+
+@node delay Option, fs Option, addopts Option, Map Options
+@comment node-name, next, previous, up
+@subsubsection delay Option
+@cindex Setting a delay on a mount location
+@cindex Delaying mounts from specific locations
+@cindex Primary server
+@cindex Secondary server
+@cindex delay, mount option
+@cindex Mount option; delay
+
+The delay, in seconds, before an attempt will be made to mount from the
+current location. Auxiliary data, such as network address, file handles
+and so on are computed regardless of this value.
+
+A delay can be used to implement the notion of primary and secondary
+file servers. The secondary servers would have a delay of a few
+seconds, thus giving the primary servers a chance to respond first.
+
+@node fs Option, opts Option, delay Option, Map Options
+@comment node-name, next, previous, up
+@subsubsection fs Option
+@cindex Setting the local mount point
+@cindex Overriding the default mount point
+@cindex fs, mount option
+@cindex Mount option; fs
+
+The local mount point. The semantics of this option vary between
+filesystems.
+
+For NFS and UFS filesystems the value of @code{$@{fs@}} is used as the
+local mount point. For other filesystem types it has other meanings
+which are described in the section describing the respective filesystem
+type. It is important that this string uniquely identifies the
+filesystem being mounted. To satisfy this requirement, it should
+contain the name of the host on which the filesystem is resident and the
+pathname of the filesystem on the local or remote host.
+
+The reason for requiring the hostname is clear if replicated filesystems
+are considered. If a fileserver goes down and a replacement filesystem
+is mounted then the @dfn{local} mount point @dfn{must} be different from
+that of the filesystem which is hung. Some encoding of the filesystem
+name is required if more than one filesystem is to be mounted from any
+given host.
+
+If the hostname is first in the path then all mounts from a particular
+host will be gathered below a single directory. If that server goes
+down then the hung mount points are less likely to be accidentally
+referenced, for example when @b{getcwd}(3) traverses the namespace to
+find the pathname of the current directory.
+
+The @samp{fs} option defaults to
+@code{$@{autodir@}/$@{rhost@}$@{rfs@}}. In addition,
+@samp{rhost} defaults to the local host name (@code{$@{host@}}) and
+@samp{rfs} defaults to the value of @code{$@{path@}}, which is the full
+path of the requested file; @samp{/home/foo} in the example above
+(@pxref{Selectors}). @code{$@{autodir@}} defaults to @samp{/a} but may
+be changed with the @code{-a} command line option. Sun's automounter
+defaults to @samp{/tmp_mnt}. Note that there is no @samp{/} between
+the @code{$@{rhost@}} and @code{$@{rfs@}} since @code{$@{rfs@}} begins
+with a @samp{/}.@refill
+
+@node opts Option, remopts Option, fs Option, Map Options
+@comment node-name, next, previous, up
+@subsubsection opts Option
+@cindex Setting system mount options
+@cindex Passing parameters to the mount system call
+@cindex mount system call
+@cindex mount system call flags
+@cindex The mount system call
+@cindex opts, mount option
+@cindex Mount option; opts
+
+The options to pass to the mount system call. A leading @samp{-} is
+silently ignored. The mount options supported generally correspond to
+those used by @b{mount}(8) and are listed below. Some additional
+pseudo-options are interpreted by @i{Amd} and are also listed.
+
+Unless specifically overridden, each of the system default mount options
+applies. Any options not recognized are ignored. If no options list is
+supplied the string @samp{rw,defaults} is used and all the system
+default mount options apply. Options which are not applicable for a
+particular operating system are silently ignored. For example, only 4.4BSD
+is known to implement the @code{compress} and @code{spongy} options.
+
+@table @code
+
+@item acdirmax=@var{n}
+@cindex Mount flags; acdirmax
+Set the maximum directory attribute cache timeout to @var{n}.
+
+@item acdirmin=@var{n}
+@cindex Mount flags; acdirmin
+Set the minimum directory attribute cache timeout to @var{n}.
+
+@item acregmax=@var{n}
+@cindex Mount flags; acregmax
+Set the maximum file attribute cache timeout to @var{n}.
+
+@item acregmin=@var{n}
+@cindex Mount flags; acregmin
+Set the minimum file attribute cache timeout to @var{n}.
+
+@item actimeo=@var{n}
+@cindex Mount flags; actimeo
+Set the overall attribute cache timeout to @var{n}.
+
+@item auto
+@cindex Mount flags; auto
+@itemx ignore
+@cindex Mount flags; ignore
+Ignore this mount by @b{df}(1).
+
+@item cache
+@cindex Mount flags; cache
+Allow data to be cached from a remote server for this mount.
+
+@item compress
+@cindex Mount flags; compress
+Use NFS compression protocol.
+
+@item defperm
+@cindex Mount flags; defperm
+Ignore the permission mode bits, and default file permissions to 0555,
+UID 0, and GID 0. Useful for CD-ROMs formatted as ISO-9660.
+
+@item dev
+@cindex Mount flags; dev
+Allow local special devices on this filesystem.
+
+@item dumbtimr
+@cindex Mount flags; dumbtimr
+(XXX: a dumb timer?)
+
+@item fsid
+@cindex Mount flags; fsid
+Set ID of filesystem.
+
+@item grpid
+@cindex Mount flags; grpid
+Use BSD directory group-id semantics.
+
+@item int
+@cindex Mount flags; int
+@itemx intr
+@cindex Mount flags; intr
+Allow keyboard interrupts on hard mounts.
+
+@item multi
+@cindex Mount flags; multi
+Perform multi-component lookup on files.
+
+@item maxgroups
+@cindex Mount flags; maxgroups
+Set the maximum number of groups to allow for this mount.
+
+@item nfsv3
+@cindex Mount flags; nfsv3
+Use NFS Version 3 for this mount.
+
+@item noac
+@cindex Mount flags; noac
+Turn off the attribute cache.
+
+@item noauto
+@cindex Mount flags; noauto
+(XXX: No automatic what?)
+
+@item nocache
+@cindex Mount flags; nocache
+Do not allow data to be cached from a remote server for this
+mount.
+
+@item noconn
+@cindex Mount flags; noconn
+Don't make a connection on datagram transports.
+
+@item nocto
+@cindex Mount flags; nocto
+No close-to-open consistency.
+
+@item nodefperm
+@cindex Mount flags; nodefperm
+Do not ignore the permission mode bits. Useful for CD-ROMS formatted as
+ISO-9660.
+
+@item nodev
+@cindex Mount flags; nodev
+@itemx nodevs
+@cindex Mount flags; nodevs
+Don't allow local special devices on this filesystem.
+
+@item noint
+@cindex Mount flags; noint
+Do not allow keyboard interrupts for this mount
+
+@item nosub
+@cindex Mount flags; nosub
+Disallow mounts beneath this mount.
+
+@item nosuid
+@cindex Mount flags; nosuid
+Don't allow set-uid or set-gid executables on this filesystem.
+
+@item noversion
+@cindex Mount flags; noversion
+Strip the extension @samp{;#} from the version string of files recorded
+on an ISO-9660 CD-ROM.
+
+@item overlay
+@cindex Mount flags; overlay
+Overlay this mount on top of an existing mount, if any.
+
+@item pgthresh=@var{n}
+@cindex Mount flags; pgthresh
+Set the paging threshold to @var{n} kilobytes.
+
+@item port=@var{n}
+@cindex Mount flags; port
+Set the NFS port to @var{n}.
+
+@item posix
+@cindex Mount flags; posix
+Turn on POSIX static pathconf for mounts.
+
+@item proto=@var{s}
+@cindex Mount flags; proto
+Use transport protocol @var{s} for NFS (can be @code{"tcp"} or @code{"udp"}).
+
+@item quota
+@cindex Mount flags; quota
+Enable quota checking on this mount.
+
+@item rdonly
+@cindex Mount flags; rdonly
+@itemx ro
+@cindex Mount flags; ro
+Mount this filesystem readonly.
+
+@item resvport
+@cindex Mount flags; resvport
+Use a reserved port (smaller than 1024) for remote NFS mounts. Most
+systems assume that, but some allow for mounts to occur on non-reserved
+ports. This causes problems when such a system tries to NFS mount one
+that requires reserved ports. It is recommended that this option always
+be on.
+
+@item retrans=@i{n}
+@cindex Mount flags; retrans
+The number of NFS retransmits made before a user error is generated by a
+@samp{soft} mounted filesystem, and before a @samp{hard} mounted
+filesystem reports @samp{NFS server @dfn{yoyo} not responding still
+trying}.
+
+@item retry
+@cindex Mount flags; retry
+Set the NFS retry counter.
+
+@item rrip
+@cindex Mount flags; rrip
+Uses the Rock Ridge Interchange Protocol (RRIP) extensions to ISO-9660.
+
+@item rsize=@var{n}
+@cindex Mount flags; rsize
+The NFS read packet size. You may need to set this if you are using
+NFS/UDP through a gateway or a slow link.
+
+@item rw
+@cindex Mount flags; rw
+Allow reads and writes on this filesystem.
+
+@item soft
+@cindex Mount flags; soft
+Give up after @dfn{retrans} retransmissions.
+
+@item spongy
+@cindex Mount flags; spongy
+Like @samp{soft} for status requests, and @samp{hard} for data transfers.
+
+@item suid
+@cindex Mount flags; suid
+Allow set-uid programs on this mount.
+
+@item symttl
+@cindex Mount flags; symttl
+Turn of the symbolic link cache time-to-live.
+
+@item sync
+@cindex Mount flags; sync
+Perform synchronous filesystem operations on this mount.
+
+@item tcp
+@cindex Mount flags; tcp
+Use TCP/IP instead of UDP/IP, ignored if the NFS implementation does not
+support TCP/IP mounts.
+
+@item timeo=@var{n}
+@cindex Mount flags; timeo
+The NFS timeout, in tenth-seconds, before a request is retransmitted.
+
+@item vers=@var{n}
+@cindex Mount flags; vers
+ Use NFS protocol version number @var{n} (can be 2 or 3).
+
+@item wsize=@var{n}
+@cindex Mount flags; wsize
+The NFS write packet size. You may need to set this if you are using
+NFS/UDP through a gateway or a slow link.
+
+@end table
+
+The following options are implemented by @i{Amd}, rather than being
+passed to the kernel.
+
+@table @code
+
+@item nounmount
+@cindex Mount flags; nounmount
+Configures the mount so that its time-to-live will
+never expire. This is also the default for some filesystem types.
+@c
+@c Implementation broken:
+
+@item ping=@var{n}
+@cindex Mount flags; ping
+The interval, in seconds, between keep-alive pings. When four
+consecutive pings have failed the mount point is marked as hung. This
+interval defaults to 30 seconds. If the ping interval is less than zero,
+no pings are sent and the host is assumed to be always
+up. By default, pings are not sent for an NFS/TCP mount.
+
+@item retry=@var{n}
+@cindex Mount flags; retry=@var{n}
+The number of times to retry the mount system call.
+
+@item utimeout=@var{n}
+@cindex Mount flags; utimeout=@var{n}
+The interval, in seconds, by which the mount's
+time-to-live is extended after an unmount attempt
+has failed. In fact the interval is extended before the unmount is
+attempted to avoid thrashing. The default value is 120 seconds (two
+minutes) or as set by the @code{-w} command line option.
+
+@end table
+
+@node remopts Option, sublink Option, opts Option, Map Options
+@comment node-name, next, previous, up
+@subsubsection remopts Option
+@cindex Setting system mount options for non-local networks
+@cindex remopts, mount option
+@cindex Mount option; remopts
+
+This option has the same use as @code{$@{opts@}} but applies only when
+the remote host is on a non-local network. For example, when using NFS
+across a gateway it is often necessary to use smaller values for the
+data read and write sizes. This can simply be done by specifying the
+small values in @var{remopts}. When a non-local host is accessed, the
+smaller sizes will automatically be used.
+
+@i{Amd} determines whether a host is local by examining the network
+interface configuration at startup. Any interface changes made after
+@i{Amd} has been started will not be noticed. The likely effect will
+be that a host may incorrectly be declared non-local.
+
+Unless otherwise set, the value of @code{$@{remopts@}} is the same as
+the value of @code{$@{opts@}}.
+
+@node sublink Option, type Option, remopts Option, Map Options
+@comment node-name, next, previous, up
+@subsubsection sublink Option
+@cindex Setting the sublink option
+@cindex sublink, mount option
+@cindex Mount option; sublink
+
+The subdirectory within the mounted filesystem to which the reference
+should point. This can be used to prevent duplicate mounts in cases
+where multiple directories in the same mounted filesystem are used.
+
+@node type Option, , sublink Option, Map Options
+@comment node-name, next, previous, up
+@subsubsection type Option
+@cindex Setting the filesystem type option
+@cindex type, mount option
+@cindex Mount option; type
+
+The filesystem type to be used. @xref{Filesystem Types}, for a full
+description of each type.@refill
+
+@c ################################################################
+@node Amd Command Line Options, Filesystem Types, Mount Maps, Top
+@comment node-name, next, previous, up
+@chapter @i{Amd} Command Line Options
+@cindex Command line options, Amd
+@cindex Amd command line options
+@cindex Overriding defaults on the command line
+
+Many of @i{Amd}'s parameters can be set from the command line. The
+command line is also used to specify automount points and maps.
+
+The general format of a command line is
+
+@example
+amd [@i{options}] [@{ @i{directory} @i{map-name} [-@i{map-options}] @} ...]
+@end example
+
+For each directory and map-name given or specified in the
+@file{amd.conf} file, @i{Amd} establishes an automount point. The
+@dfn{map-options} may be any sequence of options or
+selectors---@pxref{Location Format}. The @dfn{map-options} apply only
+to @i{Amd}'s mount point.
+
+@samp{type:=toplvl;cache:=mapdefault;fs:=$@{map@}} is the default value for the
+map options. Default options for a map are read from a special entry in
+the map whose key is the string @samp{/defaults}. When default options
+are given they are prepended to any options specified in the mount-map
+locations as explained in @ref{Map Defaults}.
+
+The @dfn{options} are any combination of those listed below.
+
+Once the command line has been parsed, the automount points are mounted.
+The mount points are created if they do not already exist, in which case they
+will be removed when @i{Amd} exits.
+Finally, @i{Amd} disassociates itself from its controlling terminal and
+forks into the background.
+
+Note: Even if @i{Amd} has been built with @samp{-DDEBUG} (via
+@code{configure --enable-debug}), it will still background itself and
+disassociate itself from the controlling terminal. To use a debugger it
+is necessary to specify @samp{-D nodaemon} on the command line.
+However, even with all of this, mounts and unmounts are performed in the
+background, and @i{Amd} will always fork before doing them. Therefore,
+debugging what happens closely during un/mounts is more challenging.
+
+@emph{All} of @i{Amd}'s command options (save @code{-F} and @code{-T})
+can be specified in the @file{amd.conf} file. @xref{Amd Configuration
+File}. If @i{Amd} is invoked without any command line options, it will
+default to using the configuration file @file{/etc/amd.conf}, if one
+exists.
+
+@menu
+* -a Option:: Automount directory.
+* -c Option:: Cache timeout interval.
+* -d Option:: Domain name.
+* -k Option:: Kernel architecture.
+* -l Option:: Log file.
+* -n Option:: Hostname normalization.
+* -o Option:: Operating system version.
+* -p Option:: Output process id.
+* -r Option:: Restart existing mounts.
+* -t Option:: Kernel RPC timeout.
+* -v Option:: Version information.
+* -w Option:: Wait interval after failed unmount.
+* -x Option:: Log options.
+* -y Option:: NIS domain.
+* -C-Option:: Cluster name.
+* -D-Option:: Debug flags.
+* -F Option:: Amd configuration file.
+* -H Option:: Show brief help.
+* -O-Option:: Operating system name.
+* -S Option:: Lock executable pages in memory.
+* -T-Option:: Set tag for configuration file.
+@end menu
+
+@c ----------------------------------------------------------------
+@node -a Option, -c Option, Amd Command Line Options, Amd Command Line Options
+@comment node-name, next, previous, up
+@section @code{-a} @var{directory}
+@cindex Automount directory
+@cindex Setting the default mount directory
+
+Specifies the default mount directory. This option changes the variable
+@code{$@{autodir@}} which otherwise defaults to @file{/a}. For example,
+some sites prefer @file{/amd} or @file{/n}.
+
+@example
+amd -a /amd ...
+@end example
+
+@c ----------------------------------------------------------------
+@node -c Option, -d Option, -a Option, Amd Command Line Options
+@comment node-name, next, previous, up
+@section @code{-c} @var{cache-interval}
+@cindex Cache interval
+@cindex Interval before a filesystem times out
+@cindex Setting the interval before a filesystem times out
+@cindex Changing the interval before a filesystem times out
+
+Selects the period, in seconds, for which a name is cached by @i{Amd}.
+If no reference is made to the volume in this period, @i{Amd} discards
+the volume name to filesystem mapping.
+
+Once the last reference to a filesystem has been removed, @i{Amd}
+attempts to unmount the filesystem. If the unmount fails the interval
+is extended by a further period as specified by the @samp{-w} command
+line option or by the @samp{utimeout} mount option.
+
+The default @dfn{cache-interval} is 300 seconds (five minutes).
+
+@c ----------------------------------------------------------------
+@node -d Option, -k Option, -c Option, Amd Command Line Options
+@comment node-name, next, previous, up
+@section @code{-d} @var{domain}
+@cindex Domain name
+@cindex Setting the local domain name
+@cindex Overriding the local domain name
+
+Specifies the host's domain. This sets the internal variable
+@code{$@{domain@}} and affects the @code{$@{hostd@}} variable.
+
+If this option is not specified and the hostname already contains the
+local domain then that is used, otherwise the default value of
+@code{$@{domain@}} is @samp{unknown.domain}.
+
+For example, if the local domain was @samp{doc.ic.ac.uk}, @i{Amd} could
+be started as follows:
+
+@example
+amd -d doc.ic.ac.uk ...
+@end example
+
+@c ----------------------------------------------------------------
+@node -k Option, -l Option, -d Option, Amd Command Line Options
+@comment node-name, next, previous, up
+@section @code{-k} @var{kernel-architecture}
+@cindex Setting the Kernel architecture
+
+Specifies the kernel architecture of the system. This is usually the
+output of @samp{uname -m} (the ``machine'' value gotten from
+@b{uname}(2)). If the @b{uname}(2) system call is not available, the
+value of @code{$@{karch@}} defaults to that of @code{$@{arch@}}.
+
+The only effect of this option is to set the variable @code{$@{karch@}}.
+
+This option would be used as follows:
+
+@example
+amd -k `arch -k` ...
+@end example
+
+@c ----------------------------------------------------------------
+@node -l Option, -n Option, -k Option, Amd Command Line Options
+@comment node-name, next, previous, up
+@section @code{-l} @var{log-option}
+@cindex Log filename
+@cindex Setting the log file
+@cindex Using syslog to log errors
+@cindex syslog
+
+Selects the form of logging to be made. Several special @dfn{log-options}
+are recognized.
+
+@enumerate
+@item
+If @dfn{log-option} is the string @samp{syslog}, @i{Amd} will use the
+@b{syslog}(3) mechanism. If your system supports syslog facilities, then
+the default facility used is @samp{LOG_DAEMON}.
+
+@item
+@cindex syslog facility; specifying an alternate
+When using syslog, if you wish to change the facility, append its name
+to the log option name, delimited by a single colon. For example, if
+@dfn{log-options} is the string @samp{syslog:local7} then @b{Amd} will
+log messages via @b{syslog}(3) using the @samp{LOG_LOCAL7} facility. If
+the facility name specified is not recognized, @i{Amd} will default to
+@samp{LOG_DAEMON}. Note: while you can use any syslog facility
+available on your system, it is generally a bad idea to use those
+reserved for other services such as @samp{kern}, @samp{lpr},
+@samp{cron}, etc.
+
+@item
+If @dfn{log-option} is the string @samp{/dev/stderr}, @i{Amd} will use
+standard error, which is also the default target for log messages. To
+implement this, @i{Amd} simulates the effect of the @samp{/dev/fd}
+driver.
+@end enumerate
+
+Any other string is taken as a filename to use for logging. Log
+messages are appended to the file if it already exists, otherwise a new
+file is created. The file is opened once and then held open, rather
+than being re-opened for each message.
+
+Normally, when long-running daemons hold an open file descriptor on a
+log file, it is impossible to ``rotate'' the log file and compress older
+logs on a daily basis. The daemon needs to be told to discard (via
+@b{close}(2)) its file handle, and re-open the log file. This is done
+using @code{amq -l} @i{log-option}. @xref{Amq -l option}.
+
+If the @samp{syslog} option is specified but the system does not support
+syslog or if the named file cannot be opened or created, @i{Amd} will
+use standard error. Error messages generated before @i{Amd} has
+finished parsing the command line are printed on standard error.
+
+Since @i{Amd} tends to generate a lot of logging information (especially
+if debugging was turned on), and due to it being an important program
+running on the system, it is usually best to log to a separate disk
+file. In that case @i{Amd} would be started as follows:
+
+@example
+amd -l /var/log/amd ...
+@end example
+
+@c ----------------------------------------------------------------
+@node -n Option, -o Option, -l Option, Amd Command Line Options
+@comment node-name, next, previous, up
+@section @code{-n}
+@cindex Hostname normalization
+@cindex Aliased hostnames
+@cindex Resolving aliased hostnames
+@cindex Normalizing hostnames
+
+Normalizes the remote hostname before using it. Normalization is done
+by replacing the value of @code{$@{rhost@}} with the (generally fully
+qualified) primary name returned by a hostname lookup.
+
+This option should be used if several names are used to refer to a
+single host in a mount map.
+
+@c ----------------------------------------------------------------
+@node -o Option, -p Option, -n Option, Amd Command Line Options
+@comment node-name, next, previous, up
+@section @code{-o} @var{op-sys-ver}
+@cindex Operating System version
+@cindex Setting the Operating System version
+
+Override the compiled-in version number of the operating system, with
+@var{op-sys-ver}. Useful when the built-in version is not desired for
+backward compatibility reasons. For example, if the built-in version is
+@samp{2.5.1}, you can override it to @samp{5.5.1}, and use older maps
+that were written with the latter in mind.
+
+@c ----------------------------------------------------------------
+@node -p Option, -r Option, -o Option, Amd Command Line Options
+@comment node-name, next, previous, up
+@section @code{-p}
+@cindex Process id
+@cindex Displaying the process id
+@cindex process id of Amd daemon
+@cindex pid file, creating with -p option
+@cindex Creating a pid file
+
+Causes @i{Amd}'s process id to be printed on standard output.
+This can be redirected to a suitable file for use with kill:
+
+@example
+amd -p > /var/run/amd.pid ...
+@end example
+
+This option only has an affect if @i{Amd} is running in daemon mode.
+If @i{Amd} is started with the @code{-D nodaemon} debug flag, this
+option is ignored.
+
+@c ----------------------------------------------------------------
+@node -r Option, -t Option, -p Option, Amd Command Line Options
+@comment node-name, next, previous, up
+@section @code{-r}
+@cindex Restarting existing mounts
+@cindex Picking up existing mounts
+
+Tells @i{Amd} to restart existing mounts (@pxref{Inheritance Filesystem}).
+@c @dfn{This option will be made the default in the next release.}
+
+@c ----------------------------------------------------------------
+@node -t Option, -v Option, -r Option, Amd Command Line Options
+@comment node-name, next, previous, up
+@section @code{-t} @var{timeout.retransmit}
+@cindex Setting Amd's RPC parameters
+
+Specifies the RPC @dfn{timeout} and @dfn{retransmit} intervals used by
+the kernel to communicate to @i{Amd}. These are used to set the
+@samp{timeo} and @samp{retrans} mount options.
+
+@i{Amd} relies on the kernel RPC retransmit mechanism to trigger mount
+retries. The value of this parameter changes the retry interval. Too
+long an interval gives poor interactive response, too short an interval
+causes excessive retries.
+
+@c ----------------------------------------------------------------
+@node -v Option, -w Option, -t Option, Amd Command Line Options
+@comment node-name, next, previous, up
+@section @code{-v}
+@cindex Version information
+@cindex Discovering version information
+@cindex How to discover your version of Amd
+
+Print version information on standard error and then exit. The output
+is of the form:
+
+@example
+Copyright (c) 1997-1998 Erez Zadok
+Copyright (c) 1990 Jan-Simon Pendry
+Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+Copyright (c) 1990 The Regents of the University of California.
+am-utils version 6.0a15 (build 61).
+Built by ezk@@cs.columbia.edu on date Wed Oct 22 15:21:03 EDT 1997.
+cpu=sparc (big-endian), arch=sun4, karch=sun4u.
+full_os=solaris2.5.1, os=sos5, osver=5.5.1, vendor=sun.
+Map support for: root, passwd, union, nisplus, nis, ndbm, file, error.
+AMFS: nfs, link, nfsx, nfsl, host, linkx, program, union, inherit,
+ ufs, lofs, hsfs, pcfs, auto, direct, toplvl, error.
+FS: autofs, cachefs, cdfs, lofs, nfs, nfs3, pcfs, tfs, tmpfs, ufs.
+Network 1: wire="mcl-lab-net.cs.columbia.edu" (netnumber=128.59.13).
+Network 2: wire="14-net.cs.columbia.edu" (netnumber=128.59.14).
+Network 3: wire="old-net.cs.columbia.edu" (netnumber=128.59.16).
+@end example
+
+The information includes the version number, number of times @i{Amd} was
+compiled on the local system, release date and name of the release.
+Following come the cpu type, byte ordering, and the architecture and
+kernel architecture as @code{$@{arch@}} and @code{$@{karch@}},
+respectively. The next line lists the full name of the system, the
+variables @code{$@{os@}} and @code{$@{osver@}}, and the vendor's
+name. @xref{Supported Platforms}.
+
+Then come a list of map types supported, filesystems internally
+supported by @i{Amd} (AMFS), and generic filesystems available (FS).
+Finally all known networks (if any) of this host are listed by name
+and number. They are available via the variables
+@code{$@{wire@}} or @code{$@{network@}}, and
+@code{$@{netnumber@}} (@pxref{Selectors}) or the @samp{in_network}
+selector function (@pxref{in_network Selector Function}).
+
+@c ----------------------------------------------------------------
+@node -w Option, -x Option, -v Option, Amd Command Line Options
+@comment node-name, next, previous, up
+@section @code{-w} @var{wait-timeout}
+@cindex Setting the interval between unmount attempts
+@cindex unmount attempt backoff interval
+
+Selects the interval in seconds between unmount attempts after the
+initial time-to-live has expired.
+
+This defaults to 120 seconds (two minutes).
+
+@c ----------------------------------------------------------------
+@node -x Option, -y Option, -w Option, Amd Command Line Options
+@comment node-name, next, previous, up
+@section @code{-x} @var{opts}
+@cindex Log message selection
+@cindex Selecting specific log messages
+@cindex How to select log messages
+@cindex syslog priorities
+
+Specifies the type and verbosity of log messages. @dfn{opts} is
+a comma separated list selected from the following options:
+
+@table @code
+@item fatal
+Fatal errors
+@item error
+Non-fatal errors
+@item user
+Non-fatal user errors
+@item warn
+Recoverable errors
+@item warning
+Alias for @code{warn}
+@item info
+Information messages
+@item map
+Mount map usage
+@item stats
+Additional statistics
+@item all
+All of the above
+@end table
+
+Initially a set of default logging flags is enabled. This is as if
+@samp{-x all,nomap,nostats} had been selected. The command line is
+parsed and logging is controlled by the @code{-x} option. The very first
+set of logging flags is saved and can not be subsequently disabled using
+@i{Amq}. This default set of options is useful for general production
+use.@refill
+
+The @samp{info} messages include details of what is mounted and
+unmounted and when filesystems have timed out. If you want to have the
+default set of messages without the @samp{info} messages then you simply
+need @samp{-x noinfo}. The messages given by @samp{user} relate to
+errors in the mount maps, so these are useful when new maps are
+installed. The following table lists the syslog priorities used for each
+of the message types.@refill
+
+@table @code
+@item fatal
+@samp{LOG_CRIT}
+@item error
+@samp{LOG_ERR}
+@item user
+@samp{LOG_WARNING}
+@item warning
+@samp{LOG_WARNING}
+@item info
+@samp{LOG_INFO}
+@item debug
+@samp{LOG_DEBUG}
+@item map
+@samp{LOG_DEBUG}
+@item stats
+@samp{LOG_INFO}
+@end table
+
+
+The options can be prefixed by the string @samp{no} to indicate
+that this option should be turned off. For example, to obtain all
+but @samp{info} messages the option @samp{-x all,noinfo} would be used.
+
+If @i{Amd} was built with debugging enabled the @code{debug} option is
+automatically enabled regardless of the command line options.
+
+@c ----------------------------------------------------------------
+@node -y Option, -C-Option, -x Option, Amd Command Line Options
+@comment node-name, next, previous, up
+@section @code{-y} @var{NIS-domain}
+@cindex NIS (YP) domain name
+@cindex Overriding the NIS (YP) domain name
+@cindex Setting the NIS (YP) domain name
+@cindex YP domain name
+
+Selects an alternate NIS domain. This is useful for debugging and
+cross-domain shared mounting. If this flag is specified, @i{Amd}
+immediately attempts to bind to a server for this domain.
+@c @i{Amd} refers to NIS maps when it starts, unless the @code{-m} option
+@c is specified, and whenever required in a mount map.
+
+@c ----------------------------------------------------------------
+@node -C-Option, -D-Option, -y Option, Amd Command Line Options
+@comment node-name, next, previous, up
+@section @code{-C} @var{cluster-name}
+@cindex Cluster names
+@cindex Setting the cluster name
+
+Specifies the name of the cluster of which the local machine is a member.
+The only effect is to set the variable @code{$@{cluster@}}.
+The @dfn{cluster-name} is will usually obtained by running another command which uses
+a database to map the local hostname into a cluster name.
+@code{$@{cluster@}} can then be used as a selector to restrict mounting of
+replicated data.
+If this option is not given, @code{$@{cluster@}} has the same value as @code{$@{domain@}}.
+This would be used as follows:
+
+@example
+amd -C `clustername` ...
+@end example
+
+@c ----------------------------------------------------------------
+@node -D-Option, -F Option, -C-Option, Amd Command Line Options
+@comment node-name, next, previous, up
+@section @code{-D} @var{opts}
+@cindex Debug options
+@cindex Setting debug flags
+
+Controls the verbosity and coverage of the debugging trace; @dfn{opts}
+is a comma separated list of debugging options. The @code{-D} option is
+only available if @i{Amd} was compiled with @samp{-DDEBUG}, or
+configured with @code{configure --enable-debug}. The memory debugging
+facilities (@samp{mem}) are only available if @i{Amd} was compiled with
+@samp{-DDEBUG_MEM} (in addition to @samp{-DDEBUG}), or configured with
+@code{configure --enable-debug=mem}.
+
+The most common options to use are @samp{-D trace} and @samp{-D test}
+(which turns on all the useful debug options). As usual, every option
+can be prefixed with @samp{no} to turn it off.
+
+@table @code
+@item all
+all options
+@item amq
+register for amq
+@item daemon
+enter daemon mode
+@item fork
+fork server
+@item full
+program trace
+@item info
+@cindex debugging hesiod resolver service
+@cindex Hesiod: turning on RES_DEBUG
+info service specific debugging (hesiod, nis, etc.) In the case of
+hesiod maps, turns on the hesiod RES_DEBUG internal debugging option.
+@item mem
+trace memory allocations
+@item mtab
+use local @file{./mtab} file
+@item str
+debug string munging
+@item test
+full debug but no daemon
+@item trace
+protocol trace
+@end table
+
+You may also refer to the program source for a more detailed explanation
+of the available options.
+
+@c ----------------------------------------------------------------
+@node -F Option, -H Option, -D-Option, Amd Command Line Options
+@comment node-name, next, previous, up
+@section @code{-F} @var{conf-file}
+@cindex Amd configuration file; specifying name
+@cindex Amd configuration file
+@cindex amd.conf file
+
+Specify an @i{Amd} configuration file @var{conf-file} to use. For a
+description of the format and syntax, @pxref{Amd Configuration File}.
+This configuration file is used to specify any options in lieu of typing
+many of them on the command line. The @file{amd.conf} file includes
+directives for every command line option @i{Amd} has, and many more that
+are only available via the configuration file facility. The
+configuration file specified by this option is processed after all other
+options had been processed, regardless of the actual location of this
+option on the command line.
+
+@c ----------------------------------------------------------------
+@node -H Option, -O-Option, -F Option, Amd Command Line Options
+@comment node-name, next, previous, up
+@section @code{-H}
+@cindex Displaying brief help
+@cindex Help; showing from Amd
+
+Print a brief help and usage string.
+
+@c ----------------------------------------------------------------
+@node -O-Option, -S Option, -H Option, Amd Command Line Options
+@comment node-name, next, previous, up
+@section @code{-O} @var{op-sys-name}
+@cindex Operating System name
+@cindex Setting the Operating System name
+
+Override the compiled-in name of the operating system, with
+@var{op-sys-name}. Useful when the built-in name is not desired for
+backward compatibility reasons. For example, if the build in name is
+@samp{sunos5}, you can override it to the old name @samp{sos5}, and use
+older maps which were written with the latter in mind.
+
+@c ----------------------------------------------------------------
+@node -S Option, -T-Option, -O-Option, Amd Command Line Options
+@comment node-name, next, previous, up
+@section @code{-S}
+@cindex plock; using
+@cindex locking executable pages in memory
+
+Do @emph{not} lock the running executable pages of @i{Amd} into memory.
+To improve @i{Amd}'s performance, systems that support the @b{plock}(3)
+call lock the @i{Amd} process into memory. This way there is less
+chance the operating system will schedule, page out, and swap the
+@i{Amd} process as needed. This tends to improve @i{Amd}'s performance,
+at the cost of reserving the memory used by the @i{Amd} process (making
+it unavailable for other processes). If this behavior is not desired,
+use the @code{-S} option.
+
+@c ----------------------------------------------------------------
+@node -T-Option, , -S Option, Amd Command Line Options
+@comment node-name, next, previous, up
+@section @code{-T} @var{tag}
+@cindex Tags for Amd configuration file
+@cindex Configuration file; tags
+
+Specify a tag to use with @file{amd.conf}. All map entries tagged with
+@var{tag} will be processed. Map entries that are not tagged are always
+processed. Map entries that are tagged with a tag other than @var{tag}
+will not be processed.
+
+@c ################################################################
+@node Filesystem Types, Amd Configuration File, Amd Command Line Options, Top
+@comment node-name, next, previous, up
+@chapter Filesystem Types
+@cindex Filesystem types
+@cindex Mount types
+@cindex Types of filesystem
+
+To mount a volume, @i{Amd} must be told the type of filesystem to be
+used. Each filesystem type typically requires additional information
+such as the fileserver name for NFS.
+
+From the point of view of @i{Amd}, a @dfn{filesystem} is anything that
+can resolve an incoming name lookup. An important feature is support
+for multiple filesystem types. Some of these filesystems are
+implemented in the local kernel and some on remote fileservers, whilst
+the others are implemented internally by @i{Amd}.@refill
+
+The two common filesystem types are UFS and NFS. Four other user
+accessible filesystems (@samp{link}, @samp{program}, @samp{auto} and
+@samp{direct}) are also implemented internally by @i{Amd} and these are
+described below. There are two additional filesystem types internal to
+@i{Amd} which are not directly accessible to the user (@samp{inherit}
+and @samp{error}). Their use is described since they may still have an
+effect visible to the user.@refill
+
+@menu
+* Network Filesystem:: A single NFS filesystem.
+* Network Host Filesystem:: NFS mount a host's entire export tree.
+* Network Filesystem Group:: An atomic group of NFS filesystems.
+* Unix Filesystem:: Native disk filesystem.
+* Caching Filesystem:: Caching from remote server filesystem.
+* CD-ROM Filesystem:: ISO9660 CD ROM.
+* Loopback Filesystem:: Local loopback-mount filesystem.
+* Memory/RAM Filesystem:: A memory or RAM-based filesystem.
+* Null Filesystem:: 4.4BSD's loopback-mount filesystem.
+* Floppy Filesystem:: MS-DOS Floppy filesystem.
+* Translucent Filesystem:: The directory merging filesystem.
+* Shared Memory+Swap Filesystem:: Sun's tmpfs filesystem.
+* User ID Mapping Filesystem:: 4.4BSD's umapfs filesystem.
+* Program Filesystem:: Generic Program mounts.
+* Symbolic Link Filesystem:: Local link.
+* Symbolic Link Filesystem II:: Local link referencing existing filesystem.
+* NFS-Link Filesystem:: Link if path exists, NFS otherwise.
+* Automount Filesystem::
+* Direct Automount Filesystem::
+* Union Filesystem::
+* Error Filesystem::
+* Top-level Filesystem::
+* Autofs Filesystem:: Sun's kernel-based automounter filesystem.
+* Root Filesystem::
+* Inheritance Filesystem::
+@end menu
+
+@c ----------------------------------------------------------------
+@node Network Filesystem, Network Host Filesystem, Filesystem Types, Filesystem Types
+@comment node-name, next, previous, up
+@section Network Filesystem (@samp{nfs})
+@cindex NFS
+@cindex Mounting an NFS filesystem
+@cindex How to mount and NFS filesystem
+@cindex nfs, filesystem type
+@cindex Filesystem type; nfs
+
+The @dfn{nfs} (@samp{type:=nfs}) filesystem type provides access to Sun's NFS.
+
+@noindent
+The following options must be specified:
+
+@table @code
+@cindex rhost, mount option
+@cindex Mount option; rhost
+@item rhost
+the remote fileserver. This must be an entry in the hosts database. IP
+addresses are not accepted. The default value is taken
+from the local host name (@code{$@{host@}}) if no other value is
+specified.
+
+@cindex rfs, mount option
+@cindex Mount option; rfs
+@item rfs
+the remote filesystem.
+If no value is specified for this option, an internal default of
+@code{$@{path@}} is used.
+@end table
+
+NFS mounts require a two stage process. First, the @dfn{file handle} of
+the remote file system must be obtained from the server. Then a mount
+system call must be done on the local system. @i{Amd} keeps a cache
+of file handles for remote file systems. The cache entries have a
+lifetime of a few minutes.
+
+If a required file handle is not in the cache, @i{Amd} sends a request
+to the remote server to obtain it. @i{Amd} @dfn{does not} wait for
+a response; it notes that one of the locations needs retrying, but
+continues with any remaining locations. When the file handle becomes
+available, and assuming none of the other locations was successfully
+mounted, @i{Amd} will retry the mount. This mechanism allows several
+NFS filesystems to be mounted in parallel.
+@c @footnote{The mechanism
+@c is general, however NFS is the only filesystem
+@c for which the required hooks have been written.}
+The first one which responds with a valid file handle will be used.
+
+@noindent
+An NFS entry might be:
+
+@example
+jsp host!=charm;type:=nfs;rhost:=charm;rfs:=/home/charm;sublink:=jsp
+@end example
+
+The mount system call and any unmount attempts are always done
+in a new task to avoid the possibility of blocking @i{Amd}.
+
+@c ----------------------------------------------------------------
+@node Network Host Filesystem, Network Filesystem Group, Network Filesystem, Filesystem Types
+@comment node-name, next, previous, up
+@section Network Host Filesystem (@samp{host})
+@cindex Network host filesystem
+@cindex Mounting entire export trees
+@cindex How to mount all NFS exported filesystems
+@cindex host, filesystem type
+@cindex Filesystem type; host
+
+@c NOTE: the current implementation of the @dfn{host} filesystem type
+@c sometimes fails to maintain a consistent view of the remote mount tree.
+@c This happens when the mount times out and only some of the remote mounts
+@c are successfully unmounted. To prevent this from occurring, use the
+@c @samp{nounmount} mount option.
+
+The @dfn{host} (@samp{type:=host}) filesystem allows access to the entire export tree of an
+NFS server. The implementation is layered above the @samp{nfs}
+implementation so keep-alives work in the same way. The only option
+which needs to be specified is @samp{rhost} which is the name of the
+fileserver to mount.
+
+The @samp{host} filesystem type works by querying the mount daemon on
+the given fileserver to obtain its export list. @i{Amd} then obtains
+filehandles for each of the exported filesystems. Any errors at this
+stage cause that particular filesystem to be ignored. Finally each
+filesystem is mounted. Again, errors are logged but ignored. One
+common reason for mounts to fail is that the mount point does not exist.
+Although @i{Amd} attempts to automatically create the mount point, it
+may be on a remote filesystem to which @i{Amd} does not have write
+permission.
+
+When an attempt to unmount a @samp{host} filesystem mount fails, @i{Amd}
+remounts any filesystems which had successfully been unmounted. To do
+this @i{Amd} queries the mount daemon again and obtains a fresh copy of
+the export list. @i{Amd} then tries to mount any exported filesystems
+which are not currently mounted.
+
+Sun's automounter provides a special @samp{-hosts} map. To achieve the
+same effect with @i{Amd} requires two steps. First a mount map must
+be created as follows:
+
+@example
+* type:=host;rhost:=$@{key@};fs:=$@{autodir@}/$@{rhost@}/root
+@end example
+
+@noindent
+and then start @i{Amd} with the following command
+
+@example
+amd /net net.map
+@end example
+
+@noindent
+where @samp{net.map} is the name of map described above. Note that the
+value of @code{$@{fs@}} is overridden in the map. This is done to avoid
+a clash between the mount tree and any other filesystem already mounted
+from the same fileserver.
+
+If different mount options are needed for different hosts then
+additional entries can be added to the map, for example
+
+@example
+host2 opts:=ro,nosuid,soft
+@end example
+
+@noindent
+would soft mount @samp{host2} read-only.
+
+@c ----------------------------------------------------------------
+@node Network Filesystem Group, Unix Filesystem, Network Host Filesystem, Filesystem Types
+@comment node-name, next, previous, up
+@section Network Filesystem Group (@samp{nfsx})
+@cindex Network filesystem group
+@cindex Atomic NFS mounts
+@cindex Mounting an atomic group of NFS filesystems
+@cindex How to mount an atomic group of NFS filesystems
+@cindex nfsx, filesystem type
+@cindex Filesystem type; nfsx
+
+The @dfn{nfsx} (@samp{type:=nfsx}) filesystem allows a group of filesystems to be mounted
+from a single NFS server. The implementation is layered above the
+@samp{nfs} implementation so keep-alives work in the same way.
+
+The options are the same as for the @samp{nfs} filesystem with one
+difference.
+
+@noindent
+The following options must be specified:
+
+@table @code
+@item rhost
+the remote fileserver. This must be an entry in the hosts database. IP
+addresses are not accepted. The default value is taken from the local
+host name (@code{$@{host@}}) if no other value is specified.
+
+@item rfs
+as a list of filesystems to mount. The list is in the form of a comma
+separated strings.
+@end table
+
+@noindent
+For example:
+
+@example
+pub type:=nfsx;rhost:=gould;\
+ rfs:=/public,/,graphics,usenet;fs:=$@{autodir@}/$@{rhost@}/root
+@end example
+
+The first string defines the root of the tree, and is applied as a
+prefix to the remaining members of the list which define the individual
+filesystems. The first string is @emph{not} used as a filesystem name.
+A parallel operation is used to determine the local mount points to
+ensure a consistent layout of a tree of mounts.
+
+Here, the @emph{three} filesystems, @samp{/public},
+@samp{/public/graphics} and @samp{/public/usenet}, would be mounted.@refill
+
+A local mount point, @code{$@{fs@}}, @emph{must} be specified. The
+default local mount point will not work correctly in the general case.
+A suggestion is to use @samp{fs:=$@{autodir@}/$@{rhost@}/root}.@refill
+
+@c ----------------------------------------------------------------
+@node Unix Filesystem, Caching Filesystem, Network Filesystem Group, Filesystem Types
+@comment node-name, next, previous, up
+@section Unix Filesystem (@samp{ufs}, @samp{xfs}, or @samp{efs})
+@cindex Unix filesystem
+@cindex UFS
+@cindex XFS
+@cindex EFS
+@cindex Mounting a UFS filesystem
+@cindex Mounting a local disk
+@cindex How to mount a UFS filesystems
+@cindex How to mount a local disk
+@cindex Disk filesystems
+@cindex ufs, filesystem type
+@cindex Filesystem type; ufs
+@cindex xfs, filesystem type
+@cindex Filesystem type; xfs
+@cindex efs, filesystem type
+@cindex Filesystem type; efs
+
+The @dfn{ufs} (@samp{type:=ufs}) filesystem type provides access to the system's standard
+disk filesystem---usually a derivative of the Berkeley Fast Filesystem.
+
+@noindent
+The following option must be specified:
+
+@table @code
+@cindex dev, mount option
+@cindex Mount option; dev
+@item dev
+the block special device to be mounted.
+@end table
+
+A UFS entry might be:
+
+@example
+jsp host==charm;type:=ufs;dev:=/dev/sd0d;sublink:=jsp
+@end example
+
+UFS is the default Unix disk-based file system, which Am-utils picks up
+during the autoconfiguration phase. Some systems have more than one
+type, such as IRIX, that comes with EFS (Extent File System) and XFS
+(Extended File System). In those cases, you may explicitly set the file
+system type, by using entries such:
+
+@example
+ez1 type:=efs;dev:=/dev/xd0a
+ez2 type:=xfs;dev:=/dev/sd3c
+@end example
+
+@c ----------------------------------------------------------------
+@node Caching Filesystem, CD-ROM Filesystem, Unix Filesystem, Filesystem Types
+@comment node-name, next, previous, up
+@section Caching Filesystem (@samp{cachefs})
+@cindex Caching Filesystem
+@cindex cachefs, filesystem type
+@cindex Filesystem type; cachefs
+
+The @dfn{cachefs} (@samp{type:=cachefs}) filesystem caches files from
+one location onto another, presumably providing faster access. It is
+particularly useful to cache from a larger and remote (slower) NFS
+partition to a smaller and local (faster) UFS directory.
+
+@noindent
+The following options must be specified:
+
+@table @code
+@cindex cachedir, mount option
+@cindex Mount option; cachedir
+@item cachedir
+the directory where the cache is stored.
+@item rfs
+the path name to the ``back file system'' to be cached from.
+@item fs
+the ``front file system'' mount point to the cached files, where @i{Amd}
+will set a symbolic link pointing to.
+@end table
+
+A CacheFS entry for, say, the @file{/import} @i{Amd} mount point, might
+be:
+
+@example
+copt type:=cachefs;cachedir:=/cache;rfs:=/import/opt;fs:=/n/import/copt
+@end example
+
+Access to the pathname @file{/import/copt} will follow a symbolic link
+to @file{/n/import/copt}. The latter is the mount point for a caching
+file system, that caches from @file{/import/opt} to @file{/cache}.
+
+@b{Caveats}:
+@enumerate
+@item This file system is currently only implemented for Solaris 2.x!
+@item Before being used for the first time, the cache directory @i{must} be
+initialized with @samp{cfsadmin -c @var{cachedir}}. See the manual page for
+@b{cfsadmin}(1M) for more information.
+@item The ``back file system'' mounted must be a complete file system, not
+a subdirectory thereof; otherwise you will get an error ``Invalid Argument''.
+@item If @i{Amd} aborts abnormally, the state of the cache may be
+inconsistent, requiring running the command @file{fsck -F cachefs
+@var{cachedir}}. Otherwise you will get the error ``No Space Left on Device''.
+@end enumerate
+
+@c ----------------------------------------------------------------
+@node CD-ROM Filesystem, Loopback Filesystem, Caching Filesystem, Filesystem Types
+@comment node-name, next, previous, up
+@section CD-ROM Filesystem (@samp{cdfs})
+@cindex CD-ROM Filesystem
+@cindex cdfs, filesystem type
+@cindex Filesystem type; cdfs
+
+The @dfn{cdfs} (@samp{type:=cdfs}) filesystem mounts a CD-ROM with an
+ISO9660 format filesystem on it.
+
+@noindent
+The following option must be specified:
+
+@table @code
+@cindex dev, mount option
+@cindex Mount option; dev
+@item dev
+the block special device to be mounted.
+@end table
+
+A cdfs entry might be:
+
+@example
+cdfs os==sunos4;type:=cdfs;dev:=/dev/sr0 \
+ os==sunos5;type:=cdfs;dev:=/dev/dsk/c0t6d0s2
+@end example
+
+@c ----------------------------------------------------------------
+@node Loopback Filesystem, Memory/RAM Filesystem, CD-ROM Filesystem, Filesystem Types
+@comment node-name, next, previous, up
+@section Loopback Filesystem (@samp{lofs})
+@cindex Loopback Filesystem
+@cindex lofs, filesystem type
+@cindex Filesystem type; lofs
+
+The @dfn{lofs} (@samp{type:=lofs}) filesystem is also called the
+loopback filesystem. It mounts a local directory on another, thus
+providing mount-time binding to another location (unlike symbolic
+links).
+
+The loopback filesystem is particularly useful within the context of a
+chroot-ed directory (via @b{chroot}(2)), to provide access to
+directories otherwise inaccessible.
+
+@noindent
+The following option must be specified:
+
+@table @code
+@cindex rfs, mount option
+@cindex Mount option; rfs
+@item rfs
+the pathname to be mounted on top of @code{$@{fs@}}.
+@end table
+
+Usually, the FTP server runs in a chroot-ed environment, for security
+reasons. In this example, lofs is used to provide a subdirectory within
+a user's home directory, also available for public ftp.
+
+@example
+lofs type:=lofs;rfs:=/home/ezk/myftpdir;fs:=/usr/ftp/pub/ezk
+@end example
+
+@c ----------------------------------------------------------------
+@node Memory/RAM Filesystem, Null Filesystem, Loopback Filesystem, Filesystem Types
+@comment node-name, next, previous, up
+@section Memory/RAM Filesystem (@samp{mfs})
+@cindex Memory/RAM Filesystem
+@cindex mfs, filesystem type
+@cindex Filesystem type; mfs
+
+The @dfn{mfs} (@samp{type:=mfs}) filesystem is available in 4.4BSD,
+Linux, and other systems. It creates a filesystem in a portion of the
+system's memory, thus providing very fast file (volatile) access.
+
+XXX: THIS FILESYSTEM IS NOT IMPLEMENTED YET!
+
+@c ----------------------------------------------------------------
+@node Null Filesystem, Floppy Filesystem, Memory/RAM Filesystem, Filesystem Types
+@comment node-name, next, previous, up
+@section Null Filesystem (@samp{nullfs})
+@cindex Null Filesystem
+@cindex nullfs, filesystem type
+@cindex Filesystem type; nullfs
+
+The @dfn{nullfs} (@samp{type:=nullfs}) filesystem is available from 4.4BSD,
+and is very similar to the loopback filesystem, @dfn{lofs}.
+
+XXX: THIS FILESYSTEM IS NOT IMPLEMENTED YET!
+
+@c ----------------------------------------------------------------
+@node Floppy Filesystem, Translucent Filesystem, Null Filesystem, Filesystem Types
+@comment node-name, next, previous, up
+@section Floppy Filesystem (@samp{pcfs})
+@cindex Floppy Filesystem
+@cindex pcfs, filesystem type
+@cindex Filesystem type; pcfs
+
+The @dfn{pcfs} (@samp{type:=pcfs}) filesystem mounts a floppy previously
+formatted for the MS-DOS format.
+
+@noindent
+The following option must be specified:
+
+@table @code
+@cindex dev, mount option
+@cindex Mount option; dev
+@item dev
+the block special device to be mounted.
+@end table
+
+A pcfs entry might be:
+
+@example
+pcfs os==sunos4;type:=pcfs;dev:=/dev/fd0 \
+ os==sunos5;type:=pcfs;dev:=/dev/diskette
+@end example
+
+@c ----------------------------------------------------------------
+@node Translucent Filesystem, Shared Memory+Swap Filesystem, Floppy Filesystem, Filesystem Types
+@comment node-name, next, previous, up
+@section Translucent Filesystem (@samp{tfs})
+@cindex Translucent Filesystem
+@cindex tfs, filesystem type
+@cindex Filesystem type; tfs
+
+The @dfn{tfs} (@samp{type:=tfs}) filesystem is an older version of the
+4.4BSD @dfn{unionfs}.
+
+XXX: THIS FILESYSTEM IS NOT IMPLEMENTED YET!
+
+@c ----------------------------------------------------------------
+@node Shared Memory+Swap Filesystem, User ID Mapping Filesystem, Translucent Filesystem, Filesystem Types
+@comment node-name, next, previous, up
+@section Shared Memory+Swap Filesystem (@samp{tmpfs})
+@cindex Shared Memory and Swap Filesystem
+@cindex tmpfs, filesystem type
+@cindex Filesystem type; tmpfs
+
+The @dfn{tmpfs} (@samp{type:=tmpfs}) filesystem shares memory between a
+the swap device and the rest of the system. It is generally used to
+provide a fast access @file{/tmp} directory, one that uses memory that
+is otherwise unused. This filesystem is available in SunOS 4.x and 5.x.
+
+XXX: THIS FILESYSTEM IS NOT IMPLEMENTED YET!
+
+@c ----------------------------------------------------------------
+@node User ID Mapping Filesystem, Program Filesystem, Shared Memory+Swap Filesystem, Filesystem Types
+@comment node-name, next, previous, up
+@section User ID Mapping Filesystem (@samp{umapfs})
+@cindex User ID Mapping Filesystem
+@cindex umapfs, filesystem type
+@cindex Filesystem type; umapfs
+
+The @dfn{umapfs} (@samp{type:=umapfs}) filesystem maps User IDs of file
+ownership, and is available from 4.4BSD.
+
+XXX: THIS FILESYSTEM IS NOT IMPLEMENTED YET!
+
+@c ----------------------------------------------------------------
+@node Program Filesystem, Symbolic Link Filesystem, User ID Mapping Filesystem, Filesystem Types
+@comment node-name, next, previous, up
+@section Program Filesystem (@samp{program})
+@cindex Program filesystem
+@cindex Mount a filesystem under program control
+@cindex program, filesystem type
+@cindex Filesystem type; program
+
+The @dfn{program} (@samp{type:=program}) filesystem type allows a program to be run whenever a
+mount or unmount is required. This allows easy addition of support for
+other filesystem types, such as MIT's Remote Virtual Disk (RVD)
+which has a programmatic interface via the commands
+@samp{rvdmount} and @samp{rvdunmount}.
+
+@noindent
+The following options must be specified:
+
+@table @code
+@cindex mount, mount option
+@cindex Mount option; mount
+@item mount
+the program which will perform the mount.
+
+@cindex unmount, mount option
+@cindex Mount option; unmount
+@item unmount
+the program which will perform the unmount.
+@end table
+
+The exit code from these two programs is interpreted as a Unix error
+code. As usual, exit code zero indicates success. To execute the
+program @i{Amd} splits the string on whitespace to create an array of
+substrings. Single quotes @samp{'} can be used to quote whitespace
+if that is required in an argument. There is no way to escape or change
+the quote character.
+
+To run the program @samp{rvdmount} with a host name and filesystem as
+arguments would be specified by @samp{mount:="/etc/rvdmount rvdmount
+fserver $@{path@}"}.
+
+The first element in the array is taken as the pathname of the program
+to execute. The other members of the array form the argument vector to
+be passed to the program, @dfn{including argument zero}. This means
+that the split string must have at least two elements. The program is
+directly executed by @i{Amd}, not via a shell. This means that scripts
+must begin with a @code{#!} interpreter specification.
+
+If a filesystem type is to be heavily used, it may be worthwhile adding
+a new filesystem type into @i{Amd}, but for most uses the program
+filesystem should suffice.
+
+When the program is run, standard input and standard error are inherited
+from the current values used by @i{Amd}. Standard output is a
+duplicate of standard error. The value specified with the @code{-l}
+command line option has no effect on standard error.
+
+@c ----------------------------------------------------------------
+@node Symbolic Link Filesystem, Symbolic Link Filesystem II, Program Filesystem, Filesystem Types
+@comment node-name, next, previous, up
+@section Symbolic Link Filesystem (@samp{link})
+@cindex Symbolic link filesystem
+@cindex Referencing part of the local name space
+@cindex Mounting part of the local name space
+@cindex How to reference part of the local name space
+@cindex link, filesystem type
+@cindex symlink, link filesystem type
+@cindex Filesystem type; link
+
+Each filesystem type creates a symbolic link to point from the volume
+name to the physical mount point. The @samp{link} filesystem does the
+same without any other side effects. This allows any part of the
+machines name space to be accessed via @i{Amd}.
+
+One common use for the symlink filesystem is @file{/homes} which can be
+made to contain an entry for each user which points to their
+(auto-mounted) home directory. Although this may seem rather expensive,
+it provides a great deal of administrative flexibility.
+
+@noindent
+The following option must be defined:
+
+@table @code
+@item fs
+The value of @var{fs} option specifies the destination of the link, as
+modified by the @var{sublink} option. If @var{sublink} is non-null, it
+is appended to @code{$@{fs@}}@code{/} and the resulting string is used
+as the target.
+@end table
+
+The @samp{link} filesystem can be thought of as identical to the
+@samp{ufs} filesystem but without actually mounting anything.
+
+An example entry might be:
+
+@example
+jsp host==charm;type:=link;fs:=/home/charm;sublink:=jsp
+@end example
+which would return a symbolic link pointing to @file{/home/charm/jsp}.
+
+@c ----------------------------------------------------------------
+@node Symbolic Link Filesystem II, NFS-Link Filesystem, Symbolic Link Filesystem, Filesystem Types
+@comment node-name, next, previous, up
+@section Symbolic Link Filesystem II (@samp{linkx})
+@cindex Symbolic link filesystem II
+@cindex Referencing an existing part of the local name space
+@cindex Mounting an existing part of the local name space
+@cindex How to reference an existing part of the local name space
+@cindex linkx, filesystem type
+@cindex symlink, linkx filesystem type
+@cindex Filesystem type; linkx
+
+The @dfn{linkx} (@samp{type:=linkx}) filesystem type is identical to @samp{link} with the
+exception that the target of the link must exist. Existence is checked
+with the @b{lstat}(2) system call.
+
+The @samp{linkx} filesystem type is particularly useful for wildcard map
+entries. In this case, a list of possible targets can be given and
+@i{Amd} will choose the first one which exists on the local machine.
+
+@c ----------------------------------------------------------------
+@node NFS-Link Filesystem, Automount Filesystem, Symbolic Link Filesystem II, Filesystem Types
+@comment node-name, next, previous, up
+@section NFS-Link Filesystem (@samp{nfsl})
+@cindex NFS-Link filesystem II
+@cindex Referencing an existing part of the name space if target exists
+@cindex Mounting a remote part of the name space if target is missing
+@cindex Symlink if target exists, NFS otherwise
+@cindex nfsl, filesystem type
+@cindex symlink, nfsl filesystem type
+@cindex Filesystem type; nfsl
+
+The @dfn{nfsl} (@samp{type:=nfsl}) filesystem type is a combination of two others:
+@samp{link} and @samp{nfs}. If the local host name is equal to the
+value of @code{$@{rhost@}}, or if the target pathname listed in
+@code{$@{fs@}} exists, @samp{nfsl} will behave exactly as
+@samp{type:=link}, and refer to the target as a symbolic link. If the
+local host name is not equal to the value of @code{$@{rhost@}}, or if
+the target of the link does not exist, @i{Amd} will treat it as
+@samp{type:=nfs}, and will mount a remote pathname for it.
+
+The @samp{nfsl} filesystem type is particularly useful as a shorthand
+for the more cumbersome and yet one of the most popular @i{Amd}
+entries. For example, you can simplify all map entries that look like:
+
+@example
+zing -fs:=/n/shekel/u/zing \
+ host!=shekel;type:=nfs;rhost:=shekel;rfs:=$@{fs@} \
+ host==shekel;type:=link
+@end example
+
+or
+
+@example
+zing -fs:=/n/shekel/u/zing \
+ exists($@{fs@});type:=link \
+ !exists($@{fs@});type:=nfs;rhost:=shekel;rfs:=$@{fs@}
+@end example
+
+into a shorter form
+
+@example
+zing type:=nfsl;fs:=/n/shekel/u/zing;rhost:=shekel;rfs:=$@{fs@}
+@end example
+
+Not just does it make the maps smaller and simpler, but it avoids
+possible mistakes that often happen when forgetting to set up the two
+entries (one for @samp{type:=nfs} and the other for @samp{type:=link})
+necessary to perform transparent mounts of existing or remote mounts.
+
+@c ----------------------------------------------------------------
+@node Automount Filesystem, Direct Automount Filesystem, NFS-Link Filesystem, Filesystem Types
+@comment node-name, next, previous, up
+@section Automount Filesystem (@samp{auto})
+@cindex Automount filesystem
+@cindex Map cache types
+@cindex Setting map cache parameters
+@cindex How to set map cache parameters
+@cindex How to start an indirect automount point
+@cindex auto, filesystem type
+@cindex Filesystem type; auto
+@cindex SIGHUP signal
+@cindex Map cache synchronizing
+@cindex Synchronizing the map cache
+@cindex Map cache options
+@cindex Regular expressions in maps
+
+The @dfn{auto} (@samp{type:=auto}) filesystem type creates a new automount point below an
+existing automount point. Top-level automount points appear as system
+mount points. An automount mount point can also appear as a
+sub-directory of an existing automount point. This allows some
+additional structure to be added, for example to mimic the mount tree of
+another machine.
+
+The following options may be specified:
+
+@table @code
+@cindex cache, mount option
+@cindex Mount option; cache
+@item cache
+specifies whether the data in this mount-map should be
+cached. The default value is @samp{none}, in which case
+no caching is done in order to conserve memory.
+However, better performance and reliability can be obtained by caching
+some or all of a mount-map.
+
+If the cache option specifies @samp{all},
+the entire map is enumerated when the mount point is created.
+
+If the cache option specifies @samp{inc}, caching is done incrementally
+as and when data is required.
+Some map types do not support cache mode @samp{all}, in which case @samp{inc}
+is used whenever @samp{all} is requested.
+
+Caching can be entirely disabled by using cache mode @samp{none}.
+
+If the cache option specifies @samp{regexp} then the entire map will be
+enumerated and each key will be treated as an egrep-style regular
+expression. The order in which a cached map is searched does not
+correspond to the ordering in the source map so the regular expressions
+should be mutually exclusive to avoid confusion.
+
+Each mount map type has a default cache type, usually @samp{inc}, which
+can be selected by specifying @samp{mapdefault}.
+
+The cache mode for a mount map can only be selected on the command line.
+Starting @i{Amd} with the command:
+
+@example
+amd /homes hesiod.homes -cache:=inc
+@end example
+
+will cause @samp{/homes} to be automounted using the @dfn{Hesiod} name
+server with local incremental caching of all successfully resolved names.
+
+All cached data is forgotten whenever @i{Amd} receives a @samp{SIGHUP}
+signal and, if cache @samp{all} mode was selected, the cache will be
+reloaded. This can be used to inform @i{Amd} that a map has been
+updated. In addition, whenever a cache lookup fails and @i{Amd} needs
+to examine a map, the map's modify time is examined. If the cache is
+out of date with respect to the map then it is flushed as if a
+@samp{SIGHUP} had been received.
+
+An additional option (@samp{sync}) may be specified to force @i{Amd} to
+check the map's modify time whenever a cached entry is being used. For
+example, an incremental, synchronized cache would be created by the
+following command:
+
+@example
+amd /homes hesiod.homes -cache:=inc,sync
+@end example
+
+@item fs
+specifies the name of the mount map to use for the new mount point.
+
+Arguably this should have been specified with the @code{$@{rfs@}} option but
+we are now stuck with it due to historical accident.
+
+@c %If the string @samp{.} is used then the same map is used;
+@c %in addition the lookup prefix is set to the name of the mount point followed
+@c %by a slash @samp{/}.
+@c %This is the same as specifying @samp{fs:=\$@{map@};pref:=\$@{key@}/}.
+@c
+
+@item pref
+alters the name that is looked up in the mount map. If
+@code{$@{pref@}}, the @dfn{prefix}, is non-null then it is prepended to
+the name requested by the kernel @dfn{before} the map is searched.
+@end table
+
+The server @samp{dylan.doc.ic.ac.uk} has two user disks:
+@samp{/dev/dsk/2s0} and @samp{/dev/dsk/5s0}. These are accessed as
+@samp{/home/dylan/dk2} and @samp{/home/dylan/dk5} respectively. Since
+@samp{/home} is already an automount point, this naming is achieved with
+the following map entries:@refill
+
+@example
+dylan type:=auto;fs:=$@{map@};pref:=$@{key@}/
+dylan/dk2 type:=ufs;dev:=/dev/dsk/2s0
+dylan/dk5 type:=ufs;dev:=/dev/dsk/5s0
+@end example
+
+@c ----------------------------------------------------------------
+@node Direct Automount Filesystem, Union Filesystem, Automount Filesystem, Filesystem Types
+@comment node-name, next, previous, up
+@section Direct Automount Filesystem (@samp{direct})
+@cindex Direct automount filesystem
+@cindex How to start a direct automount point
+@cindex direct, filesystem type
+@cindex Filesystem type; direct
+
+The @dfn{direct} (@samp{type:=direct}) filesystem is almost identical to the automount
+filesystem. Instead of appearing to be a directory of mount points, it
+appears as a symbolic link to a mounted filesystem. The mount is done
+at the time the link is accessed. @xref{Automount Filesystem} for a
+list of required options.
+
+Direct automount points are created by specifying the @samp{direct}
+filesystem type on the command line:
+
+@example
+amd ... /usr/man auto.direct -type:=direct
+@end example
+
+where @samp{auto.direct} would contain an entry such as:
+
+@example
+usr/man -type:=nfs;rfs:=/usr/man \
+ rhost:=man-server1 rhost:=man-server2
+@end example
+
+In this example, @samp{man-server1} and @samp{man-server2} are file
+servers which export copies of the manual pages. Note that the key
+which is looked up is the name of the automount point without the
+leading @samp{/}.
+
+@c ----------------------------------------------------------------
+@node Union Filesystem, Error Filesystem, Direct Automount Filesystem, Filesystem Types
+@comment node-name, next, previous, up
+@section Union Filesystem (@samp{union})
+@cindex Union filesystem
+@cindex union, filesystem type
+@cindex Filesystem type; union
+
+The @dfn{union} (@samp{type:=union}) filesystem type allows the contents of several
+directories to be merged and made visible in a single directory. This
+can be used to overcome one of the major limitations of the Unix mount
+mechanism which only allows complete directories to be mounted.
+
+For example, supposing @file{/tmp} and @file{/var/tmp} were to be merged
+into a new directory called @file{/mtmp}, with files in @file{/var/tmp}
+taking precedence. The following command could be used to achieve this
+effect:
+
+@example
+amd ... /mtmp union:/tmp:/var/tmp -type:=union
+@end example
+
+Currently, the unioned directories must @emph{not} be automounted. That
+would cause a deadlock. This seriously limits the current usefulness of
+this filesystem type and the problem will be addressed in a future
+release of @i{Amd}.
+
+Files created in the union directory are actually created in the last
+named directory. This is done by creating a wildcard entry which points
+to the correct directory. The wildcard entry is visible if the union
+directory is listed, so allowing you to see which directory has
+priority.
+
+The files visible in the union directory are computed at the time
+@i{Amd} is started, and are not kept up-to-date with respect to the
+underlying directories. Similarly, if a link is removed, for example
+with the @samp{rm} command, it will be lost forever.
+
+@c ----------------------------------------------------------------
+@node Error Filesystem, Top-level Filesystem, Union Filesystem, Filesystem Types
+@comment node-name, next, previous, up
+@section Error Filesystem (@samp{error})
+@cindex Error filesystem
+@cindex error, filesystem type
+@cindex Filesystem type; error
+
+The @dfn{error} (@samp{type:=error}) filesystem type is used internally as a catch-all in the
+case where none of the other filesystems was selected, or some other
+error occurred. Lookups and mounts always fail with ``No such file or
+directory''. All other operations trivially succeed.
+
+The error filesystem is not directly accessible.
+
+@c ----------------------------------------------------------------
+@node Top-level Filesystem, Autofs Filesystem, Error Filesystem, Filesystem Types
+@comment node-name, next, previous, up
+@section Top-level Filesystem (@samp{toplvl})
+@cindex Top level filesystem
+@cindex toplvl, filesystem type
+@cindex Filesystem type; toplvl
+
+The @dfn{toplvl} (@samp{type:=toplvl}) filesystems is derived from the @samp{auto} filesystem
+and is used to mount the top-level automount nodes. Requests of this
+type are automatically generated from the command line arguments and can
+also be passed in by using the @code{-M} option of the @dfn{Amq} command.
+That option is insecure, and is unavailable unless am-utils was
+configured with @samp{--with-amq-mount}.
+
+@c ----------------------------------------------------------------
+@node Root Filesystem, Inheritance Filesystem, Autofs Filesystem, Filesystem Types
+@comment node-name, next, previous, up
+@section Root Filesystem (@samp{root})
+@cindex Root filesystem
+@cindex root, filesystem type
+@cindex Filesystem type; root
+
+The @dfn{root} (@samp{type:=root}) filesystem type acts as an internal
+placeholder onto which @i{Amd} can pin @samp{toplvl} mounts. Only one
+node of this type need ever exist and one is created automatically
+during startup. The effect of having more than one root node is
+undefined.
+
+The root filesystem is not directly accessible.
+
+@c ----------------------------------------------------------------
+@node Autofs Filesystem, Root Filesystem, Top-level Filesystem, Filesystem Types
+@comment node-name, next, previous, up
+@section Autofs Filesystem (@samp{autofs})
+@cindex Autofs filesystem
+@cindex autofs, filesystem type
+@cindex Filesystem type; autofs
+
+The @dfn{autofs} (@samp{type:=autofs}) filesystem uses Sun's kernel-based automounter
+supporting filesystem for @i{Amd}'s mount points. Hence it is another
+type of top level filesystem.
+
+The autofs filesystem is not directly accessible from @i{Amd} maps, but
+only from the @file{amd.conf} file (@pxref{mount_type Parameter}).
+
+Note that Autofs support is still very early. See the distribution file
+@file{README.autofs} for detail of what works and what does not.
+
+@c ----------------------------------------------------------------
+@node Inheritance Filesystem, , Root Filesystem, Filesystem Types
+@comment node-name, next, previous, up
+@section Inheritance Filesystem (@samp{inherit})
+@cindex Inheritance filesystem
+@cindex Nodes generated on a restart
+@cindex inherit, filesystem type
+@cindex Filesystem type; inherit
+
+The @dfn{inheritance} (@samp{type:=inherit}) filesystem is not directly
+accessible. Instead, internal mount nodes of this type are
+automatically generated when @i{Amd} is started with the @code{-r} option.
+At this time the system mount table is scanned to locate any filesystems
+which are already mounted. If any reference to these filesystems is
+made through @i{Amd} then instead of attempting to mount it, @i{Amd}
+simulates the mount and @dfn{inherits} the filesystem. This allows a
+new version of @i{Amd} to be installed on a live system simply by
+killing the old daemon with @samp{SIGTERM} and starting the new one.@refill
+
+This filesystem type is not generally visible externally, but it is
+possible that the output from @samp{amq -m} may list @samp{inherit} as
+the filesystem type. This happens when an inherit operation cannot
+be completed for some reason, usually because a fileserver is down.
+
+@c ################################################################
+@node Amd Configuration File, Run-time Administration, Filesystem Types, Top
+@comment node-name, next, previous, up
+@chapter Amd Configuration File
+@cindex Amd Configuration File
+@cindex amd.conf
+
+The @samp{amd.conf} file is the configuration file for @i{Amd}, as part
+of the am-utils suite. This file contains runtime configuration
+information for the @i{Amd} automounter program.
+
+@menu
+* File Format::
+* The Global Section::
+* Regular Map Sections::
+* Common Parameters::
+* Global Parameters::
+* Regular Map Parameters::
+* amd.conf Examples::
+@end menu
+
+@c ================================================================
+@node File Format, The Global Section, Amd Configuration File, Amd Configuration File
+@comment node-name, next, previous, up
+@section File Format
+@cindex amd.conf file format
+
+The @samp{amd.conf} file consists of sections and parameters. A section
+begins with the name of the section in square brackets @samp{[]} and
+continues until the next section begins or the end of the file is reached.
+Sections contain parameters of the form @samp{name = value}.
+
+The file is line-based --- that is, each newline-terminated line
+represents either a comment, a section name or a parameter. No
+line-continuation syntax is available.
+
+Section names, parameter names and their values are case sensitive.
+
+Only the first equals sign in a parameter is significant. Whitespace
+before or after the first equals sign is discarded. Leading, trailing
+and internal whitespace in section and parameter names is irrelevant.
+Leading and trailing whitespace in a parameter value is discarded.
+Internal whitespace within a parameter value is not allowed, unless the
+whole parameter value is quoted with double quotes as in @samp{name =
+"some value"}.
+
+Any line beginning with a pound sign @samp{#} is ignored, as are lines
+containing only whitespace.
+
+The values following the equals sign in parameters are all either a
+string (no quotes needed if string does not include spaces) or a
+boolean, which may be given as @samp{yes}/@samp{no}. Case is significant in all
+values. Some items such as cache timeouts are numeric.
+
+@c ================================================================
+@node The Global Section, Regular Map Sections, File Format, Amd Configuration File
+@comment node-name, next, previous, up
+@section The Global Section
+@cindex amd.conf global section
+
+The global section must be specified as @samp{[global]}. Parameters in
+this section either apply to @i{Amd} as a whole, or to all other regular map
+sections which follow. There should be only one global section defined
+in one configuration file.
+
+It is highly recommended that this section be specified first in the
+configuration file. If it is not, then regular map sections which
+precede it will not use global values defined later.
+
+@c ================================================================
+@node Regular Map Sections, Common Parameters, The Global Section, Amd Configuration File
+@comment node-name, next, previous, up
+@section Regular Map Sections
+@cindex amd.conf regular map sections
+
+Parameters in regular (non-global) sections apply to a single map entry.
+For example, if the map section @samp{[/homes]} is defined, then all
+parameters following it will be applied to the @file{/homes}
+@i{Amd}-managed mount point.
+
+@c ================================================================
+@node Common Parameters, Global Parameters, Regular Map Sections, Amd Configuration File
+@comment node-name, next, previous, up
+@section Common Parameters
+@cindex amd.conf common parameters
+
+These parameters can be specified either in the global or a map-specific
+section. Entries specified in a map-specific section override the default
+value or one defined in the global section. If such a common parameter is
+specified only in the global section, it is applicable to all regular map
+sections that follow.
+
+@menu
+* browsable_dirs Parameter::
+* map_options Parameter::
+* map_type Parameter::
+* mount_type Parameter::
+* search_path Parameter::
+@end menu
+
+@c ----------------------------------------------------------------
+@node browsable_dirs Parameter, map_options Parameter, Common Parameters, Common Parameters
+@comment node-name, next, previous, up
+@subsection @t{browsable_dirs} Parameter
+@cindex browsable_dirs Parameter
+
+(type=string, default=@samp{no}). If @samp{yes}, then @i{Amd}'s top-level
+mount points will be browsable to @b{readdir}(3) calls. This means you
+could run for example @b{ls}(1) and see what keys are available to mount
+in that directory. Not all entries are made visible to @b{readdir}(3):
+the @samp{/defaults} entry, wildcard entries, and those with a @file{/}
+in them are not included. If you specify @samp{full} to this option,
+all but the @samp{/defaults} entry will be visible. Note that if you run
+a command which will attempt to @b{stat}(2) the entries, such as often
+done by @samp{ls -l} or @samp{ls -F}, @i{Amd} will attempt to mount
+@i{every} entry in that map. This is often called a ``mount storm''.
+
+@c ----------------------------------------------------------------
+@node map_options Parameter, map_type Parameter, browsable_dirs Parameter, Common Parameters
+@comment node-name, next, previous, up
+@subsection @t{map_options} Parameter
+@cindex map_options Parameter
+
+(type=string, default no options). This option is the same as
+specifying map options on the command line to @i{Amd}, such as
+@samp{cache:=all}.
+
+@c ----------------------------------------------------------------
+@node map_type Parameter, mount_type Parameter, map_options Parameter, Common Parameters
+@comment node-name, next, previous, up
+@subsection @t{map_type} Parameter
+@cindex map_type Parameter
+
+(type=string, default search all map types). If specified, @i{Amd} will
+initialize the map only for the type given. This is useful to avoid the
+default map search type used by @i{Amd} which takes longer and can have
+undesired side-effects such as initializing NIS even if not used.
+Possible values are
+
+@table @samp
+@item file
+plain files
+@item hesiod
+Hesiod name service from MIT
+@item ldap
+Lightweight Directory Access Protocol
+@item ndbm
+(New) dbm style hash files
+@item nis
+Network Information Services (version 2)
+@item nisplus
+Network Information Services Plus (version 3)
+@item passwd
+local password files
+@item union
+union maps
+@end table
+
+@c ----------------------------------------------------------------
+@node mount_type Parameter, search_path Parameter, map_type Parameter, Common Parameters
+@comment node-name, next, previous, up
+@subsection @t{mount_type} Parameter
+@cindex mount_type Parameter
+
+(type=string, default=@samp{nfs}). All @i{Amd} mount types default to NFS.
+That is, @i{Amd} is an NFS server on the map mount points, for the local
+host it is running on. If @samp{autofs} is specified, @i{Amd} will be
+an autofs server for those mount points.
+
+@c ----------------------------------------------------------------
+@node search_path Parameter, , mount_type Parameter, Common Parameters
+@comment node-name, next, previous, up
+@subsection @t{search_path} Parameter
+@cindex search_path Parameter
+
+(type=string, default no search path). This provides a
+(colon-delimited) search path for file maps. Using a search path,
+sites can allow for local map customizations and overrides, and can
+distributed maps in several locations as needed.
+
+@c ================================================================
+@node Global Parameters, Regular Map Parameters, Common Parameters, Amd Configuration File
+@comment node-name, next, previous, up
+@section Global Parameters
+@cindex amd.conf global parameters
+
+The following parameters are applicable to the @samp{[global]} section only.
+
+@menu
+* arch Parameter::
+* auto_dir Parameter::
+* cache_duration Parameter::
+* cluster Parameter::
+* debug_options Parameter::
+* dismount_interval Parameter::
+* fully_qualified_hosts Parameter::
+* hesiod_base Parameter::
+* karch Parameter::
+* ldap_base Parameter::
+* ldap_cache_maxmem Parameter::
+* ldap_cache_seconds Parameter::
+* ldap_hostports Parameter::
+* local_domain Parameter::
+* log_file Parameter::
+* log_options Parameter::
+* nfs_retransmit_counter Parameter::
+* nfs_retry_interval Parameter::
+* nis_domain Parameter::
+* normalize_hostnames Parameter::
+* os Parameter::
+* osver Parameter::
+* pid_file Parameter::
+* plock Parameter::
+* portmap_program Parameter::
+* print_pid Parameter::
+* print_version Parameter::
+* restart_mounts Parameter::
+* selectors_on_default Parameter::
+* show_statfs_entries Parameter::
+* unmount_on_exit Parameter::
+@end menu
+
+@c ----------------------------------------------------------------
+@node arch Parameter, auto_dir Parameter, Global Parameters, Global Parameters
+@comment node-name, next, previous, up
+@subsection @t{arch} Parameter
+@cindex arch Parameter
+
+(type=string, default to compiled in value). Allows you to override the
+value of the @i{arch} @i{Amd} variable.
+
+@c ----------------------------------------------------------------
+@node auto_dir Parameter, cache_duration Parameter, arch Parameter, Global Parameters
+@comment node-name, next, previous, up
+@subsection @t{auto_dir} Parameter
+@cindex auto_dir Parameter
+
+(type=string, default=@samp{/a}). Same as the @code{-a} option to @i{Amd}.
+This sets the private directory where @i{Amd} will create
+sub-directories for its real mount points.
+
+@c ----------------------------------------------------------------
+@node cache_duration Parameter, cluster Parameter, auto_dir Parameter, Global Parameters
+@comment node-name, next, previous, up
+@subsection @t{cache_duration} Parameter
+@cindex cache_duration Parameter
+
+(type=numeric, default=300). Same as the @code{-c} option to
+@i{Amd}. Sets the duration in seconds that looked up map entries remain
+in the cache.
+
+@c ----------------------------------------------------------------
+@node cluster Parameter, debug_options Parameter, cache_duration Parameter, Global Parameters
+@comment node-name, next, previous, up
+@subsection @t{cluster} Parameter
+@cindex cluster Parameter
+
+(type=string, default no cluster). Same as the @code{-C} option to
+@i{Amd}. Specifies the alternate HP-UX cluster to use.
+
+@c ----------------------------------------------------------------
+@node debug_options Parameter, dismount_interval Parameter, cluster Parameter, Global Parameters
+@comment node-name, next, previous, up
+@subsection @t{debug_options} Parameter
+@cindex debug_options Parameter
+
+(type=string, default no debug options). Same as the @code{-D}
+option to @i{Amd}. Specify any debugging options for @i{Amd}. Works
+only if am-utils was configured for debugging using the
+@code{--enable-debug} option. The @samp{mem} option alone can be turned
+on via @code{--enable-debug=mem}. Otherwise debugging options are
+ignored. Options are comma delimited, and can be preceded by the string
+@samp{no} to negate their meaning. You can get the list of supported
+debugging options by running @code{amd -v}. Possible values are:
+
+@table @samp
+@item all
+all options
+@item amq
+register for amq
+@item daemon
+enter daemon mode
+@item fork
+fork server
+@item full
+program trace
+@item mem
+trace memory allocations
+@item mtab
+use local @file{./mtab} file
+@item str
+debug string munging
+@item test
+full debug but no daemon
+@item trace
+protocol trace
+@end table
+
+@c ----------------------------------------------------------------
+@node dismount_interval Parameter, fully_qualified_hosts Parameter, debug_options Parameter, Global Parameters
+@comment node-name, next, previous, up
+@subsection @t{dismount_interval} Parameter
+@cindex dismount_interval Parameter
+
+(type=numeric, default=120). Same as the @code{-w} option to
+@i{Amd}. Specify in seconds, the time between attempts to dismount file
+systems that have exceeded their cached times.
+
+@c ----------------------------------------------------------------
+@node fully_qualified_hosts Parameter, hesiod_base Parameter, dismount_interval Parameter, Global Parameters
+@comment node-name, next, previous, up
+@subsection @t{fully_qualified_hosts} Parameter
+@cindex fully_qualified_hosts Parameter
+
+(type=string, default=@samp{no}). If @samp{yes}, @i{Amd} will perform RPC
+authentication using fully-qualified host names. This is necessary for
+some systems, and especially when performing cross-domain mounting. For
+this function to work, the @i{Amd} variable @samp{$@{hostd@}} is used,
+requiring that @samp{$@{domain@}} not be null.
+
+@c ----------------------------------------------------------------
+@node hesiod_base Parameter, karch Parameter, fully_qualified_hosts Parameter, Global Parameters
+@comment node-name, next, previous, up
+@subsection @t{hesiod_base} Parameter
+@cindex hesiod_base Parameter
+
+(type=string, default=@samp{automount}). Specify the base name for
+hesiod maps.
+
+@c ----------------------------------------------------------------
+@node karch Parameter, ldap_base Parameter, hesiod_base Parameter, Global Parameters
+@comment node-name, next, previous, up
+@subsection @t{karch} Parameter
+@cindex karch Parameter
+
+(type=string, default to karch of the system). Same as the @code{-k}
+option to @i{Amd}. Allows you to override the kernel-architecture of
+your system. Useful for example on Sun (Sparc) machines, where you can
+build one @i{Amd} binary, and run it on multiple machines, yet you want
+each one to get the correct @i{karch} variable set (for example, sun4c,
+sun4m, sun4u, etc.) Note that if not specified, @i{Amd} will use
+@b{uname}(2) to figure out the kernel architecture of the machine.
+
+@c ----------------------------------------------------------------
+@node ldap_base Parameter, ldap_cache_maxmem Parameter, karch Parameter, Global Parameters
+@comment node-name, next, previous, up
+@subsection @t{ldap_base} Parameter
+@cindex ldap_base Parameter
+
+(type=string, default not set). Specify the base name for
+LDAP.
+
+@c ----------------------------------------------------------------
+@node ldap_cache_maxmem Parameter, ldap_cache_seconds Parameter, ldap_base Parameter, Global Parameters
+@comment node-name, next, previous, up
+@subsection @t{ldap_cache_maxmem} Parameter
+@cindex ldap_cache_maxmem Parameter
+
+(type=numeric, default=131072). Specify the maximum memory @i{Amd}
+should use to cache LDAP entries.
+
+@c ----------------------------------------------------------------
+@node ldap_cache_seconds Parameter, ldap_hostports Parameter, ldap_cache_maxmem Parameter, Global Parameters
+@comment node-name, next, previous, up
+@subsection @t{ldap_cache_seconds} Parameter
+@cindex ldap_cache_seconds Parameter
+
+(type=numeric, default=0). Specify the number of seconds to keep
+entries in the cache.
+
+@c ----------------------------------------------------------------
+@node ldap_hostports Parameter, local_domain Parameter, ldap_cache_seconds Parameter, Global Parameters
+@comment node-name, next, previous, up
+@subsection @t{ldap_hostports} Parameter
+@cindex ldap_hostports Parameter
+
+(type=string, default not set). Specify
+LDAP-specific values such as country and organization.
+
+@c ----------------------------------------------------------------
+@node local_domain Parameter, log_file Parameter, ldap_hostports Parameter, Global Parameters
+@comment node-name, next, previous, up
+@subsection @t{local_domain} Parameter
+@cindex local_domain Parameter
+
+(type=string, default no sub-domain). Same as the @code{-d} option
+to @i{Amd}. Specify the local domain name. If this option is not given
+the domain name is determined from the hostname, by removing the first
+component of the fully-qualified host name.
+
+@c ----------------------------------------------------------------
+@node log_file Parameter, log_options Parameter, local_domain Parameter, Global Parameters
+@comment node-name, next, previous, up
+@subsection @t{log_file} Parameter
+@cindex log_file Parameter
+
+(type=string, default=@samp{stderr}). Same as the @code{-l} option to
+@i{Amd}. Specify a file name to log @i{Amd} events to.
+If the string @samp{/dev/stderr} is specified,
+@i{Amd} will send its events to the standard error file descriptor.
+
+If the string @samp{syslog} is given, @i{Amd} will record its events
+with the system logger @b{syslogd}(8). If your system supports syslog
+facilities, then the default facility used is @samp{LOG_DAEMON}.
+
+When using syslog, if you wish to change the facility, append its name
+to the option name, delimited by a single colon. For example, if it is
+the string @samp{syslog:local7} then @i{Amd} will log messages via
+@b{syslog}(3) using the @samp{LOG_LOCAL7} facility. If the facility
+name specified is not recognized, @i{Amd} will default to @samp{LOG_DAEMON}.
+Note: while you can use any syslog facility available on your system, it
+is generally a bad idea to use those reserved for other services such as
+@samp{kern}, @samp{lpr}, @samp{cron}, etc.
+
+@c ----------------------------------------------------------------
+@node log_options Parameter, nfs_retransmit_counter Parameter, log_file Parameter, Global Parameters
+@comment node-name, next, previous, up
+@subsection @t{log_options} Parameter
+@cindex log_options Parameter
+
+(type=string, default no logging options). Same as the @code{-x}
+option to @i{Amd}. Specify any logging options for @i{Amd}. Options
+are comma delimited, and can be preceded by the string @samp{no} to
+negate their meaning. The @samp{debug} logging option is only available
+if am-utils was configured with @code{--enable-debug}. You can get the
+list of supported debugging options by running @code{amd -v}. Possible
+values are:
+
+@table @samp
+@item all
+all messages
+@item debug
+debug messages
+@item error
+non-fatal system errors
+@item fatal
+fatal errors
+@item info
+information
+@item map
+map errors
+@item stats
+additional statistical information
+@item user
+non-fatal user errors
+@item warn
+warnings
+@item warning
+warnings
+@end table
+
+@c ----------------------------------------------------------------
+@node nfs_retransmit_counter Parameter, nfs_retry_interval Parameter, log_options Parameter, Global Parameters
+@comment node-name, next, previous, up
+@subsection @t{nfs_retransmit_counter} Parameter
+@cindex nfs_retransmit_counter Parameter
+
+(type=numeric, default=110). Same as the @i{counter} part of the
+@code{-t} @i{interval.counter} option to @i{Amd}. Specifies the
+retransmit counter's value in @emph{tenths} of seconds.
+
+@c ----------------------------------------------------------------
+@node nfs_retry_interval Parameter, nis_domain Parameter, nfs_retransmit_counter Parameter, Global Parameters
+@comment node-name, next, previous, up
+@subsection @t{nfs_retry_interval} Parameter
+@cindex nfs_retry_interval Parameter
+
+(type=numeric, default=8). Same as the @i{interval} part of the
+@code{-t} @i{interval.counter} option to @i{Amd}. Specifies the
+interval in @emph{tenths} of seconds, between NFS/RPC/UDP retries.
+
+@c ----------------------------------------------------------------
+@node nis_domain Parameter, normalize_hostnames Parameter, nfs_retry_interval Parameter, Global Parameters
+@comment node-name, next, previous, up
+@subsection @t{nis_domain} Parameter
+@cindex nis_domain Parameter
+
+(type=string, default to local NIS domain name). Same as the
+@code{-y} option to @i{Amd}. Specify an alternative NIS domain from
+which to fetch the NIS maps. The default is the system domain name.
+This option is ignored if NIS support is not available.
+
+@c ----------------------------------------------------------------
+@node normalize_hostnames Parameter, os Parameter, nis_domain Parameter, Global Parameters
+@comment node-name, next, previous, up
+@subsection @t{normalize_hostnames} Parameter
+@cindex normalize_hostnames Parameter
+
+(type=boolean, default=@samp{no}). Same as the @code{-n} option to @i{Amd}.
+If @samp{yes}, then the name referred to by @code{$@{rhost@}} is normalized
+relative to the host database before being used. The effect is to
+translate aliases into ``official'' names.
+
+@c ----------------------------------------------------------------
+@node os Parameter, osver Parameter, normalize_hostnames Parameter, Global Parameters
+@comment node-name, next, previous, up
+@subsection @t{os} Parameter
+@cindex os Parameter
+
+(type=string, default to compiled in value). Same as the @code{-O}
+option to @i{Amd}. Allows you to override the compiled-in name of the
+operating system. Useful when the built-in name is not desired for
+backward compatibility reasons. For example, if the built-in name is
+@samp{sunos5}, you can override it to @samp{sos5}, and use older maps
+which were written with the latter in mind.
+
+@c ----------------------------------------------------------------
+@node osver Parameter, pid_file Parameter, os Parameter, Global Parameters
+@comment node-name, next, previous, up
+@subsection @t{osver} Parameter
+@cindex osver Parameter
+
+(type=string, default to compiled in value). Same as the @code{-o}
+option to @i{Amd}. Allows you to override the compiled-in version
+number of the operating system. Useful when the built-in version is not
+desired for backward compatibility reasons. For example, if the build
+in version is @samp{2.5.1}, you can override it to @samp{5.5.1}, and use
+older maps that were written with the latter in mind.
+
+@c ----------------------------------------------------------------
+@node pid_file Parameter, plock Parameter, osver Parameter, Global Parameters
+@comment node-name, next, previous, up
+@subsection @t{pid_file} Parameter
+@cindex pid_file Parameter
+
+(type=string, default=@samp{/dev/stdout}). Specify a file to store the process
+ID of the running daemon into. If not specified, @i{Amd} will print its
+process id onto the standard output. Useful for killing @i{Amd} after
+it had run. Note that the PID of a running @i{Amd} can also be
+retrieved via @i{Amq} (@pxref{Amq -p option}).
+
+This file is used only if the @samp{print_pid} option is on
+(@pxref{print_pid Parameter}).
+
+@c ----------------------------------------------------------------
+@node plock Parameter, portmap_program Parameter, pid_file Parameter, Global Parameters
+@comment node-name, next, previous, up
+@subsection @t{plock} Parameter
+@cindex plock Parameter
+
+(type=boolean, default=@samp{yes}). Same as the @code{-S} option to @i{Amd}.
+If @samp{yes}, lock the running executable pages of @i{Amd} into memory.
+To improve @i{Amd}'s performance, systems that support the @b{plock}(3)
+call can lock the @i{Amd} process into memory. This way there is less
+chance the operating system will schedule, page out, and swap the
+@i{Amd} process as needed. This improves @i{Amd}'s performance, at the
+cost of reserving the memory used by the @i{Amd} process (making it
+unavailable for other processes).
+
+@c ----------------------------------------------------------------
+@node portmap_program Parameter, print_pid Parameter, plock Parameter, Global Parameters
+@comment node-name, next, previous, up
+@subsection @t{portmap_program} Parameter
+@cindex portmap_program Parameter
+
+(type=numeric, default=300019). Specify an alternate Port-mapper RPC
+program number, other than the official number. This is useful when
+running multiple @i{Amd} processes. For example, you can run another
+@i{Amd} in ``test'' mode, without affecting the primary @i{Amd} process
+in any way. For safety reasons, the alternate program numbers that can
+be specified must be in the range 300019-300029, inclusive. @i{Amq} has
+an option @code{-P} which can be used to specify an alternate program
+number of an @i{Amd} to contact. In this way, amq can fully control any
+number of @i{Amd} processes running on the same host.
+
+@c ----------------------------------------------------------------
+@node print_pid Parameter, print_version Parameter, portmap_program Parameter, Global Parameters
+@comment node-name, next, previous, up
+@subsection @t{print_pid} Parameter
+@cindex print_pid Parameter
+
+(type=boolean, default=@samp{no}). Same as the @code{-p} option to @i{Amd}.
+If @samp{yes}, @i{Amd} will print its process ID upon starting.
+
+@c ----------------------------------------------------------------
+@node print_version Parameter, restart_mounts Parameter, print_pid Parameter, Global Parameters
+@comment node-name, next, previous, up
+@subsection @t{print_version} Parameter
+@cindex print_version Parameter
+
+(type=boolean, default=@samp{no}). Same as the @code{-v} option to @i{Amd},
+but the version prints and @i{Amd} continues to run. If @samp{yes}, @i{Amd}
+will print its version information string, which includes some
+configuration and compilation values.
+
+@c ----------------------------------------------------------------
+@node restart_mounts Parameter, selectors_on_default Parameter, print_version Parameter, Global Parameters
+@comment node-name, next, previous, up
+@subsection @t{restart_mounts} Parameter
+@cindex restart_mounts Parameter
+
+(type=boolean, default=@samp{no}). Same as the @code{-r} option to @i{Amd}.
+If @samp{yes} @i{Amd} will scan the mount table to determine which file
+systems are currently mounted. Whenever one of these would have been
+auto-mounted, @i{Amd} inherits it.
+
+@c ----------------------------------------------------------------
+@node selectors_on_default Parameter, show_statfs_entries Parameter, restart_mounts Parameter, Global Parameters
+@comment node-name, next, previous, up
+@subsection @t{selectors_on_default} Parameter
+@cindex selectors_on_default Parameter
+
+(type=boolean, default=@samp{no}). If @samp{yes}, then the @samp{/defaults} entry of
+maps will be looked for and any selectors processed before setting defaults
+for all other keys in that map. Useful when you want to set different
+options for a complete map based on some parameters. For example, you
+may want to better the NFS performance over slow slip-based networks as
+follows:
+
+@example
+/defaults \
+ wire==slip-net;opts:=intr,rsize=1024,wsize=1024 \
+ wire!=slip-net;opts:=intr,rsize=8192,wsize=8192
+@end example
+
+@c ----------------------------------------------------------------
+@node show_statfs_entries Parameter, unmount_on_exit Parameter , selectors_on_default Parameter, Global Parameters
+@comment node-name, next, previous, up
+@subsection @t{show_statfs_entries} Parameter
+@cindex show_statfs_entries Parameter
+
+(type=boolean), default=@samp{no}). If @samp{yes}, then all maps which are
+browsable will also show the number of entries (keys) they have when
+@b{df}(1) runs. (This is accomplished by returning non-zero values to
+the @b{statfs}(2) system call).
+
+@c ----------------------------------------------------------------
+@node unmount_on_exit Parameter, , show_statfs_entries Parameter, Global Parameters
+@comment node-name, next, previous, up
+@subsection @t{unmount_on_exit} Parameter
+@cindex unmount_on_exit Parameter
+
+(type=boolean), default=@samp{no}). If @samp{yes}, then @i{Amd} will attempt
+to unmount all file systems which it knows about. Normally it leaves
+all (esp. NFS) mounted file systems intact. Note that @i{Amd} does not
+know about file systems mounted before it starts up, unless the
+@samp{restart_mounts} option is used (@pxref{restart_mounts Parameter}).
+
+@c ================================================================
+@node Regular Map Parameters, amd.conf Examples, Global Parameters, Amd Configuration File
+@comment node-name, next, previous, up
+@section Regular Map Parameters
+@cindex amd.conf regular map parameters
+
+The following parameters are applicable only to regular map sections.
+
+@menu
+* map_name Parameter::
+* tag Parameter::
+@end menu
+
+@c ----------------------------------------------------------------
+@node map_name Parameter, tag Parameter, Regular Map Parameters, Regular Map Parameters
+@comment node-name, next, previous, up
+@subsection map_name Parameter
+@cindex map_name Parameter
+
+(type=string, must be specified). Name of the map where the keys are
+located.
+
+@c ----------------------------------------------------------------
+@node tag Parameter, , map_name Parameter, Regular Map Parameters
+@comment node-name, next, previous, up
+@subsection tag Parameter
+@cindex tag Parameter
+
+(type=string, default no tag). Each map entry in the configuration file
+can be tagged. If no tag is specified, that map section will always be
+processed by @i{Amd}. If it is specified, then @i{Amd} will process the map
+if the @code{-T} option was given to @i{Amd}, and the value given to that
+command-line option matches that in the map section.
+
+@c ================================================================
+@node amd.conf Examples, , Regular Map Parameters, Amd Configuration File
+@comment node-name, next, previous, up
+@section amd.conf Examples
+@cindex amd.conf examples
+
+The following is the actual @code{amd.conf} file I use at the
+Computer Science Department of Columbia University.
+
+@example
+# GLOBAL OPTIONS SECTION
+[ global ]
+normalize_hostnames = no
+print_pid = no
+#pid_file = /var/run/amd.pid
+restart_mounts = yes
+#unmount_on_exit = yes
+auto_dir = /n
+log_file = /var/log/amd
+log_options = all
+#debug_options = all
+plock = no
+selectors_on_default = yes
+# config.guess picks up "sunos5" and I don't want to edit my maps yet
+os = sos5
+# if you print_version after setting up "os", it will show it.
+print_version = no
+map_type = file
+search_path = /etc/amdmaps:/usr/lib/amd:/usr/local/AMD/lib
+browsable_dirs = yes
+fully_qualified_hosts = no
+
+# DEFINE AN AMD MOUNT POINT
+[ /u ]
+map_name = amd.u
+
+[ /proj ]
+map_name = amd.proj
+
+[ /src ]
+map_name = amd.src
+
+[ /misc ]
+map_name = amd.misc
+
+[ /import ]
+map_name = amd.import
+
+[ /tftpboot/.amd ]
+tag = tftpboot
+map_name = amd.tftpboot
+@end example
+
+@c ################################################################
+@node Run-time Administration, FSinfo, Amd Configuration File, Top
+@comment node-name, next, previous, up
+@chapter Run-time Administration
+@cindex Run-time administration
+@cindex Amq command
+
+@menu
+* Starting Amd::
+* Stopping Amd::
+* Restarting Amd::
+* Controlling Amd::
+@end menu
+
+@node Starting Amd, Stopping Amd, Run-time Administration, Run-time Administration
+@comment node-name, next, previous, up
+@section Starting @i{Amd}
+@cindex Starting Amd
+@cindex Additions to /etc/rc.local
+@cindex /etc/rc.local additions
+@cindex ctl-amd
+
+@i{Amd} is best started from @samp{/etc/rc.local} on BSD systems, or
+from the appropriate start-level script in @samp{/etc/init.d} on System V
+systems.
+
+@example
+if [ -f /usr/local/sbin/ctl-amd ]; then
+ /usr/local/sbin/ctl-amd start; (echo -n ' amd') > /dev/console
+fi
+@end example
+
+@noindent
+The shell script, @samp{ctl-amd} is used to start, stop, or restart
+@i{Amd}. It is a relatively generic script. All options you want to
+set should not be made in this script, but rather updated in the
+@file{amd.conf} file. @xref{Amd Configuration File}.
+
+If you do not wish to use an @i{Amd} configuration file, you may start
+@i{Amd} manually. For example, getting the map entries via NIS:
+
+@example
+amd -r -l /var/log/amd `ypcat -k auto.master`
+@end example
+
+@node Stopping Amd, Restarting Amd, Starting Amd, Run-time Administration
+@comment node-name, next, previous, up
+@section Stopping @i{Amd}
+@cindex Stopping Amd
+@cindex SIGTERM signal
+@cindex SIGINT signal
+
+@i{Amd} stops in response to two signals.
+
+@table @samp
+@item SIGTERM
+causes the top-level automount points to be unmounted and then @i{Amd}
+to exit. Any automounted filesystems are left mounted. They can be
+recovered by restarting @i{Amd} with the @code{-r} command line option.@refill
+
+@item SIGINT
+causes @i{Amd} to attempt to unmount any filesystems which it has
+automounted, in addition to the actions of @samp{SIGTERM}. This signal
+is primarily used for debugging.@refill
+@end table
+
+Actions taken for other signals are undefined.
+
+The easiest and safest way to stop @i{Amd}, without having to find its
+process ID by hand, is to use the @file{ctl-amd} script, as with:
+
+@example
+ctl-amd stop
+@end example
+
+@node Restarting Amd, Controlling Amd, Stopping Amd, Run-time Administration
+@comment node-name, next, previous, up
+@section Restarting @i{Amd}
+@cindex Restarting Amd
+@cindex Killing and starting Amd
+
+Before @i{Amd} can be started, it is vital to ensure that no other
+@i{Amd} processes are managing any of the mount points, and that the
+previous process(es) have terminated cleanly. When a terminating signal
+is set to @i{Amd}, the automounter does @emph{not} terminate right then.
+Rather, it starts by unmounting all of its managed mount mounts in the
+background, and then terminates. It usually takes a few seconds for
+this process to happen, but it can take an arbitrarily longer time. If
+two or more @i{Amd} processes attempt to manage the same mount point, it
+usually will result in a system lockup.
+
+The easiest and safest way to restart @i{Amd}, without having to find
+its process ID by hand, sending it the @samp{SIGTERM} signal, waiting for @i{Amd}
+to die cleanly, and verifying so, is to use the @file{ctl-amd} script,
+as with:
+
+@example
+ctl-amd restart
+@end example
+
+The script will locate the process ID of @i{Amd}, kill it, and wait for
+it to die cleanly before starting a new instance of the automounter.
+@file{ctl-amd} will wait for a total of 30 seconds for @i{Amd} to die,
+and will check once every 5 seconds if it had.
+
+@node Controlling Amd, , Restarting Amd, Run-time Administration
+@comment node-name, next, previous, up
+@section Controlling @i{Amd}
+@cindex Controlling Amd
+@cindex Discovering what is going on at run-time
+@cindex Listing currently mounted filesystems
+
+It is sometimes desirable or necessary to exercise external control
+over some of @i{Amd}'s internal state. To support this requirement,
+@i{Amd} implements an RPC interface which is used by the @dfn{Amq} program.
+A variety of information is available.
+
+@i{Amq} generally applies an operation, specified by a single letter option,
+to a list of mount points. The default operation is to obtain statistics
+about each mount point. This is similar to the output shown above
+but includes information about the number and type of accesses to each
+mount point.
+
+@menu
+* Amq default:: Default command behavior.
+* Amq -f option:: Flushing the map cache.
+* Amq -h option:: Controlling a non-local host.
+* Amq -l option:: Controlling the log file.
+* Amq -m option:: Obtaining mount statistics.
+* Amq -M-option:: Mounting a volume.
+* Amq -p option:: Getting Amd's process ID.
+* Amq -P-option:: Contacting alternate Amd processes.
+* Amq -s option:: Obtaining global statistics.
+* Amq -T option:: Use TCP transport.
+* Amq -U-option:: Use UDP transport.
+* Amq -u option:: Forcing volumes to time out.
+* Amq -v option:: Version information.
+* Other Amq options:: Three other special options.
+@end menu
+
+@c ----------------------------------------------------------------
+@node Amq default, Amq -f option, Controlling Amd, Controlling Amd
+@comment node-name, next, previous, up
+@subsection @i{Amq} default information
+
+With no arguments, @dfn{Amq} obtains a brief list of all existing
+mounts created by @i{Amd}. This is different from the list displayed by
+@b{df}(1) since the latter only includes system mount points.
+
+@noindent
+The output from this option includes the following information:
+
+@itemize @bullet
+@item
+the automount point,
+@item
+the filesystem type,
+@item
+the mount map or mount information,
+@item
+the internal, or system mount point.
+@end itemize
+
+@noindent
+For example:
+
+@example
+/ root "root" sky:(pid75)
+/homes toplvl /usr/local/etc/amd.homes /homes
+/home toplvl /usr/local/etc/amd.home /home
+/homes/jsp nfs charm:/home/charm /a/charm/home/charm/jsp
+/homes/phjk nfs toytown:/home/toytown /a/toytown/home/toytown/ai/phjk
+@end example
+
+@noindent
+If an argument is given then statistics for that volume name will
+be output. For example:
+
+@example
+What Uid Getattr Lookup RdDir RdLnk Statfs Mounted@@
+/homes 0 1196 512 22 0 30 90/09/14 12:32:55
+/homes/jsp 0 0 0 0 1180 0 90/10/13 12:56:58
+@end example
+
+@table @code
+@item What
+the volume name.
+
+@item Uid
+ignored.
+
+@item Getattr
+the count of NFS @dfn{getattr} requests on this node. This should only be
+non-zero for directory nodes.
+
+@item Lookup
+the count of NFS @dfn{lookup} requests on this node. This should only be
+non-zero for directory nodes.
+
+@item RdDir
+the count of NFS @dfn{readdir} requests on this node. This should only
+be non-zero for directory nodes.
+
+@item RdLnk
+the count of NFS @dfn{readlink} requests on this node. This should be
+zero for directory nodes.
+
+@item Statfs
+the count of NFS @dfn{statfs} requests on this node. This should only
+be non-zero for top-level automount points.
+
+@item Mounted@@
+the date and time the volume name was first referenced.
+@end table
+
+@c ----------------------------------------------------------------
+@node Amq -f option, Amq -h option, Amq default, Controlling Amd
+@comment node-name, next, previous, up
+@subsection @i{Amq} @code{-f} option
+@cindex Flushing the map cache
+@cindex Map cache, flushing
+
+The @code{-f} option causes @i{Amd} to flush the internal mount map cache.
+This is useful for example in Hesiod maps since @i{Amd} will not
+automatically notice when they have been updated. The map cache can
+also be synchronized with the map source by using the @samp{sync} option
+(@pxref{Automount Filesystem}).@refill
+
+@c ----------------------------------------------------------------
+@node Amq -l option, Amq -m option, Amq -h option, Controlling Amd
+@comment node-name, next, previous, up
+@subsection @i{Amq} @code{-l} option
+@cindex Resetting the Amd log file
+@cindex Setting the Amd log file via Amq
+@cindex Log file, resetting
+
+Tell @i{Amd} to use @i{log_file} as the log file name. For security
+reasons, this @emph{must} be the same log file which @i{Amd} used when
+started. This option is therefore only useful to refresh @i{Amd}'s open
+file handle on the log file, so that it can be rotated and compressed
+via daily cron jobs.
+
+@c ----------------------------------------------------------------
+@node Amq -h option, Amq -l option, Amq -f option, Controlling Amd
+@comment node-name, next, previous, up
+@subsection @i{Amq} @code{-h} option
+@cindex Querying an alternate host
+
+By default the local host is used. In an HP-UX cluster the root server
+is used since that is the only place in the cluster where @i{Amd} will
+be running. To query @i{Amd} on another host the @code{-h} option should
+be used.
+
+@c ----------------------------------------------------------------
+@node Amq -m option, Amq -M-option, Amq -l option, Controlling Amd
+@comment node-name, next, previous, up
+@subsection @i{Amq} @code{-m} option
+
+The @code{-m} option displays similar information about mounted
+filesystems, rather than automount points. The output includes the
+following information:
+
+@itemize @bullet
+@item
+the mount information,
+@item
+the mount point,
+@item
+the filesystem type,
+@item
+the number of references to this filesystem,
+@item
+the server hostname,
+@item
+the state of the file server,
+@item
+any error which has occurred.
+@end itemize
+
+For example:
+
+@example
+"root" truth:(pid602) root 1 localhost is up
+hesiod.home /home toplvl 1 localhost is up
+hesiod.vol /vol toplvl 1 localhost is up
+hesiod.homes /homes toplvl 1 localhost is up
+amy:/home/amy /a/amy/home/amy nfs 5 amy is up
+swan:/home/swan /a/swan/home/swan nfs 0 swan is up (Permission denied)
+ex:/home/ex /a/ex/home/ex nfs 0 ex is down
+@end example
+
+When the reference count is zero the filesystem is not mounted but
+the mount point and server information is still being maintained
+by @i{Amd}.
+
+@c ----------------------------------------------------------------
+@node Amq -M-option, Amq -p option, Amq -m option, Controlling Amd
+@comment node-name, next, previous, up
+@subsection @i{Amq} @code{-M} option
+
+The @code{-M} option passes a new map entry to @i{Amd} and waits for it to
+be evaluated, possibly causing a mount. For example, the following
+command would cause @samp{/home/toytown} on host @samp{toytown} to be
+mounted locally on @samp{/mnt/toytown}.
+
+@example
+amq -M '/mnt/toytown type:=nfs;rfs:=/home/toytown;rhost:=toytown;fs:=$@{key@}'
+@end example
+
+@i{Amd} applies some simple security checks before allowing this
+operation. The check tests whether the incoming request is from a
+privileged UDP port on the local machine. ``Permission denied'' is
+returned if the check fails.
+
+This option is very insecure as it is vulnerable to attacks such as IP
+Spoofing. In other words, it is relatively easy for an attacker who
+really wants to, to make your @i{Amd} process mount any filesystem from
+the Internet! Therefore, the @emph{complete} code which supports the
+@code{-M} option in @i{Amd} and @i{Amq} is turned off by default. To turn
+it on, you have to reconfigure am-utils with @code{configure
+--enable-amq-mount}. Think twice before doing so, and use this option
+only if you absolutely need to.
+
+A future release of @i{Amd} will include code to allow the @b{mount}(8)
+command to mount automount points:
+
+@example
+mount -t amd /vol hesiod.vol
+@end example
+
+This will then allow @i{Amd} to be controlled from the standard system
+filesystem mount list.
+
+@c ----------------------------------------------------------------
+@node Amq -p option, Amq -P-option, Amq -M-option, Controlling Amd
+@comment node-name, next, previous, up
+@subsection @i{Amq} @code{-p} option
+@cindex Process ID; Amd
+@cindex Amd's process ID
+@cindex Amd's PID
+@cindex PID; Amd
+
+Return the process ID of the remote or locally running @i{Amd}. Useful
+when you need to send a signal to the local @i{Amd} process, and would
+rather not have to search through the process table. This option is
+used in the @file{ctl-amd} script.
+
+@c ----------------------------------------------------------------
+@node Amq -P-option, Amq -s option, Amq -p option, Controlling Amd
+@comment node-name, next, previous, up
+@subsection @i{Amq} @code{-P} option
+@cindex Multiple Amd processes
+@cindex Running multiple Amd
+@cindex Debugging a new Amd configuration
+@cindex RPC Program numbers; Amd
+
+Contact an alternate running @i{Amd} that had registered itself on a
+different RPC @var{program_number} and apply all other operations to
+that instance of the automounter. This is useful when you run multiple
+copies of @i{Amd}, and need to manage each one separately. If not
+specified, @i{Amq} will use the default program number for @i{Amd}, 300019.
+For security reasons, the only alternate program numbers @i{Amd} can use
+range from 300019 to 300029, inclusive.
+
+For example, to kill an alternate running @i{Amd}:
+
+@example
+kill `amq -p -P 300020`
+@end example
+
+@c ----------------------------------------------------------------
+@node Amq -s option, Amq -T option, Amq -P-option, Controlling Amd
+@comment node-name, next, previous, up
+@subsection @i{Amq} @code{-s} option
+@cindex Global statistics
+@cindex Statistics
+
+The @code{-s} option displays global statistics. If any other options are specified
+or any filesystems named then this option is ignored. For example:
+
+@example
+requests stale mount mount unmount
+deferred fhandles ok failed failed
+1054 1 487 290 7017
+@end example
+
+@table @samp
+@item Deferred requests
+are those for which an immediate reply could not be constructed. For
+example, this would happen if a background mount was required.
+
+@item Stale filehandles
+counts the number of times the kernel passes a stale filehandle to @i{Amd}.
+Large numbers indicate problems.
+
+@item Mount ok
+counts the number of automounts which were successful.
+
+@item Mount failed
+counts the number of automounts which failed.
+
+@item Unmount failed
+counts the number of times a filesystem could not be unmounted. Very
+large numbers here indicate that the time between unmount attempts
+should be increased.
+@end table
+
+@c ----------------------------------------------------------------
+@node Amq -T option, Amq -U-option, Amq -s option, Controlling Amd
+@comment node-name, next, previous, up
+@subsection @i{Amq} @code{-T} option
+@cindex Forcing Amq to use a TCP transport
+@cindex TCP; using with Amq
+
+The @code{-T} option causes the @i{Amq} to contact @i{Amd} using the TCP
+transport only (connection oriented). Normally, @i{Amq} will use TCP
+first, and if that failed, will try UDP.
+
+@c ----------------------------------------------------------------
+@node Amq -U-option, Amq -u option, Amq -T option, Controlling Amd
+@comment node-name, next, previous, up
+@subsection @i{Amq} @code{-U} option
+@cindex Forcing Amq to use a UDP transport
+@cindex UDP; using with Amq
+
+The @code{-U} option causes the @i{Amq} to contact @i{Amd} using the UDP
+transport only (connectionless). Normally, @i{Amq} will use TCP first,
+and if that failed, will try UDP.
+
+@c ----------------------------------------------------------------
+@node Amq -u option, Amq -v option, Amq -U-option, Controlling Amd
+@comment node-name, next, previous, up
+@subsection @i{Amq} @code{-u} option
+@cindex Forcing filesystem to time out
+@cindex Unmounting a filesystem
+
+The @code{-u} option causes the time-to-live interval of the named mount
+points to be expired, thus causing an unmount attempt. This is the only
+safe way to unmount an automounted filesystem. It is not possible to
+unmount a filesystem which has been mounted with the @samp{nounmount}
+flag.
+
+@c The @code{-H} option informs @i{Amd} that the specified mount point has hung -
+@c as if its keepalive timer had expired.
+
+@c ----------------------------------------------------------------
+@node Amq -v option, Other Amq options, Amq -u option, Controlling Amd
+@comment node-name, next, previous, up
+@subsection @i{Amq} @code{-v} option
+@cindex Version information at run-time
+
+The @code{-v} option displays the version of @i{Amd} in a similar way to
+@i{Amd}'s @code{-v} option.
+
+@c ----------------------------------------------------------------
+@node Other Amq options, , Amq -v option, Controlling Amd
+@comment node-name, next, previous, up
+@subsection Other @i{Amq} options
+@cindex Logging options via Amq
+@cindex Debugging options via Amq
+
+Two other operations are implemented. These modify the state of @i{Amd}
+as a whole, rather than any particular filesystem. The @code{-x} and
+@code{-D} options have exactly the same effect as @i{Amd}'s corresponding
+command line options.
+
+When @i{Amd} receives a @code{-x} flag it limits the log options being
+modified to those which were not enabled at startup. This prevents a
+user turning @emph{off} any logging option which was specified at
+startup, though any which have been turned on since then can still be
+turned off. The @code{-D} option has a similar behavior.
+
+@c ################################################################
+@node FSinfo, Hlfsd, Run-time Administration, Top
+@comment node-name, next, previous, up
+@chapter FSinfo
+@cindex FSinfo
+@cindex Filesystem info package
+
+XXX: this chapter should be reviewed by someone knowledgeable with
+fsinfo.
+
+@menu
+* FSinfo Overview:: Introduction to FSinfo.
+* Using FSinfo:: Basic concepts.
+* FSinfo Grammar:: Language syntax, semantics and examples.
+* FSinfo host definitions:: Defining a new host.
+* FSinfo host attributes:: Definable host attributes.
+* FSinfo filesystems:: Defining locally attached filesystems.
+* FSinfo static mounts:: Defining additional static mounts.
+* FSinfo automount definitions::
+* FSinfo Command Line Options::
+* FSinfo errors::
+@end menu
+
+@node FSinfo Overview, Using FSinfo, FSinfo, FSinfo
+@comment node-name, next, previous, up
+@section @i{FSinfo} overview
+@cindex FSinfo overview
+
+@i{FSinfo} is a filesystem management tool. It has been designed to
+work with @i{Amd} to help system administrators keep track of the ever
+increasing filesystem namespace under their control.
+
+The purpose of @i{FSinfo} is to generate all the important standard
+filesystem data files from a single set of input data. Starting with a
+single data source guarantees that all the generated files are
+self-consistent. One of the possible output data formats is a set of
+@i{Amd} maps which can be used amongst the set of hosts described in the
+input data.
+
+@i{FSinfo} implements a declarative language. This language is
+specifically designed for describing filesystem namespace and physical
+layouts. The basic declaration defines a mounted filesystem including
+its device name, mount point, and all the volumes and access
+permissions. @i{FSinfo} reads this information and builds an internal
+map of the entire network of hosts. Using this map, many different data
+formats can be produced including @file{/etc/fstab},
+@file{/etc/exports}, @i{Amd} mount maps and
+@file{/etc/bootparams}.@refill
+
+@node Using FSinfo, FSinfo Grammar, FSinfo Overview, FSinfo
+@comment node-name, next, previous, up
+@section Using @i{FSinfo}
+@cindex Using FSinfo
+
+The basic strategy when using @i{FSinfo} is to gather all the
+information about all disks on all machines into one set of
+declarations. For each machine being managed, the following data is
+required:
+
+@itemize @bullet
+@item
+Hostname
+@item
+List of all filesystems and, optionally, their mount points.
+@item
+Names of volumes stored on each filesystem.
+@item
+NFS export information for each volume.
+@item
+The list of static filesystem mounts.
+@end itemize
+
+The following information can also be entered into the same
+configuration files so that all data can be kept in one place.
+
+@itemize @bullet
+@item
+List of network interfaces
+@item
+IP address of each interface
+@item
+Hardware address of each interface
+@item
+Dumpset to which each filesystem belongs
+@item
+and more @dots{}
+@end itemize
+
+To generate @i{Amd} mount maps, the automount tree must also be defined
+(@pxref{FSinfo automount definitions}). This will have been designed at
+the time the volume names were allocated. Some volume names will not be
+automounted, so @i{FSinfo} needs an explicit list of which volumes
+should be automounted.@refill
+
+Hostnames are required at several places in the @i{FSinfo} language. It
+is important to stick to either fully qualified names or unqualified
+names. Using a mixture of the two will inevitably result in confusion.
+
+Sometimes volumes need to be referenced which are not defined in the set
+of hosts being managed with @i{FSinfo}. The required action is to add a
+dummy set of definitions for the host and volume names required. Since
+the files generated for those particular hosts will not be used on them,
+the exact values used is not critical.
+
+@node FSinfo Grammar, FSinfo host definitions, Using FSinfo, FSinfo
+@comment node-name, next, previous, up
+@section @i{FSinfo} grammar
+@cindex FSinfo grammar
+@cindex Grammar, FSinfo
+
+@i{FSinfo} has a relatively simple grammar. Distinct syntactic
+constructs exist for each of the different types of data, though they
+share a common flavor. Several conventions are used in the grammar
+fragments below.
+
+The notation, @i{list(}@t{xxx}@i{)}, indicates a list of zero or more
+@t{xxx}'s. The notation, @i{opt(}@t{xxx}@i{)}, indicates zero or one
+@t{xxx}. Items in double quotes, @i{eg} @t{"host"}, represent input
+tokens. Items in angle brackets, @i{eg} @var{<hostname>}, represent
+strings in the input. Strings need not be in double quotes, except to
+differentiate them from reserved words. Quoted strings may include the
+usual set of C ``@t{\}'' escape sequences with one exception: a
+backslash-newline-whitespace sequence is squashed into a single space
+character. To defeat this feature, put a further backslash at the start
+of the second line.
+
+At the outermost level of the grammar, the input consists of a
+sequence of host and automount declarations. These declarations are
+all parsed before they are analyzed. This means they can appear in
+any order and cyclic host references are possible.
+
+@example
+fsinfo : @i{list(}fsinfo_attr@i{)} ;
+
+fsinfo_attr : host | automount ;
+@end example
+
+@menu
+* FSinfo host definitions::
+* FSinfo automount definitions::
+@end menu
+
+@node FSinfo host definitions, FSinfo host attributes, FSinfo Grammar, FSinfo
+@comment node-name, next, previous, up
+@section @i{FSinfo} host definitions
+@cindex FSinfo host definitions
+@cindex Defining a host, FSinfo
+
+A host declaration consists of three parts: a set of machine attribute
+data, a list of filesystems physically attached to the machine, and a
+list of additional statically mounted filesystems.
+
+@example
+host : "host" host_data @i{list(}filesystem@i{@i{)}} @i{list(}mount@i{@i{)}} ;
+@end example
+
+Each host must be declared in this way exactly once. Such things as the
+hardware address, the architecture and operating system types and the
+cluster name are all specified within the @dfn{host data}.
+
+All the disks the machine has should then be described in the @dfn{list
+of filesystems}. When describing disks, you can specify what
+@dfn{volname} the disk/partition should have and all such entries are
+built up into a dictionary which can then be used for building the
+automounter maps.
+
+The @dfn{list of mounts} specifies all the filesystems that should be
+statically mounted on the machine.
+
+@menu
+* FSinfo host attributes::
+* FSinfo filesystems::
+* FSinfo static mounts::
+@end menu
+
+@node FSinfo host attributes, FSinfo filesystems, FSinfo host definitions , FSinfo host definitions
+@comment node-name, next, previous, up
+@section @i{FSinfo} host attributes
+@cindex FSinfo host attributes
+@cindex Defining host attributes, FSinfo
+
+The host data, @dfn{host_data}, always includes the @dfn{hostname}. In
+addition, several other host attributes can be given.
+
+@example
+host_data : @var{<hostname>}
+ | "@{" @i{list(}host_attrs@i{)} "@}" @var{<hostname>}
+ ;
+
+host_attrs : host_attr "=" @var{<string>}
+ | netif
+ ;
+
+host_attr : "config"
+ | "arch"
+ | "os"
+ | "cluster"
+ ;
+@end example
+
+The @dfn{hostname} is, typically, the fully qualified hostname of the
+machine.
+
+Examples:
+
+@example
+host dylan.doc.ic.ac.uk
+
+host @{
+ os = hpux
+ arch = hp300
+@} dougal.doc.ic.ac.uk
+@end example
+
+The options that can be given as host attributes are shown below.
+
+@menu
+* netif Option: FSinfo host netif:
+* config Option: FSinfo host config:
+* arch Option: FSinfo host arch:
+* os Option: FSinfo host os:
+* cluster Option: FSinfo host cluster:
+@end menu
+
+@node FSinfo host netif, FSinfo host config, , FSinfo host attributes
+@comment node-name, next, previous, up
+@subsection netif Option
+
+This defines the set of network interfaces configured on the machine.
+The interface attributes collected by @i{FSinfo} are the IP address,
+subnet mask and hardware address. Multiple interfaces may be defined
+for hosts with several interfaces by an entry for each interface. The
+values given are sanity checked, but are currently unused for anything
+else.
+
+@example
+netif : "netif" @var{<string>} "@{" @i{list(}netif_attrs@i{)} "@}" ;
+
+netif_attrs : netif_attr "=" @var{<string>} ;
+
+netif_attr : "inaddr" | "netmask" | "hwaddr" ;
+@end example
+
+Examples:
+
+@example
+netif ie0 @{
+ inaddr = 129.31.81.37
+ netmask = 0xfffffe00
+ hwaddr = "08:00:20:01:a6:a5"
+@}
+
+netif ec0 @{ @}
+@end example
+
+@node FSinfo host config, FSinfo host arch, FSinfo host netif, FSinfo host attributes
+@comment node-name, next, previous, up
+@subsection config Option
+@cindex FSinfo config host attribute
+@cindex config, FSinfo host attribute
+
+This option allows you to specify configuration variables for the
+startup scripts (@file{rc} scripts). A simple string should immediately
+follow the keyword.
+
+Example:
+
+@example
+config "NFS_SERVER=true"
+config "ZEPHYR=true"
+@end example
+
+This option is currently unsupported.
+
+@node FSinfo host arch, FSinfo host os, FSinfo host config, FSinfo host attributes
+@comment node-name, next, previous, up
+@subsection arch Option
+@cindex FSinfo arch host attribute
+@cindex arch, FSinfo host attribute
+
+This defines the architecture of the machine. For example:
+
+@example
+arch = hp300
+@end example
+
+This is intended to be of use when building architecture specific
+mountmaps, however, the option is currently unsupported.
+
+@node FSinfo host os, FSinfo host cluster, FSinfo host arch, FSinfo host attributes
+@comment node-name, next, previous, up
+@subsection os Option
+@cindex FSinfo os host attribute
+@cindex os, FSinfo host attribute
+
+This defines the operating system type of the host. For example:
+
+@example
+os = hpux
+@end example
+
+This information is used when creating the @file{fstab} files, for
+example in choosing which format to use for the @file{fstab} entries
+within the file.
+
+@node FSinfo host cluster, , FSinfo host os, FSinfo host attributes
+@comment node-name, next, previous, up
+@subsection cluster Option
+@cindex FSinfo cluster host attribute
+@cindex cluster, FSinfo host attribute
+
+This is used for specifying in which cluster the machine belongs. For
+example:
+
+@example
+cluster = "theory"
+@end example
+
+The cluster is intended to be used when generating the automount maps,
+although it is currently unsupported.
+
+@node FSinfo filesystems, FSinfo static mounts, FSinfo host attributes, FSinfo host definitions
+@comment node-name, next, previous, up
+@section @i{FSinfo} filesystems
+@cindex FSinfo filesystems
+
+The list of physically attached filesystems follows the machine
+attributes. These should define all the filesystems available from this
+machine, whether exported or not. In addition to the device name,
+filesystems have several attributes, such as filesystem type, mount
+options, and @samp{fsck} pass number which are needed to generate
+@file{fstab} entries.
+
+@example
+filesystem : "fs" @var{<device>} "@{" @i{list(}fs_data@i{)} "@}" ;
+
+fs_data : fs_data_attr "=" @var{<string>}
+ | mount
+ ;
+
+fs_data_attr
+ : "fstype" | "opts" | "passno"
+ | "freq" | "dumpset" | "log"
+ ;
+@end example
+
+Here, @var{<device>} is the device name of the disk (for example,
+@file{/dev/dsk/2s0}). The device name is used for building the mount
+maps and for the @file{fstab} file. The attributes that can be
+specified are shown in the following section.
+
+The @i{FSinfo} configuration file for @code{dylan.doc.ic.ac.uk} is listed below.
+
+@example
+host dylan.doc.ic.ac.uk
+
+fs /dev/dsk/0s0 @{
+ fstype = swap
+@}
+
+fs /dev/dsk/0s0 @{
+ fstype = hfs
+ opts = rw,noquota,grpid
+ passno = 0;
+ freq = 1;
+ mount / @{ @}
+@}
+
+fs /dev/dsk/1s0 @{
+ fstype = hfs
+ opts = defaults
+ passno = 1;
+ freq = 1;
+ mount /usr @{
+ local @{
+ exportfs "dougal eden dylan zebedee brian"
+ volname /nfs/hp300/local
+ @}
+ @}
+@}
+
+fs /dev/dsk/2s0 @{
+ fstype = hfs
+ opts = defaults
+ passno = 1;
+ freq = 1;
+ mount default @{
+ exportfs "toytown_clients hangers_on"
+ volname /home/dylan/dk2
+ @}
+@}
+
+fs /dev/dsk/3s0 @{
+ fstype = hfs
+ opts = defaults
+ passno = 1;
+ freq = 1;
+ mount default @{
+ exportfs "toytown_clients hangers_on"
+ volname /home/dylan/dk3
+ @}
+@}
+
+fs /dev/dsk/5s0 @{
+ fstype = hfs
+ opts = defaults
+ passno = 1;
+ freq = 1;
+ mount default @{
+ exportfs "toytown_clients hangers_on"
+ volname /home/dylan/dk5
+ @}
+@}
+@end example
+
+@menu
+* fstype Option: FSinfo filesystems fstype:
+* opts Option: FSinfo filesystems opts:
+* passno Option: FSinfo filesystems passno:
+* freq Option: FSinfo filesystems freq:
+* mount Option: FSinfo filesystems mount:
+* dumpset Option: FSinfo filesystems dumpset:
+* log Option: FSinfo filesystems log:
+@end menu
+
+@node FSinfo filesystems fstype, FSinfo filesystems opts, , FSinfo filesystems
+@comment node-name, next, previous, up
+@subsection fstype Option
+@cindex FSinfo fstype filesystems option
+@cindex fstype, FSinfo filesystems option
+@cindex export, FSinfo special fstype
+
+This specifies the type of filesystem being declared and will be placed
+into the @file{fstab} file as is. The value of this option will be
+handed to @code{mount} as the filesystem type---it should have such
+values as @code{4.2}, @code{nfs} or @code{swap}. The value is not
+examined for correctness.
+
+There is one special case. If the filesystem type is specified as
+@samp{export} then the filesystem information will not be added to the
+host's @file{fstab} information, but it will still be visible on the
+network. This is useful for defining hosts which contain referenced
+volumes but which are not under full control of @i{FSinfo}.
+
+Example:
+
+@example
+fstype = swap
+@end example
+
+@node FSinfo filesystems opts, FSinfo filesystems passno, FSinfo filesystems fstype, FSinfo filesystems
+@comment node-name, next, previous, up
+@subsection opts Option
+@cindex FSinfo opts filesystems option
+@cindex opts, FSinfo filesystems option
+
+This defines any options that should be given to @b{mount}(8) in the
+@file{fstab} file. For example:
+
+@example
+opts = rw,nosuid,grpid
+@end example
+
+@node FSinfo filesystems passno, FSinfo filesystems freq, FSinfo filesystems opts, FSinfo filesystems
+@comment node-name, next, previous, up
+@subsection passno Option
+@cindex FSinfo passno filesystems option
+@cindex passno, FSinfo filesystems option
+
+This defines the @b{fsck}(8) pass number in which to check the
+filesystem. This value will be placed into the @file{fstab} file.
+
+Example:
+
+@example
+passno = 1
+@end example
+
+@node FSinfo filesystems freq, FSinfo filesystems mount, FSinfo filesystems passno, FSinfo filesystems
+@comment node-name, next, previous, up
+@subsection freq Option
+@cindex FSinfo freq filesystems option
+@cindex freq, FSinfo filesystems option
+
+This defines the interval (in days) between dumps. The value is placed
+as is into the @file{fstab} file.
+
+Example:
+
+@example
+freq = 3
+@end example
+
+@node FSinfo filesystems mount, FSinfo filesystems dumpset, FSinfo filesystems freq, FSinfo filesystems
+@comment node-name, next, previous, up
+@subsection mount Option
+@cindex FSinfo mount filesystems option
+@cindex mount, FSinfo filesystems option
+@cindex exportfs, FSinfo mount option
+@cindex volname, FSinfo mount option
+@cindex sel, FSinfo mount option
+
+This defines the mountpoint at which to place the filesystem. If the
+mountpoint of the filesystem is specified as @code{default}, then the
+filesystem will be mounted in the automounter's tree under its volume
+name and the mount will automatically be inherited by the automounter.
+
+Following the mountpoint, namespace information for the filesystem may
+be described. The options that can be given here are @code{exportfs},
+@code{volname} and @code{sel}.
+
+The format is:
+
+@example
+mount : "mount" vol_tree ;
+
+vol_tree : @i{list(}vol_tree_attr@i{)} ;
+
+vol_tree_attr
+ : @var{<string>} "@{" @i{list(}vol_tree_info@i{)} vol_tree "@}" ;
+
+vol_tree_info
+ : "exportfs" @var{<export-data>}
+ | "volname" @var{<volname>}
+ | "sel" @var{<selector-list>}
+ ;
+@end example
+
+Example:
+
+@example
+mount default @{
+ exportfs "dylan dougal florence zebedee"
+ volname /vol/andrew
+@}
+@end example
+
+In the above example, the filesystem currently being declared will have
+an entry placed into the @file{exports} file allowing the filesystem to
+be exported to the machines @code{dylan}, @code{dougal}, @code{florence}
+and @code{zebedee}. The volume name by which the filesystem will be
+referred to remotely, is @file{/vol/andrew}. By declaring the
+mountpoint to be @code{default}, the filesystem will be mounted on the
+local machine in the automounter tree, where @i{Amd} will automatically
+inherit the mount as @file{/vol/andrew}.@refill
+
+@table @samp
+@item exportfs
+a string defining which machines the filesystem may be exported to.
+This is copied, as is, into the @file{exports} file---no sanity checking
+is performed on this string.@refill
+
+@item volname
+a string which declares the remote name by which to reference the
+filesystem. The string is entered into a dictionary and allows you to
+refer to this filesystem in other places by this volume name.@refill
+
+@item sel
+a string which is placed into the automounter maps as a selector for the
+filesystem.@refill
+
+@end table
+
+@node FSinfo filesystems dumpset, FSinfo filesystems log, FSinfo filesystems mount, FSinfo filesystems
+@comment node-name, next, previous, up
+@subsection dumpset Option
+@cindex FSinfo dumpset filesystems option
+@cindex dumpset, FSinfo filesystems option
+
+This provides support for Imperial College's local file backup tools and
+is not documented further here.
+
+@node FSinfo filesystems log, , FSinfo filesystems dumpset, FSinfo filesystems
+@comment node-name, next, previous, up
+@subsection log Option
+@cindex FSinfo log filesystems option
+@cindex log, FSinfo filesystems option
+
+Specifies the log device for the current filesystem. This is ignored if
+not required by the particular filesystem type.
+
+@node FSinfo static mounts, FSinfo automount definitions , FSinfo filesystems, FSinfo host definitions
+@comment node-name, next, previous, up
+@section @i{FSinfo} static mounts
+@cindex FSinfo static mounts
+@cindex Statically mounts filesystems, FSinfo
+
+Each host may also have a number of statically mounted filesystems. For
+example, the host may be a diskless workstation in which case it will
+have no @code{fs} declarations. In this case the @code{mount}
+declaration is used to determine from where its filesystems will be
+mounted. In addition to being added to the @file{fstab} file, this
+information can also be used to generate a suitable @file{bootparams}
+file.@refill
+
+@example
+mount : "mount" @var{<volname>} @i{list(}localinfo@i{)} ;
+
+localinfo : localinfo_attr @var{<string>} ;
+
+localinfo_attr
+ : "as"
+ | "from"
+ | "fstype"
+ | "opts"
+ ;
+@end example
+
+The filesystem specified to be mounted will be searched for in the
+dictionary of volume names built when scanning the list of hosts'
+definitions.
+
+The attributes have the following semantics:
+@table @samp
+@item from @var{machine}
+mount the filesystem from the machine with the hostname of
+@dfn{machine}.@refill
+
+@item as @var{mountpoint}
+mount the filesystem locally as the name given, in case this is
+different from the advertised volume name of the filesystem.
+
+@item opts @var{options}
+native @b{mount}(8) options.
+
+@item fstype @var{type}
+type of filesystem to be mounted.
+@end table
+
+An example:
+
+@example
+mount /export/exec/hp300/local as /usr/local
+@end example
+
+If the mountpoint specified is either @file{/} or @file{swap}, the
+machine will be considered to be booting off the net and this will be
+noted for use in generating a @file{bootparams} file for the host which
+owns the filesystems.
+
+@node FSinfo automount definitions, FSinfo Command Line Options, FSinfo static mounts, FSinfo
+@comment node-name, next, previous, up
+@section Defining an @i{Amd} Mount Map in @i{FSinfo}
+@cindex FSinfo automount definitions
+@cindex Defining an Amd mount map, FSinfo
+
+The maps used by @i{Amd} can be constructed from @i{FSinfo} by defining
+all the automount trees. @i{FSinfo} takes all the definitions found and
+builds one map for each top level tree.
+
+The automount tree is usually defined last. A single automount
+configuration will usually apply to an entire management domain. One
+@code{automount} declaration is needed for each @i{Amd} automount point.
+@i{FSinfo} determines whether the automount point is @dfn{direct}
+(@pxref{Direct Automount Filesystem}) or @dfn{indirect}
+(@pxref{Top-level Filesystem}). Direct automount points are
+distinguished by the fact that there is no underlying
+@dfn{automount_tree}.@refill
+
+@example
+automount : "automount" @i{opt(}auto_opts@i{)} automount_tree ;
+
+auto_opts : "opts" @var{<mount-options>} ;
+
+automount_tree
+ : @i{list(}automount_attr@i{)}
+ ;
+
+automount_attr
+ : @var{<string>} "=" @var{<volname>}
+ | @var{<string>} "->" @var{<symlink>}
+ | @var{<string>} "@{" automount_tree "@}"
+ ;
+@end example
+
+If @var{<mount-options>} is given, then it is the string to be placed in
+the maps for @i{Amd} for the @code{opts} option.
+
+A @dfn{map} is typically a tree of filesystems, for example @file{home}
+normally contains a tree of filesystems representing other machines in
+the network.
+
+A map can either be given as a name representing an already defined
+volume name, or it can be a tree. A tree is represented by placing
+braces after the name. For example, to define a tree @file{/vol}, the
+following map would be defined:
+
+@example
+automount /vol @{ @}
+@end example
+
+Within a tree, the only items that can appear are more maps.
+For example:
+
+@example
+automount /vol @{
+ andrew @{ @}
+ X11 @{ @}
+@}
+@end example
+
+In this case, @i{FSinfo} will look for volumes named @file{/vol/andrew}
+and @file{/vol/X11} and a map entry will be generated for each. If the
+volumes are defined more than once, then @i{FSinfo} will generate
+a series of alternate entries for them in the maps.@refill
+
+Instead of a tree, either a link (@var{name} @code{->}
+@var{destination}) or a reference can be specified (@var{name} @code{=}
+@var{destination}). A link creates a symbolic link to the string
+specified, without further processing the entry. A reference will
+examine the destination filesystem and optimize the reference. For
+example, to create an entry for @code{njw} in the @file{/homes} map,
+either of the two forms can be used:@refill
+
+@example
+automount /homes @{
+ njw -> /home/dylan/njw
+@}
+@end example
+
+or
+
+@example
+automount /homes @{
+ njw = /home/dylan/njw
+@}
+@end example
+
+In the first example, when @file{/homes/njw} is referenced from @i{Amd},
+a link will be created leading to @file{/home/dylan/njw} and the
+automounter will be referenced a second time to resolve this filename.
+The map entry would be:
+
+@example
+njw type:=link;fs:=/home/dylan/njw
+@end example
+
+In the second example, the destination directory is analyzed and found
+to be in the filesystem @file{/home/dylan} which has previously been
+defined in the maps. Hence the map entry will look like:
+
+@example
+njw rhost:=dylan;rfs:=/home/dylan;sublink:=njw
+@end example
+
+Creating only one symbolic link, and one access to @i{Amd}.
+
+@node FSinfo Command Line Options, FSinfo errors, FSinfo automount definitions, FSinfo
+@comment node-name, next, previous, up
+@section @i{FSinfo} Command Line Options
+@cindex FSinfo command line options
+@cindex Command line options, FSinfo
+
+@i{FSinfo} is started from the command line by using the command:
+
+@example
+fsinfo [@i{options}] @i{files} ...
+@end example
+
+The input to @i{FSinfo} is a single set of definitions of machines and
+automount maps. If multiple files are given on the command-line, then
+the files are concatenated together to form the input source. The files
+are passed individually through the C pre-processor before being parsed.
+
+Several options define a prefix for the name of an output file. If the
+prefix is not specified no output of that type is produced. The suffix
+used will correspond either to the hostname to which a file belongs, or
+to the type of output if only one file is produced. Dumpsets and the
+@file{bootparams} file are in the latter class. To put the output into
+a subdirectory simply put a @file{/} at the end of the prefix, making
+sure that the directory has already been made before running
+@i{Fsinfo}.
+
+@menu
+* -a FSinfo Option:: Amd automount directory:
+* -b FSinfo Option:: Prefix for bootparams files.
+* -d FSinfo Option:: Prefix for dumpset data files.
+* -e FSinfo Option:: Prefix for exports files.
+* -f FSinfo Option:: Prefix for fstab files.
+* -h FSinfo Option:: Local hostname.
+* -m FSinfo Option:: Prefix for automount maps.
+* -q FSinfo Option:: Ultra quiet mode.
+* -v FSinfo Option:: Verbose mode.
+* -I FSinfo Option:: Define new #include directory.
+* -D-FSinfo Option:: Define macro.
+* -U FSinfo Option:: Undefine macro.
+@end menu
+
+@node -a FSinfo Option, -b FSinfo Option, FSinfo Command Line Options, FSinfo Command Line Options
+@comment node-name, next, previous, up
+@subsection @code{-a} @var{autodir}
+
+Specifies the directory name in which to place the automounter's
+mountpoints. This defaults to @file{/a}. Some sites have the autodir set
+to be @file{/amd}, and this would be achieved by:
+
+@example
+fsinfo -a /amd ...
+@end example
+
+@node -b FSinfo Option, -d FSinfo Option, -a FSinfo Option, FSinfo Command Line Options
+@comment node-name, next, previous, up
+@subsection @code{-b} @var{bootparams}
+@cindex bootparams, FSinfo prefix
+
+This specifies the prefix for the @file{bootparams} filename. If it is
+not given, then the file will not be generated. The @file{bootparams}
+file will be constructed for the destination machine and will be placed
+into a file named @file{bootparams} and prefixed by this string. The
+file generated contains a list of entries describing each diskless
+client that can boot from the destination machine.
+
+As an example, to create a @file{bootparams} file in the directory
+@file{generic}, the following would be used:
+
+@example
+fsinfo -b generic/ ...
+@end example
+
+@node -d FSinfo Option, -e FSinfo Option, -b FSinfo Option, FSinfo Command Line Options
+@comment node-name, next, previous, up
+@subsection @code{-d} @var{dumpsets}
+@cindex dumpset, FSinfo prefix
+
+This specifies the prefix for the @file{dumpsets} file. If it is not
+specified, then the file will not be generated. The file will be for
+the destination machine and will be placed into a filename
+@file{dumpsets}, prefixed by this string. The @file{dumpsets} file is
+for use by Imperial College's local backup system.
+
+For example, to create a @file{dumpsets} file in the directory @file{generic},
+then you would use the following:
+
+@example
+fsinfo -d generic/ ...
+@end example
+
+@node -e FSinfo Option, -f FSinfo Option, -d FSinfo Option, FSinfo Command Line Options
+@comment node-name, next, previous, up
+@subsection @code{-e} @var{exportfs}
+@cindex exports, FSinfo prefix
+
+Defines the prefix for the @file{exports} files. If it is not given,
+then the file will not be generated. For each machine defined in the
+configuration files as having disks, an @file{exports} file is
+constructed and given a filename determined by the name of the machine,
+prefixed with this string. If a machine is defined as diskless, then no
+@file{exports} file will be created for it. The files contain entries
+for directories on the machine that may be exported to clients.
+
+Example: To create the @file{exports} files for each diskfull machine
+and place them into the directory @file{exports}:
+
+@example
+fsinfo -e exports/ ...
+@end example
+
+@node -f FSinfo Option, -h FSinfo Option, -e FSinfo Option, FSinfo Command Line Options
+@comment node-name, next, previous, up
+@subsection @code{-f} @var{fstab}
+@cindex fstab, FSinfo prefix
+
+This defines the prefix for the @file{fstab} files. The files will only
+be created if this prefix is defined. For each machine defined in the
+configuration files, a @file{fstab} file is created with the filename
+determined by prefixing this string with the name of the machine. These
+files contain entries for filesystems and partitions to mount at boot
+time.
+
+Example, to create the files in the directory @file{fstabs}:
+
+@example
+fsinfo -f fstabs/ ...
+@end example
+
+@node -h FSinfo Option, -m FSinfo Option, -f FSinfo Option, FSinfo Command Line Options
+@comment node-name, next, previous, up
+@subsection @code{-h} @var{hostname}
+@cindex hostname, FSinfo command line option
+
+Defines the hostname of the destination machine to process for. If this
+is not specified, it defaults to the local machine name, as returned by
+@b{gethostname}(2).
+
+Example:
+
+@example
+fsinfo -h dylan.doc.ic.ac.uk ...
+@end example
+
+@node -m FSinfo Option, -q FSinfo Option, -h FSinfo Option, FSinfo Command Line Options
+@comment node-name, next, previous, up
+@subsection @code{-m} @var{mount-maps}
+@cindex maps, FSinfo command line option
+
+Defines the prefix for the automounter files. The maps will only be
+produced if this prefix is defined. The mount maps suitable for the
+network defined by the configuration files will be placed into files
+with names calculated by prefixing this string to the name of each map.
+
+For example, to create the automounter maps and place them in the
+directory @file{automaps}:
+
+@example
+fsinfo -m automaps/ ...
+@end example
+
+@node -q FSinfo Option, -v FSinfo Option, -m FSinfo Option, FSinfo Command Line Options
+@comment node-name, next, previous, up
+@subsection @code{-q}
+@cindex quiet, FSinfo command line option
+
+Selects quiet mode. @i{FSinfo} suppress the ``running commentary'' and
+only outputs any error messages which are generated.
+
+@node -v FSinfo Option, -D-FSinfo Option, -q FSinfo Option, FSinfo Command Line Options
+@comment node-name, next, previous, up
+@subsection @code{-v}
+@cindex verbose, FSinfo command line option
+
+Selects verbose mode. When this is activated, the program will display
+more messages, and display all the information discovered when
+performing the semantic analysis phase. Each verbose message is output
+to @file{stdout} on a line starting with a @samp{#} character.
+
+@node -D-FSinfo Option, -I FSinfo Option, -v FSinfo Option, FSinfo Command Line Options
+@comment node-name, next, previous, up
+@subsection @code{-D} @var{name[=defn]}
+
+Defines a symbol @dfn{name} for the preprocessor when reading the
+configuration files. Equivalent to @code{#define} directive.
+
+@node -I FSinfo Option, -U FSinfo Option, -D-FSinfo Option, FSinfo Command Line Options
+@comment node-name, next, previous, up
+@subsection @code{-I} @var{directory}
+
+This option is passed into the preprocessor for the configuration files.
+It specifies directories in which to find include files
+
+@node -U FSinfo Option, , -I FSinfo Option, FSinfo Command Line Options
+@comment node-name, next, previous, up
+@subsection @code{-U} @var{name}
+
+Removes any initial definition of the symbol @dfn{name}. Inverse of the
+@code{-D} option.
+
+@node FSinfo errors, , FSinfo Command Line Options, FSinfo
+@comment node-name, next, previous, up
+@section Errors produced by @i{FSinfo}
+@cindex FSinfo error messages
+
+The following table documents the errors and warnings which @i{FSinfo} may produce.
+
+@table @t
+
+@item " expected
+Occurs if an unescaped newline is found in a quoted string.
+
+@item ambiguous mount: @var{volume} is a replicated filesystem
+If several filesystems are declared as having the same volume name, they
+will be considered replicated filesystems. To mount a replicated
+filesystem statically, a specific host will need to be named, to say
+which particular copy to try and mount, else this error will
+result.
+
+@item can't open @var{filename} for writing
+Occurs if any errors are encountered when opening an output file.
+
+@item cannot determine localname since volname @var{volume} is not uniquely defined
+If a volume is replicated and an attempt is made to mount the filesystem
+statically without specifying a local mountpoint, @i{FSinfo} cannot
+calculate a mountpoint, as the desired pathname would be
+ambiguous.
+
+@item @var{device} has duplicate exportfs data
+Produced if the @samp{exportfs} option is used multiple times within the
+same branch of a filesystem definition. For example, if you attempt to
+set the @samp{exportfs} data at different levels of the mountpoint
+directory tree.
+
+@item dump frequency for @var{host}:@var{device} is non-zero
+Occurs if @var{device} has its @samp{fstype} declared to be @samp{swap}
+or @samp{export} and the @samp{dump} option is set to a value greater
+than zero. Swap devices should not be dumped.
+
+@item duplicate host @var{hostname}!
+If a host has more than one definition.
+
+@item end of file within comment
+A comment was unterminated before the end of one of the configuration
+files.
+
+@item @var{filename}: cannot open for reading
+If a file specified on the command line as containing configuration data
+could not be opened.
+
+@item @var{filesystem} has a volname but no exportfs data
+Occurs when a volume name is declared for a file system, but the string
+specifying what machines the filesystem can be exported to is
+missing.
+
+@item fs field "@var{field-name}" already set
+Occurs when multiple definitions are given for one of the attributes of a
+host's filesystem.
+
+@item host field "@var{field-name}" already set
+If duplicate definitions are given for any of the fields with a host
+definition.
+
+@item @var{host}:@var{device} has more than one mount point
+Occurs if the mount option for a host's filesystem specifies multiple
+trees at which to place the mountpoint.
+
+@item @var{host}:@var{device} has no mount point
+Occurs if the @samp{mount} option is not specified for a host's
+filesystem.
+
+@item @var{host}:@var{device} needs field "@var{field-name}"
+Occurs when a filesystem is missing a required field. @var{field-name} could
+be one of @samp{fstype}, @samp{opts}, @samp{passno} or
+@samp{mount}.
+
+@item @var{host}:mount field specified for swap partition
+Occurs if a mountpoint is given for a filesystem whose type is declared
+to be @samp{swap}.
+
+@item malformed IP dotted quad: @var{address}
+If the Internet address of an interface is incorrectly specified. An
+Internet address definition is handled to @b{inet_addr}(3N) to see if it
+can cope. If not, then this message will be displayed.
+
+@item malformed netmask: @var{netmask}
+If the netmask cannot be decoded as though it were a hexadecimal number,
+then this message will be displayed. It will typically be caused by
+incorrect characters in the @var{netmask} value.
+
+@item mount field "@var{field-name}" already set
+Occurs when a static mount has multiple definitions of the same field.
+
+@item mount tree field "@var{field-name}" already set
+Occurs when the @var{field-name} is defined more than once during the
+definition of a filesystems mountpoint.
+
+@item netif field @var{field-name} already set
+Occurs if you attempt to define an attribute of an interface more than
+once.
+
+@item network booting requires both root and swap areas
+Occurs if a machine has mount declarations for either the root partition
+or the swap area, but not both. You cannot define a machine to only
+partially boot via the network.
+
+@item no disk mounts on @var{hostname}
+If there are no static mounts, nor local disk mounts specified for a
+machine, this message will be displayed.
+
+@item no volname given for @var{host}:@var{device}
+Occurs when a filesystem is defined to be mounted on @file{default}, but
+no volume name is given for the file system, then the mountpoint cannot
+be determined.
+
+@item not allowed '/' in a directory name
+Occurs when a pathname with multiple directory elements is specified as
+the name for an automounter tree. A tree should only have one name at
+each level.
+
+@item pass number for @var{host}:@var{device} is non-zero
+Occurs if @var{device} has its @samp{fstype} declared to be @samp{swap}
+or @samp{export} and the @b{fsck}(8) pass number is set. Swap devices should not be
+fsck'd. @xref{FSinfo filesystems fstype}.
+
+@item sub-directory @var{directory} of @var{directory-tree} starts with '/'
+Within the filesystem specification for a host, if an element
+@var{directory} of the mountpoint begins with a @samp{/} and it is not
+the start of the tree.
+
+@item sub-directory of @var{directory-tree} is named "default"
+@samp{default} is a keyword used to specify if a mountpoint should be
+automatically calculated by @i{FSinfo}. If you attempt to specify a
+directory name as this, it will use the filename of @file{default} but
+will produce this warning.
+
+@item unknown \ sequence
+Occurs if an unknown escape sequence is found inside a string. Within a
+string, you can give the standard C escape sequences for strings, such
+as newlines and tab characters.
+
+@item unknown directory attribute
+If an unknown keyword is found while reading the definition of a host's
+filesystem mount option.
+
+@item unknown filesystem attribute
+Occurs if an unrecognized keyword is used when defining a host's
+filesystems.
+
+@item unknown host attribute
+Occurs if an unrecognized keyword is used when defining a host.
+
+@item unknown mount attribute
+Occurs if an unrecognized keyword is found while parsing the list of
+static mounts.
+
+@item unknown volname @var{volume} automounted @i{[} on @i{name} @i{]}
+Occurs if @var{volume} is used in a definition of an automount map but the volume
+name has not been declared during the host filesystem definitions.
+
+@item volname @var{volume} is unknown
+Occurs if an attempt is made to mount or reference a volume name which
+has not been declared during the host filesystem definitions.
+
+@item volname @var{volume} not exported from @var{machine}
+Occurs if you attempt to mount the volume @var{volume} from a machine
+which has not declared itself to have such a filesystem
+available.
+
+@end table
+
+@c ################################################################
+@node Hlfsd, Assorted Tools, FSinfo, Top
+@comment node-name, next, previous, up
+@chapter Hlfsd
+@pindex Hlfsd
+@cindex Home-Link Filesystem
+
+@i{Hlfsd} is a daemon which implements a filesystem containing a
+symbolic link to subdirectory within a user's home directory, depending
+on the user which accessed that link. It was primarily designed to
+redirect incoming mail to users' home directories, so that it can be read
+from anywhere. It was designed and implemented by
+@email{ezk@@cs.columbia.edu,Erez Zadok} and
+@email{dupuy@@cs.columbia.edu,Alexander Dupuy}, at the
+@uref{http://www.cs.columbia.edu/,Computer Science Department} of
+@uref{http://www.columbia.edu/,Columbia University}. A
+@uref{http://www.cs.columbia.edu/~ezk/research/hlfsd/hlfsd.html,paper}
+on @i{Hlfsd} was presented at the Usenix LISA VII conference in 1993.
+
+@i{Hlfsd} operates by mounting itself as an NFS server for the directory
+containing @i{linkname}, which defaults to @file{/hlfs/home}. Lookups
+within that directory are handled by @i{Hlfsd}, which uses the
+password map to determine how to resolve the lookup. The directory will
+be created if it doesn't already exist. The symbolic link will be to
+the accessing user's home directory, with @i{subdir} appended to it. If
+not specified, @i{subdir} defaults to @file{.hlfsdir}. This directory
+will also be created if it does not already exist.
+
+A @samp{SIGTERM} sent to @i{Hlfsd} will cause it to shutdown. A @samp{SIGHUP} will
+flush the internal caches, and reload the password map. It will also
+close and reopen the log file, to enable the original log file to be
+removed or rotated. A @samp{SIGUSR1} will cause it to dump its internal table
+of user IDs and home directories to the file @file{/tmp/hlfsddump}.
+
+@menu
+* Introduction to Hlfsd::
+* Background to Mail Delivery::
+* Using Hlfsd::
+@end menu
+
+@c ================================================================
+@node Introduction to Hlfsd, Background to Mail Delivery, Hlfsd, Hlfsd
+@comment node-name, next, previous, up
+@section Introduction to Hlfsd
+@cindex Introduction to Hlfsd
+@cindex Hlfsd; introduction
+
+Electronic mail has become one of the major applications for many
+computer networks, and use of this service is expected to increase over
+time, as networks proliferate and become faster. Providing a convenient
+environment for users to read, compose, and send electronic mail has
+become a requirement for systems administrators (SAs).
+
+Widely used methods for handling mail usually require users to be logged
+into a designated ``home'' machine, where their mailbox files reside.
+Only on that one machine can they read newly arrived mail. Since users
+have to be logged into that system to read their mail, they often find
+it convenient to run all of their other processes on that system as
+well, including memory and CPU-intensive jobs. For example, in our
+department, we have allocated and configured several multi-processor
+servers to handle such demanding CPU/memory applications, but these were
+underutilized, in large part due to the inconvenience of not being able
+to read mail on those machines. (No home directories were located on
+these designated CPU-servers, since we did not want NFS service for
+users' home directories to have to compete with CPU-intensive jobs. At the
+same time, we discouraged users from running demanding applications on
+their home machines.)
+
+Many different solutions have been proposed to allow users to read their
+mail on any host. However, all of these solutions fail in one or more
+of several ways:
+
+@itemize @bullet
+
+@item
+they introduce new single points of failure
+
+@item
+they require using different mail transfer agents (MTAs) or user agents
+(UAs)
+
+@item
+they do not solve the problem for all cases, i.e. the solution is only
+partially successful for a particular environment.
+
+@end itemize
+
+We have designed a simple filesystem, called the @dfn{Home-Link File
+System}, to provide the ability to deliver mail to users' home
+directories, without modification to mail-related applications. We have
+endeavored to make it as stable as possible. Of great importance to us
+was to make sure the HLFS daemon, @file{hlfsd} , would not hang under
+any circumstances, and would take the next-best action when faced with
+problems. Compared to alternative methods, @i{Hlfsd} is a stable, more
+general solution, and easier to install/use. In fact, in some ways, we
+have even managed to improve the reliability and security of mail
+service.
+
+Our server implements a small filesystem containing a symbolic link
+to a subdirectory of the invoking user's home directory, and named symbolic
+links to users' mailbox files.
+
+The @i{Hlfsd} server finds out the @var{uid} of the process that is
+accessing its mount point, and resolves the pathname component @samp{home} as a
+symbolic link to a subdirectory within the home directory given by the
+@var{uid}'s entry in the password file. If the @var{gid} of the process
+that attempts to access a mailbox file is a special one (called
+HLFS_GID), then the server maps the name of the @emph{next} pathname
+component directly to the user's mailbox. This is necessary so that
+access to a mailbox file by users other than the owner can succeed. The
+server has safety features in case of failures such as hung filesystems
+or home directory filesystems that are inaccessible or full.
+
+On most of our machines, mail gets delivered to the directory
+@file{/var/spool/mail}. Many programs, including UAs, depend on that
+path. @i{Hlfsd} creates a directory @file{/mail}, and mounts itself on
+top of that directory. @i{Hlfsd} implements the path name component
+called @samp{home}, pointing to a subdirectory of the user's home directory.
+We have made @file{/var/spool/mail} a symbolic link to
+@file{/mail/home}, so that accessing @file{/var/spool/mail} actually
+causes access to a subdirectory within a user's home directory.
+
+The following table shows an example of how resolving the pathname
+@file{/var/mail/@i{NAME}} to @file{/users/ezk/.mailspool/@i{NAME}} proceeds.
+
+@multitable {Resolving Component} {Pathname left to resolve} {Value if symbolic link}
+
+@item @b{Resolving Component}
+@tab @b{Pathname left to resolve}
+@tab @b{Value if symbolic link}
+
+@item @t{/}
+@tab @t{var/mail/}@i{NAME}
+
+@item @t{var/}
+@tab @t{mail/}@i{NAME}
+
+@item @t{mail}@@
+@tab @t{/mail/home/}@i{NAME}
+@tab @t{mail}@@ -> @t{/mail/home}
+
+@item @t{/}
+@tab @t{mail/home/}@i{NAME}
+
+@item @t{mail/}
+@tab @t{home/}@i{NAME}
+
+@item @t{home}@@
+@tab @i{NAME}
+@tab @t{home}@@ -> @t{/users/ezk/.mailspool}
+
+@item @t{/}
+@tab @t{users/ezk/.mailspool/}@i{NAME}
+
+@item @t{users/}
+@tab @t{ezk/.mailspool/}@i{NAME}
+
+@item @t{ezk/}
+@tab @t{.mailspool/}@i{NAME}
+
+@item @t{.mailspool/}
+@tab @i{NAME}
+
+@item @i{NAME}
+
+@end multitable
+
+@c ================================================================
+@node Background to Mail Delivery, Using Hlfsd, Introduction to Hlfsd, Hlfsd
+@comment node-name, next, previous, up
+@section Background to Mail Delivery
+@cindex Background to Mail Delivery
+@cindex Hlfsd; background
+
+This section provides an in-depth discussion of why available methods
+for delivering mail to home directories are not as good as the one used
+by @i{Hlfsd}.
+
+@menu
+* Single-Host Mail Spool Directory::
+* Centralized Mail Spool Directory::
+* Distributed Mail Spool Service::
+* Why Deliver Into the Home Directory?::
+@end menu
+
+@c ----------------------------------------------------------------
+@node Single-Host Mail Spool Directory, Centralized Mail Spool Directory, Background to Mail Delivery, Background to Mail Delivery
+@comment node-name, next, previous, up
+@subsection Single-Host Mail Spool Directory
+@cindex Single-Host Mail Spool Directory
+
+The most common method for mail delivery is for mail to be appended to a
+mailbox file in a standard spool directory on the designated ``mail
+home'' machine of the user. The greatest advantage of this method is
+that it is the default method most vendors provide with their systems,
+thus very little (if any) configuration is required on the SA's part.
+All they need to set up are mail aliases directing mail to the host on
+which the user's mailbox file is assigned. (Otherwise, mail is
+delivered locally, and users find mailboxes on many machines.)
+
+As users become more sophisticated, and aided by windowing systems, they
+find themselves logging in on multiple hosts at once, performing several
+tasks concurrently. They ask to be able to read their mail on any host
+on the network, not just the one designated as their ``mail home''.
+
+@c ----------------------------------------------------------------
+@node Centralized Mail Spool Directory, Distributed Mail Spool Service, Single-Host Mail Spool Directory, Background to Mail Delivery
+@comment node-name, next, previous, up
+@subsection Centralized Mail Spool Directory
+@cindex Centralized Mail Spool Directory
+
+A popular method for providing mail readability from any host is to have
+all mail delivered to a mail spool directory on a designated
+``mail-server'' which is exported via NFS to all of the hosts on the
+network. Configuring such a system is relatively easy. On most
+systems, the bulk of the work is a one-time addition to one or two
+configuration files in @file{/etc}. The file-server's spool directory
+is then hard-mounted across every machine on the local network. In
+small environments with only a handful of hosts this can be an
+acceptable solution. In our department, with a couple of hundred active
+hosts and thousands of mail messages processed daily, this was deemed
+completely unacceptable, as it introduced several types of problems:
+
+@table @b
+
+@item Scalability and Performance
+
+As more and more machines get added to the network, more mail traffic
+has to go over NFS to and from the mail-server. Users like to run
+mail-watchers, and read their mail often. The stress on the shared
+infrastructure increases with every user and host added; loads on the
+mail server would most certainly be high since all mail delivery goes
+through that one machine.@footnote{ Delivery via NFS-mounted filesystems
+may require usage of @samp{rpc.lockd} and @samp{rpc.statd} to provide
+distributed file-locking, both of which are widely regarded as unstable
+and unreliable. Furthermore, this will degrade performance, as local
+processes as well as remote @samp{nfsd} processes are kept busy.} This
+leads to lower reliability and performance. To reduce the number of
+concurrent connections between clients and the server host, some SAs
+have resorted to automounting the mail-spool directory. But this
+solution only makes things worse: since users often run mail watchers,
+and many popular applications such as @samp{trn}, @samp{emacs},
+@samp{csh} or @samp{ksh} check periodically for new mail, the
+automounted directory would be effectively permanently mounted. If it
+gets unmounted automatically by the automounter program, it is most
+likely to get mounted shortly afterwards, consuming more I/O resources
+by the constant cycle of mount and umount calls.
+
+@item Reliability
+
+The mail-server host and its network connectivity must be very reliable.
+Worse, since the spool directory has to be hard-mounted,@footnote{No SA
+in their right minds would soft-mount read/write partitions --- the
+chances for data loss are too great.} many processes which access the
+spool directory (various shells, @samp{login}, @samp{emacs}, etc.)
+would be hung as long as connectivity to the mail-server is severed. To
+improve reliability, SAs may choose to backup the mail-server's spool
+partition several times a day. This may make things worse since reading
+or delivering mail while backups are in progress may cause backups to be
+inconsistent; more backups consume more backup-media resources, and
+increase the load on the mail-server host.
+
+@end table
+
+@c ----------------------------------------------------------------
+@node Distributed Mail Spool Service, Why Deliver Into the Home Directory?, Centralized Mail Spool Directory, Background to Mail Delivery
+@comment node-name, next, previous, up
+@subsection Distributed Mail Spool Service
+@cindex Distributed Mail Spool Service
+
+Despite the existence of a few systems that support delivery to users'
+home directories, mail delivery to home directories hasn't caught on.
+We believe the main reason is that there are too many programs that
+``know'' where mailbox files reside. Besides the obvious (the delivery
+program @file{/bin/mail} and mail readers like @file{/usr/ucb/Mail},
+@samp{mush}, @samp{mm}, etc.), other programs that know mailbox location
+are login, from, almost every shell, @samp{xbiff}, @samp{xmailbox}, and
+even some programs not directly related to mail, such as @samp{emacs}
+and @samp{trn}. Although some of these programs can be configured to
+look in different directories with the use of environment variables and
+other resources, many of them cannot. The overall porting work is
+significant.
+
+Other methods that have yet to catch on require the use of a special
+mail-reading server, such as IMAP or POP. The main disadvantage of
+these systems is that UAs need to be modified to use these services ---
+a long and involved task. That is why they are not popular at this
+time.
+
+Several other ideas have been proposed and even used in various
+environments. None of them is robust. They are mostly very
+specialized, inflexible, and do not extend to the general case. Some of
+the ideas are plain bad, potentially leading to lost or corrupt mail:
+
+@table @b
+
+@item automounters
+
+Using an automounter such as @i{Amd} to provide a set of symbolic links
+from the normal spool directory to user home directories is not
+sufficient. UAs rename, unlink, and recreate the mailbox as a regular
+file, therefore it must be a real file, not a symbolic link.
+Furthermore, it must reside in a real directory which is writable by the
+UAs and MTAs. This method may also require populating
+@file{/var/spool/mail} with symbolic links and making sure they are
+updated. Making @i{Amd} manage that directory directly fails, since
+many various lock files need to be managed as well. Also, @i{Amd} does
+not provide all of the NFS operations which are required to write mail
+such as write, create, remove, and unlink.
+
+@item @code{$MAIL}
+
+Setting this variable to an automounted directory pointing to the user's
+mail spool host only solves the problem for those programs which know
+and use @code{$MAIL}. Many programs don't, therefore this solution is partial
+and of limited flexibility. Also, it requires the SAs or the users to
+set it themselves --- an added level of inconvenience and possible
+failures.
+
+@item @t{/bin/mail}
+
+Using a different mail delivery agent could be the solution. One such
+example is @samp{hdmail}. However, @samp{hdmail} still requires
+modifying all UAs, the MTA's configuration, installing new daemons, and
+changing login scripts. This makes the system less upgradable or
+compatible with others, and adds one more complicated system for SAs to
+deal with. It is not a complete solution because it still requires each
+user have their @code{$MAIL} variable setup correctly, and that every program
+use this variable.
+
+@end table
+
+@c ----------------------------------------------------------------
+@node Why Deliver Into the Home Directory?, , Distributed Mail Spool Service, Background to Mail Delivery
+@comment node-name, next, previous, up
+@subsection Why Deliver Into the Home Directory?
+@cindex Why Deliver Into the Home Directory?
+@cindex Hlfsd; Why Deliver Into the Home Directory?
+
+There are several major reasons why SAs might want to deliver mail
+directly into the users' home directories:
+
+@table @b
+
+@item Location
+
+Many mail readers need to move mail from the spool directory to the
+user's home directory. It speeds up this operation if the two are on
+the same filesystem. If for some reason the user's home directory is
+inaccessible, it isn't that useful to be able to read mail, since there
+is no place to move it to. In some cases, trying to move mail to a
+non-existent or hung filesystem may result in mail loss.
+
+@item Distribution
+
+Having all mail spool directories spread among the many more filesystems
+minimizes the chances that complete environments will grind to a halt
+when a single server is down. It does increase the chance that there
+will be someone who is not able to read their mail when a machine is
+down, but that is usually preferred to having no one be able to read
+their mail because a centralized mail server is down. The problem of
+losing some mail due to the (presumably) higher chances that a user's
+machine is down is minimized in HLFS.
+
+@item Security
+
+Delivering mail to users' home directories has another advantage ---
+enhanced security and privacy. Since a shared system mail spool
+directory has to be world-readable and searchable, any user can see
+whether other users have mail, when they last received new mail, or when
+they last read their mail. Programs such as @samp{finger} display this
+information, which some consider an infringement of privacy. While it
+is possible to disable this feature of @samp{finger} so that remote
+users cannot see a mailbox file's status, this doesn't prevent local
+users from getting the information. Furthermore, there are more
+programs which make use of this information. In shared environments,
+disabling such programs has to be done on a system-wide basis, but with
+mail delivered to users' home directories, users less concerned with
+privacy who do want to let others know when they last received or read
+mail can easily do so using file protection bits.
+
+@c Lastly, on systems that do not export their NFS filesystem with
+@c @t{anon=0}, superusers are less likely to snoop around others' mail, as
+@c they become ``nobodies'' across NFS.
+
+@end table
+
+In summary, delivering mail to home directories provides users the
+functionality sought, and also avoids most of the problems just
+discussed.
+
+@c ================================================================
+@node Using Hlfsd, , Background to Mail Delivery, Hlfsd
+@comment node-name, next, previous, up
+@section Using Hlfsd
+@cindex Using Hlfsd
+@cindex Hlfsd; using
+
+@menu
+* Controlling Hlfsd::
+* Hlfsd Options::
+* Hlfsd Files::
+@end menu
+
+@c ----------------------------------------------------------------
+@node Controlling Hlfsd, Hlfsd Options, Using Hlfsd, Using Hlfsd
+@comment node-name, next, previous, up
+@subsection Controlling Hlfsd
+@cindex Controlling Hlfsd
+@cindex Hlfsd; controlling
+@pindex ctl-hlfsd
+
+Much the same way @i{Amd} is controlled by @file{ctl-amd}, so does
+@i{Hlfsd} get controlled by the @file{ctl-hlfsd} script:
+
+@table @t
+
+@item ctl-hlfsd start
+Start a new @i{Hlfsd}.
+
+@item ctl-hlfsd stop
+Stop a running @i{Hlfsd}.
+
+@item ctl-hlfsd restart
+Stop a running @i{Hlfsd}, wait for 10 seconds, and then start a new
+one. It is hoped that within 10 seconds, the previously running
+@i{Hlfsd} terminate properly; otherwise, starting a second one could
+cause system lockup.
+
+@end table
+
+For example, on our systems, we start @i{Hlfsd} within @file{ctl-hlfsd}
+as follows on Solaris 2 systems:
+
+@example
+hlfsd -a /var/alt_mail -x all -l /var/log/hlfsd /mail/home .mailspool
+@end example
+
+The directory @file{/var/alt_mail} is a directory in the root partition
+where alternate mail will be delivered into, when it cannot be delivered
+into the user's home directory.
+
+Normal mail gets delivered into @file{/var/mail}, but on our systems,
+that is a symbolic link to @file{/mail/home}. @file{/mail} is managed
+by @i{Hlfsd}, which creates a dynamic symlink named @samp{home},
+pointing to the subdirectory @file{.mailspool} @emph{within} the
+accessing user's home directory. This results in mail which normally
+should go to @file{/var/mail/@code{$USER}}, to go to
+@file{@code{$HOME}/.mailspool/@code{$USER}}.
+
+@i{Hlfsd} does not create the @file{/var/mail} symlink. This needs to
+be created (manually) once on each host, by the system administrators,
+as follows:
+
+@example
+mv /var/mail /var/alt_mail
+ln -s /mail/home /var/mail
+@end example
+
+@c ----------------------------------------------------------------
+@node Hlfsd Options, Hlfsd Files, Controlling Hlfsd, Using Hlfsd
+@comment node-name, next, previous, up
+@subsection Hlfsd Options
+@cindex Hlfsd Options
+@cindex Hlfsd; Options
+
+@table @t
+
+@item -a @var{alt_dir}
+Alternate directory. The name of the directory to which the symbolic
+link returned by @i{Hlfsd} will point, if it cannot access the home
+directory of the user. This defaults to @file{/var/hlfs}. This
+directory will be created if it doesn't exist. It is expected that
+either users will read these files, or the system administrators will
+run a script to resend this ``lost mail'' to its owner.
+
+@item -c @var{cache-interval}
+Caching interval. @i{Hlfsd} will cache the validity of home directories
+for this interval, in seconds. Entries which have been verified within
+the last @var{cache-interval} seconds will not be verified again, since
+the operation could be expensive, and the entries are most likely still
+valid. After the interval has expired, @i{Hlfsd} will re-verify the
+validity of the user's home directory, and reset the cache time-counter.
+The default value for @var{cache-interval} is 300 seconds (5 minutes).
+
+@item -f
+Force fast startup. This option tells @i{Hlfsd} to skip startup-time
+consistency checks such as existence of mount directory, alternate spool
+directory, symlink to be hidden under the mount directory, their
+permissions and validity.
+
+@item -g @var{group}
+Set the special group HLFS_GID to @var{group}. Programs such as
+@file{/usr/ucb/from} or @file{/usr/sbin/in.comsat}, which access the
+mailboxes of other users, must be setgid @samp{HLFS_GID} to work properly. The
+default group is @samp{hlfs}. If no group is provided, and there is no
+group @samp{hlfs}, this feature is disabled.
+
+@item -h
+Help. Print a brief help message, and exit.
+
+@item -i @var{reload-interval}
+Map-reloading interval. Each @var{reload-interval} seconds, @i{Hlfsd}
+will reload the password map. @i{Hlfsd} needs the password map for the
+UIDs and home directory pathnames. @i{Hlfsd} schedules a @samp{SIGALRM} to
+reload the password maps. A @samp{SIGHUP} sent to @i{Hlfsd} will force it to
+reload the maps immediately. The default value for
+@var{reload-interval} is 900 seconds (15 minutes.)
+
+@item -l @var{logfile}
+Specify a log file to which @i{Hlfsd} will record events. If
+@var{logfile} is the string @samp{syslog} then the log messages will be
+sent to the system log daemon by @b{syslog}(3), using the @samp{LOG_DAEMON}
+facility. This is also the default.
+
+@item -n
+No verify. @i{Hlfsd} will not verify the validity of the symbolic link
+it will be returning, or that the user's home directory contains
+sufficient disk-space for spooling. This can speed up @i{Hlfsd} at the
+cost of possibly returning symbolic links to home directories which are
+not currently accessible or are full. By default, @i{Hlfsd} validates
+the symbolic-link in the background. The @code{-n} option overrides the
+meaning of the @code{-c} option, since no caching is necessary.
+
+@item -o @var{mount-options}
+Mount options which @i{Hlfsd} will use to mount itself on top of
+@var{dirname}. By default, @var{mount-options} is set to @samp{ro}. If
+the system supports symbolic-link caching, default options are set
+to @samp{ro,nocache}.
+
+@item -p
+Print PID. Outputs the process-id of @i{Hlfsd} to standard output where
+it can be saved into a file.
+
+@item -v
+Version. Displays version information to standard error.
+
+@item -x @var{log-options}
+Specify run-time logging options. The options are a comma separated
+list chosen from: @samp{fatal}, @samp{error}, @samp{user}, @samp{warn}, @samp{info}, @samp{map}, @samp{stats}, @samp{all}.
+
+@item -C
+Force @i{Hlfsd} to run on systems that cannot turn off the NFS
+attribute-cache. Use of this option on those systems is discouraged, as
+it may result in loss or misdelivery of mail. The option is ignored on
+systems that can turn off the attribute-cache.
+
+@item -D @var{log-options}
+Select from a variety of debugging options. Prefixing an option with
+the string @samp{no} reverses the effect of that option. Options are
+cumulative. The most useful option is @samp{all}. Since this option is
+only used for debugging other options are not documented here. A fuller
+description is available in the program source. A @samp{SIGUSR1} sent
+to @i{Hlfsd} will cause it to dump its internal password map to the file
+@file{/usr/tmp/hlfsd.dump.XXXXXX}, where @samp{XXXXXX} will be replaced
+by a random string generated by @b{mktemp}(3) or (the more secure)
+@b{mkstemp}(3).
+
+@item -P @var{password-file}
+Read the user-name, user-id, and home directory information from the
+file @var{password-file}. Normally, @i{Hlfsd} will use @b{getpwent}(3)
+to read the password database. This option allows you to override the
+default database, and is useful if you want to map users' mail files to
+a directory other than their home directory. Only the username, uid,
+and home-directory fields of the file @var{password-file} are read and
+checked. All other fields are ignored. The file @var{password-file}
+must otherwise be compliant with Unix Version 7 colon-delimited format
+@b{passwd}(4).
+
+@end table
+
+@c ----------------------------------------------------------------
+@node Hlfsd Files, , Hlfsd Options, Using Hlfsd
+@comment node-name, next, previous, up
+@subsection Hlfsd Files
+@cindex Hlfsd Files
+@cindex Hlfsd; Files
+
+The following files are used by @i{Hlfsd}:
+
+@table @file
+
+@item /hlfs
+directory under which @i{Hlfsd} mounts itself and manages the symbolic
+link @file{home}.
+
+@item .hlfsdir
+default sub-directory in the user's home directory, to which the
+@file{home} symbolic link returned by @i{Hlfsd} points.
+
+@item /var/hlfs
+directory to which @file{home} symbolic link returned by @i{Hlfsd}
+points if it is unable to verify the that user's home directory is
+accessible.
+
+@end table
+
+For discussion on other files used by @i{Hlfsd}, see @ref{lostaltmail} and
+@ref{lostaltmail.conf-sample}.
+
+@c ################################################################
+@node Assorted Tools, Examples, Hlfsd, Top
+@comment node-name, next, previous, up
+@chapter Assorted Tools
+@cindex Assorted Tools
+
+The following are additional utilities and scripts included with
+am-utils, and get installed.
+
+@menu
+* am-eject::
+* amd.conf-sample::
+* amd2ldif::
+* amd2sun::
+* ctl-amd::
+* ctl-hlfsd::
+* expn::
+* fix-amd-map::
+* fixmount::
+* fixrmtab::
+* lostaltmail::
+* lostaltmail.conf-sample::
+* mk-amd-map::
+* pawd::
+* wait4amd::
+* wait4amd2die::
+* wire-test::
+@end menu
+
+@c ----------------------------------------------------------------
+@node am-eject, amd.conf-sample, Assorted Tools, Assorted Tools
+@comment node-name, next, previous, up
+@section am-eject
+@pindex am-eject
+
+A shell script unmounts a floppy or CD-ROM that is automounted, and
+then attempts to eject the removable device.
+
+@c ----------------------------------------------------------------
+@node amd.conf-sample, amd2ldif, am-eject, Assorted Tools
+@comment node-name, next, previous, up
+@section amd.conf-sample
+@pindex amd.conf-sample
+
+A sample @i{Amd} configuration file. @xref{Amd Configuration File}.
+
+@c ----------------------------------------------------------------
+@node amd2ldif, amd2sun, amd.conf-sample, Assorted Tools
+@comment node-name, next, previous, up
+@section amd2ldif
+@pindex amd2ldif
+
+A script to convert @i{Amd} maps to LDAP input files. Use it as follows:
+
+@example
+amd2ldif @i{mapname} @i{base} < @i{amd.mapfile} > @i{mapfile.ldif}
+@end example
+
+@c ----------------------------------------------------------------
+@node amd2sun, ctl-amd, amd2ldif, Assorted Tools
+@comment node-name, next, previous, up
+@section amd2sun
+@pindex amd2sun
+
+A script to convert @i{Amd} maps to Sun Automounter maps. Use it as
+follows
+
+@example
+amd2sun < @i{amd.mapfile} > @i{auto_mapfile}
+@end example
+
+@c ----------------------------------------------------------------
+@node ctl-amd, ctl-hlfsd, amd2sun, Assorted Tools
+@comment node-name, next, previous, up
+@section ctl-amd
+@pindex ctl-amd
+
+A script to start, stop, or restart @i{Amd}. Use it as follows:
+
+@table @t
+@item ctl-amd start
+Start a new @i{Amd} process.
+@item ctl-amd stop
+Stop the running @i{Amd}.
+@item ctl-amd restart
+Stop the running @i{Amd} (if any), safely wait for it to terminate, and
+then start a new process --- only if the previous one died cleanly.
+@end table
+
+@xref{Run-time Administration} for more details.
+
+@c ----------------------------------------------------------------
+@node ctl-hlfsd, expn, ctl-amd, Assorted Tools
+@comment node-name, next, previous, up
+@section ctl-hlfsd
+@pindex ctl-hlfsd
+
+A script for controlling @i{Hlfsd}, much the same way @file{ctl-amd}
+controls @i{Amd}. Use it as follows:
+
+@table @t
+@item ctl-hlfsd start
+Start a new @i{Hlfsd} process.
+@item ctl-hlfsd stop
+Stop the running @i{Hlfsd}.
+@item ctl-hlfsd restart
+Stop the running @i{Hlfsd} (if any), wait for 10 seconds for it to
+terminate, and then start a new process --- only if the previous one
+died cleanly.
+@end table
+
+@xref{Hlfsd} for more details.
+
+@c ----------------------------------------------------------------
+@node expn, fix-amd-map, ctl-hlfsd, Assorted Tools
+@comment node-name, next, previous, up
+@section expn
+@pindex expn
+
+A script to expand email addresses into their full name. It is
+generally useful when using with the @file{lostaltmail} script, but is a
+useful tools otherwise.
+
+@example
+$ expn -v ezk@@cs.columbia.edu
+ezk@@cs.columbia.edu ->
+ ezk@@shekel.mcl.cs.columbia.edu
+ezk@@shekel.mcl.cs.columbia.edu ->
+ Erez Zadok <"| /usr/local/mh/lib/slocal -user ezk || exit 75>
+ Erez Zadok <\ezk>
+ Erez Zadok </u/zing/ezk/.mailspool/backup>
+@end example
+
+@c ----------------------------------------------------------------
+@node fix-amd-map, fixmount, expn, Assorted Tools
+@comment node-name, next, previous, up
+@section fix-amd-map
+@pindex fix-amd-map
+
+Am-utils changed some of the syntax and default values of some
+variables. For example, the default value for @samp{$@{os@}} for
+Solaris 2.x (aka SunOS 5.x) systems used to be @samp{sos5}, it is now
+more automatically generated from @file{config.guess} and its value is
+@samp{sunos5}.
+
+This script converts older @i{Amd} maps to new ones. Use it as follows:
+
+@example
+fix-amd-map < @i{old.map} > @i{new.map}
+@end example
+
+@c ----------------------------------------------------------------
+@node fixmount, fixrmtab, fix-amd-map, Assorted Tools
+@comment node-name, next, previous, up
+@section fixmount
+@pindex fixmount
+
+@samp{fixmount} is a variant of @b{showmount}(8) that can delete bogus
+mount entries in remote @b{mountd}(8) daemons. This is useful to
+cleanup otherwise ever-accumulating ``junk''. Use it for example:
+
+@example
+fixmount -r @i{host}
+@end example
+
+See the online manual page for @samp{fixmount} for more details of its
+usage.
+
+@c ----------------------------------------------------------------
+@node fixrmtab, lostaltmail, fixmount, Assorted Tools
+@comment node-name, next, previous, up
+@section fixrmtab
+@pindex fixrmtab
+
+A script to invalidate @file{/etc/rmtab} entries for hosts named. Also
+restart mountd for changes to take effect. Use it for example:
+
+@example
+fixrmtab @i{host1} @i{host2} @i{...}
+@end example
+
+@c ----------------------------------------------------------------
+@node lostaltmail, lostaltmail.conf-sample, fixrmtab, Assorted Tools
+@comment node-name, next, previous, up
+@section lostaltmail
+@pindex lostaltmail
+
+A script used with @i{Hlfsd} to resend any ``lost'' mail. @i{Hlfsd}
+redirects mail which cannot be written into the user's home directory to
+an alternate directory. This is useful to continue delivering mail,
+even if the user's file system was unavailable, full, or over quota.
+But, the mail which gets delivered to the alternate directory needs to
+be resent to its respective users. This is what the @samp{lostaltmail}
+script does.
+
+Use it as follows:
+
+@example
+lostaltmail
+@end example
+
+This script needs a configuration file @samp{lostaltmail.conf} set up
+with the right parameters to properly work. @xref{Hlfsd} for more
+details.
+
+@c ----------------------------------------------------------------
+@node lostaltmail.conf-sample, mk-amd-map, lostaltmail, Assorted Tools
+@comment node-name, next, previous, up
+@section lostaltmail.conf-sample
+@pindex lostaltmail.conf-sample
+@cindex lostaltmail; configuration file
+
+This is a text file with configuration parameters needed for the
+@samp{lostaltmail} script. The script includes comments explaining each
+of the configuration variables. See it for more information. Also
+@pxref{Hlfsd} for general information.
+
+@c ----------------------------------------------------------------
+@node mk-amd-map, pawd, lostaltmail.conf-sample, Assorted Tools
+@comment node-name, next, previous, up
+@section mk-amd-map
+@pindex mk-amd-map
+
+This program converts a normal @i{Amd} map file into an ndbm database
+with the same prefix as the named file. Use it as follows:
+
+@example
+mk-amd-map @i{mapname}
+@end example
+
+@c ----------------------------------------------------------------
+@node pawd, wait4amd, mk-amd-map, Assorted Tools
+@comment node-name, next, previous, up
+@section pawd
+@pindex pawd
+
+@i{Pawd} is used to print the current working directory, adjusted to
+reflect proper paths that can be reused to go through the automounter
+for the shortest possible path. In particular, the path printed back
+does not include any of @i{Amd}'s local mount points. Using them is
+unsafe, because @i{Amd} may unmount managed file systems from the mount
+points, and thus including them in paths may not always find the files
+within.
+
+Without any arguments, @i{Pawd} will print the automounter adjusted
+current working directory. With any number of arguments, it will print
+the adjusted path of each one of the arguments.
+
+@c ----------------------------------------------------------------
+@node wait4amd, wait4amd2die, pawd, Assorted Tools
+@comment node-name, next, previous, up
+@section wait4amd
+@pindex wait4amd
+
+A script to wait for @i{Amd} to start on a particular host before
+performing an arbitrary command. The command is executed repeatedly,
+with 1 second intervals in between. You may interrupt the script using
+@samp{^C} (or whatever keyboard sequence your terminal's @samp{intr} function
+is bound to).
+
+Examples:
+
+@table @t
+@item wait4amd saturn amq -p -h saturn
+When @i{Amd} is up on host @samp{saturn}, get the process ID of that
+running @i{Amd}.
+@item wait4amd pluto rlogin pluto
+Remote login to host @samp{pluto} when @i{Amd} is up on that host. It
+is generally necessary to wait for @i{Amd} to properly start and
+initialize on a remote host before logging in to it, because otherwise
+user home directories may not be accessible across the network.
+@item wait4amd pluto
+A short-hand version of the previous command, since the most useful
+reason for this script is to login to a remote host. I use it very
+often when testing out new versions of @i{Amd}, and need to reboot hung
+hosts.
+@end table
+
+@c ----------------------------------------------------------------
+@node wait4amd2die, wire-test, wait4amd, Assorted Tools
+@comment node-name, next, previous, up
+@section wait4amd2die
+@pindex wait4amd2die
+
+This script is used internally by @samp{ctl-amd} when used to restart
+@i{Amd}. It waits for @i{Amd} to terminate. If it detected that
+@i{Amd} terminated cleanly, this script will return an exist status of
+zero. Otherwise, it will return a non-zero exit status.
+
+The script tests for @i{Amd}'s existence once every 5 seconds, six
+times, for a total of 30 seconds. It will return a zero exist status as
+soon as it detects that @i{Amd} dies.
+
+@c ----------------------------------------------------------------
+@node wire-test, , wait4amd2die, Assorted Tools
+@comment node-name, next, previous, up
+@section wire-test
+@pindex wire-test
+
+A simple program to test if some of the most basic networking functions
+in am-util's library @file{libamu} work. It also tests the combination
+of NFS protocol and version number that are supported from the current
+host, to a remote one.
+
+For example, in this test a machine which only supports NFS Version 2 is
+contacting a remote host that can support the same version, but using
+both UDP and TCP. If no host name is specified, @samp{wire-test} will
+try @file{localhost}.
+
+@example
+$ wire-test moisil
+Network name is "mcl-lab-net.cs.columbia.edu"
+Network number is "128.59.13"
+Network name is "old-net.cs.columbia.edu"
+Network number is "128.59.16"
+My IP address is 0x7f000001.
+NFS Version and protocol tests to host "moisil"...
+ testing vers=2, proto="udp" -> found version 2.
+ testing vers=3, proto="udp" -> failed!
+ testing vers=2, proto="tcp" -> found version 2.
+ testing vers=3, proto="tcp" -> failed!
+@end example
+
+@c ################################################################
+@node Examples, Internals, Assorted Tools, Top
+@comment node-name, next, previous, up
+@chapter Examples
+
+@menu
+* User Filesystems::
+* Home Directories::
+* Architecture Sharing::
+* Wildcard Names::
+* rwho servers::
+* /vol::
+* /defaults with selectors::
+* /tftpboot in a chroot-ed environment::
+
+@end menu
+
+@node User Filesystems, Home Directories, Examples, Examples
+@comment node-name, next, previous, up
+@section User Filesystems
+@cindex User filesystems
+@cindex Mounting user filesystems
+
+With more than one fileserver, the directories most frequently
+cross-mounted are those containing user home directories. A common
+convention used at Imperial College is to mount the user disks under
+@t{/home/}@i{machine}.
+
+Typically, the @samp{/etc/fstab} file contained a long list of entries
+such as:
+
+@example
+@i{machine}:/home/@i{machine} /home/@i{machine} nfs ...
+@end example
+
+for each fileserver on the network.
+
+There are numerous problems with this system. The mount list can become
+quite large and some of the machines may be down when a system is
+booted. When a new fileserver is installed, @samp{/etc/fstab} must be
+updated on every machine, the mount directory created and the filesystem
+mounted.
+
+In many environments most people use the same few workstations, but
+it is convenient to go to a colleague's machine and access your own
+files. When a server goes down, it can cause a process on a client
+machine to hang. By minimizing the mounted filesystems to only include
+those actively being used, there is less chance that a filesystem will
+be mounted when a server goes down.
+
+The following is a short extract from a map taken from a research fileserver
+at Imperial College.
+
+Note the entry for @samp{localhost} which is used for users such as
+the operator (@samp{opr}) who have a home directory on most machine as
+@samp{/home/localhost/opr}.
+
+@example
+/defaults opts:=rw,intr,grpid,nosuid
+charm host!=$@{key@};type:=nfs;rhost:=$@{key@};rfs:=/home/$@{key@} \
+ host==$@{key@};type:=ufs;dev:=/dev/xd0g
+#
+...
+
+#
+localhost type:=link;fs:=$@{host@}
+...
+#
+# dylan has two user disks so have a
+# top directory in which to mount them.
+#
+dylan type:=auto;fs:=$@{map@};pref:=$@{key@}/
+#
+dylan/dk2 host!=dylan;type:=nfs;rhost:=dylan;rfs:=/home/$@{key@} \
+ host==dylan;type:=ufs;dev:=/dev/dsk/2s0
+#
+dylan/dk5 host!=dylan;type:=nfs;rhost:=dylan;rfs:=/home/$@{key@} \
+ host==dylan;type:=ufs;dev:=/dev/dsk/5s0
+...
+#
+toytown host!=$@{key@};type:=nfs;rhost:=$@{key@};rfs:=/home/$@{key@} \
+ host==$@{key@};type:=ufs;dev:=/dev/xy1g
+...
+#
+zebedee host!=$@{key@};type:=nfs;rhost:=$@{key@};rfs:=/home/$@{key@} \
+ host==$@{key@};type:=ufs;dev:=/dev/dsk/1s0
+#
+# Just for access...
+#
+gould type:=auto;fs:=$@{map@};pref:=$@{key@}/
+gould/staff host!=gould;type:=nfs;rhost:=gould;rfs:=/home/$@{key@}
+#
+gummo host!=$@{key@};type:=nfs;rhost:=$@{key@};rfs:=/home/$@{key@}
+...
+@end example
+
+This map is shared by most of the machines listed so on those
+systems any of the user disks is accessible via a consistent name.
+@i{Amd} is started with the following command
+
+@example
+amd /home amd.home
+@end example
+
+Note that when mounting a remote filesystem, the @dfn{automounted}
+mount point is referenced, so that the filesystem will be mounted if
+it is not yet (at the time the remote @samp{mountd} obtains the file handle).
+
+@node Home Directories, Architecture Sharing, User Filesystems, Examples
+@comment node-name, next, previous, up
+@section Home Directories
+@cindex Home directories
+@cindex Example of mounting home directories
+@cindex Mount home directories
+
+One convention for home directories is to locate them in @samp{/homes}
+so user @samp{jsp}'s home directory is @samp{/homes/jsp}. With more
+than a single fileserver it is convenient to spread user files across
+several machines. All that is required is a mount-map which converts
+login names to an automounted directory.
+
+Such a map might be started by the command:
+
+@example
+amd /homes amd.homes
+@end example
+
+where the map @samp{amd.homes} contained the entries:
+
+@example
+/defaults type:=link # All the entries are of type:=link
+jsp fs:=/home/charm/jsp
+njw fs:=/home/dylan/dk5/njw
+...
+phjk fs:=/home/toytown/ai/phjk
+sjv fs:=/home/ganymede/sjv
+@end example
+
+Whenever a login name is accessed in @samp{/homes} a symbolic link
+appears pointing to the real location of that user's home directory. In
+this example, @samp{/homes/jsp} would appear to be a symbolic link
+pointing to @samp{/home/charm/jsp}. Of course, @samp{/home} would also
+be an automount point.
+
+This system causes an extra level of symbolic links to be used.
+Although that turns out to be relatively inexpensive, an alternative is
+to directly mount the required filesystems in the @samp{/homes}
+map. The required map is simple, but long, and its creation is best automated.
+The entry for @samp{jsp} could be:
+
+@example
+jsp -sublink:=$@{key@};rfs:=/home/charm \
+ host==charm;type:=ufs;dev:=/dev/xd0g \
+ host!=charm;type:=nfs;rhost:=charm
+@end example
+
+This map can become quite big if it contains a large number of entries.
+By combining two other features of @i{Amd} it can be greatly simplified.
+
+First the UFS partitions should be mounted under the control of
+@samp{/etc/fstab}, taking care that they are mounted in the same place
+that @i{Amd} would have automounted them. In most cases this would be
+something like @samp{/a/@dfn{host}/home/@dfn{host}} and
+@samp{/etc/fstab} on host @samp{charm} would have a line:@refill
+
+@example
+/dev/xy0g /a/charm/home/charm 4.2 rw,nosuid,grpid 1 5
+@end example
+
+The map can then be changed to:
+
+@example
+/defaults type:=nfs;sublink:=$@{key@};opts:=rw,intr,nosuid,grpid
+jsp rhost:=charm;rfs:=/home/charm
+njw rhost:=dylan;rfs:=/home/dylan/dk5
+...
+phjk rhost:=toytown;rfs:=/home/toytown;sublink:=ai/$@{key@}
+sjv rhost:=ganymede;rfs:=/home/ganymede
+@end example
+
+This map operates as usual on a remote machine (@i{ie} @code{$@{host@}}
+not equal to @code{$@{rhost@}}). On the machine where the filesystem is
+stored (@i{ie} @code{$@{host@}} equal to @code{$@{rhost@}}), @i{Amd}
+will construct a local filesystem mount point which corresponds to the
+name of the locally mounted UFS partition. If @i{Amd} is started with
+the @code{-r} option then instead of attempting an NFS mount, @i{Amd} will
+simply inherit the UFS mount (@pxref{Inheritance Filesystem}). If
+@code{-r} is not used then a loopback NFS mount will be made. This type of
+mount is known to cause a deadlock on many systems.
+
+@node Architecture Sharing, Wildcard Names, Home Directories, Examples
+@comment node-name, next, previous, up
+@section Architecture Sharing
+@cindex Architecture sharing
+@cindex Sharing a fileserver between architectures
+@cindex Architecture dependent volumes
+
+@c %At the moment some of the research machines have sets of software
+@c %mounted in @samp{/vol}. This contains subdirectories for \TeX,
+@c %system sources, local sources, prolog libraries and so on.
+Often a filesystem will be shared by machines of different architectures.
+Separate trees can be maintained for the executable images for each
+architecture, but it may be more convenient to have a shared tree,
+with distinct subdirectories.
+
+A shared tree might have the following structure on the fileserver (called
+@samp{fserver} in the example):
+
+@example
+local/tex
+local/tex/fonts
+local/tex/lib
+local/tex/bin
+local/tex/bin/sun3
+local/tex/bin/sun4
+local/tex/bin/hp9000
+...
+@end example
+
+In this example, the subdirectories of @samp{local/tex/bin} should be
+hidden when accessed via the automount point (conventionally @samp{/vol}).
+A mount-map for @samp{/vol} to achieve this would look like:
+
+@example
+/defaults sublink:=$@{/key@};rhost:=fserver;type:=link
+tex type:=auto;fs:=$@{map@};pref:=$@{key@}/
+tex/fonts host!=fserver;type:=nfs;rfs:=/vol/tex \
+ host==fserver;fs:=/usr/local/tex
+tex/lib host!=fserver;type:=nfs;rfs:=/vol/tex \
+ host==fserver;fs:=/usr/local/tex
+tex/bin -sublink:=$@{/key@}/$@{arch@} \
+ host!=fserver;type:=nfs;rfs:=/vol/tex \
+ host:=fserver;fs:=/usr/local/tex
+@end example
+
+When @samp{/vol/tex/bin} is referenced, the current machine architecture
+is automatically appended to the path by the @code{$@{sublink@}}
+variable. This means that users can have @samp{/vol/tex/bin} in their
+@samp{PATH} without concern for architecture dependencies.
+
+@node Wildcard Names, rwho servers, Architecture Sharing, Examples
+@comment node-name, next, previous, up
+@section Wildcard Names & Replicated Servers
+
+By using the wildcard facility, @i{Amd} can @dfn{overlay} an existing
+directory with additional entries.
+The system files are usually mounted under @samp{/usr}. If instead,
+@i{Amd} is mounted on @samp{/usr}, additional
+names can be overlayed to augment or replace names in the ``master'' @samp{/usr}.
+A map to do this would have the form:
+
+@example
+local type:=auto;fs:=local-map
+share type:=auto;fs:=share-map
+* -type:=nfs;rfs:=/export/exec/$@{arch@};sublink:="$@{key@}" \
+ rhost:=fserv1 rhost:=fserv2 rhost:=fserv3
+@end example
+
+Note that the assignment to @code{$@{sublink@}} is surrounded by double
+quotes to prevent the incoming key from causing the map to be
+misinterpreted. This map has the effect of directing any access to
+@samp{/usr/local} or @samp{/usr/share} to another automount point.
+
+In this example, it is assumed that the @samp{/usr} files are replicated
+on three fileservers: @samp{fserv1}, @samp{fserv2} and @samp{fserv3}.
+For any references other than to @samp{local} and @samp{share} one of
+the servers is used and a symbolic link to
+@t{$@{autodir@}/$@{rhost@}/export/exec/$@{arch@}/@i{whatever}} is
+returned once an appropriate filesystem has been mounted.@refill
+
+@node rwho servers, /vol, Wildcard Names, Examples
+@comment node-name, next, previous, up
+@section @samp{rwho} servers
+@cindex rwho servers
+@cindex Architecture specific mounts
+@cindex Example of architecture specific mounts
+
+The @samp{/usr/spool/rwho} directory is a good candidate for automounting.
+For efficiency reasons it is best to capture the rwho data on a small
+number of machines and then mount that information onto a large number
+of clients. The data written into the rwho files is byte order dependent
+so only servers with the correct byte ordering can be used by a client:
+
+@example
+/defaults type:=nfs
+usr/spool/rwho -byte==little;rfs:=/usr/spool/rwho \
+ rhost:=vaxA rhost:=vaxB \
+ || -rfs:=/usr/spool/rwho \
+ rhost:=sun4 rhost:=hp300
+@end example
+
+@node /vol, /defaults with selectors, rwho servers, Examples
+@comment node-name, next, previous, up
+@section @samp{/vol}
+@cindex /vol
+@cindex Catch-all mount point
+@cindex Generic volume name
+
+@samp{/vol} is used as a catch-all for volumes which do not have other
+conventional names.
+
+Below is part of the @samp{/vol} map for the domain @samp{doc.ic.ac.uk}.
+The @samp{r+d} tree is used for new or experimental software that needs
+to be available everywhere without installing it on all the fileservers.
+Users wishing to try out the new software then simply include
+@samp{/vol/r+d/@{bin,ucb@}} in their path.@refill
+
+The main tree resides on one host @samp{gould.doc.ic.ac.uk}, which has
+different @samp{bin}, @samp{etc}, @samp{lib} and @samp{ucb}
+sub-directories for each machine architecture. For example,
+@samp{/vol/r+d/bin} for a Sun-4 would be stored in the sub-directory
+@samp{bin/sun4} of the filesystem @samp{/usr/r+d}. When it was accessed
+a symbolic link pointing to @samp{/a/gould/usr/r+d/bin/sun4} would be
+returned.@refill
+
+@example
+/defaults type:=nfs;opts:=rw,grpid,nosuid,intr,soft
+wp -opts:=rw,grpid,nosuid;rhost:=charm \
+ host==charm;type:=link;fs:=/usr/local/wp \
+ host!=charm;type:=nfs;rfs:=/vol/wp
+...
+#
+src -opts:=rw,grpid,nosuid;rhost:=charm \
+ host==charm;type:=link;fs:=/usr/src \
+ host!=charm;type:=nfs;rfs:=/vol/src
+#
+r+d type:=auto;fs:=$@{map@};pref:=r+d/
+# per architecture bin,etc,lib&ucb...
+r+d/bin rhost:=gould.doc.ic.ac.uk;rfs:=/usr/r+d;sublink:=$@{/key@}/$@{arch@}
+r+d/etc rhost:=gould.doc.ic.ac.uk;rfs:=/usr/r+d;sublink:=$@{/key@}/$@{arch@}
+r+d/include rhost:=gould.doc.ic.ac.uk;rfs:=/usr/r+d;sublink:=$@{/key@}
+r+d/lib rhost:=gould.doc.ic.ac.uk;rfs:=/usr/r+d;sublink:=$@{/key@}/$@{arch@}
+r+d/man rhost:=gould.doc.ic.ac.uk;rfs:=/usr/r+d;sublink:=$@{/key@}
+r+d/src rhost:=gould.doc.ic.ac.uk;rfs:=/usr/r+d;sublink:=$@{/key@}
+r+d/ucb rhost:=gould.doc.ic.ac.uk;rfs:=/usr/r+d;sublink:=$@{/key@}/$@{arch@}
+# hades pictures
+pictures -opts:=rw,grpid,nosuid;rhost:=thpfs \
+ host==thpfs;type:=link;fs:=/nbsd/pictures \
+ host!=thpfs;type:=nfs;rfs:=/nbsd;sublink:=pictures
+# hades tools
+hades -opts:=rw,grpid,nosuid;rhost:=thpfs \
+ host==thpfs;type:=link;fs:=/nbsd/hades \
+ host!=thpfs;type:=nfs;rfs:=/nbsd;sublink:=hades
+# bsd tools for hp.
+bsd -opts:=rw,grpid,nosuid;arch==hp9000;rhost:=thpfs \
+ host==thpfs;type:=link;fs:=/nbsd/bsd \
+ host!=thpfs;type:=nfs;rfs:=/nbsd;sublink:=bsd
+@end example
+
+@node /defaults with selectors, /tftpboot in a chroot-ed environment, /vol, Examples
+@comment node-name, next, previous, up
+@section @samp{/defaults} with selectors
+@cindex /defaults with selectors
+@cindex selectors on default
+
+It is sometimes useful to have different defaults for a given map. To
+achieve this, the @samp{/defaults} entry must be able to process normal
+selectors. This feature is turned on by setting
+@samp{selectors_on_default = yes} in the @file{amd.conf} file.
+@xref{selectors_on_default Parameter}.
+
+In this example, I set different default NFS mount options for hosts
+which are running over a slower network link. By setting a smaller size
+for the NFS read and write buffer sizes, you can greatly improve remote
+file service performance.
+
+@example
+/defaults \
+ wire==slip-net;opts:=rw,intr,rsize=1024,wsize=1024,timeo=20,retrans=10 \
+ wire!=slip-net;opts:=rw,intr
+@end example
+
+@node /tftpboot in a chroot-ed environment, , /defaults with selectors, Examples
+@comment node-name, next, previous, up
+@section @samp{/tftpboot} in a chroot-ed environment
+@cindex /tftpboot in a chroot-ed environment
+@cindex chroot: /tftpboot example
+
+In this complex example, we attempt to run an @i{Amd} process
+@emph{inside} a chroot-ed environment. @samp{tftpd} (Trivial FTP) is
+used to trivially retrieve files used to boot X-Terminals, Network
+Printers, Network routers, diskless workstations, and other such
+devices. For security reasons, @samp{tftpd} (and also @samp{ftpd})
+processes are run using the @b{chroot}(2) system call. This provides an
+environment for these processes, where access to any files outside the
+directory where the chroot-ed process runs is denied.
+
+For example, if you start @samp{tftpd} on your system with
+
+@example
+chroot /tftpboot /usr/sbin/tftpd
+@end example
+
+@noindent
+then the @samp{tftpd} process will not be able to access any files
+outside @file{/tftpboot}. This ensures that no one can retrieve files
+such as @file{/etc/passwd} and run password crackers on it.
+
+Since the TFTP service works by broadcast, it is necessary to have at
+least one TFTP server running on each subnet. If you have lots of files
+that you need to make available for @samp{tftp}, and many subnets, it
+could take significant amounts of disk space on each host serving them.
+
+A solution we implemented at Columbia University was to have every host
+run @samp{tftpd}, but have those servers retrieve the boot files from
+two replicated servers. Those replicated servers have special
+partitions dedicated to the many network boot files.
+
+We start @i{Amd} as follows:
+
+@example
+amd /tftpboot/.amd amd.tftpboot
+@end example
+
+That is, @i{Amd} is serving the directory @file{/tftpboot/.amd}. The
+@samp{tftp} server runs inside @file{/tftpboot} and is chroot-ed in that
+directory too. The @file{amd.tftpboot} map looks like:
+
+@example
+#
+# Amd /tftpboot directory -> host map
+#
+
+/defaults opts:=nosuid,ro,intr,soft;fs:=/tftpboot/import;type:=nfs
+
+tp host==lol;rfs:=/n/lol/import/tftpboot;type:=lofs \
+ host==ober;rfs:=/n/ober/misc/win/tftpboot;type:=lofs \
+ rhost:=ober;rfs:=/n/ober/misc/win/tftpboot \
+ rhost:=lol;rfs:=/n/lol/import/tftpboot
+@end example
+
+To help understand this example, I list a few of the file entries that
+are created inside @file{/tftpboot}:
+
+@example
+$ ls -la /tftpboot
+dr-xr-xr-x 2 root 512 Aug 30 23:11 .amd
+drwxrwsr-x 12 root 512 Aug 30 08:00 import
+lrwxrwxrwx 1 root 33 Feb 27 1997 adminpr.cfg -> ./.amd/tp/hplj/adminpr.cfg
+lrwxrwxrwx 1 root 22 Dec 5 1996 tekxp -> ./.amd/tp/xterms/tekxp
+lrwxrwxrwx 1 root 1 Dec 5 1996 tftpboot -> .
+@end example
+
+Here is an explanation of each of the entries listed above:
+
+@table @code
+
+@item .amd
+This is the @i{Amd} mount point. Note that you do not need to run a
+separate @i{Amd} process for the TFTP service. The @b{chroot}(2) system
+call only protects against file access, but the same process can still
+serve files and directories inside and outside the chroot-ed
+environment, because @i{Amd} itself was not run in chroot-ed mode.
+
+@item import
+This is the mount point where @i{Amd} will mount the directories
+containing the boot files. The map is designed so that remote
+directories will be NFS mounted (even if they are already mounted
+elsewhere), and local directories are loopback mounted (since they are
+not accessible outside the chroot-ed @file{/tftpboot} directory).
+
+@item adminpr.cfg
+@itemx tekxp
+Two manually created symbolic links to directories @emph{inside} the
+@i{Amd}-managed directory. The crossing of the component @file{tp} will
+cause @i{Amd} to automount one of the remote replicas. Once crossed,
+access to files inside proceeds as usual. The @samp{adminpr.cfg} is a
+configuration file for an HP Laser-Jet 4si printer, and the @samp{tekxp}
+is a directory for Tektronix X-Terminal boot files.
+
+@item tftpboot
+This innocent looking symlink is important. Usually, when devices boot
+via the TFTP service, they perform the @samp{get file} command to
+retrieve @var{file}. However, some devices assume that @samp{tftpd}
+does not run in a chroot-ed environment, but rather ``unprotected'', and
+thus use a full pathname for files to retrieve, as in @samp{get
+/tftpboot/file}. This symlink effectively strips out the leading
+@file{/tftpboot/}.
+
+@end table
+
+@c ################################################################
+@node Internals, Acknowledgments & Trademarks, Examples, Top
+@comment node-name, next, previous, up
+@chapter Internals
+
+Note that there are more error and logging messages possible than are
+listed here. Most of them are self-explanatory. Refer to the program
+sources for more details on the rest.
+
+@menu
+* Log Messages::
+@end menu
+
+@node Log Messages, , Internals, Internals
+@comment node-name, next, previous, up
+@section Log Messages
+
+In the following sections a brief explanation is given of some of the
+log messages made by @i{Amd}. Where the message is in @samp{typewriter}
+font, it corresponds exactly to the message produced by @i{Amd}. Words
+in @dfn{italic} are replaced by an appropriate string. Variables,
+@code{$@{@i{var}@}}, indicate that the value of the appropriate variable is
+output.
+
+Log messages are either sent directly to a file,
+or logged via the @b{syslog}(3) mechanism. @xref{log_file Parameter}
+In either case, entries in the file are of the form:
+@example
+@i{date-string} @i{hostname} @t{amd[}@i{pid}@t{]} @i{message}
+@end example
+
+@menu
+* Fatal errors::
+* Info messages::
+@end menu
+
+@node Fatal errors, Info messages, Log Messages, Log Messages
+@comment node-name, next, previous, up
+@subsection Fatal errors
+
+@i{Amd} attempts to deal with unusual events. Whenever it is not
+possible to deal with such an error, @i{Amd} will log an appropriate
+message and, if it cannot possibly continue, will either exit or abort.
+These messages are selected by @samp{-x fatal} on the command line.
+When @b{syslog}(3) is being used, they are logged with level
+@samp{LOG_FATAL}. Even if @i{Amd} continues to operate it is likely to
+remain in a precarious state and should be restarted at the earliest
+opportunity.
+
+@table @t
+
+@item Attempting to inherit not-a-filesystem
+The prototype mount point created during a filesystem restart did not
+contain a reference to the restarted filesystem. This error ``should
+never happen''.
+
+@item Can't bind to domain "@i{NIS-domain}"
+A specific NIS domain was requested on the command line, but no server
+for that domain is available on the local net.
+
+@item Can't determine IP address of this host (@i{hostname})
+When @i{Amd} starts it determines its own IP address. If this lookup
+fails then @i{Amd} cannot continue. The hostname it looks up is that
+obtained returned by @b{gethostname}(2) system call.
+
+@item Can't find root file handle for @i{automount point}
+@i{Amd} creates its own file handles for the automount points. When it
+mounts itself as a server, it must pass these file handles to the local
+kernel. If the filehandle is not obtainable the mount point is ignored.
+This error ``should never happen''.
+
+@item Must be root to mount filesystems (euid = @i{euid})
+To prevent embarrassment, @i{Amd} makes sure it has appropriate system
+privileges. This amounts to having an euid of 0. The check is made
+after argument processing complete to give non-root users a chance to
+access the @code{-v} option.
+
+@item No work to do - quitting
+No automount points were given on the command line and so there is no
+work to do.
+
+@item Out of memory
+While attempting to malloc some memory, the memory space available to
+@i{Amd} was exhausted. This is an unrecoverable error.
+
+@item Out of memory in realloc
+While attempting to realloc some memory, the memory space available to
+@i{Amd} was exhausted. This is an unrecoverable error.
+
+@item cannot create rpc/udp service
+Either the NFS or AMQ endpoint could not be created.
+
+@item gethostname: @i{description}
+The @b{gethostname}(2) system call failed during startup.
+
+@item host name is not set
+The @b{gethostname}(2) system call returned a zero length host name.
+This can happen if @i{Amd} is started in single user mode just after
+booting the system.
+
+@item ifs_match called!
+An internal error occurred while restarting a pre-mounted filesystem.
+This error ``should never happen''.
+
+@item mount_afs: @i{description}
+An error occurred while @i{Amd} was mounting itself.
+
+@item run_rpc failed
+Somehow the main NFS server loop failed. This error ``should never
+happen''.
+
+@item unable to free rpc arguments in amqprog_1
+The incoming arguments to the AMQ server could not be free'ed.
+
+@item unable to free rpc arguments in nfs_program_1
+The incoming arguments to the NFS server could not be free'ed.
+
+@item unable to register (AMQ_PROGRAM, AMQ_VERSION, udp)
+The AMQ server could not be registered with the local portmapper or the
+internal RPC dispatcher.
+
+@item unable to register (NFS_PROGRAM, NFS_VERSION, 0)
+The NFS server could not be registered with the internal RPC dispatcher.
+
+@end table
+
+XXX: This section needs to be updated
+
+@node Info messages, , Fatal errors, Log Messages
+@comment node-name, next, previous, up
+@subsection Info messages
+
+@i{Amd} generates information messages to record state changes. These
+messages are selected by @samp{-x info} on the command line. When
+@b{syslog}(3) is being used, they are logged with level @samp{LOG_INFO}.
+
+The messages listed below can be generated and are in a format suitable
+for simple statistical analysis. @dfn{mount-info} is the string
+that is displayed by @dfn{Amq} in its mount information column and
+placed in the system mount table.
+
+@table @t
+
+@item "@t{$@{@i{path}@}}" forcibly timed out
+An automount point has been timed out by the @i{Amq} command.
+
+@item "@t{$@{@i{path}@}}" has timed out
+No access to the automount point has been made within the timeout
+period.
+
+@item Filehandle denied for "$@{@i{rhost}@}:$@{@i{rfs}@}"
+The mount daemon refused to return a file handle for the requested filesystem.
+
+@item Filehandle error for "$@{@i{rhost}@}:$@{@i{rfs}@}": @i{description}
+The mount daemon gave some other error for the requested filesystem.
+
+@item Finishing with status @i{exit-status}
+@i{Amd} is about to exit with the given exit status.
+
+@item Re-synchronizing cache for map @t{$@{@i{map}@}}
+The named map has been modified and the internal cache is being re-synchronized.
+
+@item file server @t{$@{@i{rhost}@}} is down - timeout of "@t{$@{@i{path}@}}" ignored
+An automount point has timed out, but the corresponding file server is
+known to be down. This message is only produced once for each mount
+point for which the server is down.
+
+@item file server @t{$@{@i{rhost}@}} type nfs is down
+An NFS file server that was previously up is now down.
+
+@item file server @t{$@{@i{rhost}@}} type nfs is up
+An NFS file server that was previously down is now up.
+
+@item file server @t{$@{@i{rhost}@}} type nfs starts down
+A new NFS file server has been referenced and is known to be down.
+
+@item file server @t{$@{@i{rhost}@}} type nfs starts up
+A new NFS file server has been referenced and is known to be up.
+
+@item mount of "@t{$@{@i{path}@}}" on @t{$@{@i{fs}@}} timed out
+Attempts to mount a filesystem for the given automount point have failed
+to complete within 30 seconds.
+
+@item @i{mount-info} mounted fstype @t{$@{@i{type}@}} on @t{$@{@i{fs}@}}
+A new file system has been mounted.
+
+@item @i{mount-info} restarted fstype @t{$@{@i{type}@}} on @t{$@{@i{fs}@}}
+@i{Amd} is using a pre-mounted filesystem to satisfy a mount request.
+
+@item @i{mount-info} unmounted fstype @t{$@{@i{type}@}} from @t{$@{@i{fs}@}}
+A file system has been unmounted.
+
+@item @i{mount-info} unmounted fstype @t{$@{@i{type}@}} from @t{$@{@i{fs}@}} link @t{$@{@i{fs}@}}/@t{$@{@i{sublink}@}}
+A file system of which only a sub-directory was in use has been unmounted.
+
+@item restarting @i{mount-info} on @t{$@{@i{fs}@}}
+A pre-mounted file system has been noted.
+
+@end table
+
+XXX: This section needs to be updated
+
+@c ################################################################
+@node Acknowledgments & Trademarks, Index, Internals, Top
+@comment node-name, next, previous, up
+@unnumbered Acknowledgments & Trademarks
+
+Many thanks to the @email{amd-dev@@majordomo.cs.columbia.edu,Amd
+Developers} mailing list through the months developing am-utils. These
+members have contributed to the discussions, ideas, code and
+documentation, and subjected their systems to alpha quality code.
+Special thanks go to those
+@uref{http://www.cs.columbia.edu/~ezk/am-utils/AUTHORS.txt,authors} who
+have submitted patches.
+
+Thanks to the Formal Methods Group at Imperial College for suffering
+patiently while @i{Amd} was being developed on their machines.
+
+Thanks to the many people who have helped with the development of
+@i{Amd}, especially Piete Brooks at the Cambridge University Computing
+Lab for many hours of testing, experimentation and discussion.
+
+Thanks to the @email{amd-workers@@majordomo.glue.umd.edu,Amd Workers}
+mailing list members for many suggestions and bug reports to @i{Amd}.
+
+@itemize @bullet
+@item
+@b{DEC}, @b{VAX} and @b{Ultrix} are registered trademarks of Digital
+Equipment Corporation.
+@item
+@b{AIX} and @b{IBM} are registered trademarks of International Business
+Machines Corporation.
+@item
+@b{Sun}, @b{NFS} and @b{SunOS} are registered trademarks of Sun
+Microsystems, Inc.
+@item
+@b{UNIX} is a registered trademark in the USA and other countries,
+exclusively licensed through X/Open Company, Ltd.
+@item
+All other registered trademarks are owned by their respective owners.
+@end itemize
+
+@c ################################################################
+@node Index, , Acknowledgments & Trademarks, Top
+@comment node-name, next, previous, up
+@unnumbered Index
+
+@printindex cp
+
+@contents
+@bye
+
+@c ====================================================================
+@c ISPELL LOCAL WORDS:
+@c LocalWords: setfilename amdref overfullrule settitle titlepage titlefont nz
+@c LocalWords: authorfont vskip ifinfo iftex cindex unnumberedsec dfn xref vol
+@c LocalWords: locationN pxref jpo nott concentrix Sjoerd sjoerd cwi Eitan vuw
+@c LocalWords: Mizrotsky eitan shumuji dgux fpx scp hcx metcalf masala hlh OTS
+@c LocalWords: Presnell srp cgl Trost trost ogi pyrOSx OSx tubsibr riscix iX
+@c LocalWords: Piete pb Lindblad cjl ai umax utek xinu Mitchum D'Souza dsouza
+@c LocalWords: mrc apu alliant aviion AViiON fps macII multimax tahoe vax emph
+@c LocalWords: mapdefault valA valB valC YPTSDIR ETCDIR substr MAKEDBM YPDBDIR
+@c LocalWords: NOPUSH njw dylan dk dylan njw anydir domN achilles mjh pref sel
+@c LocalWords: gdef loc loc loc ldots autodir remopts rwho rwho styx styx yoyo
+@c LocalWords: noindent gould rvdmount rvdunmount fserver mtmp unioned logfile
+@c LocalWords: dmn esac phjk toytown toytown toytown toytown phjk RdDir RdLnk
+@c LocalWords: volname attrs netif dougal inaddr hwaddr ec mountmaps passno xy
+@c LocalWords: freq dumpset hfs brian florence localinfo fstabs automaps defn
+@c LocalWords: localname fsck'd opr gummo sjv ganymede sjv fserv fserv fserv
+@c LocalWords: vaxA vaxB wp thpfs nbsd asis ifs amqprog free'ed printindex gov
+@c LocalWords: LocalWords syncodeindex Distrib bsdnet lanl AutoMounter acis ic
+@c LocalWords: ac uk aix bsd Mullender nl il DG lcs hpux irix ucsf NeXT cse cl
+@c LocalWords: mt FX hp ibm mips utils def def Domainname eg hostd getwd tmp
+@c LocalWords: subsubsection rw grpid intr noconn nocto nodevs nosuid retrans
+@c LocalWords: rsize tcp timeo nounmount utimeout DDEBUG nodaemon fd hostnames
+@c LocalWords: pid Amd's pendry vangogh nfsx backoff stats nomap nostats CRIT
+@c LocalWords: noinfo clustername RVD dsk dsk amq hostports osver statfs str
+@c LocalWords: ou counter's amdmaps proj src tftpboot sh mv cd sbin ypcat inet
+@c LocalWords: Getattr getattr localhost fhandles netmask fstype noquota addr
+@c LocalWords: exportfs Dumpsets dumpsets pindex ldif fixmount fixrmtab euid
+@c LocalWords: lostaltmail realloc netnumber itemx primnetnum primnetname ARG
+@c LocalWords: subsnetname subsnetnum netgrp netgroup multitable Shlib dec osf
+@c LocalWords: hppa pc bsdi freebsd netbsd openbsd ncr sysv rs acdirmax fsid
+@c LocalWords: acdirmin acregmax acregmin actimeo dumbtimr nfsv noac noauto sd
+@c LocalWords: nocache nodev noint nosub pgthresh posix rdonly suid symttl mfs
+@c LocalWords: AMFS umapfs myftpdir unionfs es mapname mapfile mapfile slocal
+@c LocalWords: mailspool saturn saturn notknown lol ober dr xr xr drwxrwsr cfg
+@c LocalWords: lrwxrwxrwx adminpr hplj adminpr cfg tekxp xterms tekxp Dupuy tp
+@c LocalWords: linkname hlfsddump dirname rmtab pluto rlogin direntry pg vr dn
+@c LocalWords: maxmem hlfsdir xmailbox showmount cn amdmap amdmapName resvport
+@c LocalWords: objectClass amdmapKey amdmapValue ln powerpc amdmapTimestamp ez
+@c LocalWords: moisil FSinfo Libtool Unmounting sublink fileservers NullProc
+@c LocalWords: gethostname mount's unmounts linkx remounts unmounting UAs SA's
+@c LocalWords: mountpoint mountpoints unescaped UIDs util's overlayed uref EFS
+@c LocalWords: serv maxgroups nfsl cachedir copt cfsadmin efs addopts fg
+@c LocalWords: nointr
diff --git a/contrib/amd/doc/stamp-vti b/contrib/amd/doc/stamp-vti
new file mode 100644
index 000000000000..225ecdbacb37
--- /dev/null
+++ b/contrib/amd/doc/stamp-vti
@@ -0,0 +1,3 @@
+@set UPDATED 22 April 1998
+@set EDITION 6.0a16
+@set VERSION 6.0a16
diff --git a/contrib/amd/doc/texinfo.tex b/contrib/amd/doc/texinfo.tex
new file mode 100644
index 000000000000..2ce38f94db16
--- /dev/null
+++ b/contrib/amd/doc/texinfo.tex
@@ -0,0 +1,4935 @@
+%% TeX macros to handle Texinfo files.
+%% $Id: texinfo.tex,v 2.218 1997/07/26 19:12:35 karl Exp $
+
+% Copyright (C) 1985, 86, 88, 90, 91, 92, 93,
+% 94, 95, 96, 97 Free Software Foundation, Inc.
+
+%This texinfo.tex file is free software; you can redistribute it and/or
+%modify it under the terms of the GNU General Public License as
+%published by the Free Software Foundation; either version 2, or (at
+%your option) any later version.
+
+%This texinfo.tex file 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.
+
+%You should have received a copy of the GNU General Public License
+%along with this texinfo.tex file; see the file COPYING. If not, write
+%to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+%Boston, MA 02111-1307, USA.
+
+
+%In other words, you are welcome to use, share and improve this program.
+%You are forbidden to forbid anyone else to use, share and improve
+%what you give them. Help stamp out software-hoarding!
+
+
+% Send bug reports to bug-texinfo@prep.ai.mit.edu.
+% Please include a *precise* test case in each bug report.
+
+
+% Make it possible to create a .fmt file just by loading this file:
+% if the underlying format is not loaded, start by loading it now.
+% Added by gildea November 1993.
+\expandafter\ifx\csname fmtname\endcsname\relax\input plain\fi
+
+% This automatically updates the version number based on RCS.
+\def\deftexinfoversion$#1: #2 ${\def\texinfoversion{#2}}
+\deftexinfoversion$Revision: 2.218 $
+\message{Loading texinfo package [Version \texinfoversion]:}
+
+% If in a .fmt file, print the version number
+% and turn on active characters that we couldn't do earlier because
+% they might have appeared in the input file name.
+\everyjob{\message{[Texinfo version \texinfoversion]}\message{}
+ \catcode`+=\active \catcode`\_=\active}
+
+% Save some parts of plain tex whose names we will redefine.
+
+\let\ptexb=\b
+\let\ptexbullet=\bullet
+\let\ptexc=\c
+\let\ptexcomma=\,
+\let\ptexdot=\.
+\let\ptexdots=\dots
+\let\ptexend=\end
+\let\ptexequiv = \equiv
+\let\ptexi=\i
+\let\ptexlbrace=\{
+\let\ptexrbrace=\}
+\let\ptexstar=\*
+\let\ptext=\t
+
+% Be sure we're in horizontal mode when doing a tie, since we make space
+% equivalent to this in @example-like environments. Otherwise, a space
+% at the beginning of a line will start with \penalty -- and
+% since \penalty is valid in vertical mode, we'd end up putting the
+% penalty on the vertical list instead of in the new paragraph.
+{\catcode`@ = 11
+ % Avoid using \@M directly, because that causes trouble
+ % if the definition is written into an index file.
+ \global\let\tiepenalty = \@M
+ \gdef\tie{\leavevmode\penalty\tiepenalty\ }
+}
+
+
+\message{Basics,}
+\chardef\other=12
+
+% If this character appears in an error message or help string, it
+% starts a new line in the output.
+\newlinechar = `^^J
+
+% Set up fixed words for English.
+\ifx\putwordChapter\undefined{\gdef\putwordChapter{Chapter}}\fi%
+\def\putwordInfo{Info}%
+\ifx\putwordSee\undefined{\gdef\putwordSee{See}}\fi%
+\ifx\putwordsee\undefined{\gdef\putwordsee{see}}\fi%
+\ifx\putwordfile\undefined{\gdef\putwordfile{file}}\fi%
+\ifx\putwordpage\undefined{\gdef\putwordpage{page}}\fi%
+\ifx\putwordsection\undefined{\gdef\putwordsection{section}}\fi%
+\ifx\putwordSection\undefined{\gdef\putwordSection{Section}}\fi%
+\ifx\putwordTableofContents\undefined{\gdef\putwordTableofContents{Table of Contents}}\fi%
+\ifx\putwordShortContents\undefined{\gdef\putwordShortContents{Short Contents}}\fi%
+\ifx\putwordAppendix\undefined{\gdef\putwordAppendix{Appendix}}\fi%
+
+% Ignore a token.
+%
+\def\gobble#1{}
+
+\hyphenation{ap-pen-dix}
+\hyphenation{mini-buf-fer mini-buf-fers}
+\hyphenation{eshell}
+\hyphenation{white-space}
+
+% Margin to add to right of even pages, to left of odd pages.
+\newdimen \bindingoffset
+\newdimen \normaloffset
+\newdimen\pagewidth \newdimen\pageheight
+
+% Sometimes it is convenient to have everything in the transcript file
+% and nothing on the terminal. We don't just call \tracingall here,
+% since that produces some useless output on the terminal.
+%
+\def\gloggingall{\begingroup \globaldefs = 1 \loggingall \endgroup}%
+\def\loggingall{\tracingcommands2 \tracingstats2
+ \tracingpages1 \tracingoutput1 \tracinglostchars1
+ \tracingmacros2 \tracingparagraphs1 \tracingrestores1
+ \showboxbreadth\maxdimen\showboxdepth\maxdimen
+}%
+
+% For @cropmarks command.
+% Do @cropmarks to get crop marks.
+%
+\newif\ifcropmarks
+\let\cropmarks = \cropmarkstrue
+%
+% Dimensions to add cropmarks at corners.
+% Added by P. A. MacKay, 12 Nov. 1986
+%
+\newdimen\cornerlong \newdimen\cornerthick
+\newdimen\topandbottommargin
+\newdimen\outerhsize \newdimen\outervsize
+\cornerlong=1pc\cornerthick=.3pt % These set size of cropmarks
+\outerhsize=7in
+%\outervsize=9.5in
+% Alternative @smallbook page size is 9.25in
+\outervsize=9.25in
+\topandbottommargin=.75in
+
+% Main output routine.
+\chardef\PAGE = 255
+\output = {\onepageout{\pagecontents\PAGE}}
+
+\newbox\headlinebox
+\newbox\footlinebox
+
+% \onepageout takes a vbox as an argument. Note that \pagecontents
+% does insertions, but you have to call it yourself.
+\def\onepageout#1{%
+ \ifcropmarks \hoffset=0pt \else \hoffset=\normaloffset \fi
+ %
+ \ifodd\pageno \advance\hoffset by \bindingoffset
+ \else \advance\hoffset by -\bindingoffset\fi
+ %
+ % Do this outside of the \shipout so @code etc. will be expanded in
+ % the headline as they should be, not taken literally (outputting ''code).
+ \setbox\headlinebox = \vbox{\let\hsize=\pagewidth \makeheadline}%
+ \setbox\footlinebox = \vbox{\let\hsize=\pagewidth \makefootline}%
+ %
+ {%
+ % Have to do this stuff outside the \shipout because we want it to
+ % take effect in \write's, yet the group defined by the \vbox ends
+ % before the \shipout runs.
+ %
+ \escapechar = `\\ % use backslash in output files.
+ \indexdummies % don't expand commands in the output.
+ \normalturnoffactive % \ in index entries must not stay \, e.g., if
+ % the page break happens to be in the middle of an example.
+ \shipout\vbox{%
+ \ifcropmarks \vbox to \outervsize\bgroup
+ \hsize = \outerhsize
+ \line{\ewtop\hfil\ewtop}%
+ \nointerlineskip
+ \line{%
+ \vbox{\moveleft\cornerthick\nstop}%
+ \hfill
+ \vbox{\moveright\cornerthick\nstop}%
+ }%
+ \vskip\topandbottommargin
+ \line\bgroup
+ \hfil % center the page within the outer (page) hsize.
+ \ifodd\pageno\hskip\bindingoffset\fi
+ \vbox\bgroup
+ \fi
+ %
+ \unvbox\headlinebox
+ \pagebody{#1}%
+ \ifdim\ht\footlinebox > 0pt
+ % Only leave this space if the footline is nonempty.
+ % (We lessened \vsize for it in \oddfootingxxx.)
+ % The \baselineskip=24pt in plain's \makefootline has no effect.
+ \vskip 2\baselineskip
+ \unvbox\footlinebox
+ \fi
+ %
+ \ifcropmarks
+ \egroup % end of \vbox\bgroup
+ \hfil\egroup % end of (centering) \line\bgroup
+ \vskip\topandbottommargin plus1fill minus1fill
+ \boxmaxdepth = \cornerthick
+ \line{%
+ \vbox{\moveleft\cornerthick\nsbot}%
+ \hfill
+ \vbox{\moveright\cornerthick\nsbot}%
+ }%
+ \nointerlineskip
+ \line{\ewbot\hfil\ewbot}%
+ \egroup % \vbox from first cropmarks clause
+ \fi
+ }% end of \shipout\vbox
+ }% end of group with \turnoffactive
+ \advancepageno
+ \ifnum\outputpenalty>-20000 \else\dosupereject\fi
+}
+
+\newinsert\margin \dimen\margin=\maxdimen
+
+\def\pagebody#1{\vbox to\pageheight{\boxmaxdepth=\maxdepth #1}}
+{\catcode`\@ =11
+\gdef\pagecontents#1{\ifvoid\topins\else\unvbox\topins\fi
+% marginal hacks, juha@viisa.uucp (Juha Takala)
+\ifvoid\margin\else % marginal info is present
+ \rlap{\kern\hsize\vbox to\z@{\kern1pt\box\margin \vss}}\fi
+\dimen@=\dp#1 \unvbox#1
+\ifvoid\footins\else\vskip\skip\footins\footnoterule \unvbox\footins\fi
+\ifr@ggedbottom \kern-\dimen@ \vfil \fi}
+}
+
+% Here are the rules for the cropmarks. Note that they are
+% offset so that the space between them is truly \outerhsize or \outervsize
+% (P. A. MacKay, 12 November, 1986)
+%
+\def\ewtop{\vrule height\cornerthick depth0pt width\cornerlong}
+\def\nstop{\vbox
+ {\hrule height\cornerthick depth\cornerlong width\cornerthick}}
+\def\ewbot{\vrule height0pt depth\cornerthick width\cornerlong}
+\def\nsbot{\vbox
+ {\hrule height\cornerlong depth\cornerthick width\cornerthick}}
+
+% Parse an argument, then pass it to #1. The argument is the rest of
+% the input line (except we remove a trailing comment). #1 should be a
+% macro which expects an ordinary undelimited TeX argument.
+%
+\def\parsearg#1{%
+ \let\next = #1%
+ \begingroup
+ \obeylines
+ \futurelet\temp\parseargx
+}
+
+% If the next token is an obeyed space (from an @example environment or
+% the like), remove it and recurse. Otherwise, we're done.
+\def\parseargx{%
+ % \obeyedspace is defined far below, after the definition of \sepspaces.
+ \ifx\obeyedspace\temp
+ \expandafter\parseargdiscardspace
+ \else
+ \expandafter\parseargline
+ \fi
+}
+
+% Remove a single space (as the delimiter token to the macro call).
+{\obeyspaces %
+ \gdef\parseargdiscardspace {\futurelet\temp\parseargx}}
+
+{\obeylines %
+ \gdef\parseargline#1^^M{%
+ \endgroup % End of the group started in \parsearg.
+ %
+ % First remove any @c comment, then any @comment.
+ % Result of each macro is put in \toks0.
+ \argremovec #1\c\relax %
+ \expandafter\argremovecomment \the\toks0 \comment\relax %
+ %
+ % Call the caller's macro, saved as \next in \parsearg.
+ \expandafter\next\expandafter{\the\toks0}%
+ }%
+}
+
+% Since all \c{,omment} does is throw away the argument, we can let TeX
+% do that for us. The \relax here is matched by the \relax in the call
+% in \parseargline; it could be more or less anything, its purpose is
+% just to delimit the argument to the \c.
+\def\argremovec#1\c#2\relax{\toks0 = {#1}}
+\def\argremovecomment#1\comment#2\relax{\toks0 = {#1}}
+
+% \argremovec{,omment} might leave us with trailing spaces, though; e.g.,
+% @end itemize @c foo
+% will have two active spaces as part of the argument with the
+% `itemize'. Here we remove all active spaces from #1, and assign the
+% result to \toks0.
+%
+% This loses if there are any *other* active characters besides spaces
+% in the argument -- _ ^ +, for example -- since they get expanded.
+% Fortunately, Texinfo does not define any such commands. (If it ever
+% does, the catcode of the characters in questionwill have to be changed
+% here.) But this means we cannot call \removeactivespaces as part of
+% \argremovec{,omment}, since @c uses \parsearg, and thus the argument
+% that \parsearg gets might well have any character at all in it.
+%
+\def\removeactivespaces#1{%
+ \begingroup
+ \ignoreactivespaces
+ \edef\temp{#1}%
+ \global\toks0 = \expandafter{\temp}%
+ \endgroup
+}
+
+% Change the active space to expand to nothing.
+%
+\begingroup
+ \obeyspaces
+ \gdef\ignoreactivespaces{\obeyspaces\let =\empty}
+\endgroup
+
+
+\def\flushcr{\ifx\par\lisppar \def\next##1{}\else \let\next=\relax \fi \next}
+
+%% These are used to keep @begin/@end levels from running away
+%% Call \inENV within environments (after a \begingroup)
+\newif\ifENV \ENVfalse \def\inENV{\ifENV\relax\else\ENVtrue\fi}
+\def\ENVcheck{%
+\ifENV\errmessage{Still within an environment. Type Return to continue.}
+\endgroup\fi} % This is not perfect, but it should reduce lossage
+
+% @begin foo is the same as @foo, for now.
+\newhelp\EMsimple{Type <Return> to continue.}
+
+\outer\def\begin{\parsearg\beginxxx}
+
+\def\beginxxx #1{%
+\expandafter\ifx\csname #1\endcsname\relax
+{\errhelp=\EMsimple \errmessage{Undefined command @begin #1}}\else
+\csname #1\endcsname\fi}
+
+% @end foo executes the definition of \Efoo.
+%
+\def\end{\parsearg\endxxx}
+\def\endxxx #1{%
+ \removeactivespaces{#1}%
+ \edef\endthing{\the\toks0}%
+ %
+ \expandafter\ifx\csname E\endthing\endcsname\relax
+ \expandafter\ifx\csname \endthing\endcsname\relax
+ % There's no \foo, i.e., no ``environment'' foo.
+ \errhelp = \EMsimple
+ \errmessage{Undefined command `@end \endthing'}%
+ \else
+ \unmatchedenderror\endthing
+ \fi
+ \else
+ % Everything's ok; the right environment has been started.
+ \csname E\endthing\endcsname
+ \fi
+}
+
+% There is an environment #1, but it hasn't been started. Give an error.
+%
+\def\unmatchedenderror#1{%
+ \errhelp = \EMsimple
+ \errmessage{This `@end #1' doesn't have a matching `@#1'}%
+}
+
+% Define the control sequence \E#1 to give an unmatched @end error.
+%
+\def\defineunmatchedend#1{%
+ \expandafter\def\csname E#1\endcsname{\unmatchedenderror{#1}}%
+}
+
+
+% Single-spacing is done by various environments (specifically, in
+% \nonfillstart and \quotations).
+\newskip\singlespaceskip \singlespaceskip = 12.5pt
+\def\singlespace{%
+ % Why was this kern here? It messes up equalizing space above and below
+ % environments. --karl, 6may93
+ %{\advance \baselineskip by -\singlespaceskip
+ %\kern \baselineskip}%
+ \setleading \singlespaceskip
+}
+
+%% Simple single-character @ commands
+
+% @@ prints an @
+% Kludge this until the fonts are right (grr).
+\def\@{{\tt \char '100}}
+
+% This is turned off because it was never documented
+% and you can use @w{...} around a quote to suppress ligatures.
+%% Define @` and @' to be the same as ` and '
+%% but suppressing ligatures.
+%\def\`{{`}}
+%\def\'{{'}}
+
+% Used to generate quoted braces.
+\def\mylbrace {{\tt \char '173}}
+\def\myrbrace {{\tt \char '175}}
+\let\{=\mylbrace
+\let\}=\myrbrace
+\begingroup
+ % Definitions to produce actual \{ & \} command in an index.
+ \catcode`\{ = 12 \catcode`\} = 12
+ \catcode`\[ = 1 \catcode`\] = 2
+ \catcode`\@ = 0 \catcode`\\ = 12
+ @gdef@lbracecmd[\{]%
+ @gdef@rbracecmd[\}]%
+@endgroup
+
+% Accents: @, @dotaccent @ringaccent @ubaraccent @udotaccent
+% Others are defined by plain TeX: @` @' @" @^ @~ @= @v @H.
+\let\, = \c
+\let\dotaccent = \.
+\def\ringaccent#1{{\accent23 #1}}
+\let\tieaccent = \t
+\let\ubaraccent = \b
+\let\udotaccent = \d
+
+% Other special characters: @questiondown @exclamdown
+% Plain TeX defines: @AA @AE @O @OE @L (and lowercase versions) @ss.
+\def\questiondown{?`}
+\def\exclamdown{!`}
+
+% Dotless i and dotless j, used for accents.
+\def\imacro{i}
+\def\jmacro{j}
+\def\dotless#1{%
+ \def\temp{#1}%
+ \ifx\temp\imacro \ptexi
+ \else\ifx\temp\jmacro \j
+ \else \errmessage{@dotless can be used only with i or j}%
+ \fi\fi
+}
+
+% @: forces normal size whitespace following.
+\def\:{\spacefactor=1000 }
+
+% @* forces a line break.
+\def\*{\hfil\break\hbox{}\ignorespaces}
+
+% @. is an end-of-sentence period.
+\def\.{.\spacefactor=3000 }
+
+% @enddots{} is an end-of-sentence ellipsis.
+\gdef\enddots{$\mathinner{\ldotp\ldotp\ldotp\ldotp}$\spacefactor=3000}
+
+% @! is an end-of-sentence bang.
+\gdef\!{!\spacefactor=3000 }
+
+% @? is an end-of-sentence query.
+\gdef\?{?\spacefactor=3000 }
+
+% @w prevents a word break. Without the \leavevmode, @w at the
+% beginning of a paragraph, when TeX is still in vertical mode, would
+% produce a whole line of output instead of starting the paragraph.
+\def\w#1{\leavevmode\hbox{#1}}
+
+% @group ... @end group forces ... to be all on one page, by enclosing
+% it in a TeX vbox. We use \vtop instead of \vbox to construct the box
+% to keep its height that of a normal line. According to the rules for
+% \topskip (p.114 of the TeXbook), the glue inserted is
+% max (\topskip - \ht (first item), 0). If that height is large,
+% therefore, no glue is inserted, and the space between the headline and
+% the text is small, which looks bad.
+%
+\def\group{\begingroup
+ \ifnum\catcode13=\active \else
+ \errhelp = \groupinvalidhelp
+ \errmessage{@group invalid in context where filling is enabled}%
+ \fi
+ %
+ % The \vtop we start below produces a box with normal height and large
+ % depth; thus, TeX puts \baselineskip glue before it, and (when the
+ % next line of text is done) \lineskip glue after it. (See p.82 of
+ % the TeXbook.) Thus, space below is not quite equal to space
+ % above. But it's pretty close.
+ \def\Egroup{%
+ \egroup % End the \vtop.
+ \endgroup % End the \group.
+ }%
+ %
+ \vtop\bgroup
+ % We have to put a strut on the last line in case the @group is in
+ % the midst of an example, rather than completely enclosing it.
+ % Otherwise, the interline space between the last line of the group
+ % and the first line afterwards is too small. But we can't put the
+ % strut in \Egroup, since there it would be on a line by itself.
+ % Hence this just inserts a strut at the beginning of each line.
+ \everypar = {\strut}%
+ %
+ % Since we have a strut on every line, we don't need any of TeX's
+ % normal interline spacing.
+ \offinterlineskip
+ %
+ % OK, but now we have to do something about blank
+ % lines in the input in @example-like environments, which normally
+ % just turn into \lisppar, which will insert no space now that we've
+ % turned off the interline space. Simplest is to make them be an
+ % empty paragraph.
+ \ifx\par\lisppar
+ \edef\par{\leavevmode \par}%
+ %
+ % Reset ^^M's definition to new definition of \par.
+ \obeylines
+ \fi
+ %
+ % Do @comment since we are called inside an environment such as
+ % @example, where each end-of-line in the input causes an
+ % end-of-line in the output. We don't want the end-of-line after
+ % the `@group' to put extra space in the output. Since @group
+ % should appear on a line by itself (according to the Texinfo
+ % manual), we don't worry about eating any user text.
+ \comment
+}
+%
+% TeX puts in an \escapechar (i.e., `@') at the beginning of the help
+% message, so this ends up printing `@group can only ...'.
+%
+\newhelp\groupinvalidhelp{%
+group can only be used in environments such as @example,^^J%
+where each line of input produces a line of output.}
+
+% @need space-in-mils
+% forces a page break if there is not space-in-mils remaining.
+
+\newdimen\mil \mil=0.001in
+
+\def\need{\parsearg\needx}
+
+% Old definition--didn't work.
+%\def\needx #1{\par %
+%% This method tries to make TeX break the page naturally
+%% if the depth of the box does not fit.
+%{\baselineskip=0pt%
+%\vtop to #1\mil{\vfil}\kern -#1\mil\penalty 10000
+%\prevdepth=-1000pt
+%}}
+
+\def\needx#1{%
+ % Go into vertical mode, so we don't make a big box in the middle of a
+ % paragraph.
+ \par
+ %
+ % Don't add any leading before our big empty box, but allow a page
+ % break, since the best break might be right here.
+ \allowbreak
+ \nointerlineskip
+ \vtop to #1\mil{\vfil}%
+ %
+ % TeX does not even consider page breaks if a penalty added to the
+ % main vertical list is 10000 or more. But in order to see if the
+ % empty box we just added fits on the page, we must make it consider
+ % page breaks. On the other hand, we don't want to actually break the
+ % page after the empty box. So we use a penalty of 9999.
+ %
+ % There is an extremely small chance that TeX will actually break the
+ % page at this \penalty, if there are no other feasible breakpoints in
+ % sight. (If the user is using lots of big @group commands, which
+ % almost-but-not-quite fill up a page, TeX will have a hard time doing
+ % good page breaking, for example.) However, I could not construct an
+ % example where a page broke at this \penalty; if it happens in a real
+ % document, then we can reconsider our strategy.
+ \penalty9999
+ %
+ % Back up by the size of the box, whether we did a page break or not.
+ \kern -#1\mil
+ %
+ % Do not allow a page break right after this kern.
+ \nobreak
+}
+
+% @br forces paragraph break
+
+\let\br = \par
+
+% @dots{} output some dots
+
+\def\dots{$\ldots$}
+
+% @page forces the start of a new page
+
+\def\page{\par\vfill\supereject}
+
+% @exdent text....
+% outputs text on separate line in roman font, starting at standard page margin
+
+% This records the amount of indent in the innermost environment.
+% That's how much \exdent should take out.
+\newskip\exdentamount
+
+% This defn is used inside fill environments such as @defun.
+\def\exdent{\parsearg\exdentyyy}
+\def\exdentyyy #1{{\hfil\break\hbox{\kern -\exdentamount{\rm#1}}\hfil\break}}
+
+% This defn is used inside nofill environments such as @example.
+\def\nofillexdent{\parsearg\nofillexdentyyy}
+\def\nofillexdentyyy #1{{\advance \leftskip by -\exdentamount
+\leftline{\hskip\leftskip{\rm#1}}}}
+
+% @inmargin{TEXT} puts TEXT in the margin next to the current paragraph.
+
+\def\inmargin#1{%
+\strut\vadjust{\nobreak\kern-\strutdepth
+ \vtop to \strutdepth{\baselineskip\strutdepth\vss
+ \llap{\rightskip=\inmarginspacing \vbox{\noindent #1}}\null}}}
+\newskip\inmarginspacing \inmarginspacing=1cm
+\def\strutdepth{\dp\strutbox}
+
+%\hbox{{\rm#1}}\hfil\break}}
+
+% @include file insert text of that file as input.
+% Allow normal characters that we make active in the argument (a file name).
+\def\include{\begingroup
+ \catcode`\\=12
+ \catcode`~=12
+ \catcode`^=12
+ \catcode`_=12
+ \catcode`|=12
+ \catcode`<=12
+ \catcode`>=12
+ \catcode`+=12
+ \parsearg\includezzz}
+% Restore active chars for included file.
+\def\includezzz#1{\endgroup\begingroup
+ % Read the included file in a group so nested @include's work.
+ \def\thisfile{#1}%
+ \input\thisfile
+\endgroup}
+
+\def\thisfile{}
+
+% @center line outputs that line, centered
+
+\def\center{\parsearg\centerzzz}
+\def\centerzzz #1{{\advance\hsize by -\leftskip
+\advance\hsize by -\rightskip
+\centerline{#1}}}
+
+% @sp n outputs n lines of vertical space
+
+\def\sp{\parsearg\spxxx}
+\def\spxxx #1{\vskip #1\baselineskip}
+
+% @comment ...line which is ignored...
+% @c is the same as @comment
+% @ignore ... @end ignore is another way to write a comment
+
+\def\comment{\catcode 64=\other \catcode 123=\other \catcode 125=\other%
+\parsearg \commentxxx}
+
+\def\commentxxx #1{\catcode 64=0 \catcode 123=1 \catcode 125=2 }
+
+\let\c=\comment
+
+% @paragraphindent is defined for the Info formatting commands only.
+\let\paragraphindent=\comment
+
+% Prevent errors for section commands.
+% Used in @ignore and in failing conditionals.
+\def\ignoresections{%
+\let\chapter=\relax
+\let\unnumbered=\relax
+\let\top=\relax
+\let\unnumberedsec=\relax
+\let\unnumberedsection=\relax
+\let\unnumberedsubsec=\relax
+\let\unnumberedsubsection=\relax
+\let\unnumberedsubsubsec=\relax
+\let\unnumberedsubsubsection=\relax
+\let\section=\relax
+\let\subsec=\relax
+\let\subsubsec=\relax
+\let\subsection=\relax
+\let\subsubsection=\relax
+\let\appendix=\relax
+\let\appendixsec=\relax
+\let\appendixsection=\relax
+\let\appendixsubsec=\relax
+\let\appendixsubsection=\relax
+\let\appendixsubsubsec=\relax
+\let\appendixsubsubsection=\relax
+\let\contents=\relax
+\let\smallbook=\relax
+\let\titlepage=\relax
+}
+
+% Used in nested conditionals, where we have to parse the Texinfo source
+% and so want to turn off most commands, in case they are used
+% incorrectly.
+%
+\def\ignoremorecommands{%
+ \let\defcodeindex = \relax
+ \let\defcv = \relax
+ \let\deffn = \relax
+ \let\deffnx = \relax
+ \let\defindex = \relax
+ \let\defivar = \relax
+ \let\defmac = \relax
+ \let\defmethod = \relax
+ \let\defop = \relax
+ \let\defopt = \relax
+ \let\defspec = \relax
+ \let\deftp = \relax
+ \let\deftypefn = \relax
+ \let\deftypefun = \relax
+ \let\deftypevar = \relax
+ \let\deftypevr = \relax
+ \let\defun = \relax
+ \let\defvar = \relax
+ \let\defvr = \relax
+ \let\ref = \relax
+ \let\xref = \relax
+ \let\printindex = \relax
+ \let\pxref = \relax
+ \let\settitle = \relax
+ \let\setchapternewpage = \relax
+ \let\setchapterstyle = \relax
+ \let\everyheading = \relax
+ \let\evenheading = \relax
+ \let\oddheading = \relax
+ \let\everyfooting = \relax
+ \let\evenfooting = \relax
+ \let\oddfooting = \relax
+ \let\headings = \relax
+ \let\include = \relax
+ \let\lowersections = \relax
+ \let\down = \relax
+ \let\raisesections = \relax
+ \let\up = \relax
+ \let\set = \relax
+ \let\clear = \relax
+ \let\item = \relax
+}
+
+% Ignore @ignore ... @end ignore.
+%
+\def\ignore{\doignore{ignore}}
+
+% Ignore @ifinfo, @ifhtml, @ifnottex, @html, @menu, and @direntry text.
+%
+\def\ifinfo{\doignore{ifinfo}}
+\def\ifhtml{\doignore{ifhtml}}
+\def\ifnottex{\doignore{ifnottex}}
+\def\html{\doignore{html}}
+\def\menu{\doignore{menu}}
+\def\direntry{\doignore{direntry}}
+
+% Also ignore @macro ... @end macro. The user must run texi2dvi,
+% which runs makeinfo to do macro expansion. Ignore @unmacro, too.
+\def\macro{\doignore{macro}}
+\let\unmacro = \comment
+
+
+% @dircategory CATEGORY -- specify a category of the dir file
+% which this file should belong to. Ignore this in TeX.
+\let\dircategory = \comment
+
+% Ignore text until a line `@end #1'.
+%
+\def\doignore#1{\begingroup
+ % Don't complain about control sequences we have declared \outer.
+ \ignoresections
+ %
+ % Define a command to swallow text until we reach `@end #1'.
+ \long\def\doignoretext##1\end #1{\enddoignore}%
+ %
+ % Make sure that spaces turn into tokens that match what \doignoretext wants.
+ \catcode32 = 10
+ %
+ % Ignore braces, too, so mismatched braces don't cause trouble.
+ \catcode`\{ = 9
+ \catcode`\} = 9
+ %
+ % And now expand that command.
+ \doignoretext
+}
+
+% What we do to finish off ignored text.
+%
+\def\enddoignore{\endgroup\ignorespaces}%
+
+\newif\ifwarnedobs\warnedobsfalse
+\def\obstexwarn{%
+ \ifwarnedobs\relax\else
+ % We need to warn folks that they may have trouble with TeX 3.0.
+ % This uses \immediate\write16 rather than \message to get newlines.
+ \immediate\write16{}
+ \immediate\write16{***WARNING*** for users of Unix TeX 3.0!}
+ \immediate\write16{This manual trips a bug in TeX version 3.0 (tex hangs).}
+ \immediate\write16{If you are running another version of TeX, relax.}
+ \immediate\write16{If you are running Unix TeX 3.0, kill this TeX process.}
+ \immediate\write16{ Then upgrade your TeX installation if you can.}
+ \immediate\write16{ (See ftp://ftp.gnu.ai.mit.edu/pub/gnu/TeX.README.)}
+ \immediate\write16{If you are stuck with version 3.0, run the}
+ \immediate\write16{ script ``tex3patch'' from the Texinfo distribution}
+ \immediate\write16{ to use a workaround.}
+ \immediate\write16{}
+ \global\warnedobstrue
+ \fi
+}
+
+% **In TeX 3.0, setting text in \nullfont hangs tex. For a
+% workaround (which requires the file ``dummy.tfm'' to be installed),
+% uncomment the following line:
+%%%%%\font\nullfont=dummy\let\obstexwarn=\relax
+
+% Ignore text, except that we keep track of conditional commands for
+% purposes of nesting, up to an `@end #1' command.
+%
+\def\nestedignore#1{%
+ \obstexwarn
+ % We must actually expand the ignored text to look for the @end
+ % command, so that nested ignore constructs work. Thus, we put the
+ % text into a \vbox and then do nothing with the result. To minimize
+ % the change of memory overflow, we follow the approach outlined on
+ % page 401 of the TeXbook: make the current font be a dummy font.
+ %
+ \setbox0 = \vbox\bgroup
+ % Don't complain about control sequences we have declared \outer.
+ \ignoresections
+ %
+ % Define `@end #1' to end the box, which will in turn undefine the
+ % @end command again.
+ \expandafter\def\csname E#1\endcsname{\egroup\ignorespaces}%
+ %
+ % We are going to be parsing Texinfo commands. Most cause no
+ % trouble when they are used incorrectly, but some commands do
+ % complicated argument parsing or otherwise get confused, so we
+ % undefine them.
+ %
+ % We can't do anything about stray @-signs, unfortunately;
+ % they'll produce `undefined control sequence' errors.
+ \ignoremorecommands
+ %
+ % Set the current font to be \nullfont, a TeX primitive, and define
+ % all the font commands to also use \nullfont. We don't use
+ % dummy.tfm, as suggested in the TeXbook, because not all sites
+ % might have that installed. Therefore, math mode will still
+ % produce output, but that should be an extremely small amount of
+ % stuff compared to the main input.
+ %
+ \nullfont
+ \let\tenrm = \nullfont \let\tenit = \nullfont \let\tensl = \nullfont
+ \let\tenbf = \nullfont \let\tentt = \nullfont \let\smallcaps = \nullfont
+ \let\tensf = \nullfont
+ % Similarly for index fonts (mostly for their use in
+ % smallexample)
+ \let\indrm = \nullfont \let\indit = \nullfont \let\indsl = \nullfont
+ \let\indbf = \nullfont \let\indtt = \nullfont \let\indsc = \nullfont
+ \let\indsf = \nullfont
+ %
+ % Don't complain when characters are missing from the fonts.
+ \tracinglostchars = 0
+ %
+ % Don't bother to do space factor calculations.
+ \frenchspacing
+ %
+ % Don't report underfull hboxes.
+ \hbadness = 10000
+ %
+ % Do minimal line-breaking.
+ \pretolerance = 10000
+ %
+ % Do not execute instructions in @tex
+ \def\tex{\doignore{tex}}%
+}
+
+% @set VAR sets the variable VAR to an empty value.
+% @set VAR REST-OF-LINE sets VAR to the value REST-OF-LINE.
+%
+% Since we want to separate VAR from REST-OF-LINE (which might be
+% empty), we can't just use \parsearg; we have to insert a space of our
+% own to delimit the rest of the line, and then take it out again if we
+% didn't need it. Make sure the catcode of space is correct to avoid
+% losing inside @example, for instance.
+%
+\def\set{\begingroup\catcode` =10
+ \catcode`\-=12 \catcode`\_=12 % Allow - and _ in VAR.
+ \parsearg\setxxx}
+\def\setxxx#1{\setyyy#1 \endsetyyy}
+\def\setyyy#1 #2\endsetyyy{%
+ \def\temp{#2}%
+ \ifx\temp\empty \global\expandafter\let\csname SET#1\endcsname = \empty
+ \else \setzzz{#1}#2\endsetzzz % Remove the trailing space \setxxx inserted.
+ \fi
+ \endgroup
+}
+% Can't use \xdef to pre-expand #2 and save some time, since \temp or
+% \next or other control sequences that we've defined might get us into
+% an infinite loop. Consider `@set foo @cite{bar}'.
+\def\setzzz#1#2 \endsetzzz{\expandafter\gdef\csname SET#1\endcsname{#2}}
+
+% @clear VAR clears (i.e., unsets) the variable VAR.
+%
+\def\clear{\parsearg\clearxxx}
+\def\clearxxx#1{\global\expandafter\let\csname SET#1\endcsname=\relax}
+
+% @value{foo} gets the text saved in variable foo.
+%
+\def\value{\begingroup
+ \catcode`\-=12 \catcode`\_=12 % Allow - and _ in VAR.
+ \valuexxx}
+\def\valuexxx#1{%
+ \expandafter\ifx\csname SET#1\endcsname\relax
+ {\{No value for ``#1''\}}%
+ \else
+ \csname SET#1\endcsname
+ \fi
+\endgroup}
+
+% @ifset VAR ... @end ifset reads the `...' iff VAR has been defined
+% with @set.
+%
+\def\ifset{\parsearg\ifsetxxx}
+\def\ifsetxxx #1{%
+ \expandafter\ifx\csname SET#1\endcsname\relax
+ \expandafter\ifsetfail
+ \else
+ \expandafter\ifsetsucceed
+ \fi
+}
+\def\ifsetsucceed{\conditionalsucceed{ifset}}
+\def\ifsetfail{\nestedignore{ifset}}
+\defineunmatchedend{ifset}
+
+% @ifclear VAR ... @end ifclear reads the `...' iff VAR has never been
+% defined with @set, or has been undefined with @clear.
+%
+\def\ifclear{\parsearg\ifclearxxx}
+\def\ifclearxxx #1{%
+ \expandafter\ifx\csname SET#1\endcsname\relax
+ \expandafter\ifclearsucceed
+ \else
+ \expandafter\ifclearfail
+ \fi
+}
+\def\ifclearsucceed{\conditionalsucceed{ifclear}}
+\def\ifclearfail{\nestedignore{ifclear}}
+\defineunmatchedend{ifclear}
+
+% @iftex, @ifnothtml, @ifnotinfo always succeed; we read the text
+% following, through the first @end iftex (etc.). Make `@end iftex'
+% (etc.) valid only after an @iftex.
+%
+\def\iftex{\conditionalsucceed{iftex}}
+\def\ifnothtml{\conditionalsucceed{ifnothtml}}
+\def\ifnotinfo{\conditionalsucceed{ifnotinfo}}
+\defineunmatchedend{iftex}
+\defineunmatchedend{ifnothtml}
+\defineunmatchedend{ifnotinfo}
+
+% We can't just want to start a group at @iftex (for example) and end it
+% at @end iftex, since then @set commands inside the conditional have no
+% effect (they'd get reverted at the end of the group). So we must
+% define \Eiftex to redefine itself to be its previous value. (We can't
+% just define it to fail again with an ``unmatched end'' error, since
+% the @ifset might be nested.)
+%
+\def\conditionalsucceed#1{%
+ \edef\temp{%
+ % Remember the current value of \E#1.
+ \let\nece{prevE#1} = \nece{E#1}%
+ %
+ % At the `@end #1', redefine \E#1 to be its previous value.
+ \def\nece{E#1}{\let\nece{E#1} = \nece{prevE#1}}%
+ }%
+ \temp
+}
+
+% We need to expand lots of \csname's, but we don't want to expand the
+% control sequences after we've constructed them.
+%
+\def\nece#1{\expandafter\noexpand\csname#1\endcsname}
+
+% @asis just yields its argument. Used with @table, for example.
+%
+\def\asis#1{#1}
+
+% @math means output in math mode.
+% We don't use $'s directly in the definition of \math because control
+% sequences like \math are expanded when the toc file is written. Then,
+% we read the toc file back, the $'s will be normal characters (as they
+% should be, according to the definition of Texinfo). So we must use a
+% control sequence to switch into and out of math mode.
+%
+% This isn't quite enough for @math to work properly in indices, but it
+% seems unlikely it will ever be needed there.
+%
+\let\implicitmath = $
+\def\math#1{\implicitmath #1\implicitmath}
+
+% @bullet and @minus need the same treatment as @math, just above.
+\def\bullet{\implicitmath\ptexbullet\implicitmath}
+\def\minus{\implicitmath-\implicitmath}
+
+\def\node{\ENVcheck\parsearg\nodezzz}
+\def\nodezzz#1{\nodexxx [#1,]}
+\def\nodexxx[#1,#2]{\gdef\lastnode{#1}}
+\let\nwnode=\node
+\let\lastnode=\relax
+
+\def\donoderef{\ifx\lastnode\relax\else
+\expandafter\expandafter\expandafter\setref{\lastnode}\fi
+\global\let\lastnode=\relax}
+
+\def\unnumbnoderef{\ifx\lastnode\relax\else
+\expandafter\expandafter\expandafter\unnumbsetref{\lastnode}\fi
+\global\let\lastnode=\relax}
+
+\def\appendixnoderef{\ifx\lastnode\relax\else
+\expandafter\expandafter\expandafter\appendixsetref{\lastnode}\fi
+\global\let\lastnode=\relax}
+
+% @refill is a no-op.
+\let\refill=\relax
+
+% @setfilename is done at the beginning of every texinfo file.
+% So open here the files we need to have open while reading the input.
+% This makes it possible to make a .fmt file for texinfo.
+\def\setfilename{%
+ \readauxfile
+ \opencontents
+ \openindices
+ \fixbackslash % Turn off hack to swallow `\input texinfo'.
+ \global\let\setfilename=\comment % Ignore extra @setfilename cmds.
+ %
+ % If texinfo.cnf is present on the system, read it.
+ % Useful for site-wide @afourpaper, etc.
+ % Just to be on the safe side, close the input stream before the \input.
+ \openin 1 texinfo.cnf
+ \ifeof1 \let\temp=\relax \else \def\temp{\input texinfo.cnf }\fi
+ \closein1
+ \temp
+ %
+ \comment % Ignore the actual filename.
+}
+
+% @bye.
+\outer\def\bye{\pagealignmacro\tracingstats=1\ptexend}
+
+% \def\macro#1{\begingroup\ignoresections\catcode`\#=6\def\macrotemp{#1}\parsearg\macroxxx}
+% \def\macroxxx#1#2 \end macro{%
+% \expandafter\gdef\macrotemp#1{#2}%
+% \endgroup}
+
+%\def\linemacro#1{\begingroup\ignoresections\catcode`\#=6\def\macrotemp{#1}\parsearg\linemacroxxx}
+%\def\linemacroxxx#1#2 \end linemacro{%
+%\let\parsearg=\relax
+%\edef\macrotempx{\csname M\butfirst\expandafter\string\macrotemp\endcsname}%
+%\expandafter\xdef\macrotemp{\parsearg\macrotempx}%
+%\expandafter\gdef\macrotempx#1{#2}%
+%\endgroup}
+
+%\def\butfirst#1{}
+
+
+\message{fonts,}
+
+% Font-change commands.
+
+% Texinfo supports the sans serif font style, which plain TeX does not.
+% So we set up a \sf analogous to plain's \rm, etc.
+\newfam\sffam
+\def\sf{\fam=\sffam \tensf}
+\let\li = \sf % Sometimes we call it \li, not \sf.
+
+% We don't need math for this one.
+\def\ttsl{\tenttsl}
+
+% Use Computer Modern fonts at \magstephalf (11pt).
+\newcount\mainmagstep
+\mainmagstep=\magstephalf
+
+% Set the font macro #1 to the font named #2, adding on the
+% specified font prefix (normally `cm').
+% #3 is the font's design size, #4 is a scale factor
+\def\setfont#1#2#3#4{\font#1=\fontprefix#2#3 scaled #4}
+
+% Use cm as the default font prefix.
+% To specify the font prefix, you must define \fontprefix
+% before you read in texinfo.tex.
+\ifx\fontprefix\undefined
+\def\fontprefix{cm}
+\fi
+% Support font families that don't use the same naming scheme as CM.
+\def\rmshape{r}
+\def\rmbshape{bx} %where the normal face is bold
+\def\bfshape{b}
+\def\bxshape{bx}
+\def\ttshape{tt}
+\def\ttbshape{tt}
+\def\ttslshape{sltt}
+\def\itshape{ti}
+\def\itbshape{bxti}
+\def\slshape{sl}
+\def\slbshape{bxsl}
+\def\sfshape{ss}
+\def\sfbshape{ss}
+\def\scshape{csc}
+\def\scbshape{csc}
+
+\ifx\bigger\relax
+\let\mainmagstep=\magstep1
+\setfont\textrm\rmshape{12}{1000}
+\setfont\texttt\ttshape{12}{1000}
+\else
+\setfont\textrm\rmshape{10}{\mainmagstep}
+\setfont\texttt\ttshape{10}{\mainmagstep}
+\fi
+% Instead of cmb10, you many want to use cmbx10.
+% cmbx10 is a prettier font on its own, but cmb10
+% looks better when embedded in a line with cmr10.
+\setfont\textbf\bfshape{10}{\mainmagstep}
+\setfont\textit\itshape{10}{\mainmagstep}
+\setfont\textsl\slshape{10}{\mainmagstep}
+\setfont\textsf\sfshape{10}{\mainmagstep}
+\setfont\textsc\scshape{10}{\mainmagstep}
+\setfont\textttsl\ttslshape{10}{\mainmagstep}
+\font\texti=cmmi10 scaled \mainmagstep
+\font\textsy=cmsy10 scaled \mainmagstep
+
+% A few fonts for @defun, etc.
+\setfont\defbf\bxshape{10}{\magstep1} %was 1314
+\setfont\deftt\ttshape{10}{\magstep1}
+\def\df{\let\tentt=\deftt \let\tenbf = \defbf \bf}
+
+% Fonts for indices and small examples (9pt).
+% We actually use the slanted font rather than the italic,
+% because texinfo normally uses the slanted fonts for that.
+% Do not make many font distinctions in general in the index, since they
+% aren't very useful.
+\setfont\ninett\ttshape{9}{1000}
+\setfont\indrm\rmshape{9}{1000}
+\setfont\indit\slshape{9}{1000}
+\let\indsl=\indit
+\let\indtt=\ninett
+\let\indttsl=\ninett
+\let\indsf=\indrm
+\let\indbf=\indrm
+\setfont\indsc\scshape{10}{900}
+\font\indi=cmmi9
+\font\indsy=cmsy9
+
+% Fonts for title page:
+\setfont\titlerm\rmbshape{12}{\magstep3}
+\setfont\titleit\itbshape{10}{\magstep4}
+\setfont\titlesl\slbshape{10}{\magstep4}
+\setfont\titlett\ttbshape{12}{\magstep3}
+\setfont\titlettsl\ttslshape{10}{\magstep4}
+\setfont\titlesf\sfbshape{17}{\magstep1}
+\let\titlebf=\titlerm
+\setfont\titlesc\scbshape{10}{\magstep4}
+\font\titlei=cmmi12 scaled \magstep3
+\font\titlesy=cmsy10 scaled \magstep4
+\def\authorrm{\secrm}
+
+% Chapter (and unnumbered) fonts (17.28pt).
+\setfont\chaprm\rmbshape{12}{\magstep2}
+\setfont\chapit\itbshape{10}{\magstep3}
+\setfont\chapsl\slbshape{10}{\magstep3}
+\setfont\chaptt\ttbshape{12}{\magstep2}
+\setfont\chapttsl\ttslshape{10}{\magstep3}
+\setfont\chapsf\sfbshape{17}{1000}
+\let\chapbf=\chaprm
+\setfont\chapsc\scbshape{10}{\magstep3}
+\font\chapi=cmmi12 scaled \magstep2
+\font\chapsy=cmsy10 scaled \magstep3
+
+% Section fonts (14.4pt).
+\setfont\secrm\rmbshape{12}{\magstep1}
+\setfont\secit\itbshape{10}{\magstep2}
+\setfont\secsl\slbshape{10}{\magstep2}
+\setfont\sectt\ttbshape{12}{\magstep1}
+\setfont\secttsl\ttslshape{10}{\magstep2}
+\setfont\secsf\sfbshape{12}{\magstep1}
+\let\secbf\secrm
+\setfont\secsc\scbshape{10}{\magstep2}
+\font\seci=cmmi12 scaled \magstep1
+\font\secsy=cmsy10 scaled \magstep2
+
+% \setfont\ssecrm\bxshape{10}{\magstep1} % This size an font looked bad.
+% \setfont\ssecit\itshape{10}{\magstep1} % The letters were too crowded.
+% \setfont\ssecsl\slshape{10}{\magstep1}
+% \setfont\ssectt\ttshape{10}{\magstep1}
+% \setfont\ssecsf\sfshape{10}{\magstep1}
+
+%\setfont\ssecrm\bfshape{10}{1315} % Note the use of cmb rather than cmbx.
+%\setfont\ssecit\itshape{10}{1315} % Also, the size is a little larger than
+%\setfont\ssecsl\slshape{10}{1315} % being scaled magstep1.
+%\setfont\ssectt\ttshape{10}{1315}
+%\setfont\ssecsf\sfshape{10}{1315}
+
+%\let\ssecbf=\ssecrm
+
+% Subsection fonts (13.15pt).
+\setfont\ssecrm\rmbshape{12}{\magstephalf}
+\setfont\ssecit\itbshape{10}{1315}
+\setfont\ssecsl\slbshape{10}{1315}
+\setfont\ssectt\ttbshape{12}{\magstephalf}
+\setfont\ssecttsl\ttslshape{10}{1315}
+\setfont\ssecsf\sfbshape{12}{\magstephalf}
+\let\ssecbf\ssecrm
+\setfont\ssecsc\scbshape{10}{\magstep1}
+\font\sseci=cmmi12 scaled \magstephalf
+\font\ssecsy=cmsy10 scaled 1315
+% The smallcaps and symbol fonts should actually be scaled \magstep1.5,
+% but that is not a standard magnification.
+
+% In order for the font changes to affect most math symbols and letters,
+% we have to define the \textfont of the standard families. Since
+% texinfo doesn't allow for producing subscripts and superscripts, we
+% don't bother to reset \scriptfont and \scriptscriptfont (which would
+% also require loading a lot more fonts).
+%
+\def\resetmathfonts{%
+ \textfont0 = \tenrm \textfont1 = \teni \textfont2 = \tensy
+ \textfont\itfam = \tenit \textfont\slfam = \tensl \textfont\bffam = \tenbf
+ \textfont\ttfam = \tentt \textfont\sffam = \tensf
+}
+
+
+% The font-changing commands redefine the meanings of \tenSTYLE, instead
+% of just \STYLE. We do this so that font changes will continue to work
+% in math mode, where it is the current \fam that is relevant in most
+% cases, not the current font. Plain TeX does \def\bf{\fam=\bffam
+% \tenbf}, for example. By redefining \tenbf, we obviate the need to
+% redefine \bf itself.
+\def\textfonts{%
+ \let\tenrm=\textrm \let\tenit=\textit \let\tensl=\textsl
+ \let\tenbf=\textbf \let\tentt=\texttt \let\smallcaps=\textsc
+ \let\tensf=\textsf \let\teni=\texti \let\tensy=\textsy \let\tenttsl=\textttsl
+ \resetmathfonts}
+\def\titlefonts{%
+ \let\tenrm=\titlerm \let\tenit=\titleit \let\tensl=\titlesl
+ \let\tenbf=\titlebf \let\tentt=\titlett \let\smallcaps=\titlesc
+ \let\tensf=\titlesf \let\teni=\titlei \let\tensy=\titlesy
+ \let\tenttsl=\titlettsl
+ \resetmathfonts \setleading{25pt}}
+\def\titlefont#1{{\titlefonts #1}}
+\def\chapfonts{%
+ \let\tenrm=\chaprm \let\tenit=\chapit \let\tensl=\chapsl
+ \let\tenbf=\chapbf \let\tentt=\chaptt \let\smallcaps=\chapsc
+ \let\tensf=\chapsf \let\teni=\chapi \let\tensy=\chapsy \let\tenttsl=\chapttsl
+ \resetmathfonts \setleading{19pt}}
+\def\secfonts{%
+ \let\tenrm=\secrm \let\tenit=\secit \let\tensl=\secsl
+ \let\tenbf=\secbf \let\tentt=\sectt \let\smallcaps=\secsc
+ \let\tensf=\secsf \let\teni=\seci \let\tensy=\secsy \let\tenttsl=\secttsl
+ \resetmathfonts \setleading{16pt}}
+\def\subsecfonts{%
+ \let\tenrm=\ssecrm \let\tenit=\ssecit \let\tensl=\ssecsl
+ \let\tenbf=\ssecbf \let\tentt=\ssectt \let\smallcaps=\ssecsc
+ \let\tensf=\ssecsf \let\teni=\sseci \let\tensy=\ssecsy \let\tenttsl=\ssecttsl
+ \resetmathfonts \setleading{15pt}}
+\let\subsubsecfonts = \subsecfonts % Maybe make sssec fonts scaled magstephalf?
+\def\indexfonts{%
+ \let\tenrm=\indrm \let\tenit=\indit \let\tensl=\indsl
+ \let\tenbf=\indbf \let\tentt=\indtt \let\smallcaps=\indsc
+ \let\tensf=\indsf \let\teni=\indi \let\tensy=\indsy \let\tenttsl=\indttsl
+ \resetmathfonts \setleading{12pt}}
+
+% Set up the default fonts, so we can use them for creating boxes.
+%
+\textfonts
+
+% Count depth in font-changes, for error checks
+\newcount\fontdepth \fontdepth=0
+
+% Fonts for short table of contents.
+\setfont\shortcontrm\rmshape{12}{1000}
+\setfont\shortcontbf\bxshape{12}{1000}
+\setfont\shortcontsl\slshape{12}{1000}
+
+%% Add scribe-like font environments, plus @l for inline lisp (usually sans
+%% serif) and @ii for TeX italic
+
+% \smartitalic{ARG} outputs arg in italics, followed by an italic correction
+% unless the following character is such as not to need one.
+\def\smartitalicx{\ifx\next,\else\ifx\next-\else\ifx\next.\else\/\fi\fi\fi}
+\def\smartitalic#1{{\sl #1}\futurelet\next\smartitalicx}
+
+\let\i=\smartitalic
+\let\var=\smartitalic
+\let\dfn=\smartitalic
+\let\emph=\smartitalic
+\let\cite=\smartitalic
+
+\def\b#1{{\bf #1}}
+\let\strong=\b
+
+% We can't just use \exhyphenpenalty, because that only has effect at
+% the end of a paragraph. Restore normal hyphenation at the end of the
+% group within which \nohyphenation is presumably called.
+%
+\def\nohyphenation{\hyphenchar\font = -1 \aftergroup\restorehyphenation}
+\def\restorehyphenation{\hyphenchar\font = `- }
+
+\def\t#1{%
+ {\tt \rawbackslash \frenchspacing #1}%
+ \null
+}
+\let\ttfont=\t
+\def\samp #1{`\tclose{#1}'\null}
+\setfont\smallrm\rmshape{8}{1000}
+\font\smallsy=cmsy9
+\def\key#1{{\smallrm\textfont2=\smallsy \leavevmode\hbox{%
+ \raise0.4pt\hbox{$\langle$}\kern-.08em\vtop{%
+ \vbox{\hrule\kern-0.4pt
+ \hbox{\raise0.4pt\hbox{\vphantom{$\langle$}}#1}}%
+ \kern-0.4pt\hrule}%
+ \kern-.06em\raise0.4pt\hbox{$\rangle$}}}}
+% The old definition, with no lozenge:
+%\def\key #1{{\ttsl \nohyphenation \uppercase{#1}}\null}
+\def\ctrl #1{{\tt \rawbackslash \hat}#1}
+
+\let\file=\samp
+
+% @code is a modification of @t,
+% which makes spaces the same size as normal in the surrounding text.
+\def\tclose#1{%
+ {%
+ % Change normal interword space to be same as for the current font.
+ \spaceskip = \fontdimen2\font
+ %
+ % Switch to typewriter.
+ \tt
+ %
+ % But `\ ' produces the large typewriter interword space.
+ \def\ {{\spaceskip = 0pt{} }}%
+ %
+ % Turn off hyphenation.
+ \nohyphenation
+ %
+ \rawbackslash
+ \frenchspacing
+ #1%
+ }%
+ \null
+}
+
+% We *must* turn on hyphenation at `-' and `_' in \code.
+% Otherwise, it is too hard to avoid overfull hboxes
+% in the Emacs manual, the Library manual, etc.
+
+% Unfortunately, TeX uses one parameter (\hyphenchar) to control
+% both hyphenation at - and hyphenation within words.
+% We must therefore turn them both off (\tclose does that)
+% and arrange explicitly to hyphenate at a dash.
+% -- rms.
+{
+\catcode`\-=\active
+\catcode`\_=\active
+\catcode`\|=\active
+\global\def\code{\begingroup \catcode`\-=\active \let-\codedash \catcode`\_=\active \let_\codeunder \codex}
+% The following is used by \doprintindex to insure that long function names
+% wrap around. It is necessary for - and _ to be active before the index is
+% read from the file, as \entry parses the arguments long before \code is
+% ever called. -- mycroft
+% _ is always active; and it shouldn't be \let = to an _ that is a
+% subscript character anyway. Then, @cindex @samp{_} (for example)
+% fails. --karl
+\global\def\indexbreaks{%
+ \catcode`\-=\active \let-\realdash
+}
+}
+
+\def\realdash{-}
+\def\codedash{-\discretionary{}{}{}}
+\def\codeunder{\ifusingtt{\normalunderscore\discretionary{}{}{}}{\_}}
+\def\codex #1{\tclose{#1}\endgroup}
+
+%\let\exp=\tclose %Was temporary
+
+% @kbd is like @code, except that if the argument is just one @key command,
+% then @kbd has no effect.
+
+% @kbdinputstyle -- arg is `distinct' (@kbd uses slanted tty font always),
+% `example' (@kbd uses ttsl only inside of @example and friends),
+% or `code' (@kbd uses normal tty font always).
+\def\kbdinputstyle{\parsearg\kbdinputstylexxx}
+\def\kbdinputstylexxx#1{%
+ \def\arg{#1}%
+ \ifx\arg\worddistinct
+ \gdef\kbdexamplefont{\ttsl}\gdef\kbdfont{\ttsl}%
+ \else\ifx\arg\wordexample
+ \gdef\kbdexamplefont{\ttsl}\gdef\kbdfont{\tt}%
+ \else\ifx\arg\wordcode
+ \gdef\kbdexamplefont{\tt}\gdef\kbdfont{\tt}%
+ \fi\fi\fi
+}
+\def\worddistinct{distinct}
+\def\wordexample{example}
+\def\wordcode{code}
+
+% Default is kbdinputdistinct. (Too much of a hassle to call the macro,
+% the catcodes are wrong for parsearg to work.)
+\gdef\kbdexamplefont{\ttsl}\gdef\kbdfont{\ttsl}
+
+\def\xkey{\key}
+\def\kbdfoo#1#2#3\par{\def\one{#1}\def\three{#3}\def\threex{??}%
+\ifx\one\xkey\ifx\threex\three \key{#2}%
+\else{\tclose{\kbdfont\look}}\fi
+\else{\tclose{\kbdfont\look}}\fi}
+
+% @url. Quotes do not seem necessary, so use \code.
+\let\url=\code
+
+% @uref (abbreviation for `urlref') takes an optional second argument
+% specifying the text to display. First (mandatory) arg is the url.
+% Perhaps eventually put in a hypertex \special here.
+%
+\def\uref#1{\urefxxx #1,,\finish}
+\def\urefxxx#1,#2,#3\finish{%
+ \setbox0 = \hbox{\ignorespaces #2}%
+ \ifdim\wd0 > 0pt
+ \unhbox0\ (\code{#1})%
+ \else
+ \code{#1}%
+ \fi
+}
+
+% rms does not like the angle brackets --karl, 17may97.
+% So now @email is just like @uref.
+%\def\email#1{$\langle${\tt #1}$\rangle$}
+\let\email=\uref
+
+% Check if we are currently using a typewriter font. Since all the
+% Computer Modern typewriter fonts have zero interword stretch (and
+% shrink), and it is reasonable to expect all typewriter fonts to have
+% this property, we can check that font parameter.
+%
+\def\ifmonospace{\ifdim\fontdimen3\font=0pt }
+
+% Typeset a dimension, e.g., `in' or `pt'. The only reason for the
+% argument is to make the input look right: @dmn{pt} instead of
+% @dmn{}pt.
+%
+\def\dmn#1{\thinspace #1}
+
+\def\kbd#1{\def\look{#1}\expandafter\kbdfoo\look??\par}
+
+% @l was never documented to mean ``switch to the Lisp font'',
+% and it is not used as such in any manual I can find. We need it for
+% Polish suppressed-l. --karl, 22sep96.
+%\def\l#1{{\li #1}\null}
+
+\def\r#1{{\rm #1}} % roman font
+% Use of \lowercase was suggested.
+\def\sc#1{{\smallcaps#1}} % smallcaps font
+\def\ii#1{{\it #1}} % italic font
+
+% @pounds{} is a sterling sign.
+\def\pounds{{\it\$}}
+
+
+\message{page headings,}
+
+\newskip\titlepagetopglue \titlepagetopglue = 1.5in
+\newskip\titlepagebottomglue \titlepagebottomglue = 2pc
+
+% First the title page. Must do @settitle before @titlepage.
+\newif\ifseenauthor
+\newif\iffinishedtitlepage
+
+\def\shorttitlepage{\parsearg\shorttitlepagezzz}
+\def\shorttitlepagezzz #1{\begingroup\hbox{}\vskip 1.5in \chaprm \centerline{#1}%
+ \endgroup\page\hbox{}\page}
+
+\def\titlepage{\begingroup \parindent=0pt \textfonts
+ \let\subtitlerm=\tenrm
+% I deinstalled the following change because \cmr12 is undefined.
+% This change was not in the ChangeLog anyway. --rms.
+% \let\subtitlerm=\cmr12
+ \def\subtitlefont{\subtitlerm \normalbaselineskip = 13pt \normalbaselines}%
+ %
+ \def\authorfont{\authorrm \normalbaselineskip = 16pt \normalbaselines}%
+ %
+ % Leave some space at the very top of the page.
+ \vglue\titlepagetopglue
+ %
+ % Now you can print the title using @title.
+ \def\title{\parsearg\titlezzz}%
+ \def\titlezzz##1{\leftline{\titlefonts\rm ##1}
+ % print a rule at the page bottom also.
+ \finishedtitlepagefalse
+ \vskip4pt \hrule height 4pt width \hsize \vskip4pt}%
+ % No rule at page bottom unless we print one at the top with @title.
+ \finishedtitlepagetrue
+ %
+ % Now you can put text using @subtitle.
+ \def\subtitle{\parsearg\subtitlezzz}%
+ \def\subtitlezzz##1{{\subtitlefont \rightline{##1}}}%
+ %
+ % @author should come last, but may come many times.
+ \def\author{\parsearg\authorzzz}%
+ \def\authorzzz##1{\ifseenauthor\else\vskip 0pt plus 1filll\seenauthortrue\fi
+ {\authorfont \leftline{##1}}}%
+ %
+ % Most title ``pages'' are actually two pages long, with space
+ % at the top of the second. We don't want the ragged left on the second.
+ \let\oldpage = \page
+ \def\page{%
+ \iffinishedtitlepage\else
+ \finishtitlepage
+ \fi
+ \oldpage
+ \let\page = \oldpage
+ \hbox{}}%
+% \def\page{\oldpage \hbox{}}
+}
+
+\def\Etitlepage{%
+ \iffinishedtitlepage\else
+ \finishtitlepage
+ \fi
+ % It is important to do the page break before ending the group,
+ % because the headline and footline are only empty inside the group.
+ % If we use the new definition of \page, we always get a blank page
+ % after the title page, which we certainly don't want.
+ \oldpage
+ \endgroup
+ \HEADINGSon
+}
+
+\def\finishtitlepage{%
+ \vskip4pt \hrule height 2pt width \hsize
+ \vskip\titlepagebottomglue
+ \finishedtitlepagetrue
+}
+
+%%% Set up page headings and footings.
+
+\let\thispage=\folio
+
+\newtoks \evenheadline % Token sequence for heading line of even pages
+\newtoks \oddheadline % Token sequence for heading line of odd pages
+\newtoks \evenfootline % Token sequence for footing line of even pages
+\newtoks \oddfootline % Token sequence for footing line of odd pages
+
+% Now make Tex use those variables
+\headline={{\textfonts\rm \ifodd\pageno \the\oddheadline
+ \else \the\evenheadline \fi}}
+\footline={{\textfonts\rm \ifodd\pageno \the\oddfootline
+ \else \the\evenfootline \fi}\HEADINGShook}
+\let\HEADINGShook=\relax
+
+% Commands to set those variables.
+% For example, this is what @headings on does
+% @evenheading @thistitle|@thispage|@thischapter
+% @oddheading @thischapter|@thispage|@thistitle
+% @evenfooting @thisfile||
+% @oddfooting ||@thisfile
+
+\def\evenheading{\parsearg\evenheadingxxx}
+\def\oddheading{\parsearg\oddheadingxxx}
+\def\everyheading{\parsearg\everyheadingxxx}
+
+\def\evenfooting{\parsearg\evenfootingxxx}
+\def\oddfooting{\parsearg\oddfootingxxx}
+\def\everyfooting{\parsearg\everyfootingxxx}
+
+{\catcode`\@=0 %
+
+\gdef\evenheadingxxx #1{\evenheadingyyy #1@|@|@|@|\finish}
+\gdef\evenheadingyyy #1@|#2@|#3@|#4\finish{%
+\global\evenheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}}}
+
+\gdef\oddheadingxxx #1{\oddheadingyyy #1@|@|@|@|\finish}
+\gdef\oddheadingyyy #1@|#2@|#3@|#4\finish{%
+\global\oddheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}}}
+
+\gdef\everyheadingxxx#1{\oddheadingxxx{#1}\evenheadingxxx{#1}}%
+
+\gdef\evenfootingxxx #1{\evenfootingyyy #1@|@|@|@|\finish}
+\gdef\evenfootingyyy #1@|#2@|#3@|#4\finish{%
+\global\evenfootline={\rlap{\centerline{#2}}\line{#1\hfil#3}}}
+
+\gdef\oddfootingxxx #1{\oddfootingyyy #1@|@|@|@|\finish}
+\gdef\oddfootingyyy #1@|#2@|#3@|#4\finish{%
+ \global\oddfootline = {\rlap{\centerline{#2}}\line{#1\hfil#3}}%
+ %
+ % Leave some space for the footline. Hopefully ok to assume
+ % @evenfooting will not be used by itself.
+ \global\advance\pageheight by -\baselineskip
+ \global\advance\vsize by -\baselineskip
+}
+
+\gdef\everyfootingxxx#1{\oddfootingxxx{#1}\evenfootingxxx{#1}}
+%
+}% unbind the catcode of @.
+
+% @headings double turns headings on for double-sided printing.
+% @headings single turns headings on for single-sided printing.
+% @headings off turns them off.
+% @headings on same as @headings double, retained for compatibility.
+% @headings after turns on double-sided headings after this page.
+% @headings doubleafter turns on double-sided headings after this page.
+% @headings singleafter turns on single-sided headings after this page.
+% By default, they are off at the start of a document,
+% and turned `on' after @end titlepage.
+
+\def\headings #1 {\csname HEADINGS#1\endcsname}
+
+\def\HEADINGSoff{
+\global\evenheadline={\hfil} \global\evenfootline={\hfil}
+\global\oddheadline={\hfil} \global\oddfootline={\hfil}}
+\HEADINGSoff
+% When we turn headings on, set the page number to 1.
+% For double-sided printing, put current file name in lower left corner,
+% chapter name on inside top of right hand pages, document
+% title on inside top of left hand pages, and page numbers on outside top
+% edge of all pages.
+\def\HEADINGSdouble{
+\global\pageno=1
+\global\evenfootline={\hfil}
+\global\oddfootline={\hfil}
+\global\evenheadline={\line{\folio\hfil\thistitle}}
+\global\oddheadline={\line{\thischapter\hfil\folio}}
+\global\let\contentsalignmacro = \chapoddpage
+}
+\let\contentsalignmacro = \chappager
+
+% For single-sided printing, chapter title goes across top left of page,
+% page number on top right.
+\def\HEADINGSsingle{
+\global\pageno=1
+\global\evenfootline={\hfil}
+\global\oddfootline={\hfil}
+\global\evenheadline={\line{\thischapter\hfil\folio}}
+\global\oddheadline={\line{\thischapter\hfil\folio}}
+\global\let\contentsalignmacro = \chappager
+}
+\def\HEADINGSon{\HEADINGSdouble}
+
+\def\HEADINGSafter{\let\HEADINGShook=\HEADINGSdoublex}
+\let\HEADINGSdoubleafter=\HEADINGSafter
+\def\HEADINGSdoublex{%
+\global\evenfootline={\hfil}
+\global\oddfootline={\hfil}
+\global\evenheadline={\line{\folio\hfil\thistitle}}
+\global\oddheadline={\line{\thischapter\hfil\folio}}
+\global\let\contentsalignmacro = \chapoddpage
+}
+
+\def\HEADINGSsingleafter{\let\HEADINGShook=\HEADINGSsinglex}
+\def\HEADINGSsinglex{%
+\global\evenfootline={\hfil}
+\global\oddfootline={\hfil}
+\global\evenheadline={\line{\thischapter\hfil\folio}}
+\global\oddheadline={\line{\thischapter\hfil\folio}}
+\global\let\contentsalignmacro = \chappager
+}
+
+% Subroutines used in generating headings
+% Produces Day Month Year style of output.
+\def\today{\number\day\space
+\ifcase\month\or
+January\or February\or March\or April\or May\or June\or
+July\or August\or September\or October\or November\or December\fi
+\space\number\year}
+
+% Use this if you want the Month Day, Year style of output.
+%\def\today{\ifcase\month\or
+%January\or February\or March\or April\or May\or June\or
+%July\or August\or September\or October\or November\or December\fi
+%\space\number\day, \number\year}
+
+% @settitle line... specifies the title of the document, for headings
+% It generates no output of its own
+
+\def\thistitle{No Title}
+\def\settitle{\parsearg\settitlezzz}
+\def\settitlezzz #1{\gdef\thistitle{#1}}
+
+
+\message{tables,}
+
+% @tabs -- simple alignment
+
+% These don't work. For one thing, \+ is defined as outer.
+% So these macros cannot even be defined.
+
+%\def\tabs{\parsearg\tabszzz}
+%\def\tabszzz #1{\settabs\+#1\cr}
+%\def\tabline{\parsearg\tablinezzz}
+%\def\tablinezzz #1{\+#1\cr}
+%\def\&{&}
+
+% Tables -- @table, @ftable, @vtable, @item(x), @kitem(x), @xitem(x).
+
+% default indentation of table text
+\newdimen\tableindent \tableindent=.8in
+% default indentation of @itemize and @enumerate text
+\newdimen\itemindent \itemindent=.3in
+% margin between end of table item and start of table text.
+\newdimen\itemmargin \itemmargin=.1in
+
+% used internally for \itemindent minus \itemmargin
+\newdimen\itemmax
+
+% Note @table, @vtable, and @vtable define @item, @itemx, etc., with
+% these defs.
+% They also define \itemindex
+% to index the item name in whatever manner is desired (perhaps none).
+
+\newif\ifitemxneedsnegativevskip
+
+\def\itemxpar{\par\ifitemxneedsnegativevskip\nobreak\vskip-\parskip\nobreak\fi}
+
+\def\internalBitem{\smallbreak \parsearg\itemzzz}
+\def\internalBitemx{\itemxpar \parsearg\itemzzz}
+
+\def\internalBxitem "#1"{\def\xitemsubtopix{#1} \smallbreak \parsearg\xitemzzz}
+\def\internalBxitemx "#1"{\def\xitemsubtopix{#1} \itemxpar \parsearg\xitemzzz}
+
+\def\internalBkitem{\smallbreak \parsearg\kitemzzz}
+\def\internalBkitemx{\itemxpar \parsearg\kitemzzz}
+
+\def\kitemzzz #1{\dosubind {kw}{\code{#1}}{for {\bf \lastfunction}}%
+ \itemzzz {#1}}
+
+\def\xitemzzz #1{\dosubind {kw}{\code{#1}}{for {\bf \xitemsubtopic}}%
+ \itemzzz {#1}}
+
+\def\itemzzz #1{\begingroup %
+ \advance\hsize by -\rightskip
+ \advance\hsize by -\tableindent
+ \setbox0=\hbox{\itemfont{#1}}%
+ \itemindex{#1}%
+ \nobreak % This prevents a break before @itemx.
+ %
+ % Be sure we are not still in the middle of a paragraph.
+ %{\parskip = 0in
+ %\par
+ %}%
+ %
+ % If the item text does not fit in the space we have, put it on a line
+ % by itself, and do not allow a page break either before or after that
+ % line. We do not start a paragraph here because then if the next
+ % command is, e.g., @kindex, the whatsit would get put into the
+ % horizontal list on a line by itself, resulting in extra blank space.
+ \ifdim \wd0>\itemmax
+ %
+ % Make this a paragraph so we get the \parskip glue and wrapping,
+ % but leave it ragged-right.
+ \begingroup
+ \advance\leftskip by-\tableindent
+ \advance\hsize by\tableindent
+ \advance\rightskip by0pt plus1fil
+ \leavevmode\unhbox0\par
+ \endgroup
+ %
+ % We're going to be starting a paragraph, but we don't want the
+ % \parskip glue -- logically it's part of the @item we just started.
+ \nobreak \vskip-\parskip
+ %
+ % Stop a page break at the \parskip glue coming up. Unfortunately
+ % we can't prevent a possible page break at the following
+ % \baselineskip glue.
+ \nobreak
+ \endgroup
+ \itemxneedsnegativevskipfalse
+ \else
+ % The item text fits into the space. Start a paragraph, so that the
+ % following text (if any) will end up on the same line. Since that
+ % text will be indented by \tableindent, we make the item text be in
+ % a zero-width box.
+ \noindent
+ \rlap{\hskip -\tableindent\box0}\ignorespaces%
+ \endgroup%
+ \itemxneedsnegativevskiptrue%
+ \fi
+}
+
+\def\item{\errmessage{@item while not in a table}}
+\def\itemx{\errmessage{@itemx while not in a table}}
+\def\kitem{\errmessage{@kitem while not in a table}}
+\def\kitemx{\errmessage{@kitemx while not in a table}}
+\def\xitem{\errmessage{@xitem while not in a table}}
+\def\xitemx{\errmessage{@xitemx while not in a table}}
+
+%% Contains a kludge to get @end[description] to work
+\def\description{\tablez{\dontindex}{1}{}{}{}{}}
+
+\def\table{\begingroup\inENV\obeylines\obeyspaces\tablex}
+{\obeylines\obeyspaces%
+\gdef\tablex #1^^M{%
+\tabley\dontindex#1 \endtabley}}
+
+\def\ftable{\begingroup\inENV\obeylines\obeyspaces\ftablex}
+{\obeylines\obeyspaces%
+\gdef\ftablex #1^^M{%
+\tabley\fnitemindex#1 \endtabley
+\def\Eftable{\endgraf\afterenvbreak\endgroup}%
+\let\Etable=\relax}}
+
+\def\vtable{\begingroup\inENV\obeylines\obeyspaces\vtablex}
+{\obeylines\obeyspaces%
+\gdef\vtablex #1^^M{%
+\tabley\vritemindex#1 \endtabley
+\def\Evtable{\endgraf\afterenvbreak\endgroup}%
+\let\Etable=\relax}}
+
+\def\dontindex #1{}
+\def\fnitemindex #1{\doind {fn}{\code{#1}}}%
+\def\vritemindex #1{\doind {vr}{\code{#1}}}%
+
+{\obeyspaces %
+\gdef\tabley#1#2 #3 #4 #5 #6 #7\endtabley{\endgroup%
+\tablez{#1}{#2}{#3}{#4}{#5}{#6}}}
+
+\def\tablez #1#2#3#4#5#6{%
+\aboveenvbreak %
+\begingroup %
+\def\Edescription{\Etable}% Necessary kludge.
+\let\itemindex=#1%
+\ifnum 0#3>0 \advance \leftskip by #3\mil \fi %
+\ifnum 0#4>0 \tableindent=#4\mil \fi %
+\ifnum 0#5>0 \advance \rightskip by #5\mil \fi %
+\def\itemfont{#2}%
+\itemmax=\tableindent %
+\advance \itemmax by -\itemmargin %
+\advance \leftskip by \tableindent %
+\exdentamount=\tableindent
+\parindent = 0pt
+\parskip = \smallskipamount
+\ifdim \parskip=0pt \parskip=2pt \fi%
+\def\Etable{\endgraf\afterenvbreak\endgroup}%
+\let\item = \internalBitem %
+\let\itemx = \internalBitemx %
+\let\kitem = \internalBkitem %
+\let\kitemx = \internalBkitemx %
+\let\xitem = \internalBxitem %
+\let\xitemx = \internalBxitemx %
+}
+
+% This is the counter used by @enumerate, which is really @itemize
+
+\newcount \itemno
+
+\def\itemize{\parsearg\itemizezzz}
+
+\def\itemizezzz #1{%
+ \begingroup % ended by the @end itemsize
+ \itemizey {#1}{\Eitemize}
+}
+
+\def\itemizey #1#2{%
+\aboveenvbreak %
+\itemmax=\itemindent %
+\advance \itemmax by -\itemmargin %
+\advance \leftskip by \itemindent %
+\exdentamount=\itemindent
+\parindent = 0pt %
+\parskip = \smallskipamount %
+\ifdim \parskip=0pt \parskip=2pt \fi%
+\def#2{\endgraf\afterenvbreak\endgroup}%
+\def\itemcontents{#1}%
+\let\item=\itemizeitem}
+
+% Set sfcode to normal for the chars that usually have another value.
+% These are `.?!:;,'
+\def\frenchspacing{\sfcode46=1000 \sfcode63=1000 \sfcode33=1000
+ \sfcode58=1000 \sfcode59=1000 \sfcode44=1000 }
+
+% \splitoff TOKENS\endmark defines \first to be the first token in
+% TOKENS, and \rest to be the remainder.
+%
+\def\splitoff#1#2\endmark{\def\first{#1}\def\rest{#2}}%
+
+% Allow an optional argument of an uppercase letter, lowercase letter,
+% or number, to specify the first label in the enumerated list. No
+% argument is the same as `1'.
+%
+\def\enumerate{\parsearg\enumeratezzz}
+\def\enumeratezzz #1{\enumeratey #1 \endenumeratey}
+\def\enumeratey #1 #2\endenumeratey{%
+ \begingroup % ended by the @end enumerate
+ %
+ % If we were given no argument, pretend we were given `1'.
+ \def\thearg{#1}%
+ \ifx\thearg\empty \def\thearg{1}\fi
+ %
+ % Detect if the argument is a single token. If so, it might be a
+ % letter. Otherwise, the only valid thing it can be is a number.
+ % (We will always have one token, because of the test we just made.
+ % This is a good thing, since \splitoff doesn't work given nothing at
+ % all -- the first parameter is undelimited.)
+ \expandafter\splitoff\thearg\endmark
+ \ifx\rest\empty
+ % Only one token in the argument. It could still be anything.
+ % A ``lowercase letter'' is one whose \lccode is nonzero.
+ % An ``uppercase letter'' is one whose \lccode is both nonzero, and
+ % not equal to itself.
+ % Otherwise, we assume it's a number.
+ %
+ % We need the \relax at the end of the \ifnum lines to stop TeX from
+ % continuing to look for a <number>.
+ %
+ \ifnum\lccode\expandafter`\thearg=0\relax
+ \numericenumerate % a number (we hope)
+ \else
+ % It's a letter.
+ \ifnum\lccode\expandafter`\thearg=\expandafter`\thearg\relax
+ \lowercaseenumerate % lowercase letter
+ \else
+ \uppercaseenumerate % uppercase letter
+ \fi
+ \fi
+ \else
+ % Multiple tokens in the argument. We hope it's a number.
+ \numericenumerate
+ \fi
+}
+
+% An @enumerate whose labels are integers. The starting integer is
+% given in \thearg.
+%
+\def\numericenumerate{%
+ \itemno = \thearg
+ \startenumeration{\the\itemno}%
+}
+
+% The starting (lowercase) letter is in \thearg.
+\def\lowercaseenumerate{%
+ \itemno = \expandafter`\thearg
+ \startenumeration{%
+ % Be sure we're not beyond the end of the alphabet.
+ \ifnum\itemno=0
+ \errmessage{No more lowercase letters in @enumerate; get a bigger
+ alphabet}%
+ \fi
+ \char\lccode\itemno
+ }%
+}
+
+% The starting (uppercase) letter is in \thearg.
+\def\uppercaseenumerate{%
+ \itemno = \expandafter`\thearg
+ \startenumeration{%
+ % Be sure we're not beyond the end of the alphabet.
+ \ifnum\itemno=0
+ \errmessage{No more uppercase letters in @enumerate; get a bigger
+ alphabet}
+ \fi
+ \char\uccode\itemno
+ }%
+}
+
+% Call itemizey, adding a period to the first argument and supplying the
+% common last two arguments. Also subtract one from the initial value in
+% \itemno, since @item increments \itemno.
+%
+\def\startenumeration#1{%
+ \advance\itemno by -1
+ \itemizey{#1.}\Eenumerate\flushcr
+}
+
+% @alphaenumerate and @capsenumerate are abbreviations for giving an arg
+% to @enumerate.
+%
+\def\alphaenumerate{\enumerate{a}}
+\def\capsenumerate{\enumerate{A}}
+\def\Ealphaenumerate{\Eenumerate}
+\def\Ecapsenumerate{\Eenumerate}
+
+% Definition of @item while inside @itemize.
+
+\def\itemizeitem{%
+\advance\itemno by 1
+{\let\par=\endgraf \smallbreak}%
+\ifhmode \errmessage{In hmode at itemizeitem}\fi
+{\parskip=0in \hskip 0pt
+\hbox to 0pt{\hss \itemcontents\hskip \itemmargin}%
+\vadjust{\penalty 1200}}%
+\flushcr}
+
+% @multitable macros
+% Amy Hendrickson, 8/18/94, 3/6/96
+%
+% @multitable ... @end multitable will make as many columns as desired.
+% Contents of each column will wrap at width given in preamble. Width
+% can be specified either with sample text given in a template line,
+% or in percent of \hsize, the current width of text on page.
+
+% Table can continue over pages but will only break between lines.
+
+% To make preamble:
+%
+% Either define widths of columns in terms of percent of \hsize:
+% @multitable @columnfractions .25 .3 .45
+% @item ...
+%
+% Numbers following @columnfractions are the percent of the total
+% current hsize to be used for each column. You may use as many
+% columns as desired.
+
+
+% Or use a template:
+% @multitable {Column 1 template} {Column 2 template} {Column 3 template}
+% @item ...
+% using the widest term desired in each column.
+%
+% For those who want to use more than one line's worth of words in
+% the preamble, break the line within one argument and it
+% will parse correctly, i.e.,
+%
+% @multitable {Column 1 template} {Column 2 template} {Column 3
+% template}
+% Not:
+% @multitable {Column 1 template} {Column 2 template}
+% {Column 3 template}
+
+% Each new table line starts with @item, each subsequent new column
+% starts with @tab. Empty columns may be produced by supplying @tab's
+% with nothing between them for as many times as empty columns are needed,
+% ie, @tab@tab@tab will produce two empty columns.
+
+% @item, @tab, @multitable or @end multitable do not need to be on their
+% own lines, but it will not hurt if they are.
+
+% Sample multitable:
+
+% @multitable {Column 1 template} {Column 2 template} {Column 3 template}
+% @item first col stuff @tab second col stuff @tab third col
+% @item
+% first col stuff
+% @tab
+% second col stuff
+% @tab
+% third col
+% @item first col stuff @tab second col stuff
+% @tab Many paragraphs of text may be used in any column.
+%
+% They will wrap at the width determined by the template.
+% @item@tab@tab This will be in third column.
+% @end multitable
+
+% Default dimensions may be reset by user.
+% @multitableparskip is vertical space between paragraphs in table.
+% @multitableparindent is paragraph indent in table.
+% @multitablecolmargin is horizontal space to be left between columns.
+% @multitablelinespace is space to leave between table items, baseline
+% to baseline.
+% 0pt means it depends on current normal line spacing.
+
+%%%%
+% Dimensions
+
+\newskip\multitableparskip
+\newskip\multitableparindent
+\newdimen\multitablecolspace
+\newskip\multitablelinespace
+\multitableparskip=0pt
+\multitableparindent=6pt
+\multitablecolspace=12pt
+\multitablelinespace=0pt
+
+%%%%
+% Macros used to set up halign preamble:
+\let\endsetuptable\relax
+\def\xendsetuptable{\endsetuptable}
+\let\columnfractions\relax
+\def\xcolumnfractions{\columnfractions}
+\newif\ifsetpercent
+
+%% 2/1/96, to allow fractions to be given with more than one digit.
+\def\pickupwholefraction#1 {\global\advance\colcount by1 %
+\expandafter\xdef\csname col\the\colcount\endcsname{.#1\hsize}%
+\setuptable}
+
+\newcount\colcount
+\def\setuptable#1{\def\firstarg{#1}%
+\ifx\firstarg\xendsetuptable\let\go\relax%
+\else
+ \ifx\firstarg\xcolumnfractions\global\setpercenttrue%
+ \else
+ \ifsetpercent
+ \let\go\pickupwholefraction % In this case arg of setuptable
+ % is the decimal point before the
+ % number given in percent of hsize.
+ % We don't need this so we don't use it.
+ \else
+ \global\advance\colcount by1
+ \setbox0=\hbox{#1 }% Add a normal word space as a separator;
+ % typically that is always in the input, anyway.
+ \expandafter\xdef\csname col\the\colcount\endcsname{\the\wd0}%
+ \fi%
+ \fi%
+\ifx\go\pickupwholefraction\else\let\go\setuptable\fi%
+\fi\go}
+
+%%%%
+% multitable syntax
+\def\tab{&\hskip1sp\relax} % 2/2/96
+ % tiny skip here makes sure this column space is
+ % maintained, even if it is never used.
+
+
+%%%%
+% @multitable ... @end multitable definitions:
+
+\def\multitable{\parsearg\dotable}
+
+\def\dotable#1{\bgroup
+\let\item\cr
+\tolerance=9500
+\hbadness=9500
+\setmultitablespacing
+\parskip=\multitableparskip
+\parindent=\multitableparindent
+\overfullrule=0pt
+\global\colcount=0\relax%
+\def\Emultitable{\global\setpercentfalse\global\everycr{}\cr\egroup\egroup}%
+ % To parse everything between @multitable and @item :
+\setuptable#1 \endsetuptable
+ % Need to reset this to 0 after \setuptable.
+\global\colcount=0\relax%
+ %
+ % This preamble sets up a generic column definition, which will
+ % be used as many times as user calls for columns.
+ % \vtop will set a single line and will also let text wrap and
+ % continue for many paragraphs if desired.
+\halign\bgroup&\global\advance\colcount by 1\relax%
+\multistrut\vtop{\hsize=\expandafter\csname col\the\colcount\endcsname
+ % In order to keep entries from bumping into each other
+ % we will add a \leftskip of \multitablecolspace to all columns after
+ % the first one.
+ % If a template has been used, we will add \multitablecolspace
+ % to the width of each template entry.
+ % If user has set preamble in terms of percent of \hsize
+ % we will use that dimension as the width of the column, and
+ % the \leftskip will keep entries from bumping into each other.
+ % Table will start at left margin and final column will justify at
+ % right margin.
+\ifnum\colcount=1
+\else
+ \ifsetpercent
+ \else
+ % If user has <not> set preamble in terms of percent of \hsize
+ % we will advance \hsize by \multitablecolspace
+ \advance\hsize by \multitablecolspace
+ \fi
+ % In either case we will make \leftskip=\multitablecolspace:
+\leftskip=\multitablecolspace
+\fi
+ % Ignoring space at the beginning and end avoids an occasional spurious
+ % blank line, when TeX decides to break the line at the space before the
+ % box from the multistrut, so the strut ends up on a line by itself.
+ % For example:
+ % @multitable @columnfractions .11 .89
+ % @item @code{#}
+ % @tab Legal holiday which is valid in major parts of the whole country.
+ % Is automatically provided with highlighting sequences respectively marking
+ % characters.
+ \noindent\ignorespaces##\unskip\multistrut}\cr
+ % \everycr will reset column counter, \colcount, at the end of
+ % each line. Every column entry will cause \colcount to advance by one.
+ % The table preamble
+ % looks at the current \colcount to find the correct column width.
+\global\everycr{\noalign{%
+% \filbreak%% keeps underfull box messages off when table breaks over pages.
+% Maybe so, but it also creates really weird page breaks when the table
+% breaks over pages Wouldn't \vfil be better? Wait until the problem
+% manifests itself, so it can be fixed for real --karl.
+\global\colcount=0\relax}}
+}
+
+\def\setmultitablespacing{% test to see if user has set \multitablelinespace.
+% If so, do nothing. If not, give it an appropriate dimension based on
+% current baselineskip.
+\ifdim\multitablelinespace=0pt
+%% strut to put in table in case some entry doesn't have descenders,
+%% to keep lines equally spaced
+\let\multistrut = \strut
+%% Test to see if parskip is larger than space between lines of
+%% table. If not, do nothing.
+%% If so, set to same dimension as multitablelinespace.
+\else
+\gdef\multistrut{\vrule height\multitablelinespace depth\dp0
+width0pt\relax} \fi
+\ifdim\multitableparskip>\multitablelinespace
+\global\multitableparskip=\multitablelinespace
+\global\advance\multitableparskip-7pt %% to keep parskip somewhat smaller
+ %% than skip between lines in the table.
+\fi%
+\ifdim\multitableparskip=0pt
+\global\multitableparskip=\multitablelinespace
+\global\advance\multitableparskip-7pt %% to keep parskip somewhat smaller
+ %% than skip between lines in the table.
+\fi}
+
+
+\message{indexing,}
+% Index generation facilities
+
+% Define \newwrite to be identical to plain tex's \newwrite
+% except not \outer, so it can be used within \newindex.
+{\catcode`\@=11
+\gdef\newwrite{\alloc@7\write\chardef\sixt@@n}}
+
+% \newindex {foo} defines an index named foo.
+% It automatically defines \fooindex such that
+% \fooindex ...rest of line... puts an entry in the index foo.
+% It also defines \fooindfile to be the number of the output channel for
+% the file that accumulates this index. The file's extension is foo.
+% The name of an index should be no more than 2 characters long
+% for the sake of vms.
+
+\def\newindex #1{
+\expandafter\newwrite \csname#1indfile\endcsname% Define number for output file
+\openout \csname#1indfile\endcsname \jobname.#1 % Open the file
+\expandafter\xdef\csname#1index\endcsname{% % Define \xxxindex
+\noexpand\doindex {#1}}
+}
+
+% @defindex foo == \newindex{foo}
+
+\def\defindex{\parsearg\newindex}
+
+% Define @defcodeindex, like @defindex except put all entries in @code.
+
+\def\newcodeindex #1{
+\expandafter\newwrite \csname#1indfile\endcsname% Define number for output file
+\openout \csname#1indfile\endcsname \jobname.#1 % Open the file
+\expandafter\xdef\csname#1index\endcsname{% % Define \xxxindex
+\noexpand\docodeindex {#1}}
+}
+
+\def\defcodeindex{\parsearg\newcodeindex}
+
+% @synindex foo bar makes index foo feed into index bar.
+% Do this instead of @defindex foo if you don't want it as a separate index.
+\def\synindex #1 #2 {%
+\expandafter\let\expandafter\synindexfoo\expandafter=\csname#2indfile\endcsname
+\expandafter\let\csname#1indfile\endcsname=\synindexfoo
+\expandafter\xdef\csname#1index\endcsname{% % Define \xxxindex
+\noexpand\doindex {#2}}%
+}
+
+% @syncodeindex foo bar similar, but put all entries made for index foo
+% inside @code.
+\def\syncodeindex #1 #2 {%
+\expandafter\let\expandafter\synindexfoo\expandafter=\csname#2indfile\endcsname
+\expandafter\let\csname#1indfile\endcsname=\synindexfoo
+\expandafter\xdef\csname#1index\endcsname{% % Define \xxxindex
+\noexpand\docodeindex {#2}}%
+}
+
+% Define \doindex, the driver for all \fooindex macros.
+% Argument #1 is generated by the calling \fooindex macro,
+% and it is "foo", the name of the index.
+
+% \doindex just uses \parsearg; it calls \doind for the actual work.
+% This is because \doind is more useful to call from other macros.
+
+% There is also \dosubind {index}{topic}{subtopic}
+% which makes an entry in a two-level index such as the operation index.
+
+\def\doindex#1{\edef\indexname{#1}\parsearg\singleindexer}
+\def\singleindexer #1{\doind{\indexname}{#1}}
+
+% like the previous two, but they put @code around the argument.
+\def\docodeindex#1{\edef\indexname{#1}\parsearg\singlecodeindexer}
+\def\singlecodeindexer #1{\doind{\indexname}{\code{#1}}}
+
+\def\indexdummies{%
+% Take care of the plain tex accent commands.
+\def\"{\realbackslash "}%
+\def\`{\realbackslash `}%
+\def\'{\realbackslash '}%
+\def\^{\realbackslash ^}%
+\def\~{\realbackslash ~}%
+\def\={\realbackslash =}%
+\def\b{\realbackslash b}%
+\def\c{\realbackslash c}%
+\def\d{\realbackslash d}%
+\def\u{\realbackslash u}%
+\def\v{\realbackslash v}%
+\def\H{\realbackslash H}%
+% Take care of the plain tex special European modified letters.
+\def\oe{\realbackslash oe}%
+\def\ae{\realbackslash ae}%
+\def\aa{\realbackslash aa}%
+\def\OE{\realbackslash OE}%
+\def\AE{\realbackslash AE}%
+\def\AA{\realbackslash AA}%
+\def\o{\realbackslash o}%
+\def\O{\realbackslash O}%
+\def\l{\realbackslash l}%
+\def\L{\realbackslash L}%
+\def\ss{\realbackslash ss}%
+% Take care of texinfo commands likely to appear in an index entry.
+% (Must be a way to avoid doing expansion at all, and thus not have to
+% laboriously list every single command here.)
+\def\@{@}% will be @@ when we switch to @ as escape char.
+%\let\{ = \lbracecmd
+%\let\} = \rbracecmd
+\def\_{{\realbackslash _}}%
+\def\w{\realbackslash w }%
+\def\bf{\realbackslash bf }%
+%\def\rm{\realbackslash rm }%
+\def\sl{\realbackslash sl }%
+\def\sf{\realbackslash sf}%
+\def\tt{\realbackslash tt}%
+\def\gtr{\realbackslash gtr}%
+\def\less{\realbackslash less}%
+\def\hat{\realbackslash hat}%
+%\def\char{\realbackslash char}%
+\def\TeX{\realbackslash TeX}%
+\def\dots{\realbackslash dots }%
+\def\result{\realbackslash result}%
+\def\equiv{\realbackslash equiv}%
+\def\expansion{\realbackslash expansion}%
+\def\print{\realbackslash print}%
+\def\error{\realbackslash error}%
+\def\point{\realbackslash point}%
+\def\copyright{\realbackslash copyright}%
+\def\tclose##1{\realbackslash tclose {##1}}%
+\def\code##1{\realbackslash code {##1}}%
+\def\dotless##1{\realbackslash dotless {##1}}%
+\def\samp##1{\realbackslash samp {##1}}%
+\def\,##1{\realbackslash ,{##1}}%
+\def\t##1{\realbackslash t {##1}}%
+\def\r##1{\realbackslash r {##1}}%
+\def\i##1{\realbackslash i {##1}}%
+\def\b##1{\realbackslash b {##1}}%
+\def\sc##1{\realbackslash sc {##1}}%
+\def\cite##1{\realbackslash cite {##1}}%
+\def\key##1{\realbackslash key {##1}}%
+\def\file##1{\realbackslash file {##1}}%
+\def\var##1{\realbackslash var {##1}}%
+\def\kbd##1{\realbackslash kbd {##1}}%
+\def\dfn##1{\realbackslash dfn {##1}}%
+\def\emph##1{\realbackslash emph {##1}}%
+\def\value##1{\realbackslash value {##1}}%
+\unsepspaces
+}
+
+% If an index command is used in an @example environment, any spaces
+% therein should become regular spaces in the raw index file, not the
+% expansion of \tie (\\leavevmode \penalty \@M \ ).
+{\obeyspaces
+ \gdef\unsepspaces{\obeyspaces\let =\space}}
+
+% \indexnofonts no-ops all font-change commands.
+% This is used when outputting the strings to sort the index by.
+\def\indexdummyfont#1{#1}
+\def\indexdummytex{TeX}
+\def\indexdummydots{...}
+
+\def\indexnofonts{%
+% Just ignore accents.
+\let\,=\indexdummyfont
+\let\"=\indexdummyfont
+\let\`=\indexdummyfont
+\let\'=\indexdummyfont
+\let\^=\indexdummyfont
+\let\~=\indexdummyfont
+\let\==\indexdummyfont
+\let\b=\indexdummyfont
+\let\c=\indexdummyfont
+\let\d=\indexdummyfont
+\let\u=\indexdummyfont
+\let\v=\indexdummyfont
+\let\H=\indexdummyfont
+\let\dotless=\indexdummyfont
+% Take care of the plain tex special European modified letters.
+\def\oe{oe}%
+\def\ae{ae}%
+\def\aa{aa}%
+\def\OE{OE}%
+\def\AE{AE}%
+\def\AA{AA}%
+\def\o{o}%
+\def\O{O}%
+\def\l{l}%
+\def\L{L}%
+\def\ss{ss}%
+\let\w=\indexdummyfont
+\let\t=\indexdummyfont
+\let\r=\indexdummyfont
+\let\i=\indexdummyfont
+\let\b=\indexdummyfont
+\let\emph=\indexdummyfont
+\let\strong=\indexdummyfont
+\let\cite=\indexdummyfont
+\let\sc=\indexdummyfont
+%Don't no-op \tt, since it isn't a user-level command
+% and is used in the definitions of the active chars like <, >, |...
+%\let\tt=\indexdummyfont
+\let\tclose=\indexdummyfont
+\let\code=\indexdummyfont
+\let\file=\indexdummyfont
+\let\samp=\indexdummyfont
+\let\kbd=\indexdummyfont
+\let\key=\indexdummyfont
+\let\var=\indexdummyfont
+\let\TeX=\indexdummytex
+\let\dots=\indexdummydots
+\def\@{@}%
+}
+
+% To define \realbackslash, we must make \ not be an escape.
+% We must first make another character (@) an escape
+% so we do not become unable to do a definition.
+
+{\catcode`\@=0 \catcode`\\=\other
+@gdef@realbackslash{\}}
+
+\let\indexbackslash=0 %overridden during \printindex.
+
+\let\SETmarginindex=\relax %initialize!
+% workhorse for all \fooindexes
+% #1 is name of index, #2 is stuff to put there
+\def\doind #1#2{%
+ % Put the index entry in the margin if desired.
+ \ifx\SETmarginindex\relax\else
+ \insert\margin{\hbox{\vrule height8pt depth3pt width0pt #2}}%
+ \fi
+ {%
+ \count255=\lastpenalty
+ {%
+ \indexdummies % Must do this here, since \bf, etc expand at this stage
+ \escapechar=`\\
+ {%
+ \let\folio=0% We will expand all macros now EXCEPT \folio.
+ \def\rawbackslashxx{\indexbackslash}% \indexbackslash isn't defined now
+ % so it will be output as is; and it will print as backslash.
+ %
+ % First process the index-string with all font commands turned off
+ % to get the string to sort by.
+ {\indexnofonts \xdef\indexsorttmp{#2}}%
+ %
+ % Now produce the complete index entry, with both the sort key and the
+ % original text, including any font commands.
+ \toks0 = {#2}%
+ \edef\temp{%
+ \write\csname#1indfile\endcsname{%
+ \realbackslash entry{\indexsorttmp}{\folio}{\the\toks0}}%
+ }%
+ \temp
+ }%
+ }%
+ \penalty\count255
+ }%
+}
+
+\def\dosubind #1#2#3{%
+{\count10=\lastpenalty %
+{\indexdummies % Must do this here, since \bf, etc expand at this stage
+\escapechar=`\\%
+{\let\folio=0%
+\def\rawbackslashxx{\indexbackslash}%
+%
+% Now process the index-string once, with all font commands turned off,
+% to get the string to sort the index by.
+{\indexnofonts
+\xdef\temp1{#2 #3}%
+}%
+% Now produce the complete index entry. We process the index-string again,
+% this time with font commands expanded, to get what to print in the index.
+\edef\temp{%
+\write \csname#1indfile\endcsname{%
+\realbackslash entry {\temp1}{\folio}{#2}{#3}}}%
+\temp }%
+}\penalty\count10}}
+
+% The index entry written in the file actually looks like
+% \entry {sortstring}{page}{topic}
+% or
+% \entry {sortstring}{page}{topic}{subtopic}
+% The texindex program reads in these files and writes files
+% containing these kinds of lines:
+% \initial {c}
+% before the first topic whose initial is c
+% \entry {topic}{pagelist}
+% for a topic that is used without subtopics
+% \primary {topic}
+% for the beginning of a topic that is used with subtopics
+% \secondary {subtopic}{pagelist}
+% for each subtopic.
+
+% Define the user-accessible indexing commands
+% @findex, @vindex, @kindex, @cindex.
+
+\def\findex {\fnindex}
+\def\kindex {\kyindex}
+\def\cindex {\cpindex}
+\def\vindex {\vrindex}
+\def\tindex {\tpindex}
+\def\pindex {\pgindex}
+
+\def\cindexsub {\begingroup\obeylines\cindexsub}
+{\obeylines %
+\gdef\cindexsub "#1" #2^^M{\endgroup %
+\dosubind{cp}{#2}{#1}}}
+
+% Define the macros used in formatting output of the sorted index material.
+
+% @printindex causes a particular index (the ??s file) to get printed.
+% It does not print any chapter heading (usually an @unnumbered).
+%
+\def\printindex{\parsearg\doprintindex}
+\def\doprintindex#1{\begingroup
+ \dobreak \chapheadingskip{10000}%
+ %
+ \indexfonts \rm
+ \tolerance = 9500
+ \indexbreaks
+ %
+ % See if the index file exists and is nonempty.
+ \openin 1 \jobname.#1s
+ \ifeof 1
+ % \enddoublecolumns gets confused if there is no text in the index,
+ % and it loses the chapter title and the aux file entries for the
+ % index. The easiest way to prevent this problem is to make sure
+ % there is some text.
+ (Index is nonexistent)
+ \else
+ %
+ % If the index file exists but is empty, then \openin leaves \ifeof
+ % false. We have to make TeX try to read something from the file, so
+ % it can discover if there is anything in it.
+ \read 1 to \temp
+ \ifeof 1
+ (Index is empty)
+ \else
+ % Index files are almost Texinfo source, but we use \ as the escape
+ % character. It would be better to use @, but that's too big a change
+ % to make right now.
+ \def\indexbackslash{\rawbackslashxx}%
+ \catcode`\\ = 0
+ \catcode`\@ = 11
+ \escapechar = `\\
+ \begindoublecolumns
+ \input \jobname.#1s
+ \enddoublecolumns
+ \fi
+ \fi
+ \closein 1
+\endgroup}
+
+% These macros are used by the sorted index file itself.
+% Change them to control the appearance of the index.
+
+% Same as \bigskipamount except no shrink.
+% \balancecolumns gets confused if there is any shrink.
+\newskip\initialskipamount \initialskipamount 12pt plus4pt
+
+\def\initial #1{%
+{\let\tentt=\sectt \let\tt=\sectt \let\sf=\sectt
+\ifdim\lastskip<\initialskipamount
+\removelastskip \penalty-200 \vskip \initialskipamount\fi
+\line{\secbf#1\hfill}\kern 2pt\penalty10000}}
+
+% This typesets a paragraph consisting of #1, dot leaders, and then #2
+% flush to the right margin. It is used for index and table of contents
+% entries. The paragraph is indented by \leftskip.
+%
+\def\entry #1#2{\begingroup
+ %
+ % Start a new paragraph if necessary, so our assignments below can't
+ % affect previous text.
+ \par
+ %
+ % Do not fill out the last line with white space.
+ \parfillskip = 0in
+ %
+ % No extra space above this paragraph.
+ \parskip = 0in
+ %
+ % Do not prefer a separate line ending with a hyphen to fewer lines.
+ \finalhyphendemerits = 0
+ %
+ % \hangindent is only relevant when the entry text and page number
+ % don't both fit on one line. In that case, bob suggests starting the
+ % dots pretty far over on the line. Unfortunately, a large
+ % indentation looks wrong when the entry text itself is broken across
+ % lines. So we use a small indentation and put up with long leaders.
+ %
+ % \hangafter is reset to 1 (which is the value we want) at the start
+ % of each paragraph, so we need not do anything with that.
+ \hangindent=2em
+ %
+ % When the entry text needs to be broken, just fill out the first line
+ % with blank space.
+ \rightskip = 0pt plus1fil
+ %
+ % Start a ``paragraph'' for the index entry so the line breaking
+ % parameters we've set above will have an effect.
+ \noindent
+ %
+ % Insert the text of the index entry. TeX will do line-breaking on it.
+ #1%
+ % The following is kludged to not output a line of dots in the index if
+ % there are no page numbers. The next person who breaks this will be
+ % cursed by a Unix daemon.
+ \def\tempa{{\rm }}%
+ \def\tempb{#2}%
+ \edef\tempc{\tempa}%
+ \edef\tempd{\tempb}%
+ \ifx\tempc\tempd\ \else%
+ %
+ % If we must, put the page number on a line of its own, and fill out
+ % this line with blank space. (The \hfil is overwhelmed with the
+ % fill leaders glue in \indexdotfill if the page number does fit.)
+ \hfil\penalty50
+ \null\nobreak\indexdotfill % Have leaders before the page number.
+ %
+ % The `\ ' here is removed by the implicit \unskip that TeX does as
+ % part of (the primitive) \par. Without it, a spurious underfull
+ % \hbox ensues.
+ \ #2% The page number ends the paragraph.
+ \fi%
+ \par
+\endgroup}
+
+% Like \dotfill except takes at least 1 em.
+\def\indexdotfill{\cleaders
+ \hbox{$\mathsurround=0pt \mkern1.5mu ${\it .}$ \mkern1.5mu$}\hskip 1em plus 1fill}
+
+\def\primary #1{\line{#1\hfil}}
+
+\newskip\secondaryindent \secondaryindent=0.5cm
+
+\def\secondary #1#2{
+{\parfillskip=0in \parskip=0in
+\hangindent =1in \hangafter=1
+\noindent\hskip\secondaryindent\hbox{#1}\indexdotfill #2\par
+}}
+
+% Define two-column mode, which we use to typeset indexes.
+% Adapted from the TeXbook, page 416, which is to say,
+% the manmac.tex format used to print the TeXbook itself.
+\catcode`\@=11
+
+\newbox\partialpage
+\newdimen\doublecolumnhsize
+
+\def\begindoublecolumns{\begingroup % ended by \enddoublecolumns
+ % Grab any single-column material above us.
+ \output = {\global\setbox\partialpage = \vbox{%
+ %
+ % Here is a possibility not foreseen in manmac: if we accumulate a
+ % whole lot of material, we might end up calling this \output
+ % routine twice in a row (see the doublecol-lose test, which is
+ % essentially a couple of indexes with @setchapternewpage off). In
+ % that case, we must prevent the second \partialpage from
+ % simply overwriting the first, causing us to lose the page.
+ % This will preserve it until a real output routine can ship it
+ % out. Generally, \partialpage will be empty when this runs and
+ % this will be a no-op.
+ \unvbox\partialpage
+ %
+ % Unvbox the main output page.
+ \unvbox255
+ \kern-\topskip \kern\baselineskip
+ }}%
+ \eject
+ %
+ % Use the double-column output routine for subsequent pages.
+ \output = {\doublecolumnout}%
+ %
+ % Change the page size parameters. We could do this once outside this
+ % routine, in each of @smallbook, @afourpaper, and the default 8.5x11
+ % format, but then we repeat the same computation. Repeating a couple
+ % of assignments once per index is clearly meaningless for the
+ % execution time, so we may as well do it in one place.
+ %
+ % First we halve the line length, less a little for the gutter between
+ % the columns. We compute the gutter based on the line length, so it
+ % changes automatically with the paper format. The magic constant
+ % below is chosen so that the gutter has the same value (well, +-<1pt)
+ % as it did when we hard-coded it.
+ %
+ % We put the result in a separate register, \doublecolumhsize, so we
+ % can restore it in \pagesofar, after \hsize itself has (potentially)
+ % been clobbered.
+ %
+ \doublecolumnhsize = \hsize
+ \advance\doublecolumnhsize by -.04154\hsize
+ \divide\doublecolumnhsize by 2
+ \hsize = \doublecolumnhsize
+ %
+ % Double the \vsize as well. (We don't need a separate register here,
+ % since nobody clobbers \vsize.)
+ \vsize = 2\vsize
+}
+\def\doublecolumnout{%
+ \splittopskip=\topskip \splitmaxdepth=\maxdepth
+ % Get the available space for the double columns -- the normal
+ % (undoubled) page height minus any material left over from the
+ % previous page.
+ \dimen@=\pageheight \advance\dimen@ by-\ht\partialpage
+ % box0 will be the left-hand column, box2 the right.
+ \setbox0=\vsplit255 to\dimen@ \setbox2=\vsplit255 to\dimen@
+ \onepageout\pagesofar
+ \unvbox255
+ \penalty\outputpenalty
+}
+\def\pagesofar{%
+ % Re-output the contents of the output page -- any previous material,
+ % followed by the two boxes we just split.
+ \unvbox\partialpage
+ \hsize = \doublecolumnhsize
+ \wd0=\hsize \wd2=\hsize \hbox to\pagewidth{\box0\hfil\box2}%
+}
+\def\enddoublecolumns{%
+ \output = {\balancecolumns}\eject % split what we have
+ \endgroup % started in \begindoublecolumns
+ %
+ % Back to normal single-column typesetting, but take account of the
+ % fact that we just accumulated some stuff on the output page.
+ \pagegoal = \vsize
+}
+\def\balancecolumns{%
+ % Called at the end of the double column material.
+ \setbox0 = \vbox{\unvbox255}%
+ \dimen@ = \ht0
+ \advance\dimen@ by \topskip
+ \advance\dimen@ by-\baselineskip
+ \divide\dimen@ by 2
+ \splittopskip = \topskip
+ % Loop until we get a decent breakpoint.
+ {\vbadness=10000 \loop
+ \global\setbox3=\copy0
+ \global\setbox1=\vsplit3 to\dimen@
+ \ifdim\ht3>\dimen@ \global\advance\dimen@ by1pt
+ \repeat}%
+ \setbox0=\vbox to\dimen@{\unvbox1}%
+ \setbox2=\vbox to\dimen@{\unvbox3}%
+ \pagesofar
+}
+\catcode`\@ = \other
+
+
+\message{sectioning,}
+% Define chapters, sections, etc.
+
+\newcount\chapno
+\newcount\secno \secno=0
+\newcount\subsecno \subsecno=0
+\newcount\subsubsecno \subsubsecno=0
+
+% This counter is funny since it counts through charcodes of letters A, B, ...
+\newcount\appendixno \appendixno = `\@
+\def\appendixletter{\char\the\appendixno}
+
+\newwrite\contentsfile
+% This is called from \setfilename.
+\def\opencontents{\openout\contentsfile = \jobname.toc }
+
+% Each @chapter defines this as the name of the chapter.
+% page headings and footings can use it. @section does likewise
+
+\def\thischapter{} \def\thissection{}
+\def\seccheck#1{\ifnum \pageno<0
+ \errmessage{@#1 not allowed after generating table of contents}%
+\fi}
+
+\def\chapternofonts{%
+ \let\rawbackslash=\relax
+ \let\frenchspacing=\relax
+ \def\result{\realbackslash result}%
+ \def\equiv{\realbackslash equiv}%
+ \def\expansion{\realbackslash expansion}%
+ \def\print{\realbackslash print}%
+ \def\TeX{\realbackslash TeX}%
+ \def\dots{\realbackslash dots}%
+ \def\result{\realbackslash result}%
+ \def\equiv{\realbackslash equiv}%
+ \def\expansion{\realbackslash expansion}%
+ \def\print{\realbackslash print}%
+ \def\error{\realbackslash error}%
+ \def\point{\realbackslash point}%
+ \def\copyright{\realbackslash copyright}%
+ \def\tt{\realbackslash tt}%
+ \def\bf{\realbackslash bf}%
+ \def\w{\realbackslash w}%
+ \def\less{\realbackslash less}%
+ \def\gtr{\realbackslash gtr}%
+ \def\hat{\realbackslash hat}%
+ \def\char{\realbackslash char}%
+ \def\tclose##1{\realbackslash tclose{##1}}%
+ \def\code##1{\realbackslash code{##1}}%
+ \def\samp##1{\realbackslash samp{##1}}%
+ \def\r##1{\realbackslash r{##1}}%
+ \def\b##1{\realbackslash b{##1}}%
+ \def\key##1{\realbackslash key{##1}}%
+ \def\file##1{\realbackslash file{##1}}%
+ \def\kbd##1{\realbackslash kbd{##1}}%
+ % These are redefined because @smartitalic wouldn't work inside xdef.
+ \def\i##1{\realbackslash i{##1}}%
+ \def\cite##1{\realbackslash cite{##1}}%
+ \def\var##1{\realbackslash var{##1}}%
+ \def\emph##1{\realbackslash emph{##1}}%
+ \def\dfn##1{\realbackslash dfn{##1}}%
+}
+
+\newcount\absseclevel % used to calculate proper heading level
+\newcount\secbase\secbase=0 % @raise/lowersections modify this count
+
+% @raisesections: treat @section as chapter, @subsection as section, etc.
+\def\raisesections{\global\advance\secbase by -1}
+\let\up=\raisesections % original BFox name
+
+% @lowersections: treat @chapter as section, @section as subsection, etc.
+\def\lowersections{\global\advance\secbase by 1}
+\let\down=\lowersections % original BFox name
+
+% Choose a numbered-heading macro
+% #1 is heading level if unmodified by @raisesections or @lowersections
+% #2 is text for heading
+\def\numhead#1#2{\absseclevel=\secbase\advance\absseclevel by #1
+\ifcase\absseclevel
+ \chapterzzz{#2}
+\or
+ \seczzz{#2}
+\or
+ \numberedsubseczzz{#2}
+\or
+ \numberedsubsubseczzz{#2}
+\else
+ \ifnum \absseclevel<0
+ \chapterzzz{#2}
+ \else
+ \numberedsubsubseczzz{#2}
+ \fi
+\fi
+}
+
+% like \numhead, but chooses appendix heading levels
+\def\apphead#1#2{\absseclevel=\secbase\advance\absseclevel by #1
+\ifcase\absseclevel
+ \appendixzzz{#2}
+\or
+ \appendixsectionzzz{#2}
+\or
+ \appendixsubseczzz{#2}
+\or
+ \appendixsubsubseczzz{#2}
+\else
+ \ifnum \absseclevel<0
+ \appendixzzz{#2}
+ \else
+ \appendixsubsubseczzz{#2}
+ \fi
+\fi
+}
+
+% like \numhead, but chooses numberless heading levels
+\def\unnmhead#1#2{\absseclevel=\secbase\advance\absseclevel by #1
+\ifcase\absseclevel
+ \unnumberedzzz{#2}
+\or
+ \unnumberedseczzz{#2}
+\or
+ \unnumberedsubseczzz{#2}
+\or
+ \unnumberedsubsubseczzz{#2}
+\else
+ \ifnum \absseclevel<0
+ \unnumberedzzz{#2}
+ \else
+ \unnumberedsubsubseczzz{#2}
+ \fi
+\fi
+}
+
+
+\def\thischaptername{No Chapter Title}
+\outer\def\chapter{\parsearg\chapteryyy}
+\def\chapteryyy #1{\numhead0{#1}} % normally numhead0 calls chapterzzz
+\def\chapterzzz #1{\seccheck{chapter}%
+\secno=0 \subsecno=0 \subsubsecno=0
+\global\advance \chapno by 1 \message{\putwordChapter \the\chapno}%
+\chapmacro {#1}{\the\chapno}%
+\gdef\thissection{#1}%
+\gdef\thischaptername{#1}%
+% We don't substitute the actual chapter name into \thischapter
+% because we don't want its macros evaluated now.
+\xdef\thischapter{\putwordChapter{} \the\chapno: \noexpand\thischaptername}%
+{\chapternofonts%
+\toks0 = {#1}%
+\edef\temp{{\realbackslash chapentry{\the\toks0}{\the\chapno}{\noexpand\folio}}}%
+\escapechar=`\\%
+\write \contentsfile \temp %
+\donoderef %
+\global\let\section = \numberedsec
+\global\let\subsection = \numberedsubsec
+\global\let\subsubsection = \numberedsubsubsec
+}}
+
+\outer\def\appendix{\parsearg\appendixyyy}
+\def\appendixyyy #1{\apphead0{#1}} % normally apphead0 calls appendixzzz
+\def\appendixzzz #1{\seccheck{appendix}%
+\secno=0 \subsecno=0 \subsubsecno=0
+\global\advance \appendixno by 1 \message{Appendix \appendixletter}%
+\chapmacro {#1}{\putwordAppendix{} \appendixletter}%
+\gdef\thissection{#1}%
+\gdef\thischaptername{#1}%
+\xdef\thischapter{\putwordAppendix{} \appendixletter: \noexpand\thischaptername}%
+{\chapternofonts%
+\toks0 = {#1}%
+\edef\temp{{\realbackslash chapentry{\the\toks0}%
+ {\putwordAppendix{} \appendixletter}{\noexpand\folio}}}%
+\escapechar=`\\%
+\write \contentsfile \temp %
+\appendixnoderef %
+\global\let\section = \appendixsec
+\global\let\subsection = \appendixsubsec
+\global\let\subsubsection = \appendixsubsubsec
+}}
+
+% @centerchap is like @unnumbered, but the heading is centered.
+\outer\def\centerchap{\parsearg\centerchapyyy}
+\def\centerchapyyy #1{{\let\unnumbchapmacro=\centerchapmacro \unnumberedyyy{#1}}}
+
+\outer\def\top{\parsearg\unnumberedyyy}
+\outer\def\unnumbered{\parsearg\unnumberedyyy}
+\def\unnumberedyyy #1{\unnmhead0{#1}} % normally unnmhead0 calls unnumberedzzz
+\def\unnumberedzzz #1{\seccheck{unnumbered}%
+\secno=0 \subsecno=0 \subsubsecno=0
+%
+% This used to be simply \message{#1}, but TeX fully expands the
+% argument to \message. Therefore, if #1 contained @-commands, TeX
+% expanded them. For example, in `@unnumbered The @cite{Book}', TeX
+% expanded @cite (which turns out to cause errors because \cite is meant
+% to be executed, not expanded).
+%
+% Anyway, we don't want the fully-expanded definition of @cite to appear
+% as a result of the \message, we just want `@cite' itself. We use
+% \the<toks register> to achieve this: TeX expands \the<toks> only once,
+% simply yielding the contents of the <toks register>.
+\toks0 = {#1}\message{(\the\toks0)}%
+%
+\unnumbchapmacro {#1}%
+\gdef\thischapter{#1}\gdef\thissection{#1}%
+{\chapternofonts%
+\toks0 = {#1}%
+\edef\temp{{\realbackslash unnumbchapentry{\the\toks0}{\noexpand\folio}}}%
+\escapechar=`\\%
+\write \contentsfile \temp %
+\unnumbnoderef %
+\global\let\section = \unnumberedsec
+\global\let\subsection = \unnumberedsubsec
+\global\let\subsubsection = \unnumberedsubsubsec
+}}
+
+\outer\def\numberedsec{\parsearg\secyyy}
+\def\secyyy #1{\numhead1{#1}} % normally calls seczzz
+\def\seczzz #1{\seccheck{section}%
+\subsecno=0 \subsubsecno=0 \global\advance \secno by 1 %
+\gdef\thissection{#1}\secheading {#1}{\the\chapno}{\the\secno}%
+{\chapternofonts%
+\toks0 = {#1}%
+\edef\temp{{\realbackslash secentry %
+{\the\toks0}{\the\chapno}{\the\secno}{\noexpand\folio}}}%
+\escapechar=`\\%
+\write \contentsfile \temp %
+\donoderef %
+\penalty 10000 %
+}}
+
+\outer\def\appendixsection{\parsearg\appendixsecyyy}
+\outer\def\appendixsec{\parsearg\appendixsecyyy}
+\def\appendixsecyyy #1{\apphead1{#1}} % normally calls appendixsectionzzz
+\def\appendixsectionzzz #1{\seccheck{appendixsection}%
+\subsecno=0 \subsubsecno=0 \global\advance \secno by 1 %
+\gdef\thissection{#1}\secheading {#1}{\appendixletter}{\the\secno}%
+{\chapternofonts%
+\toks0 = {#1}%
+\edef\temp{{\realbackslash secentry %
+{\the\toks0}{\appendixletter}{\the\secno}{\noexpand\folio}}}%
+\escapechar=`\\%
+\write \contentsfile \temp %
+\appendixnoderef %
+\penalty 10000 %
+}}
+
+\outer\def\unnumberedsec{\parsearg\unnumberedsecyyy}
+\def\unnumberedsecyyy #1{\unnmhead1{#1}} % normally calls unnumberedseczzz
+\def\unnumberedseczzz #1{\seccheck{unnumberedsec}%
+\plainsecheading {#1}\gdef\thissection{#1}%
+{\chapternofonts%
+\toks0 = {#1}%
+\edef\temp{{\realbackslash unnumbsecentry{\the\toks0}{\noexpand\folio}}}%
+\escapechar=`\\%
+\write \contentsfile \temp %
+\unnumbnoderef %
+\penalty 10000 %
+}}
+
+\outer\def\numberedsubsec{\parsearg\numberedsubsecyyy}
+\def\numberedsubsecyyy #1{\numhead2{#1}} % normally calls numberedsubseczzz
+\def\numberedsubseczzz #1{\seccheck{subsection}%
+\gdef\thissection{#1}\subsubsecno=0 \global\advance \subsecno by 1 %
+\subsecheading {#1}{\the\chapno}{\the\secno}{\the\subsecno}%
+{\chapternofonts%
+\toks0 = {#1}%
+\edef\temp{{\realbackslash subsecentry %
+{\the\toks0}{\the\chapno}{\the\secno}{\the\subsecno}{\noexpand\folio}}}%
+\escapechar=`\\%
+\write \contentsfile \temp %
+\donoderef %
+\penalty 10000 %
+}}
+
+\outer\def\appendixsubsec{\parsearg\appendixsubsecyyy}
+\def\appendixsubsecyyy #1{\apphead2{#1}} % normally calls appendixsubseczzz
+\def\appendixsubseczzz #1{\seccheck{appendixsubsec}%
+\gdef\thissection{#1}\subsubsecno=0 \global\advance \subsecno by 1 %
+\subsecheading {#1}{\appendixletter}{\the\secno}{\the\subsecno}%
+{\chapternofonts%
+\toks0 = {#1}%
+\edef\temp{{\realbackslash subsecentry %
+{\the\toks0}{\appendixletter}{\the\secno}{\the\subsecno}{\noexpand\folio}}}%
+\escapechar=`\\%
+\write \contentsfile \temp %
+\appendixnoderef %
+\penalty 10000 %
+}}
+
+\outer\def\unnumberedsubsec{\parsearg\unnumberedsubsecyyy}
+\def\unnumberedsubsecyyy #1{\unnmhead2{#1}} %normally calls unnumberedsubseczzz
+\def\unnumberedsubseczzz #1{\seccheck{unnumberedsubsec}%
+\plainsubsecheading {#1}\gdef\thissection{#1}%
+{\chapternofonts%
+\toks0 = {#1}%
+\edef\temp{{\realbackslash unnumbsubsecentry{\the\toks0}{\noexpand\folio}}}%
+\escapechar=`\\%
+\write \contentsfile \temp %
+\unnumbnoderef %
+\penalty 10000 %
+}}
+
+\outer\def\numberedsubsubsec{\parsearg\numberedsubsubsecyyy}
+\def\numberedsubsubsecyyy #1{\numhead3{#1}} % normally numberedsubsubseczzz
+\def\numberedsubsubseczzz #1{\seccheck{subsubsection}%
+\gdef\thissection{#1}\global\advance \subsubsecno by 1 %
+\subsubsecheading {#1}
+ {\the\chapno}{\the\secno}{\the\subsecno}{\the\subsubsecno}%
+{\chapternofonts%
+\toks0 = {#1}%
+\edef\temp{{\realbackslash subsubsecentry{\the\toks0}
+ {\the\chapno}{\the\secno}{\the\subsecno}{\the\subsubsecno}
+ {\noexpand\folio}}}%
+\escapechar=`\\%
+\write \contentsfile \temp %
+\donoderef %
+\penalty 10000 %
+}}
+
+\outer\def\appendixsubsubsec{\parsearg\appendixsubsubsecyyy}
+\def\appendixsubsubsecyyy #1{\apphead3{#1}} % normally appendixsubsubseczzz
+\def\appendixsubsubseczzz #1{\seccheck{appendixsubsubsec}%
+\gdef\thissection{#1}\global\advance \subsubsecno by 1 %
+\subsubsecheading {#1}
+ {\appendixletter}{\the\secno}{\the\subsecno}{\the\subsubsecno}%
+{\chapternofonts%
+\toks0 = {#1}%
+\edef\temp{{\realbackslash subsubsecentry{\the\toks0}%
+ {\appendixletter}
+ {\the\secno}{\the\subsecno}{\the\subsubsecno}{\noexpand\folio}}}%
+\escapechar=`\\%
+\write \contentsfile \temp %
+\appendixnoderef %
+\penalty 10000 %
+}}
+
+\outer\def\unnumberedsubsubsec{\parsearg\unnumberedsubsubsecyyy}
+\def\unnumberedsubsubsecyyy #1{\unnmhead3{#1}} %normally unnumberedsubsubseczzz
+\def\unnumberedsubsubseczzz #1{\seccheck{unnumberedsubsubsec}%
+\plainsubsubsecheading {#1}\gdef\thissection{#1}%
+{\chapternofonts%
+\toks0 = {#1}%
+\edef\temp{{\realbackslash unnumbsubsubsecentry{\the\toks0}{\noexpand\folio}}}%
+\escapechar=`\\%
+\write \contentsfile \temp %
+\unnumbnoderef %
+\penalty 10000 %
+}}
+
+% These are variants which are not "outer", so they can appear in @ifinfo.
+% Actually, they should now be obsolete; ordinary section commands should work.
+\def\infotop{\parsearg\unnumberedzzz}
+\def\infounnumbered{\parsearg\unnumberedzzz}
+\def\infounnumberedsec{\parsearg\unnumberedseczzz}
+\def\infounnumberedsubsec{\parsearg\unnumberedsubseczzz}
+\def\infounnumberedsubsubsec{\parsearg\unnumberedsubsubseczzz}
+
+\def\infoappendix{\parsearg\appendixzzz}
+\def\infoappendixsec{\parsearg\appendixseczzz}
+\def\infoappendixsubsec{\parsearg\appendixsubseczzz}
+\def\infoappendixsubsubsec{\parsearg\appendixsubsubseczzz}
+
+\def\infochapter{\parsearg\chapterzzz}
+\def\infosection{\parsearg\sectionzzz}
+\def\infosubsection{\parsearg\subsectionzzz}
+\def\infosubsubsection{\parsearg\subsubsectionzzz}
+
+% These macros control what the section commands do, according
+% to what kind of chapter we are in (ordinary, appendix, or unnumbered).
+% Define them by default for a numbered chapter.
+\global\let\section = \numberedsec
+\global\let\subsection = \numberedsubsec
+\global\let\subsubsection = \numberedsubsubsec
+
+% Define @majorheading, @heading and @subheading
+
+% NOTE on use of \vbox for chapter headings, section headings, and
+% such:
+% 1) We use \vbox rather than the earlier \line to permit
+% overlong headings to fold.
+% 2) \hyphenpenalty is set to 10000 because hyphenation in a
+% heading is obnoxious; this forbids it.
+% 3) Likewise, headings look best if no \parindent is used, and
+% if justification is not attempted. Hence \raggedright.
+
+
+\def\majorheading{\parsearg\majorheadingzzz}
+\def\majorheadingzzz #1{%
+{\advance\chapheadingskip by 10pt \chapbreak }%
+{\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000
+ \parindent=0pt\raggedright
+ \rm #1\hfill}}\bigskip \par\penalty 200}
+
+\def\chapheading{\parsearg\chapheadingzzz}
+\def\chapheadingzzz #1{\chapbreak %
+{\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000
+ \parindent=0pt\raggedright
+ \rm #1\hfill}}\bigskip \par\penalty 200}
+
+% @heading, @subheading, @subsubheading.
+\def\heading{\parsearg\plainsecheading}
+\def\subheading{\parsearg\plainsubsecheading}
+\def\subsubheading{\parsearg\plainsubsubsecheading}
+
+% These macros generate a chapter, section, etc. heading only
+% (including whitespace, linebreaking, etc. around it),
+% given all the information in convenient, parsed form.
+
+%%% Args are the skip and penalty (usually negative)
+\def\dobreak#1#2{\par\ifdim\lastskip<#1\removelastskip\penalty#2\vskip#1\fi}
+
+\def\setchapterstyle #1 {\csname CHAPF#1\endcsname}
+
+%%% Define plain chapter starts, and page on/off switching for it
+% Parameter controlling skip before chapter headings (if needed)
+
+\newskip\chapheadingskip
+
+\def\chapbreak{\dobreak \chapheadingskip {-4000}}
+\def\chappager{\par\vfill\supereject}
+\def\chapoddpage{\chappager \ifodd\pageno \else \hbox to 0pt{} \chappager\fi}
+
+\def\setchapternewpage #1 {\csname CHAPPAG#1\endcsname}
+
+\def\CHAPPAGoff{
+\global\let\contentsalignmacro = \chappager
+\global\let\pchapsepmacro=\chapbreak
+\global\let\pagealignmacro=\chappager}
+
+\def\CHAPPAGon{
+\global\let\contentsalignmacro = \chappager
+\global\let\pchapsepmacro=\chappager
+\global\let\pagealignmacro=\chappager
+\global\def\HEADINGSon{\HEADINGSsingle}}
+
+\def\CHAPPAGodd{
+\global\let\contentsalignmacro = \chapoddpage
+\global\let\pchapsepmacro=\chapoddpage
+\global\let\pagealignmacro=\chapoddpage
+\global\def\HEADINGSon{\HEADINGSdouble}}
+
+\CHAPPAGon
+
+\def\CHAPFplain{
+\global\let\chapmacro=\chfplain
+\global\let\unnumbchapmacro=\unnchfplain
+\global\let\centerchapmacro=\centerchfplain}
+
+% Plain chapter opening.
+% #1 is the text, #2 the chapter number or empty if unnumbered.
+\def\chfplain#1#2{%
+ \pchapsepmacro
+ {%
+ \chapfonts \rm
+ \def\chapnum{#2}%
+ \setbox0 = \hbox{#2\ifx\chapnum\empty\else\enspace\fi}%
+ \vbox{\hyphenpenalty=10000 \tolerance=5000 \parindent=0pt \raggedright
+ \hangindent = \wd0 \centerparametersmaybe
+ \unhbox0 #1\par}%
+ }%
+ \nobreak\bigskip % no page break after a chapter title
+ \nobreak
+}
+
+% Plain opening for unnumbered.
+\def\unnchfplain#1{\chfplain{#1}{}}
+
+% @centerchap -- centered and unnumbered.
+\let\centerparametersmaybe = \relax
+\def\centerchfplain#1{{%
+ \def\centerparametersmaybe{%
+ \advance\rightskip by 3\rightskip
+ \leftskip = \rightskip
+ \parfillskip = 0pt
+ }%
+ \chfplain{#1}{}%
+}}
+
+\CHAPFplain % The default
+
+\def\unnchfopen #1{%
+\chapoddpage {\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000
+ \parindent=0pt\raggedright
+ \rm #1\hfill}}\bigskip \par\penalty 10000 %
+}
+
+\def\chfopen #1#2{\chapoddpage {\chapfonts
+\vbox to 3in{\vfil \hbox to\hsize{\hfil #2} \hbox to\hsize{\hfil #1} \vfil}}%
+\par\penalty 5000 %
+}
+
+\def\centerchfopen #1{%
+\chapoddpage {\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000
+ \parindent=0pt
+ \hfill {\rm #1}\hfill}}\bigskip \par\penalty 10000 %
+}
+
+\def\CHAPFopen{
+\global\let\chapmacro=\chfopen
+\global\let\unnumbchapmacro=\unnchfopen
+\global\let\centerchapmacro=\centerchfopen}
+
+
+% Section titles.
+\newskip\secheadingskip
+\def\secheadingbreak{\dobreak \secheadingskip {-1000}}
+\def\secheading#1#2#3{\sectionheading{sec}{#2.#3}{#1}}
+\def\plainsecheading#1{\sectionheading{sec}{}{#1}}
+
+% Subsection titles.
+\newskip \subsecheadingskip
+\def\subsecheadingbreak{\dobreak \subsecheadingskip {-500}}
+\def\subsecheading#1#2#3#4{\sectionheading{subsec}{#2.#3.#4}{#1}}
+\def\plainsubsecheading#1{\sectionheading{subsec}{}{#1}}
+
+% Subsubsection titles.
+\let\subsubsecheadingskip = \subsecheadingskip
+\let\subsubsecheadingbreak = \subsecheadingbreak
+\def\subsubsecheading#1#2#3#4#5{\sectionheading{subsubsec}{#2.#3.#4.#5}{#1}}
+\def\plainsubsubsecheading#1{\sectionheading{subsubsec}{}{#1}}
+
+
+% Print any size section title.
+%
+% #1 is the section type (sec/subsec/subsubsec), #2 is the section
+% number (maybe empty), #3 the text.
+\def\sectionheading#1#2#3{%
+ {%
+ \expandafter\advance\csname #1headingskip\endcsname by \parskip
+ \csname #1headingbreak\endcsname
+ }%
+ {%
+ % Switch to the right set of fonts.
+ \csname #1fonts\endcsname \rm
+ %
+ % Only insert the separating space if we have a section number.
+ \def\secnum{#2}%
+ \setbox0 = \hbox{#2\ifx\secnum\empty\else\enspace\fi}%
+ %
+ \vbox{\hyphenpenalty=10000 \tolerance=5000 \parindent=0pt \raggedright
+ \hangindent = \wd0 % zero if no section number
+ \unhbox0 #3}%
+ }%
+ \ifdim\parskip<10pt \nobreak\kern10pt\nobreak\kern-\parskip\fi \nobreak
+}
+
+
+\message{toc printing,}
+% Finish up the main text and prepare to read what we've written
+% to \contentsfile.
+
+\newskip\contentsrightmargin \contentsrightmargin=1in
+\def\startcontents#1{%
+ % If @setchapternewpage on, and @headings double, the contents should
+ % start on an odd page, unlike chapters. Thus, we maintain
+ % \contentsalignmacro in parallel with \pagealignmacro.
+ % From: Torbjorn Granlund <tege@matematik.su.se>
+ \contentsalignmacro
+ \immediate\closeout \contentsfile
+ \ifnum \pageno>0
+ \pageno = -1 % Request roman numbered pages.
+ \fi
+ % Don't need to put `Contents' or `Short Contents' in the headline.
+ % It is abundantly clear what they are.
+ \unnumbchapmacro{#1}\def\thischapter{}%
+ \begingroup % Set up to handle contents files properly.
+ \catcode`\\=0 \catcode`\{=1 \catcode`\}=2 \catcode`\@=11
+ % We can't do this, because then an actual ^ in a section
+ % title fails, e.g., @chapter ^ -- exponentiation. --karl, 9jul97.
+ %\catcode`\^=7 % to see ^^e4 as \"a etc. juha@piuha.ydi.vtt.fi
+ \raggedbottom % Worry more about breakpoints than the bottom.
+ \advance\hsize by -\contentsrightmargin % Don't use the full line length.
+}
+
+
+% Normal (long) toc.
+\outer\def\contents{%
+ \startcontents{\putwordTableofContents}%
+ \input \jobname.toc
+ \endgroup
+ \vfill \eject
+}
+
+% And just the chapters.
+\outer\def\summarycontents{%
+ \startcontents{\putwordShortContents}%
+ %
+ \let\chapentry = \shortchapentry
+ \let\unnumbchapentry = \shortunnumberedentry
+ % We want a true roman here for the page numbers.
+ \secfonts
+ \let\rm=\shortcontrm \let\bf=\shortcontbf \let\sl=\shortcontsl
+ \rm
+ \hyphenpenalty = 10000
+ \advance\baselineskip by 1pt % Open it up a little.
+ \def\secentry ##1##2##3##4{}
+ \def\unnumbsecentry ##1##2{}
+ \def\subsecentry ##1##2##3##4##5{}
+ \def\unnumbsubsecentry ##1##2{}
+ \def\subsubsecentry ##1##2##3##4##5##6{}
+ \def\unnumbsubsubsecentry ##1##2{}
+ \input \jobname.toc
+ \endgroup
+ \vfill \eject
+}
+\let\shortcontents = \summarycontents
+
+% These macros generate individual entries in the table of contents.
+% The first argument is the chapter or section name.
+% The last argument is the page number.
+% The arguments in between are the chapter number, section number, ...
+
+% Chapter-level things, for both the long and short contents.
+\def\chapentry#1#2#3{\dochapentry{#2\labelspace#1}{#3}}
+
+% See comments in \dochapentry re vbox and related settings
+\def\shortchapentry#1#2#3{%
+ \tocentry{\shortchaplabel{#2}\labelspace #1}{\doshortpageno{#3}}%
+}
+
+% Typeset the label for a chapter or appendix for the short contents.
+% The arg is, e.g. `Appendix A' for an appendix, or `3' for a chapter.
+% We could simplify the code here by writing out an \appendixentry
+% command in the toc file for appendices, instead of using \chapentry
+% for both, but it doesn't seem worth it.
+\setbox0 = \hbox{\shortcontrm \putwordAppendix }
+\newdimen\shortappendixwidth \shortappendixwidth = \wd0
+
+\def\shortchaplabel#1{%
+ % We typeset #1 in a box of constant width, regardless of the text of
+ % #1, so the chapter titles will come out aligned.
+ \setbox0 = \hbox{#1}%
+ \dimen0 = \ifdim\wd0 > \shortappendixwidth \shortappendixwidth \else 0pt \fi
+ %
+ % This space should be plenty, since a single number is .5em, and the
+ % widest letter (M) is 1em, at least in the Computer Modern fonts.
+ % (This space doesn't include the extra space that gets added after
+ % the label; that gets put in by \shortchapentry above.)
+ \advance\dimen0 by 1.1em
+ \hbox to \dimen0{#1\hfil}%
+}
+
+\def\unnumbchapentry#1#2{\dochapentry{#1}{#2}}
+\def\shortunnumberedentry#1#2{\tocentry{#1}{\doshortpageno{#2}}}
+
+% Sections.
+\def\secentry#1#2#3#4{\dosecentry{#2.#3\labelspace#1}{#4}}
+\def\unnumbsecentry#1#2{\dosecentry{#1}{#2}}
+
+% Subsections.
+\def\subsecentry#1#2#3#4#5{\dosubsecentry{#2.#3.#4\labelspace#1}{#5}}
+\def\unnumbsubsecentry#1#2{\dosubsecentry{#1}{#2}}
+
+% And subsubsections.
+\def\subsubsecentry#1#2#3#4#5#6{%
+ \dosubsubsecentry{#2.#3.#4.#5\labelspace#1}{#6}}
+\def\unnumbsubsubsecentry#1#2{\dosubsubsecentry{#1}{#2}}
+
+% This parameter controls the indentation of the various levels.
+\newdimen\tocindent \tocindent = 3pc
+
+% Now for the actual typesetting. In all these, #1 is the text and #2 is the
+% page number.
+%
+% If the toc has to be broken over pages, we want it to be at chapters
+% if at all possible; hence the \penalty.
+\def\dochapentry#1#2{%
+ \penalty-300 \vskip1\baselineskip plus.33\baselineskip minus.25\baselineskip
+ \begingroup
+ \chapentryfonts
+ \tocentry{#1}{\dopageno{#2}}%
+ \endgroup
+ \nobreak\vskip .25\baselineskip plus.1\baselineskip
+}
+
+\def\dosecentry#1#2{\begingroup
+ \secentryfonts \leftskip=\tocindent
+ \tocentry{#1}{\dopageno{#2}}%
+\endgroup}
+
+\def\dosubsecentry#1#2{\begingroup
+ \subsecentryfonts \leftskip=2\tocindent
+ \tocentry{#1}{\dopageno{#2}}%
+\endgroup}
+
+\def\dosubsubsecentry#1#2{\begingroup
+ \subsubsecentryfonts \leftskip=3\tocindent
+ \tocentry{#1}{\dopageno{#2}}%
+\endgroup}
+
+% Final typesetting of a toc entry; we use the same \entry macro as for
+% the index entries, but we want to suppress hyphenation here. (We
+% can't do that in the \entry macro, since index entries might consist
+% of hyphenated-identifiers-that-do-not-fit-on-a-line-and-nothing-else.)
+%
+% \turnoffactive is for the sake of @" used for umlauts.
+\def\tocentry#1#2{\begingroup
+ \vskip 0pt plus1pt % allow a little stretch for the sake of nice page breaks
+ \entry{\turnoffactive #1}{\turnoffactive #2}%
+\endgroup}
+
+% Space between chapter (or whatever) number and the title.
+\def\labelspace{\hskip1em \relax}
+
+\def\dopageno#1{{\rm #1}}
+\def\doshortpageno#1{{\rm #1}}
+
+\def\chapentryfonts{\secfonts \rm}
+\def\secentryfonts{\textfonts}
+\let\subsecentryfonts = \textfonts
+\let\subsubsecentryfonts = \textfonts
+
+
+\message{environments,}
+
+% Since these characters are used in examples, it should be an even number of
+% \tt widths. Each \tt character is 1en, so two makes it 1em.
+% Furthermore, these definitions must come after we define our fonts.
+\newbox\dblarrowbox \newbox\longdblarrowbox
+\newbox\pushcharbox \newbox\bullbox
+\newbox\equivbox \newbox\errorbox
+
+%{\tentt
+%\global\setbox\dblarrowbox = \hbox to 1em{\hfil$\Rightarrow$\hfil}
+%\global\setbox\longdblarrowbox = \hbox to 1em{\hfil$\mapsto$\hfil}
+%\global\setbox\pushcharbox = \hbox to 1em{\hfil$\dashv$\hfil}
+%\global\setbox\equivbox = \hbox to 1em{\hfil$\ptexequiv$\hfil}
+% Adapted from the manmac format (p.420 of TeXbook)
+%\global\setbox\bullbox = \hbox to 1em{\kern.15em\vrule height .75ex width .85ex
+% depth .1ex\hfil}
+%}
+
+% @point{}, @result{}, @expansion{}, @print{}, @equiv{}.
+\def\point{$\star$}
+\def\result{\leavevmode\raise.15ex\hbox to 1em{\hfil$\Rightarrow$\hfil}}
+\def\expansion{\leavevmode\raise.1ex\hbox to 1em{\hfil$\mapsto$\hfil}}
+\def\print{\leavevmode\lower.1ex\hbox to 1em{\hfil$\dashv$\hfil}}
+\def\equiv{\leavevmode\lower.1ex\hbox to 1em{\hfil$\ptexequiv$\hfil}}
+
+% Adapted from the TeXbook's \boxit.
+{\tentt \global\dimen0 = 3em}% Width of the box.
+\dimen2 = .55pt % Thickness of rules
+% The text. (`r' is open on the right, `e' somewhat less so on the left.)
+\setbox0 = \hbox{\kern-.75pt \tensf error\kern-1.5pt}
+
+\global\setbox\errorbox=\hbox to \dimen0{\hfil
+ \hsize = \dimen0 \advance\hsize by -5.8pt % Space to left+right.
+ \advance\hsize by -2\dimen2 % Rules.
+ \vbox{
+ \hrule height\dimen2
+ \hbox{\vrule width\dimen2 \kern3pt % Space to left of text.
+ \vtop{\kern2.4pt \box0 \kern2.4pt}% Space above/below.
+ \kern3pt\vrule width\dimen2}% Space to right.
+ \hrule height\dimen2}
+ \hfil}
+
+% The @error{} command.
+\def\error{\leavevmode\lower.7ex\copy\errorbox}
+
+% @tex ... @end tex escapes into raw Tex temporarily.
+% One exception: @ is still an escape character, so that @end tex works.
+% But \@ or @@ will get a plain tex @ character.
+
+\def\tex{\begingroup
+\catcode `\\=0 \catcode `\{=1 \catcode `\}=2
+\catcode `\$=3 \catcode `\&=4 \catcode `\#=6
+\catcode `\^=7 \catcode `\_=8 \catcode `\~=13 \let~=\tie
+\catcode `\%=14
+\catcode 43=12 % plus
+\catcode`\"=12
+\catcode`\==12
+\catcode`\|=12
+\catcode`\<=12
+\catcode`\>=12
+\escapechar=`\\
+%
+\let\,=\ptexcomma
+\let\{=\ptexlbrace
+\let\}=\ptexrbrace
+\let\.=\ptexdot
+\let\*=\ptexstar
+\let\dots=\ptexdots
+\def\endldots{\mathinner{\ldots\ldots\ldots\ldots}}%
+\def\enddots{\relax\ifmmode\endldots\else$\mathsurround=0pt \endldots\,$\fi}%
+\def\@{@}%
+\let\bullet=\ptexbullet
+\let\b=\ptexb \let\c=\ptexc \let\i=\ptexi \let\t=\ptext
+%
+\let\Etex=\endgroup}
+
+% Define @lisp ... @endlisp.
+% @lisp does a \begingroup so it can rebind things,
+% including the definition of @endlisp (which normally is erroneous).
+
+% Amount to narrow the margins by for @lisp.
+\newskip\lispnarrowing \lispnarrowing=0.4in
+
+% This is the definition that ^^M gets inside @lisp, @example, and other
+% such environments. \null is better than a space, since it doesn't
+% have any width.
+\def\lisppar{\null\endgraf}
+
+% Make each space character in the input produce a normal interword
+% space in the output. Don't allow a line break at this space, as this
+% is used only in environments like @example, where each line of input
+% should produce a line of output anyway.
+%
+{\obeyspaces %
+\gdef\sepspaces{\obeyspaces\let =\tie}}
+
+% Define \obeyedspace to be our active space, whatever it is. This is
+% for use in \parsearg.
+{\sepspaces%
+\global\let\obeyedspace= }
+
+% This space is always present above and below environments.
+\newskip\envskipamount \envskipamount = 0pt
+
+% Make spacing and below environment symmetrical. We use \parskip here
+% to help in doing that, since in @example-like environments \parskip
+% is reset to zero; thus the \afterenvbreak inserts no space -- but the
+% start of the next paragraph will insert \parskip
+%
+\def\aboveenvbreak{{\advance\envskipamount by \parskip
+\endgraf \ifdim\lastskip<\envskipamount
+\removelastskip \penalty-50 \vskip\envskipamount \fi}}
+
+\let\afterenvbreak = \aboveenvbreak
+
+% \nonarrowing is a flag. If "set", @lisp etc don't narrow margins.
+\let\nonarrowing=\relax
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% \cartouche: draw rectangle w/rounded corners around argument
+\font\circle=lcircle10
+\newdimen\circthick
+\newdimen\cartouter\newdimen\cartinner
+\newskip\normbskip\newskip\normpskip\newskip\normlskip
+\circthick=\fontdimen8\circle
+%
+\def\ctl{{\circle\char'013\hskip -6pt}}% 6pt from pl file: 1/2charwidth
+\def\ctr{{\hskip 6pt\circle\char'010}}
+\def\cbl{{\circle\char'012\hskip -6pt}}
+\def\cbr{{\hskip 6pt\circle\char'011}}
+\def\carttop{\hbox to \cartouter{\hskip\lskip
+ \ctl\leaders\hrule height\circthick\hfil\ctr
+ \hskip\rskip}}
+\def\cartbot{\hbox to \cartouter{\hskip\lskip
+ \cbl\leaders\hrule height\circthick\hfil\cbr
+ \hskip\rskip}}
+%
+\newskip\lskip\newskip\rskip
+
+\long\def\cartouche{%
+\begingroup
+ \lskip=\leftskip \rskip=\rightskip
+ \leftskip=0pt\rightskip=0pt %we want these *outside*.
+ \cartinner=\hsize \advance\cartinner by-\lskip
+ \advance\cartinner by-\rskip
+ \cartouter=\hsize
+ \advance\cartouter by 18pt % allow for 3pt kerns on either
+% side, and for 6pt waste from
+% each corner char
+ \normbskip=\baselineskip \normpskip=\parskip \normlskip=\lineskip
+ % Flag to tell @lisp, etc., not to narrow margin.
+ \let\nonarrowing=\comment
+ \vbox\bgroup
+ \baselineskip=0pt\parskip=0pt\lineskip=0pt
+ \carttop
+ \hbox\bgroup
+ \hskip\lskip
+ \vrule\kern3pt
+ \vbox\bgroup
+ \hsize=\cartinner
+ \kern3pt
+ \begingroup
+ \baselineskip=\normbskip
+ \lineskip=\normlskip
+ \parskip=\normpskip
+ \vskip -\parskip
+\def\Ecartouche{%
+ \endgroup
+ \kern3pt
+ \egroup
+ \kern3pt\vrule
+ \hskip\rskip
+ \egroup
+ \cartbot
+ \egroup
+\endgroup
+}}
+
+
+% This macro is called at the beginning of all the @example variants,
+% inside a group.
+\def\nonfillstart{%
+ \aboveenvbreak
+ \inENV % This group ends at the end of the body
+ \hfuzz = 12pt % Don't be fussy
+ \sepspaces % Make spaces be word-separators rather than space tokens.
+ \singlespace
+ \let\par = \lisppar % don't ignore blank lines
+ \obeylines % each line of input is a line of output
+ \parskip = 0pt
+ \parindent = 0pt
+ \emergencystretch = 0pt % don't try to avoid overfull boxes
+ % @cartouche defines \nonarrowing to inhibit narrowing
+ % at next level down.
+ \ifx\nonarrowing\relax
+ \advance \leftskip by \lispnarrowing
+ \exdentamount=\lispnarrowing
+ \let\exdent=\nofillexdent
+ \let\nonarrowing=\relax
+ \fi
+}
+
+% To ending an @example-like environment, we first end the paragraph
+% (via \afterenvbreak's vertical glue), and then the group. That way we
+% keep the zero \parskip that the environments set -- \parskip glue
+% will be inserted at the beginning of the next paragraph in the
+% document, after the environment.
+%
+\def\nonfillfinish{\afterenvbreak\endgroup}%
+
+\def\lisp{\begingroup
+ \nonfillstart
+ \let\Elisp = \nonfillfinish
+ \tt
+ % Make @kbd do something special, if requested.
+ \let\kbdfont\kbdexamplefont
+ \rawbackslash % have \ input char produce \ char from current font
+ \gobble
+}
+
+% Define the \E... control sequence only if we are inside the
+% environment, so the error checking in \end will work.
+%
+% We must call \lisp last in the definition, since it reads the
+% return following the @example (or whatever) command.
+%
+\def\example{\begingroup \def\Eexample{\nonfillfinish\endgroup}\lisp}
+\def\smallexample{\begingroup \def\Esmallexample{\nonfillfinish\endgroup}\lisp}
+\def\smalllisp{\begingroup \def\Esmalllisp{\nonfillfinish\endgroup}\lisp}
+
+% @smallexample and @smalllisp. This is not used unless the @smallbook
+% command is given. Originally contributed by Pavel@xerox.
+%
+\def\smalllispx{\begingroup
+ \nonfillstart
+ \let\Esmalllisp = \nonfillfinish
+ \let\Esmallexample = \nonfillfinish
+ %
+ % Smaller fonts for small examples.
+ \indexfonts \tt
+ \rawbackslash % make \ output the \ character from the current font (tt)
+ \gobble
+}
+
+% This is @display; same as @lisp except use roman font.
+%
+\def\display{\begingroup
+ \nonfillstart
+ \let\Edisplay = \nonfillfinish
+ \gobble
+}
+
+% This is @format; same as @display except don't narrow margins.
+%
+\def\format{\begingroup
+ \let\nonarrowing = t
+ \nonfillstart
+ \let\Eformat = \nonfillfinish
+ \gobble
+}
+
+% @flushleft (same as @format) and @flushright.
+%
+\def\flushleft{\begingroup
+ \let\nonarrowing = t
+ \nonfillstart
+ \let\Eflushleft = \nonfillfinish
+ \gobble
+}
+\def\flushright{\begingroup
+ \let\nonarrowing = t
+ \nonfillstart
+ \let\Eflushright = \nonfillfinish
+ \advance\leftskip by 0pt plus 1fill
+ \gobble}
+
+% @quotation does normal linebreaking (hence we can't use \nonfillstart)
+% and narrows the margins.
+%
+\def\quotation{%
+ \begingroup\inENV %This group ends at the end of the @quotation body
+ {\parskip=0pt \aboveenvbreak}% because \aboveenvbreak inserts \parskip
+ \singlespace
+ \parindent=0pt
+ % We have retained a nonzero parskip for the environment, since we're
+ % doing normal filling. So to avoid extra space below the environment...
+ \def\Equotation{\parskip = 0pt \nonfillfinish}%
+ %
+ % @cartouche defines \nonarrowing to inhibit narrowing at next level down.
+ \ifx\nonarrowing\relax
+ \advance\leftskip by \lispnarrowing
+ \advance\rightskip by \lispnarrowing
+ \exdentamount = \lispnarrowing
+ \let\nonarrowing = \relax
+ \fi
+}
+
+\message{defuns,}
+% Define formatter for defuns
+% First, allow user to change definition object font (\df) internally
+\def\setdeffont #1 {\csname DEF#1\endcsname}
+
+\newskip\defbodyindent \defbodyindent=.4in
+\newskip\defargsindent \defargsindent=50pt
+\newskip\deftypemargin \deftypemargin=12pt
+\newskip\deflastargmargin \deflastargmargin=18pt
+
+\newcount\parencount
+% define \functionparens, which makes ( and ) and & do special things.
+% \functionparens affects the group it is contained in.
+\def\activeparens{%
+\catcode`\(=\active \catcode`\)=\active \catcode`\&=\active
+\catcode`\[=\active \catcode`\]=\active}
+
+% Make control sequences which act like normal parenthesis chars.
+\let\lparen = ( \let\rparen = )
+
+{\activeparens % Now, smart parens don't turn on until &foo (see \amprm)
+
+% Be sure that we always have a definition for `(', etc. For example,
+% if the fn name has parens in it, \boldbrax will not be in effect yet,
+% so TeX would otherwise complain about undefined control sequence.
+\global\let(=\lparen \global\let)=\rparen
+\global\let[=\lbrack \global\let]=\rbrack
+
+\gdef\functionparens{\boldbrax\let&=\amprm\parencount=0 }
+\gdef\boldbrax{\let(=\opnr\let)=\clnr\let[=\lbrb\let]=\rbrb}
+% This is used to turn on special parens
+% but make & act ordinary (given that it's active).
+\gdef\boldbraxnoamp{\let(=\opnr\let)=\clnr\let[=\lbrb\let]=\rbrb\let&=\ampnr}
+
+% Definitions of (, ) and & used in args for functions.
+% This is the definition of ( outside of all parentheses.
+\gdef\oprm#1 {{\rm\char`\(}#1 \bf \let(=\opnested
+ \global\advance\parencount by 1
+}
+%
+% This is the definition of ( when already inside a level of parens.
+\gdef\opnested{\char`\(\global\advance\parencount by 1 }
+%
+\gdef\clrm{% Print a paren in roman if it is taking us back to depth of 0.
+ % also in that case restore the outer-level definition of (.
+ \ifnum \parencount=1 {\rm \char `\)}\sl \let(=\oprm \else \char `\) \fi
+ \global\advance \parencount by -1 }
+% If we encounter &foo, then turn on ()-hacking afterwards
+\gdef\amprm#1 {{\rm\&#1}\let(=\oprm \let)=\clrm\ }
+%
+\gdef\normalparens{\boldbrax\let&=\ampnr}
+} % End of definition inside \activeparens
+%% These parens (in \boldbrax) actually are a little bolder than the
+%% contained text. This is especially needed for [ and ]
+\def\opnr{{\sf\char`\(}\global\advance\parencount by 1 }
+\def\clnr{{\sf\char`\)}\global\advance\parencount by -1 }
+\def\ampnr{\&}
+\def\lbrb{{\bf\char`\[}}
+\def\rbrb{{\bf\char`\]}}
+
+% First, defname, which formats the header line itself.
+% #1 should be the function name.
+% #2 should be the type of definition, such as "Function".
+
+\def\defname #1#2{%
+% Get the values of \leftskip and \rightskip as they were
+% outside the @def...
+\dimen2=\leftskip
+\advance\dimen2 by -\defbodyindent
+\dimen3=\rightskip
+\advance\dimen3 by -\defbodyindent
+\noindent %
+\setbox0=\hbox{\hskip \deflastargmargin{\rm #2}\hskip \deftypemargin}%
+\dimen0=\hsize \advance \dimen0 by -\wd0 % compute size for first line
+\dimen1=\hsize \advance \dimen1 by -\defargsindent %size for continuations
+\parshape 2 0in \dimen0 \defargsindent \dimen1 %
+% Now output arg 2 ("Function" or some such)
+% ending at \deftypemargin from the right margin,
+% but stuck inside a box of width 0 so it does not interfere with linebreaking
+{% Adjust \hsize to exclude the ambient margins,
+% so that \rightline will obey them.
+\advance \hsize by -\dimen2 \advance \hsize by -\dimen3
+\rlap{\rightline{{\rm #2}\hskip \deftypemargin}}}%
+% Make all lines underfull and no complaints:
+\tolerance=10000 \hbadness=10000
+\advance\leftskip by -\defbodyindent
+\exdentamount=\defbodyindent
+{\df #1}\enskip % Generate function name
+}
+
+% Actually process the body of a definition
+% #1 should be the terminating control sequence, such as \Edefun.
+% #2 should be the "another name" control sequence, such as \defunx.
+% #3 should be the control sequence that actually processes the header,
+% such as \defunheader.
+
+\def\defparsebody #1#2#3{\begingroup\inENV% Environment for definitionbody
+\medbreak %
+% Define the end token that this defining construct specifies
+% so that it will exit this group.
+\def#1{\endgraf\endgroup\medbreak}%
+\def#2{\begingroup\obeylines\activeparens\spacesplit#3}%
+\parindent=0in
+\advance\leftskip by \defbodyindent \advance \rightskip by \defbodyindent
+\exdentamount=\defbodyindent
+\begingroup %
+\catcode 61=\active % 61 is `='
+\obeylines\activeparens\spacesplit#3}
+
+\def\defmethparsebody #1#2#3#4 {\begingroup\inENV %
+\medbreak %
+% Define the end token that this defining construct specifies
+% so that it will exit this group.
+\def#1{\endgraf\endgroup\medbreak}%
+\def#2##1 {\begingroup\obeylines\activeparens\spacesplit{#3{##1}}}%
+\parindent=0in
+\advance\leftskip by \defbodyindent \advance \rightskip by \defbodyindent
+\exdentamount=\defbodyindent
+\begingroup\obeylines\activeparens\spacesplit{#3{#4}}}
+
+\def\defopparsebody #1#2#3#4#5 {\begingroup\inENV %
+\medbreak %
+% Define the end token that this defining construct specifies
+% so that it will exit this group.
+\def#1{\endgraf\endgroup\medbreak}%
+\def#2##1 ##2 {\def#4{##1}%
+\begingroup\obeylines\activeparens\spacesplit{#3{##2}}}%
+\parindent=0in
+\advance\leftskip by \defbodyindent \advance \rightskip by \defbodyindent
+\exdentamount=\defbodyindent
+\begingroup\obeylines\activeparens\spacesplit{#3{#5}}}
+
+% These parsing functions are similar to the preceding ones
+% except that they do not make parens into active characters.
+% These are used for "variables" since they have no arguments.
+
+\def\defvarparsebody #1#2#3{\begingroup\inENV% Environment for definitionbody
+\medbreak %
+% Define the end token that this defining construct specifies
+% so that it will exit this group.
+\def#1{\endgraf\endgroup\medbreak}%
+\def#2{\begingroup\obeylines\spacesplit#3}%
+\parindent=0in
+\advance\leftskip by \defbodyindent \advance \rightskip by \defbodyindent
+\exdentamount=\defbodyindent
+\begingroup %
+\catcode 61=\active %
+\obeylines\spacesplit#3}
+
+% This is used for \def{tp,vr}parsebody. It could probably be used for
+% some of the others, too, with some judicious conditionals.
+%
+\def\parsebodycommon#1#2#3{%
+ \begingroup\inENV %
+ \medbreak %
+ % Define the end token that this defining construct specifies
+ % so that it will exit this group.
+ \def#1{\endgraf\endgroup\medbreak}%
+ \def#2##1 {\begingroup\obeylines\spacesplit{#3{##1}}}%
+ \parindent=0in
+ \advance\leftskip by \defbodyindent \advance \rightskip by \defbodyindent
+ \exdentamount=\defbodyindent
+ \begingroup\obeylines
+}
+
+\def\defvrparsebody#1#2#3#4 {%
+ \parsebodycommon{#1}{#2}{#3}%
+ \spacesplit{#3{#4}}%
+}
+
+% This loses on `@deftp {Data Type} {struct termios}' -- it thinks the
+% type is just `struct', because we lose the braces in `{struct
+% termios}' when \spacesplit reads its undelimited argument. Sigh.
+% \let\deftpparsebody=\defvrparsebody
+%
+% So, to get around this, we put \empty in with the type name. That
+% way, TeX won't find exactly `{...}' as an undelimited argument, and
+% won't strip off the braces.
+%
+\def\deftpparsebody #1#2#3#4 {%
+ \parsebodycommon{#1}{#2}{#3}%
+ \spacesplit{\parsetpheaderline{#3{#4}}}\empty
+}
+
+% Fine, but then we have to eventually remove the \empty *and* the
+% braces (if any). That's what this does.
+%
+\def\removeemptybraces\empty#1\relax{#1}
+
+% After \spacesplit has done its work, this is called -- #1 is the final
+% thing to call, #2 the type name (which starts with \empty), and #3
+% (which might be empty) the arguments.
+%
+\def\parsetpheaderline#1#2#3{%
+ #1{\removeemptybraces#2\relax}{#3}%
+}%
+
+\def\defopvarparsebody #1#2#3#4#5 {\begingroup\inENV %
+\medbreak %
+% Define the end token that this defining construct specifies
+% so that it will exit this group.
+\def#1{\endgraf\endgroup\medbreak}%
+\def#2##1 ##2 {\def#4{##1}%
+\begingroup\obeylines\spacesplit{#3{##2}}}%
+\parindent=0in
+\advance\leftskip by \defbodyindent \advance \rightskip by \defbodyindent
+\exdentamount=\defbodyindent
+\begingroup\obeylines\spacesplit{#3{#5}}}
+
+% Split up #2 at the first space token.
+% call #1 with two arguments:
+% the first is all of #2 before the space token,
+% the second is all of #2 after that space token.
+% If #2 contains no space token, all of it is passed as the first arg
+% and the second is passed as empty.
+
+{\obeylines
+\gdef\spacesplit#1#2^^M{\endgroup\spacesplitfoo{#1}#2 \relax\spacesplitfoo}%
+\long\gdef\spacesplitfoo#1#2 #3#4\spacesplitfoo{%
+\ifx\relax #3%
+#1{#2}{}\else #1{#2}{#3#4}\fi}}
+
+% So much for the things common to all kinds of definitions.
+
+% Define @defun.
+
+% First, define the processing that is wanted for arguments of \defun
+% Use this to expand the args and terminate the paragraph they make up
+
+\def\defunargs #1{\functionparens \sl
+% Expand, preventing hyphenation at `-' chars.
+% Note that groups don't affect changes in \hyphenchar.
+\hyphenchar\tensl=0
+#1%
+\hyphenchar\tensl=45
+\ifnum\parencount=0 \else \errmessage{Unbalanced parentheses in @def}\fi%
+\interlinepenalty=10000
+\advance\rightskip by 0pt plus 1fil
+\endgraf\penalty 10000\vskip -\parskip\penalty 10000%
+}
+
+\def\deftypefunargs #1{%
+% Expand, preventing hyphenation at `-' chars.
+% Note that groups don't affect changes in \hyphenchar.
+% Use \boldbraxnoamp, not \functionparens, so that & is not special.
+\boldbraxnoamp
+\tclose{#1}% avoid \code because of side effects on active chars
+\interlinepenalty=10000
+\advance\rightskip by 0pt plus 1fil
+\endgraf\penalty 10000\vskip -\parskip\penalty 10000%
+}
+
+% Do complete processing of one @defun or @defunx line already parsed.
+
+% @deffn Command forward-char nchars
+
+\def\deffn{\defmethparsebody\Edeffn\deffnx\deffnheader}
+
+\def\deffnheader #1#2#3{\doind {fn}{\code{#2}}%
+\begingroup\defname {#2}{#1}\defunargs{#3}\endgroup %
+\catcode 61=\other % Turn off change made in \defparsebody
+}
+
+% @defun == @deffn Function
+
+\def\defun{\defparsebody\Edefun\defunx\defunheader}
+
+\def\defunheader #1#2{\doind {fn}{\code{#1}}% Make entry in function index
+\begingroup\defname {#1}{Function}%
+\defunargs {#2}\endgroup %
+\catcode 61=\other % Turn off change made in \defparsebody
+}
+
+% @deftypefun int foobar (int @var{foo}, float @var{bar})
+
+\def\deftypefun{\defparsebody\Edeftypefun\deftypefunx\deftypefunheader}
+
+% #1 is the data type. #2 is the name and args.
+\def\deftypefunheader #1#2{\deftypefunheaderx{#1}#2 \relax}
+% #1 is the data type, #2 the name, #3 the args.
+\def\deftypefunheaderx #1#2 #3\relax{%
+\doind {fn}{\code{#2}}% Make entry in function index
+\begingroup\defname {\defheaderxcond#1\relax$$$#2}{Function}%
+\deftypefunargs {#3}\endgroup %
+\catcode 61=\other % Turn off change made in \defparsebody
+}
+
+% @deftypefn {Library Function} int foobar (int @var{foo}, float @var{bar})
+
+\def\deftypefn{\defmethparsebody\Edeftypefn\deftypefnx\deftypefnheader}
+
+% \defheaderxcond#1\relax$$$
+% puts #1 in @code, followed by a space, but does nothing if #1 is null.
+\def\defheaderxcond#1#2$$${\ifx#1\relax\else\code{#1#2} \fi}
+
+% #1 is the classification. #2 is the data type. #3 is the name and args.
+\def\deftypefnheader #1#2#3{\deftypefnheaderx{#1}{#2}#3 \relax}
+% #1 is the classification, #2 the data type, #3 the name, #4 the args.
+\def\deftypefnheaderx #1#2#3 #4\relax{%
+\doind {fn}{\code{#3}}% Make entry in function index
+\begingroup
+\normalparens % notably, turn off `&' magic, which prevents
+% at least some C++ text from working
+\defname {\defheaderxcond#2\relax$$$#3}{#1}%
+\deftypefunargs {#4}\endgroup %
+\catcode 61=\other % Turn off change made in \defparsebody
+}
+
+% @defmac == @deffn Macro
+
+\def\defmac{\defparsebody\Edefmac\defmacx\defmacheader}
+
+\def\defmacheader #1#2{\doind {fn}{\code{#1}}% Make entry in function index
+\begingroup\defname {#1}{Macro}%
+\defunargs {#2}\endgroup %
+\catcode 61=\other % Turn off change made in \defparsebody
+}
+
+% @defspec == @deffn Special Form
+
+\def\defspec{\defparsebody\Edefspec\defspecx\defspecheader}
+
+\def\defspecheader #1#2{\doind {fn}{\code{#1}}% Make entry in function index
+\begingroup\defname {#1}{Special Form}%
+\defunargs {#2}\endgroup %
+\catcode 61=\other % Turn off change made in \defparsebody
+}
+
+% This definition is run if you use @defunx
+% anywhere other than immediately after a @defun or @defunx.
+
+\def\deffnx #1 {\errmessage{@deffnx in invalid context}}
+\def\defunx #1 {\errmessage{@defunx in invalid context}}
+\def\defmacx #1 {\errmessage{@defmacx in invalid context}}
+\def\defspecx #1 {\errmessage{@defspecx in invalid context}}
+\def\deftypefnx #1 {\errmessage{@deftypefnx in invalid context}}
+\def\deftypemethodx #1 {\errmessage{@deftypemethodx in invalid context}}
+\def\deftypeunx #1 {\errmessage{@deftypeunx in invalid context}}
+
+% @defmethod, and so on
+
+% @defop {Funny Method} foo-class frobnicate argument
+
+\def\defop #1 {\def\defoptype{#1}%
+\defopparsebody\Edefop\defopx\defopheader\defoptype}
+
+\def\defopheader #1#2#3{%
+\dosubind {fn}{\code{#2}}{on #1}% Make entry in function index
+\begingroup\defname {#2}{\defoptype{} on #1}%
+\defunargs {#3}\endgroup %
+}
+
+% @deftypemethod foo-class return-type foo-method args
+%
+\def\deftypemethod{%
+ \defmethparsebody\Edeftypemethod\deftypemethodx\deftypemethodheader}
+%
+% #1 is the class name, #2 the data type, #3 the method name, #4 the args.
+\def\deftypemethodheader#1#2#3#4{%
+ \deftypefnheaderx{Method on #1}{#2}#3 #4\relax
+}
+
+% @defmethod == @defop Method
+
+\def\defmethod{\defmethparsebody\Edefmethod\defmethodx\defmethodheader}
+
+\def\defmethodheader #1#2#3{%
+\dosubind {fn}{\code{#2}}{on #1}% entry in function index
+\begingroup\defname {#2}{Method on #1}%
+\defunargs {#3}\endgroup %
+}
+
+% @defcv {Class Option} foo-class foo-flag
+
+\def\defcv #1 {\def\defcvtype{#1}%
+\defopvarparsebody\Edefcv\defcvx\defcvarheader\defcvtype}
+
+\def\defcvarheader #1#2#3{%
+\dosubind {vr}{\code{#2}}{of #1}% Make entry in var index
+\begingroup\defname {#2}{\defcvtype{} of #1}%
+\defvarargs {#3}\endgroup %
+}
+
+% @defivar == @defcv {Instance Variable}
+
+\def\defivar{\defvrparsebody\Edefivar\defivarx\defivarheader}
+
+\def\defivarheader #1#2#3{%
+\dosubind {vr}{\code{#2}}{of #1}% Make entry in var index
+\begingroup\defname {#2}{Instance Variable of #1}%
+\defvarargs {#3}\endgroup %
+}
+
+% These definitions are run if you use @defmethodx, etc.,
+% anywhere other than immediately after a @defmethod, etc.
+
+\def\defopx #1 {\errmessage{@defopx in invalid context}}
+\def\defmethodx #1 {\errmessage{@defmethodx in invalid context}}
+\def\defcvx #1 {\errmessage{@defcvx in invalid context}}
+\def\defivarx #1 {\errmessage{@defivarx in invalid context}}
+
+% Now @defvar
+
+% First, define the processing that is wanted for arguments of @defvar.
+% This is actually simple: just print them in roman.
+% This must expand the args and terminate the paragraph they make up
+\def\defvarargs #1{\normalparens #1%
+\interlinepenalty=10000
+\endgraf\penalty 10000\vskip -\parskip\penalty 10000}
+
+% @defvr Counter foo-count
+
+\def\defvr{\defvrparsebody\Edefvr\defvrx\defvrheader}
+
+\def\defvrheader #1#2#3{\doind {vr}{\code{#2}}%
+\begingroup\defname {#2}{#1}\defvarargs{#3}\endgroup}
+
+% @defvar == @defvr Variable
+
+\def\defvar{\defvarparsebody\Edefvar\defvarx\defvarheader}
+
+\def\defvarheader #1#2{\doind {vr}{\code{#1}}% Make entry in var index
+\begingroup\defname {#1}{Variable}%
+\defvarargs {#2}\endgroup %
+}
+
+% @defopt == @defvr {User Option}
+
+\def\defopt{\defvarparsebody\Edefopt\defoptx\defoptheader}
+
+\def\defoptheader #1#2{\doind {vr}{\code{#1}}% Make entry in var index
+\begingroup\defname {#1}{User Option}%
+\defvarargs {#2}\endgroup %
+}
+
+% @deftypevar int foobar
+
+\def\deftypevar{\defvarparsebody\Edeftypevar\deftypevarx\deftypevarheader}
+
+% #1 is the data type. #2 is the name, perhaps followed by text that
+% is actually part of the data type, which should not be put into the index.
+\def\deftypevarheader #1#2{%
+\dovarind#2 \relax% Make entry in variables index
+\begingroup\defname {\defheaderxcond#1\relax$$$#2}{Variable}%
+\interlinepenalty=10000
+\endgraf\penalty 10000\vskip -\parskip\penalty 10000
+\endgroup}
+\def\dovarind#1 #2\relax{\doind{vr}{\code{#1}}}
+
+% @deftypevr {Global Flag} int enable
+
+\def\deftypevr{\defvrparsebody\Edeftypevr\deftypevrx\deftypevrheader}
+
+\def\deftypevrheader #1#2#3{\dovarind#3 \relax%
+\begingroup\defname {\defheaderxcond#2\relax$$$#3}{#1}
+\interlinepenalty=10000
+\endgraf\penalty 10000\vskip -\parskip\penalty 10000
+\endgroup}
+
+% This definition is run if you use @defvarx
+% anywhere other than immediately after a @defvar or @defvarx.
+
+\def\defvrx #1 {\errmessage{@defvrx in invalid context}}
+\def\defvarx #1 {\errmessage{@defvarx in invalid context}}
+\def\defoptx #1 {\errmessage{@defoptx in invalid context}}
+\def\deftypevarx #1 {\errmessage{@deftypevarx in invalid context}}
+\def\deftypevrx #1 {\errmessage{@deftypevrx in invalid context}}
+
+% Now define @deftp
+% Args are printed in bold, a slight difference from @defvar.
+
+\def\deftpargs #1{\bf \defvarargs{#1}}
+
+% @deftp Class window height width ...
+
+\def\deftp{\deftpparsebody\Edeftp\deftpx\deftpheader}
+
+\def\deftpheader #1#2#3{\doind {tp}{\code{#2}}%
+\begingroup\defname {#2}{#1}\deftpargs{#3}\endgroup}
+
+% This definition is run if you use @deftpx, etc
+% anywhere other than immediately after a @deftp, etc.
+
+\def\deftpx #1 {\errmessage{@deftpx in invalid context}}
+
+
+\message{cross reference,}
+% Define cross-reference macros
+\newwrite \auxfile
+
+\newif\ifhavexrefs % True if xref values are known.
+\newif\ifwarnedxrefs % True if we warned once that they aren't known.
+
+% @inforef is simple.
+\def\inforef #1{\inforefzzz #1,,,,**}
+\def\inforefzzz #1,#2,#3,#4**{\putwordSee{} \putwordInfo{} \putwordfile{} \file{\ignorespaces #3{}},
+ node \samp{\ignorespaces#1{}}}
+
+% \setref{foo} defines a cross-reference point named foo.
+
+\def\setref#1{%
+\dosetq{#1-title}{Ytitle}%
+\dosetq{#1-pg}{Ypagenumber}%
+\dosetq{#1-snt}{Ysectionnumberandtype}}
+
+\def\unnumbsetref#1{%
+\dosetq{#1-title}{Ytitle}%
+\dosetq{#1-pg}{Ypagenumber}%
+\dosetq{#1-snt}{Ynothing}}
+
+\def\appendixsetref#1{%
+\dosetq{#1-title}{Ytitle}%
+\dosetq{#1-pg}{Ypagenumber}%
+\dosetq{#1-snt}{Yappendixletterandtype}}
+
+% \xref, \pxref, and \ref generate cross-references to specified points.
+% For \xrefX, #1 is the node name, #2 the name of the Info
+% cross-reference, #3 the printed node name, #4 the name of the Info
+% file, #5 the name of the printed manual. All but the node name can be
+% omitted.
+%
+\def\pxref#1{\putwordsee{} \xrefX[#1,,,,,,,]}
+\def\xref#1{\putwordSee{} \xrefX[#1,,,,,,,]}
+\def\ref#1{\xrefX[#1,,,,,,,]}
+\def\xrefX[#1,#2,#3,#4,#5,#6]{\begingroup
+ \def\printedmanual{\ignorespaces #5}%
+ \def\printednodename{\ignorespaces #3}%
+ \setbox1=\hbox{\printedmanual}%
+ \setbox0=\hbox{\printednodename}%
+ \ifdim \wd0 = 0pt
+ % No printed node name was explicitly given.
+ \expandafter\ifx\csname SETxref-automatic-section-title\endcsname\relax
+ % Use the node name inside the square brackets.
+ \def\printednodename{\ignorespaces #1}%
+ \else
+ % Use the actual chapter/section title appear inside
+ % the square brackets. Use the real section title if we have it.
+ \ifdim \wd1>0pt%
+ % It is in another manual, so we don't have it.
+ \def\printednodename{\ignorespaces #1}%
+ \else
+ \ifhavexrefs
+ % We know the real title if we have the xref values.
+ \def\printednodename{\refx{#1-title}{}}%
+ \else
+ % Otherwise just copy the Info node name.
+ \def\printednodename{\ignorespaces #1}%
+ \fi%
+ \fi
+ \fi
+ \fi
+ %
+ % If we use \unhbox0 and \unhbox1 to print the node names, TeX does not
+ % insert empty discretionaries after hyphens, which means that it will
+ % not find a line break at a hyphen in a node names. Since some manuals
+ % are best written with fairly long node names, containing hyphens, this
+ % is a loss. Therefore, we give the text of the node name again, so it
+ % is as if TeX is seeing it for the first time.
+ \ifdim \wd1 > 0pt
+ \putwordsection{} ``\printednodename'' in \cite{\printedmanual}%
+ \else
+ % _ (for example) has to be the character _ for the purposes of the
+ % control sequence corresponding to the node, but it has to expand
+ % into the usual \leavevmode...\vrule stuff for purposes of
+ % printing. So we \turnoffactive for the \refx-snt, back on for the
+ % printing, back off for the \refx-pg.
+ {\turnoffactive \refx{#1-snt}{}}%
+ \space [\printednodename],\space
+ \turnoffactive \putwordpage\tie\refx{#1-pg}{}%
+ \fi
+\endgroup}
+
+% \dosetq is the interface for calls from other macros
+
+% Use \turnoffactive so that punctuation chars such as underscore
+% work in node names.
+\def\dosetq #1#2{{\let\folio=0 \turnoffactive
+\edef\next{\write\auxfile{\internalsetq {#1}{#2}}}%
+\next}}
+
+% \internalsetq {foo}{page} expands into
+% CHARACTERS 'xrdef {foo}{...expansion of \Ypage...}
+% When the aux file is read, ' is the escape character
+
+\def\internalsetq #1#2{'xrdef {#1}{\csname #2\endcsname}}
+
+% Things to be expanded by \internalsetq
+
+\def\Ypagenumber{\folio}
+
+\def\Ytitle{\thissection}
+
+\def\Ynothing{}
+
+\def\Ysectionnumberandtype{%
+\ifnum\secno=0 \putwordChapter\xreftie\the\chapno %
+\else \ifnum \subsecno=0 \putwordSection\xreftie\the\chapno.\the\secno %
+\else \ifnum \subsubsecno=0 %
+\putwordSection\xreftie\the\chapno.\the\secno.\the\subsecno %
+\else %
+\putwordSection\xreftie\the\chapno.\the\secno.\the\subsecno.\the\subsubsecno %
+\fi \fi \fi }
+
+\def\Yappendixletterandtype{%
+\ifnum\secno=0 \putwordAppendix\xreftie'char\the\appendixno{}%
+\else \ifnum \subsecno=0 \putwordSection\xreftie'char\the\appendixno.\the\secno %
+\else \ifnum \subsubsecno=0 %
+\putwordSection\xreftie'char\the\appendixno.\the\secno.\the\subsecno %
+\else %
+\putwordSection\xreftie'char\the\appendixno.\the\secno.\the\subsecno.\the\subsubsecno %
+\fi \fi \fi }
+
+\gdef\xreftie{'tie}
+
+% Use TeX 3.0's \inputlineno to get the line number, for better error
+% messages, but if we're using an old version of TeX, don't do anything.
+%
+\ifx\inputlineno\thisisundefined
+ \let\linenumber = \empty % Non-3.0.
+\else
+ \def\linenumber{\the\inputlineno:\space}
+\fi
+
+% Define \refx{NAME}{SUFFIX} to reference a cross-reference string named NAME.
+% If its value is nonempty, SUFFIX is output afterward.
+
+\def\refx#1#2{%
+ \expandafter\ifx\csname X#1\endcsname\relax
+ % If not defined, say something at least.
+ $\langle$un\-de\-fined$\rangle$%
+ \ifhavexrefs
+ \message{\linenumber Undefined cross reference `#1'.}%
+ \else
+ \ifwarnedxrefs\else
+ \global\warnedxrefstrue
+ \message{Cross reference values unknown; you must run TeX again.}%
+ \fi
+ \fi
+ \else
+ % It's defined, so just use it.
+ \csname X#1\endcsname
+ \fi
+ #2% Output the suffix in any case.
+}
+
+% This is the macro invoked by entries in the aux file.
+\def\xrdef #1#2{{%
+ \catcode`\'=\other
+ \expandafter\gdef\csname X#1\endcsname{#2}%
+}}
+
+% Read the last existing aux file, if any. No error if none exists.
+\def\readauxfile{\begingroup
+ \catcode`\^^@=\other
+ \catcode`\^^A=\other
+ \catcode`\^^B=\other
+ \catcode`\^^C=\other
+ \catcode`\^^D=\other
+ \catcode`\^^E=\other
+ \catcode`\^^F=\other
+ \catcode`\^^G=\other
+ \catcode`\^^H=\other
+ \catcode`\^^K=\other
+ \catcode`\^^L=\other
+ \catcode`\^^N=\other
+ \catcode`\^^P=\other
+ \catcode`\^^Q=\other
+ \catcode`\^^R=\other
+ \catcode`\^^S=\other
+ \catcode`\^^T=\other
+ \catcode`\^^U=\other
+ \catcode`\^^V=\other
+ \catcode`\^^W=\other
+ \catcode`\^^X=\other
+ \catcode`\^^Z=\other
+ \catcode`\^^[=\other
+ \catcode`\^^\=\other
+ \catcode`\^^]=\other
+ \catcode`\^^^=\other
+ \catcode`\^^_=\other
+ \catcode`\@=\other
+ \catcode`\^=\other
+ % It was suggested to define this as 7, which would allow ^^e4 etc.
+ % in xref tags, i.e., node names. But since ^^e4 notation isn't
+ % supported in the main text, it doesn't seem desirable. Furthermore,
+ % that is not enough: for node names that actually contain a ^
+ % character, we would end up writing a line like this: 'xrdef {'hat
+ % b-title}{'hat b} and \xrdef does a \csname...\endcsname on the first
+ % argument, and \hat is not an expandable control sequence. It could
+ % all be worked out, but why? Either we support ^^ or we don't.
+ %
+ % The other change necessary for this was to define \auxhat:
+ % \def\auxhat{\def^{'hat }}% extra space so ok if followed by letter
+ % and then to call \auxhat in \setq.
+ %
+ \catcode`\~=\other
+ \catcode`\[=\other
+ \catcode`\]=\other
+ \catcode`\"=\other
+ \catcode`\_=\other
+ \catcode`\|=\other
+ \catcode`\<=\other
+ \catcode`\>=\other
+ \catcode`\$=\other
+ \catcode`\#=\other
+ \catcode`\&=\other
+ % `\+ does not work, so use 43.
+ \catcode43=\other
+ % Make the characters 128-255 be printing characters
+ {%
+ \count 1=128
+ \def\loop{%
+ \catcode\count 1=\other
+ \advance\count 1 by 1
+ \ifnum \count 1<256 \loop \fi
+ }%
+ }%
+ % The aux file uses ' as the escape (for now).
+ % Turn off \ as an escape so we do not lose on
+ % entries which were dumped with control sequences in their names.
+ % For example, 'xrdef {$\leq $-fun}{page ...} made by @defun ^^
+ % Reference to such entries still does not work the way one would wish,
+ % but at least they do not bomb out when the aux file is read in.
+ \catcode`\{=1
+ \catcode`\}=2
+ \catcode`\%=\other
+ \catcode`\'=0
+ \catcode`\\=\other
+ %
+ \openin 1 \jobname.aux
+ \ifeof 1 \else
+ \closein 1
+ \input \jobname.aux
+ \global\havexrefstrue
+ \global\warnedobstrue
+ \fi
+ % Open the new aux file. TeX will close it automatically at exit.
+ \openout\auxfile=\jobname.aux
+\endgroup}
+
+
+% Footnotes.
+
+\newcount \footnoteno
+
+% The trailing space in the following definition for supereject is
+% vital for proper filling; pages come out unaligned when you do a
+% pagealignmacro call if that space before the closing brace is
+% removed. (Generally, numeric constants should always be followed by a
+% space to prevent strange expansion errors.)
+\def\supereject{\par\penalty -20000\footnoteno =0 }
+
+% @footnotestyle is meaningful for info output only.
+\let\footnotestyle=\comment
+
+\let\ptexfootnote=\footnote
+
+{\catcode `\@=11
+%
+% Auto-number footnotes. Otherwise like plain.
+\gdef\footnote{%
+ \global\advance\footnoteno by \@ne
+ \edef\thisfootno{$^{\the\footnoteno}$}%
+ %
+ % In case the footnote comes at the end of a sentence, preserve the
+ % extra spacing after we do the footnote number.
+ \let\@sf\empty
+ \ifhmode\edef\@sf{\spacefactor\the\spacefactor}\/\fi
+ %
+ % Remove inadvertent blank space before typesetting the footnote number.
+ \unskip
+ \thisfootno\@sf
+ \footnotezzz
+}%
+
+% Don't bother with the trickery in plain.tex to not require the
+% footnote text as a parameter. Our footnotes don't need to be so general.
+%
+% Oh yes, they do; otherwise, @ifset and anything else that uses
+% \parseargline fail inside footnotes because the tokens are fixed when
+% the footnote is read. --karl, 16nov96.
+%
+\long\gdef\footnotezzz{\insert\footins\bgroup
+ % We want to typeset this text as a normal paragraph, even if the
+ % footnote reference occurs in (for example) a display environment.
+ % So reset some parameters.
+ \interlinepenalty\interfootnotelinepenalty
+ \splittopskip\ht\strutbox % top baseline for broken footnotes
+ \splitmaxdepth\dp\strutbox
+ \floatingpenalty\@MM
+ \leftskip\z@skip
+ \rightskip\z@skip
+ \spaceskip\z@skip
+ \xspaceskip\z@skip
+ \parindent\defaultparindent
+ %
+ % Hang the footnote text off the number.
+ \hang
+ \textindent{\thisfootno}%
+ %
+ % Don't crash into the line above the footnote text. Since this
+ % expands into a box, it must come within the paragraph, lest it
+ % provide a place where TeX can split the footnote.
+ \footstrut
+ \futurelet\next\fo@t
+}
+\def\fo@t{\ifcat\bgroup\noexpand\next \let\next\f@@t
+ \else\let\next\f@t\fi \next}
+\def\f@@t{\bgroup\aftergroup\@foot\let\next}
+\def\f@t#1{#1\@foot}
+\def\@foot{\strut\egroup}
+
+}%end \catcode `\@=11
+
+% Set the baselineskip to #1, and the lineskip and strut size
+% correspondingly. There is no deep meaning behind these magic numbers
+% used as factors; they just match (closely enough) what Knuth defined.
+%
+\def\lineskipfactor{.08333}
+\def\strutheightpercent{.70833}
+\def\strutdepthpercent {.29167}
+%
+\def\setleading#1{%
+ \normalbaselineskip = #1\relax
+ \normallineskip = \lineskipfactor\normalbaselineskip
+ \normalbaselines
+ \setbox\strutbox =\hbox{%
+ \vrule width0pt height\strutheightpercent\baselineskip
+ depth \strutdepthpercent \baselineskip
+ }%
+}
+
+% @| inserts a changebar to the left of the current line. It should
+% surround any changed text. This approach does *not* work if the
+% change spans more than two lines of output. To handle that, we would
+% have adopt a much more difficult approach (putting marks into the main
+% vertical list for the beginning and end of each change).
+%
+\def\|{%
+ % \vadjust can only be used in horizontal mode.
+ \leavevmode
+ %
+ % Append this vertical mode material after the current line in the output.
+ \vadjust{%
+ % We want to insert a rule with the height and depth of the current
+ % leading; that is exactly what \strutbox is supposed to record.
+ \vskip-\baselineskip
+ %
+ % \vadjust-items are inserted at the left edge of the type. So
+ % the \llap here moves out into the left-hand margin.
+ \llap{%
+ %
+ % For a thicker or thinner bar, change the `1pt'.
+ \vrule height\baselineskip width1pt
+ %
+ % This is the space between the bar and the text.
+ \hskip 12pt
+ }%
+ }%
+}
+
+% For a final copy, take out the rectangles
+% that mark overfull boxes (in case you have decided
+% that the text looks ok even though it passes the margin).
+%
+\def\finalout{\overfullrule=0pt}
+
+% @image. We use the macros from epsf.tex to support this.
+% If epsf.tex is not installed and @image is used, we complain.
+%
+% Check for and read epsf.tex up front. If we read it only at @image
+% time, we might be inside a group, and then its definitions would get
+% undone and the next image would fail.
+\openin 1 = xepsf.tex
+\ifeof 1 \else
+ \closein 1
+ \def\epsfannounce{\toks0 = }% do not bother showing banner
+ \input epsf.tex
+\fi
+%
+\newif\ifwarnednoepsf
+\newhelp\noepsfhelp{epsf.tex must be installed for images to
+ work. It is also included in the Texinfo distribution, or you can get
+ it from ftp://ftp.tug.org/tex/epsf.tex.}
+%
+% Only complain once about lack of epsf.tex.
+\def\image#1{%
+ \ifx\epsfbox\undefined
+ \ifwarnednoepsf \else
+ \errhelp = \noepsfhelp
+ \errmessage{epsf.tex not found, images will be ignored}%
+ \global\warnednoepsftrue
+ \fi
+ \else
+ \imagexxx #1,,,\finish
+ \fi
+}
+%
+% Arguments to @image:
+% #1 is (mandatory) image filename; we tack on .eps extension.
+% #2 is (optional) width, #3 is (optional) height.
+% #4 is just the usual extra ignored arg for parsing this stuff.
+\def\imagexxx#1,#2,#3,#4\finish{%
+ % \epsfbox itself resets \epsf?size at each figure.
+ \setbox0 = \hbox{\ignorespaces #2}\ifdim\wd0 > 0pt \epsfxsize=#2\relax \fi
+ \setbox0 = \hbox{\ignorespaces #3}\ifdim\wd0 > 0pt \epsfysize=#3\relax \fi
+ \epsfbox{#1.eps}%
+}
+
+% End of control word definitions.
+
+
+\message{and turning on texinfo input format.}
+
+\def\openindices{%
+ \newindex{cp}%
+ \newcodeindex{fn}%
+ \newcodeindex{vr}%
+ \newcodeindex{tp}%
+ \newcodeindex{ky}%
+ \newcodeindex{pg}%
+}
+
+% Set some numeric style parameters, for 8.5 x 11 format.
+
+\hsize = 6in
+\hoffset = .25in
+\newdimen\defaultparindent \defaultparindent = 15pt
+\parindent = \defaultparindent
+\parskip 3pt plus 2pt minus 1pt
+\setleading{13.2pt}
+\advance\topskip by 1.2cm
+
+\chapheadingskip = 15pt plus 4pt minus 2pt
+\secheadingskip = 12pt plus 3pt minus 2pt
+\subsecheadingskip = 9pt plus 2pt minus 2pt
+
+% Prevent underfull vbox error messages.
+\vbadness=10000
+
+% Following George Bush, just get rid of widows and orphans.
+\widowpenalty=10000
+\clubpenalty=10000
+
+% Use TeX 3.0's \emergencystretch to help line breaking, but if we're
+% using an old version of TeX, don't do anything. We want the amount of
+% stretch added to depend on the line length, hence the dependence on
+% \hsize. This makes it come to about 9pt for the 8.5x11 format.
+%
+\ifx\emergencystretch\thisisundefined
+ % Allow us to assign to \emergencystretch anyway.
+ \def\emergencystretch{\dimen0}%
+\else
+ \emergencystretch = \hsize
+ \divide\emergencystretch by 45
+\fi
+
+% Use @smallbook to reset parameters for 7x9.5 format (or else 7x9.25)
+\def\smallbook{
+ \global\chapheadingskip = 15pt plus 4pt minus 2pt
+ \global\secheadingskip = 12pt plus 3pt minus 2pt
+ \global\subsecheadingskip = 9pt plus 2pt minus 2pt
+ %
+ \global\lispnarrowing = 0.3in
+ \setleading{12pt}
+ \advance\topskip by -1cm
+ \global\parskip 2pt plus 1pt
+ \global\hsize = 5in
+ \global\vsize=7.5in
+ \global\tolerance=700
+ \global\hfuzz=1pt
+ \global\contentsrightmargin=0pt
+ \global\deftypemargin=0pt
+ \global\defbodyindent=.5cm
+ %
+ \global\pagewidth=\hsize
+ \global\pageheight=\vsize
+ %
+ \global\let\smalllisp=\smalllispx
+ \global\let\smallexample=\smalllispx
+ \global\def\Esmallexample{\Esmalllisp}
+}
+
+% Use @afourpaper to print on European A4 paper.
+\def\afourpaper{
+\global\tolerance=700
+\global\hfuzz=1pt
+\setleading{12pt}
+\global\parskip 15pt plus 1pt
+
+\global\vsize= 53\baselineskip
+\advance\vsize by \topskip
+%\global\hsize= 5.85in % A4 wide 10pt
+\global\hsize= 6.5in
+\global\outerhsize=\hsize
+\global\advance\outerhsize by 0.5in
+\global\outervsize=\vsize
+\global\advance\outervsize by 0.6in
+
+\global\pagewidth=\hsize
+\global\pageheight=\vsize
+}
+
+\bindingoffset=0pt
+\normaloffset=\hoffset
+\pagewidth=\hsize
+\pageheight=\vsize
+
+% Allow control of the text dimensions. Parameters in order: textheight;
+% textwidth; voffset; hoffset; binding offset; topskip.
+% All require a dimension;
+% header is additional; added length extends the bottom of the page.
+
+\def\changepagesizes#1#2#3#4#5#6{
+ \global\vsize= #1
+ \global\topskip= #6
+ \advance\vsize by \topskip
+ \global\voffset= #3
+ \global\hsize= #2
+ \global\outerhsize=\hsize
+ \global\advance\outerhsize by 0.5in
+ \global\outervsize=\vsize
+ \global\advance\outervsize by 0.6in
+ \global\pagewidth=\hsize
+ \global\pageheight=\vsize
+ \global\normaloffset= #4
+ \global\bindingoffset= #5}
+
+% A specific text layout, 24x15cm overall, intended for A4 paper. Top margin
+% 29mm, hence bottom margin 28mm, nominal side margin 3cm.
+\def\afourlatex
+ {\global\tolerance=700
+ \global\hfuzz=1pt
+ \setleading{12pt}
+ \global\parskip 15pt plus 1pt
+ \advance\baselineskip by 1.6pt
+ \changepagesizes{237mm}{150mm}{3.6mm}{3.6mm}{3mm}{7mm}
+ }
+
+% Use @afourwide to print on European A4 paper in wide format.
+\def\afourwide{\afourpaper
+\changepagesizes{9.5in}{6.5in}{\hoffset}{\normaloffset}{\bindingoffset}{7mm}}
+
+% Define macros to output various characters with catcode for normal text.
+\catcode`\"=\other
+\catcode`\~=\other
+\catcode`\^=\other
+\catcode`\_=\other
+\catcode`\|=\other
+\catcode`\<=\other
+\catcode`\>=\other
+\catcode`\+=\other
+\def\normaldoublequote{"}
+\def\normaltilde{~}
+\def\normalcaret{^}
+\def\normalunderscore{_}
+\def\normalverticalbar{|}
+\def\normalless{<}
+\def\normalgreater{>}
+\def\normalplus{+}
+
+% This macro is used to make a character print one way in ttfont
+% where it can probably just be output, and another way in other fonts,
+% where something hairier probably needs to be done.
+%
+% #1 is what to print if we are indeed using \tt; #2 is what to print
+% otherwise. Since all the Computer Modern typewriter fonts have zero
+% interword stretch (and shrink), and it is reasonable to expect all
+% typewriter fonts to have this, we can check that font parameter.
+%
+\def\ifusingtt#1#2{\ifdim \fontdimen3\the\font=0pt #1\else #2\fi}
+
+% Turn off all special characters except @
+% (and those which the user can use as if they were ordinary).
+% Most of these we simply print from the \tt font, but for some, we can
+% use math or other variants that look better in normal text.
+
+\catcode`\"=\active
+\def\activedoublequote{{\tt \char '042}}
+\let"=\activedoublequote
+\catcode`\~=\active
+\def~{{\tt \char '176}}
+\chardef\hat=`\^
+\catcode`\^=\active
+\def^{{\tt \hat}}
+
+\catcode`\_=\active
+\def_{\ifusingtt\normalunderscore\_}
+% Subroutine for the previous macro.
+\def\_{\leavevmode \kern.06em \vbox{\hrule width.3em height.1ex}}
+
+\catcode`\|=\active
+\def|{{\tt \char '174}}
+\chardef \less=`\<
+\catcode`\<=\active
+\def<{{\tt \less}}
+\chardef \gtr=`\>
+\catcode`\>=\active
+\def>{{\tt \gtr}}
+\catcode`\+=\active
+\def+{{\tt \char 43}}
+%\catcode 27=\active
+%\def^^[{$\diamondsuit$}
+
+% Set up an active definition for =, but don't enable it most of the time.
+{\catcode`\==\active
+\global\def={{\tt \char 61}}}
+
+\catcode`+=\active
+\catcode`\_=\active
+
+% If a .fmt file is being used, characters that might appear in a file
+% name cannot be active until we have parsed the command line.
+% So turn them off again, and have \everyjob (or @setfilename) turn them on.
+% \otherifyactive is called near the end of this file.
+\def\otherifyactive{\catcode`+=\other \catcode`\_=\other}
+
+\catcode`\@=0
+
+% \rawbackslashxx output one backslash character in current font
+\global\chardef\rawbackslashxx=`\\
+%{\catcode`\\=\other
+%@gdef@rawbackslashxx{\}}
+
+% \rawbackslash redefines \ as input to do \rawbackslashxx.
+{\catcode`\\=\active
+@gdef@rawbackslash{@let\=@rawbackslashxx }}
+
+% \normalbackslash outputs one backslash in fixed width font.
+\def\normalbackslash{{\tt\rawbackslashxx}}
+
+% Say @foo, not \foo, in error messages.
+\escapechar=`\@
+
+% \catcode 17=0 % Define control-q
+\catcode`\\=\active
+
+% Used sometimes to turn off (effectively) the active characters
+% even after parsing them.
+@def@turnoffactive{@let"=@normaldoublequote
+@let\=@realbackslash
+@let~=@normaltilde
+@let^=@normalcaret
+@let_=@normalunderscore
+@let|=@normalverticalbar
+@let<=@normalless
+@let>=@normalgreater
+@let+=@normalplus}
+
+@def@normalturnoffactive{@let"=@normaldoublequote
+@let\=@normalbackslash
+@let~=@normaltilde
+@let^=@normalcaret
+@let_=@normalunderscore
+@let|=@normalverticalbar
+@let<=@normalless
+@let>=@normalgreater
+@let+=@normalplus}
+
+% Make _ and + \other characters, temporarily.
+% This is canceled by @fixbackslash.
+@otherifyactive
+
+% If a .fmt file is being used, we don't want the `\input texinfo' to show up.
+% That is what \eatinput is for; after that, the `\' should revert to printing
+% a backslash.
+%
+@gdef@eatinput input texinfo{@fixbackslash}
+@global@let\ = @eatinput
+
+% On the other hand, perhaps the file did not have a `\input texinfo'. Then
+% the first `\{ in the file would cause an error. This macro tries to fix
+% that, assuming it is called before the first `\' could plausibly occur.
+% Also back turn on active characters that might appear in the input
+% file name, in case not using a pre-dumped format.
+%
+@gdef@fixbackslash{@ifx\@eatinput @let\ = @normalbackslash @fi
+ @catcode`+=@active @catcode`@_=@active}
+
+%% These look ok in all fonts, so just make them not special. The @rm below
+%% makes sure that the current font starts out as the newly loaded cmr10
+@catcode`@$=@other @catcode`@%=@other @catcode`@&=@other @catcode`@#=@other
+
+@textfonts
+@rm
+
+@c Local variables:
+@c page-delimiter: "^\\\\message"
+@c End:
diff --git a/contrib/amd/doc/version.texi b/contrib/amd/doc/version.texi
new file mode 100644
index 000000000000..225ecdbacb37
--- /dev/null
+++ b/contrib/amd/doc/version.texi
@@ -0,0 +1,3 @@
+@set UPDATED 22 April 1998
+@set EDITION 6.0a16
+@set VERSION 6.0a16
diff --git a/contrib/amd/fixmount/fixmount.8 b/contrib/amd/fixmount/fixmount.8
new file mode 100644
index 000000000000..f9a87f03d51c
--- /dev/null
+++ b/contrib/amd/fixmount/fixmount.8
@@ -0,0 +1,159 @@
+.\"
+.\" Copyright (c) 1997-1998 Erez Zadok
+.\" Copyright (c) 1990 Jan-Simon Pendry
+.\" Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+.\" Copyright (c) 1990 The Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Jan-Simon Pendry at Imperial College, London.
+.\"
+.\" 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.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgment:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+.\"
+.\" %W% (Berkeley) %G%
+.\"
+.\" $Id: fixmount.8,v 5.2.2.1 1992/02/09 15:11:15 jsp beta $
+.\"
+.TH FIXMOUNT 8L "26 Feb 1993"
+.SH NAME
+fixmount \- fix remote mount entries
+.SH SYNOPSIS
+.B fixmount
+[
+.B \-adervq
+]
+[
+.B \-h
+.I name
+]
+.I host
+\&...
+.SH DESCRIPTION
+.IX "fixmount command" "" "\fLfixmount\fP \(em fix remote mount entries"
+.LP
+.B fixmount
+is a variant of
+.BR showmount (8)
+that can delete bogus mount entries in remote
+.BR mountd (8C)
+daemons.
+The actions specified by the options are performed for each
+.I host
+in turn.
+.SH OPTIONS
+.TP
+.B \-a \-d \-e
+These options work as in
+.BR showmount (8)
+except that only entries pertaining to the local host are printed.
+.TP
+.B \-r
+Removes those remote mount entries on
+.I host
+that do not correspond to current mounts, i.e., which are left-over
+from a crash or are the result of improper mount protocol.
+The actuality of mounts is verified using the entries in
+.BR /etc/mtab .
+.TP
+.B \-v
+Verify remote mounts. Similar to
+.B \-r
+except that only a notification message is printed for each bogus entry
+found. The remote mount table is not changed.
+.TP
+.B \-A
+Issues a command to the remote mountd declaring that ALL of its filesystems
+have been unmounted. This should be used with caution, as it removes all
+remote mount entries pertaining to the local system, whether or not any
+filesystems are still mounted locally.
+.TP
+.B \-q
+Be quiet.
+Suppresses error messages due to timeouts and "Program not registered",
+i.e., due to remote hosts not supporting RPC or not running mountd.
+.TP
+.BI \-h \ name
+Pretend the local hostname is
+.IR name .
+This is useful after the local hostname has been changed and rmtab entries
+using the old name remain on a remote machine.
+Unfortunately, most mountd's won't be able to successfully handle removal
+of such entries, so this option is useful in combination with
+.B \-v
+only.
+.br
+This option also saves time as comparisons of remotely recorded and local
+hostnames by address are avoided.
+.SH FILES
+.PD 0
+.TP 20
+.B /etc/mtab
+List of current mounts.
+.TP
+.B /etc/rmtab
+Backup file for remote mount entries on NFS server.
+.PD
+.SH "SEE ALSO"
+.BR showmount (8),
+.BR mtab (5),
+.BR rmtab (5),
+.BR mountd (8C).
+.SH BUGS
+No attempt is made to verify the information in
+.B /etc/mtab
+itself.
+.PP
+Since swap file mounts are not recorded in
+.BR /etc/mtab ,
+a heuristic specific to SunOS is used to determine whether such a mount
+is actual (replacing the string "swap" with "root" and verifying the resulting
+path).
+.PP
+Symbolic links on the server will cause the path in the remote entry to differ
+from the one in
+.BR /etc/mtab .
+To catch those cases, a filesystem is also deemed mounted if its
+.I local
+mount point is identical to the remote entry.
+I.e., on a SunOS diskless client,
+.B server:/export/share/sunos.4.1.1
+is actually
+.BR /usr/share .
+Since the local mount point is
+.B /usr/share
+as well this will be handled correctly.
+.PP
+There is no way to clear a stale entry in a remote mountd after the
+local hostname (or whatever reverse name resolution returns for it)
+has been changed. To take care of these cases,
+the remote /etc/rmtab file has to be edited and mountd restarted.
+.PP
+The RPC timeouts for mountd calls can only be changed by recompiling.
+The defaults are 2 seconds for client handle creation and 5 seconds for
+RPC calls.
diff --git a/contrib/amd/fixmount/fixmount.c b/contrib/amd/fixmount/fixmount.c
new file mode 100644
index 000000000000..9769a419afd2
--- /dev/null
+++ b/contrib/amd/fixmount/fixmount.c
@@ -0,0 +1,612 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: fixmount.c,v 5.2.2.2 1992/05/31 16:35:45 jsp Exp $
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+
+#define CREATE_TIMEOUT 2 /* seconds */
+#define CALL_TIMEOUT 5 /* seconds */
+
+#ifndef INADDR_NONE
+# define INADDR_NONE 0xffffffff
+#endif /* not INADDR_NONE */
+
+/* Constant defs */
+#define ALL 1
+#define DIRS 2
+
+#define DODUMP 0x1
+#define DOEXPORTS 0x2
+#define DOREMOVE 0x4
+#define DOVERIFY 0x8
+#define DOREMALL 0x10
+
+extern int fixmount_check_mount(char *host, struct in_addr hostaddr, char *path);
+
+static char dir_path[NFS_MAXPATHLEN];
+static char localhost[] = "localhost";
+static char thishost[MAXHOSTNAMELEN] = "";
+static exports mntexports;
+static int quiet = 0;
+static int type = 0;
+static jmp_buf before_rpc;
+static mountlist mntdump;
+static struct in_addr thisaddr;
+static CLIENT *clnt_create_timeout(char *, struct timeval *);
+
+RETSIGTYPE create_timeout(int);
+int is_same_host(char *, char *, struct in_addr);
+int main(int, char *[]);
+int remove_all(CLIENT *, char *);
+int remove_mount(CLIENT *, char *, mountlist, int);
+void fix_rmtab(CLIENT *, char *, mountlist, int, int);
+void print_dump(mountlist);
+void usage(void);
+
+/* dummy variables */
+char *progname;
+char hostname[MAXHOSTNAMELEN];
+int orig_umask, foreground, debug_flags;
+pid_t mypid;
+serv_state amd_state;
+
+void
+usage(void)
+{
+ fprintf(stderr, "usage: fixmount [-adervAqf] [-h hostname] host ...\n");
+ exit(1);
+}
+
+
+/*
+ * Check hostname against other name and its IP address
+ */
+int
+is_same_host(char *name1, char *name2, struct in_addr addr2)
+{
+ if (strcasecmp(name1, name2) == 0) {
+ return 1;
+ } else if (addr2.s_addr == INADDR_NONE) {
+ return 0;
+ } else {
+ static char lasthost[MAXHOSTNAMELEN] = "";
+ static struct in_addr addr1;
+ struct hostent *he;
+
+ /*
+ * To save nameserver lookups, and because this function
+ * is typically called repeatedly on the same names,
+ * cache the last lookup result and reuse it if possible.
+ */
+ if (strcasecmp(name1, lasthost) == 0) {
+ return (addr1.s_addr == addr2.s_addr);
+ } else if (!(he = gethostbyname(name1))) {
+ return 0;
+ } else {
+ strncpy(lasthost, name1, sizeof(lasthost) - 1);
+ memcpy(&addr1, he->h_addr, sizeof(addr1));
+ return (addr1.s_addr == addr2.s_addr);
+ }
+ }
+}
+
+
+/*
+ * Print the binary tree in inorder so that output is sorted.
+ */
+void
+print_dump(mountlist mp)
+{
+ if (mp == NULL)
+ return;
+ if (is_same_host(mp->ml_hostname, thishost, thisaddr)) {
+ switch (type) {
+ case ALL:
+ printf("%s:%s\n", mp->ml_hostname, mp->ml_directory);
+ break;
+ case DIRS:
+ printf("%s\n", mp->ml_directory);
+ break;
+ default:
+ printf("%s\n", mp->ml_hostname);
+ break;
+ };
+ }
+ if (mp->ml_next)
+ print_dump(mp->ml_next);
+}
+
+
+/*
+ * remove entry from remote rmtab
+ */
+int
+remove_mount(CLIENT *client, char *host, mountlist ml, int fixit)
+{
+ enum clnt_stat estat;
+ struct timeval tv;
+ char *pathp = dir_path;
+
+ strncpy(dir_path, ml->ml_directory, sizeof(dir_path));
+
+ if (!fixit) {
+ printf("%s: bogus mount %s:%s\n", host, ml->ml_hostname, ml->ml_directory);
+ fflush(stdout);
+ } else {
+ printf("%s: removing %s:%s\n", host, ml->ml_hostname, ml->ml_directory);
+ fflush(stdout);
+
+ tv.tv_sec = CALL_TIMEOUT;
+ tv.tv_usec = 0;
+
+ if ((estat = clnt_call(client,
+ MOUNTPROC_UMNT,
+ (XDRPROC_T_TYPE) xdr_dirpath,
+ (char *) &pathp,
+ (XDRPROC_T_TYPE) xdr_void,
+ (char *) 0,
+ tv)) != RPC_SUCCESS) {
+ fprintf(stderr, "%s:%s MOUNTPROC_UMNT: ",
+ host, ml->ml_directory);
+ clnt_perrno(estat);
+ fflush(stderr);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+
+/*
+ * fix mount list on remote host
+ */
+void
+fix_rmtab(CLIENT *client, char *host, mountlist mp, int fixit, int force)
+{
+ mountlist p;
+ struct hostent *he;
+ struct in_addr hostaddr;
+
+ /*
+ * Obtain remote address for comparisons
+ */
+ if ((he = gethostbyname(host))) {
+ memcpy(&hostaddr, he->h_addr, sizeof(hostaddr));
+ } else {
+ hostaddr.s_addr = INADDR_NONE;
+ }
+
+ for (p = mp; p; p = p->ml_next) {
+ if (is_same_host(p->ml_hostname, thishost, thisaddr)) {
+ if (force || !fixmount_check_mount(host, hostaddr, p->ml_directory))
+ remove_mount(client, host, p, fixit);
+ }
+ }
+}
+
+
+/*
+ * remove all entries from remote rmtab
+ */
+int
+remove_all(CLIENT *client, char *host)
+{
+ enum clnt_stat estat;
+ struct timeval tv;
+
+ printf("%s: removing ALL\n", host);
+ fflush(stdout);
+
+ tv.tv_sec = CALL_TIMEOUT;
+ tv.tv_usec = 0;
+
+ if ((estat = clnt_call(client,
+ MOUNTPROC_UMNTALL,
+ (XDRPROC_T_TYPE) xdr_void,
+ (char *) 0,
+ (XDRPROC_T_TYPE) xdr_void,
+ (char *) 0,
+ tv)) != RPC_SUCCESS) {
+ /*
+ * RPC_SYSTEMERROR is returned even if all went well
+ */
+ if (estat != RPC_SYSTEMERROR) {
+ fprintf(stderr, "%s MOUNTPROC_UMNTALL: ", host);
+ clnt_perrno(estat);
+ fflush(stderr);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+/*
+ * This command queries the NFS mount daemon for it's mount list and/or
+ * it's exports list and prints them out.
+ * See "NFS: Network File System Protocol Specification, RFC1094, Appendix A"
+ * for detailed information on the protocol.
+ */
+int
+main(int argc, char *argv[])
+{
+ AUTH *auth;
+ CLIENT *client;
+ char *host;
+ enum clnt_stat estat;
+ exports exp;
+ extern char *optarg;
+ extern int optind;
+ groups grp;
+ int ch;
+ int force = 0;
+ int morethanone;
+ register int rpcs = 0;
+ struct timeval tv;
+
+ while ((ch = getopt(argc, argv, "adervAqfh:")) != EOF)
+ switch ((char) ch) {
+
+ case 'a':
+ if (type == 0) {
+ type = ALL;
+ rpcs |= DODUMP;
+ } else
+ usage();
+ break;
+
+ case 'd':
+ if (type == 0) {
+ type = DIRS;
+ rpcs |= DODUMP;
+ } else
+ usage();
+ break;
+
+ case 'e':
+ rpcs |= DOEXPORTS;
+ break;
+
+ case 'r':
+ rpcs |= DOREMOVE;
+ break;
+
+ case 'A':
+ rpcs |= DOREMALL;
+ break;
+
+ case 'v':
+ rpcs |= DOVERIFY;
+ break;
+
+ case 'q':
+ quiet = 1;
+ break;
+
+ case 'f':
+ force = 1;
+ break;
+
+ case 'h':
+ strncpy(thishost, optarg, sizeof(thishost));
+ thishost[sizeof(thishost) - 1] = '\0';
+ break;
+
+ case '?':
+ default:
+ usage();
+ }
+
+ if (optind == argc)
+ usage();
+
+ if (rpcs == 0)
+ rpcs = DODUMP;
+
+ if (!*thishost) {
+ struct hostent *he;
+
+ if (gethostname(thishost, sizeof(thishost)) < 0) {
+ perror("gethostname");
+ exit(1);
+ }
+
+ /*
+ * We need the hostname as it appears to the other side's
+ * mountd, so get our own hostname by reverse address
+ * resolution.
+ */
+ if (!(he = gethostbyname(thishost))) {
+ fprintf(stderr, "gethostbyname failed on %s\n",
+ thishost);
+ exit(1);
+ }
+ memcpy(&thisaddr, he->h_addr, sizeof(thisaddr));
+ if (!(he = gethostbyaddr((char *) &thisaddr, sizeof(thisaddr),
+ he->h_addrtype))) {
+ fprintf(stderr, "gethostbyaddr failed on %s\n",
+ inet_ntoa(thisaddr));
+ exit(1);
+ }
+ strncpy(thishost, he->h_name, sizeof(thishost));
+ thishost[sizeof(thishost) - 1] = '\0';
+ } else {
+ thisaddr.s_addr = INADDR_NONE;
+ }
+
+ if (!(auth = authunix_create_default())) {
+ fprintf(stderr, "couldn't create authentication handle\n");
+ exit(1);
+ }
+ morethanone = (optind + 1 < argc);
+
+ for (; optind < argc; optind++) {
+
+ host = argv[optind];
+ tv.tv_sec = CREATE_TIMEOUT;
+ tv.tv_usec = 0;
+
+ if (!(client = clnt_create_timeout(host, &tv)))
+ continue;
+
+ client->cl_auth = auth;
+ tv.tv_sec = CALL_TIMEOUT;
+ tv.tv_usec = 0;
+
+ if (rpcs & (DODUMP | DOREMOVE | DOVERIFY))
+ if ((estat = clnt_call(client,
+ MOUNTPROC_DUMP,
+ (XDRPROC_T_TYPE) xdr_void,
+ (char *) 0,
+ (XDRPROC_T_TYPE) xdr_mountlist,
+ (char *) &mntdump,
+ tv)) != RPC_SUCCESS) {
+ fprintf(stderr, "%s: MOUNTPROC_DUMP: ", host);
+ clnt_perrno(estat);
+ fflush(stderr);
+ mntdump = NULL;
+ goto next;
+ }
+ if (rpcs & DOEXPORTS)
+ if ((estat = clnt_call(client,
+ MOUNTPROC_EXPORT,
+ (XDRPROC_T_TYPE) xdr_void,
+ (char *) 0,
+ (XDRPROC_T_TYPE) xdr_exports,
+ (char *) &mntexports,
+ tv)) != RPC_SUCCESS) {
+ fprintf(stderr, "%s: MOUNTPROC_EXPORT: ", host);
+ clnt_perrno(estat);
+ fflush(stderr);
+ mntexports = NULL;
+ goto next;
+ }
+
+ /* Now just print out the results */
+ if ((rpcs & (DODUMP | DOEXPORTS)) &&
+ morethanone) {
+ printf(">>> %s <<<\n", host);
+ fflush(stdout);
+ }
+
+ if (rpcs & DODUMP) {
+ print_dump(mntdump);
+ }
+
+ if (rpcs & DOEXPORTS) {
+ exp = mntexports;
+ while (exp) {
+ printf("%-35s", exp->ex_dir);
+ grp = exp->ex_groups;
+ if (grp == NULL) {
+ printf("Everyone\n");
+ } else {
+ while (grp) {
+ printf("%s ", grp->gr_name);
+ grp = grp->gr_next;
+ }
+ printf("\n");
+ }
+ exp = exp->ex_next;
+ }
+ }
+
+ if (rpcs & DOVERIFY)
+ fix_rmtab(client, host, mntdump, 0, force);
+
+ if (rpcs & DOREMOVE)
+ fix_rmtab(client, host, mntdump, 1, force);
+
+ if (rpcs & DOREMALL)
+ remove_all(client, host);
+
+ next:
+ if (mntdump)
+ (void) clnt_freeres(client,
+ (XDRPROC_T_TYPE) xdr_mountlist,
+ (char *) &mntdump);
+ if (mntexports)
+ (void) clnt_freeres(client,
+ (XDRPROC_T_TYPE) xdr_exports,
+ (char *) &mntexports);
+
+ clnt_destroy(client);
+ }
+ exit(0);
+ return 0; /* should never reach here */
+}
+
+
+RETSIGTYPE
+create_timeout(int sig)
+{
+ signal(SIGALRM, SIG_DFL);
+ longjmp(before_rpc, 1);
+}
+
+
+#ifndef HAVE_TRANSPORT_TYPE_TLI
+/*
+ * inetresport creates a datagram socket and attempts to bind it to a
+ * secure port.
+ * returns: The bound socket, or -1 to indicate an error.
+ */
+static int
+inetresport(int ty)
+{
+ int alport;
+ struct sockaddr_in addr;
+ int fd;
+
+ /* Use internet address family */
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = INADDR_ANY;
+ if ((fd = socket(AF_INET, ty, 0)) < 0)
+ return -1;
+
+ for (alport = IPPORT_RESERVED - 1; alport > IPPORT_RESERVED / 2 + 1; alport--) {
+ addr.sin_port = htons((u_short) alport);
+ if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) >= 0)
+ return fd;
+ if (errno != EADDRINUSE) {
+ close(fd);
+ return -1;
+ }
+ }
+ close(fd);
+ errno = EAGAIN;
+ return -1;
+}
+
+
+/*
+ * Privsock() calls inetresport() to attempt to bind a socket to a secure
+ * port. If inetresport() fails, privsock returns a magic socket number which
+ * indicates to RPC that it should make its own socket.
+ * returns: A privileged socket # or RPC_ANYSOCK.
+ */
+static int
+privsock(int ty)
+{
+ int sock = inetresport(ty);
+
+ if (sock < 0) {
+ errno = 0;
+ /* Couldn't get a secure port, let RPC make an insecure one */
+ sock = RPC_ANYSOCK;
+ }
+ return sock;
+}
+#endif /* not HAVE_TRANSPORT_TYPE_TLI */
+
+
+static CLIENT *
+clnt_create_timeout(char *host, struct timeval *tvp)
+{
+ CLIENT *clnt;
+ struct sockaddr_in host_addr;
+ struct hostent *hp;
+#ifndef HAVE_TRANSPORT_TYPE_TLI
+ int s;
+#endif /* not HAVE_TRANSPORT_TYPE_TLI */
+
+ if (setjmp(before_rpc)) {
+ if (!quiet) {
+ fprintf(stderr, "%s: ", host);
+ clnt_perrno(RPC_TIMEDOUT);
+ fprintf(stderr, "\n");
+ fflush(stderr);
+ }
+ return NULL;
+ }
+ signal(SIGALRM, create_timeout);
+ ualarm(tvp->tv_sec * 1000000 + tvp->tv_usec, 0);
+
+ /*
+ * Get address of host
+ */
+ if ((hp = gethostbyname(host)) == 0 && !STREQ(host, localhost)) {
+ fprintf(stderr, "can't get address of %s\n", host);
+ return NULL;
+ }
+ memset(&host_addr, 0, sizeof host_addr);
+ host_addr.sin_family = AF_INET;
+ if (hp) {
+ memmove((voidp) &host_addr.sin_addr, (voidp) hp->h_addr,
+ sizeof(host_addr.sin_addr));
+ } else {
+ /* fake "localhost" */
+ host_addr.sin_addr.s_addr = htonl(0x7f000001);
+ }
+
+#ifdef HAVE_TRANSPORT_TYPE_TLI
+ /* try TCP first (in case return data is large), then UDP */
+ clnt = clnt_create(host, MOUNTPROG, MOUNTVERS, "tcp");
+ if (!clnt)
+ clnt = clnt_create(host, MOUNTPROG, MOUNTVERS, "udp");
+#else /* not HAVE_TRANSPORT_TYPE_TLI */
+ s = RPC_ANYSOCK;
+ clnt = clnttcp_create(&host_addr, MOUNTPROG, MOUNTVERS, &s, 0, 0);
+ if (!clnt) {
+ /* XXX: do we need to close(s) ? */
+ s = privsock(SOCK_DGRAM);
+ clnt = clntudp_create(&host_addr, MOUNTPROG, MOUNTVERS, *tvp, &s);
+ }
+#endif /* not HAVE_TRANSPORT_TYPE_TLI */
+
+ if (!clnt) {
+ ualarm(0, 0);
+ if (!quiet) {
+ clnt_pcreateerror(host);
+ fflush(stderr);
+ }
+ return NULL;
+ }
+
+ ualarm(0, 0);
+ return clnt;
+}
diff --git a/contrib/amd/fsinfo/fsi_analyze.c b/contrib/amd/fsinfo/fsi_analyze.c
new file mode 100644
index 000000000000..de2991368ccd
--- /dev/null
+++ b/contrib/amd/fsinfo/fsi_analyze.c
@@ -0,0 +1,670 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: fsi_analyze.c,v 5.2.2.1 1992/02/09 15:09:41 jsp beta $
+ *
+ */
+
+/*
+ * Analyze filesystem declarations
+ *
+ * Note: most of this is magic!
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <fsi_data.h>
+#include <fsinfo.h>
+
+char *disk_fs_strings[] =
+{
+ "fstype", "opts", "dumpset", "passno", "freq", "mount", "log", 0,
+};
+
+char *mount_strings[] =
+{
+ "volname", "exportfs", 0,
+};
+
+char *fsmount_strings[] =
+{
+ "as", "volname", "fstype", "opts", "from", 0,
+};
+
+char *host_strings[] =
+{
+ "host", "netif", "config", "arch", "cluster", "os", 0,
+};
+
+char *ether_if_strings[] =
+{
+ "inaddr", "netmask", "hwaddr", 0,
+};
+
+
+/*
+ * Strip off the trailing part of a domain
+ * to produce a short-form domain relative
+ * to the local host domain.
+ * Note that this has no effect if the domain
+ * names do not have the same number of
+ * components. If that restriction proves
+ * to be a problem then the loop needs recoding
+ * to skip from right to left and do partial
+ * matches along the way -- ie more expensive.
+ */
+void
+domain_strip(char *otherdom, char *localdom)
+{
+ char *p1, *p2;
+
+ if ((p1 = strchr(otherdom, '.')) &&
+ (p2 = strchr(localdom, '.')) &&
+ STREQ(p1 + 1, p2 + 1))
+ *p1 = '\0';
+}
+
+
+/*
+ * Take a little-endian domain name and
+ * transform into a big-endian Un*x pathname.
+ * For example: kiska.doc.ic -> ic/doc/kiska
+ */
+static char *
+compute_hostpath(char *hn)
+{
+ char *p = strdup(hn);
+ char *d;
+ char path[MAXPATHLEN];
+
+ domain_strip(p, hostname);
+ path[0] = '\0';
+
+ do {
+ d = strrchr(p, '.');
+ if (d) {
+ *d = 0;
+ strcat(path, d + 1);
+ strcat(path, "/");
+ } else {
+ strcat(path, p);
+ }
+ } while (d);
+
+ log("hostpath of '%s' is '%s'", hn, path);
+
+ strcpy(p, path);
+ return p;
+}
+
+
+static dict_ent *
+find_volname(char *nn)
+{
+ dict_ent *de;
+ char *p = strdup(nn);
+ char *q;
+
+ do {
+ log("Searching for volname %s", p);
+ de = dict_locate(dict_of_volnames, p);
+ q = strrchr(p, '/');
+ if (q)
+ *q = '\0';
+ } while (!de && q);
+
+ XFREE(p);
+ return de;
+}
+
+
+static void
+show_required(ioloc *l, int mask, char *info, char *hostname, char *strings[])
+{
+ int i;
+ log("mask left for %s:%s is %#x", hostname, info, mask);
+
+ for (i = 0; strings[i]; i++)
+ if (ISSET(mask, i))
+ lerror(l, "%s:%s needs field \"%s\"", hostname, info, strings[i]);
+}
+
+
+/*
+ * Check and fill in "exportfs" details.
+ * Make sure the m_exported field references
+ * the most local node with an "exportfs" entry.
+ */
+static int
+check_exportfs(qelem *q, fsi_mount *e)
+{
+ fsi_mount *mp;
+ int errors = 0;
+
+ ITER(mp, fsi_mount, q) {
+ if (ISSET(mp->m_mask, DM_EXPORTFS)) {
+ if (e)
+ lwarning(mp->m_ioloc, "%s has duplicate exportfs data", mp->m_name);
+ mp->m_exported = mp;
+ if (!ISSET(mp->m_mask, DM_VOLNAME))
+ set_mount(mp, DM_VOLNAME, strdup(mp->m_name));
+ } else {
+ mp->m_exported = e;
+ }
+
+ /*
+ * Recursively descend the mount tree
+ */
+ if (mp->m_mount)
+ errors += check_exportfs(mp->m_mount, mp->m_exported);
+
+ /*
+ * If a volume name has been specified, but this node and none
+ * of its parents has been exported, report an error.
+ */
+ if (ISSET(mp->m_mask, DM_VOLNAME) && !mp->m_exported) {
+ lerror(mp->m_ioloc, "%s has a volname but no exportfs data", mp->m_name);
+ errors++;
+ }
+ }
+
+ return errors;
+}
+
+
+static int
+analyze_dkmount_tree(qelem *q, fsi_mount *parent, disk_fs *dk)
+{
+ fsi_mount *mp;
+ int errors = 0;
+
+ ITER(mp, fsi_mount, q) {
+ log("Mount %s:", mp->m_name);
+ if (parent) {
+ char n[MAXPATHLEN];
+ sprintf(n, "%s/%s", parent->m_name, mp->m_name);
+ if (*mp->m_name == '/')
+ lerror(mp->m_ioloc, "sub-directory %s of %s starts with '/'", mp->m_name, parent->m_name);
+ else if (STREQ(mp->m_name, "default"))
+ lwarning(mp->m_ioloc, "sub-directory of %s is named \"default\"", parent->m_name);
+ log("Changing name %s to %s", mp->m_name, n);
+ XFREE(mp->m_name);
+ mp->m_name = strdup(n);
+ }
+
+ mp->m_name_len = strlen(mp->m_name);
+ mp->m_parent = parent;
+ mp->m_dk = dk;
+ if (mp->m_mount)
+ analyze_dkmount_tree(mp->m_mount, mp, dk);
+ }
+
+ return errors;
+}
+
+
+/*
+ * The mount tree is a singleton list
+ * containing the top-level mount
+ * point for a disk.
+ */
+static int
+analyze_dkmounts(disk_fs *dk, qelem *q)
+{
+ int errors = 0;
+ fsi_mount *mp, *mp2 = 0;
+ int i = 0;
+
+ /*
+ * First scan the list of subdirs to make
+ * sure there is only one - and remember it
+ */
+ if (q) {
+ ITER(mp, fsi_mount, q) {
+ mp2 = mp;
+ i++;
+ }
+ }
+
+ /*
+ * Check...
+ */
+ if (i < 1) {
+ lerror(dk->d_ioloc, "%s:%s has no mount point", dk->d_host->h_hostname, dk->d_dev);
+ return 1;
+ }
+
+ if (i > 1) {
+ lerror(dk->d_ioloc, "%s:%s has more than one mount point", dk->d_host->h_hostname, dk->d_dev);
+ errors++;
+ }
+
+ /*
+ * Now see if a default mount point is required
+ */
+ if (STREQ(mp2->m_name, "default")) {
+ if (ISSET(mp2->m_mask, DM_VOLNAME)) {
+ char nbuf[1024];
+ compute_automount_point(nbuf, dk->d_host, mp2->m_volname);
+ XFREE(mp2->m_name);
+ mp2->m_name = strdup(nbuf);
+ log("%s:%s has default mount on %s", dk->d_host->h_hostname, dk->d_dev, mp2->m_name);
+ } else {
+ lerror(dk->d_ioloc, "no volname given for %s:%s", dk->d_host->h_hostname, dk->d_dev);
+ errors++;
+ }
+ }
+
+ /*
+ * Fill in the disk mount point
+ */
+ if (!errors && mp2 && mp2->m_name)
+ dk->d_mountpt = strdup(mp2->m_name);
+ else
+ dk->d_mountpt = strdup("error");
+
+ /*
+ * Analyze the mount tree
+ */
+ errors += analyze_dkmount_tree(q, 0, dk);
+
+ /*
+ * Analyze the export tree
+ */
+ errors += check_exportfs(q, 0);
+
+ return errors;
+}
+
+
+static void
+fixup_required_disk_info(disk_fs *dp)
+{
+ /*
+ * "fstype"
+ */
+ if (ISSET(dp->d_mask, DF_FSTYPE)) {
+ if (STREQ(dp->d_fstype, "swap")) {
+
+ /*
+ * Fixup for a swap device
+ */
+ if (!ISSET(dp->d_mask, DF_PASSNO)) {
+ dp->d_passno = 0;
+ BITSET(dp->d_mask, DF_PASSNO);
+ } else if (dp->d_freq != 0) {
+ lwarning(dp->d_ioloc,
+ "Pass number for %s:%s is non-zero",
+ dp->d_host->h_hostname, dp->d_dev);
+ }
+
+ /*
+ * "freq"
+ */
+ if (!ISSET(dp->d_mask, DF_FREQ)) {
+ dp->d_freq = 0;
+ BITSET(dp->d_mask, DF_FREQ);
+ } else if (dp->d_freq != 0) {
+ lwarning(dp->d_ioloc,
+ "dump frequency for %s:%s is non-zero",
+ dp->d_host->h_hostname, dp->d_dev);
+ }
+
+ /*
+ * "opts"
+ */
+ if (!ISSET(dp->d_mask, DF_OPTS))
+ set_disk_fs(dp, DF_OPTS, strdup("swap"));
+
+ /*
+ * "mount"
+ */
+ if (!ISSET(dp->d_mask, DF_MOUNT)) {
+ qelem *q = new_que();
+ fsi_mount *m = new_mount();
+
+ m->m_name = strdup("swap");
+ m->m_mount = new_que();
+ ins_que(&m->m_q, q->q_back);
+ dp->d_mount = q;
+ BITSET(dp->d_mask, DF_MOUNT);
+ } else {
+ lerror(dp->d_ioloc, "%s: mount field specified for swap partition", dp->d_host->h_hostname);
+ }
+ } else if (STREQ(dp->d_fstype, "export")) {
+
+ /*
+ * "passno"
+ */
+ if (!ISSET(dp->d_mask, DF_PASSNO)) {
+ dp->d_passno = 0;
+ BITSET(dp->d_mask, DF_PASSNO);
+ } else if (dp->d_passno != 0) {
+ lwarning(dp->d_ioloc,
+ "pass number for %s:%s is non-zero",
+ dp->d_host->h_hostname, dp->d_dev);
+ }
+
+ /*
+ * "freq"
+ */
+ if (!ISSET(dp->d_mask, DF_FREQ)) {
+ dp->d_freq = 0;
+ BITSET(dp->d_mask, DF_FREQ);
+ } else if (dp->d_freq != 0) {
+ lwarning(dp->d_ioloc,
+ "dump frequency for %s:%s is non-zero",
+ dp->d_host->h_hostname, dp->d_dev);
+ }
+
+ /*
+ * "opts"
+ */
+ if (!ISSET(dp->d_mask, DF_OPTS))
+ set_disk_fs(dp, DF_OPTS, strdup("rw,defaults"));
+
+ }
+ }
+}
+
+
+static void
+fixup_required_mount_info(fsmount *fp, dict_ent *de)
+{
+ if (!ISSET(fp->f_mask, FM_FROM)) {
+ if (de->de_count != 1) {
+ lerror(fp->f_ioloc, "ambiguous mount: %s is a replicated filesystem", fp->f_volname);
+ } else {
+ dict_data *dd;
+ fsi_mount *mp = 0;
+ dd = AM_FIRST(dict_data, &de->de_q);
+ mp = (fsi_mount *) dd->dd_data;
+ if (!mp)
+ abort();
+ fp->f_ref = mp;
+ set_fsmount(fp, FM_FROM, mp->m_dk->d_host->h_hostname);
+ log("set: %s comes from %s", fp->f_volname, fp->f_from);
+ }
+ }
+
+ if (!ISSET(fp->f_mask, FM_FSTYPE)) {
+ set_fsmount(fp, FM_FSTYPE, strdup("nfs"));
+ log("set: fstype is %s", fp->f_fstype);
+ }
+
+ if (!ISSET(fp->f_mask, FM_OPTS)) {
+ set_fsmount(fp, FM_OPTS, strdup("rw,nosuid,grpid,defaults"));
+ log("set: opts are %s", fp->f_opts);
+ }
+
+ if (!ISSET(fp->f_mask, FM_LOCALNAME)) {
+ if (fp->f_ref) {
+ set_fsmount(fp, FM_LOCALNAME, strdup(fp->f_volname));
+ log("set: localname is %s", fp->f_localname);
+ } else {
+ lerror(fp->f_ioloc, "cannot determine localname since volname %s is not uniquely defined", fp->f_volname);
+ }
+ }
+}
+
+
+/*
+ * For each disk on a host
+ * analyze the mount information
+ * and fill in any derivable
+ * details.
+ */
+static void
+analyze_drives(host *hp)
+{
+ qelem *q = hp->h_disk_fs;
+ disk_fs *dp;
+
+ ITER(dp, disk_fs, q) {
+ int req;
+ log("Disk %s:", dp->d_dev);
+ dp->d_host = hp;
+ fixup_required_disk_info(dp);
+ req = ~dp->d_mask & DF_REQUIRED;
+ if (req)
+ show_required(dp->d_ioloc, req, dp->d_dev, hp->h_hostname, disk_fs_strings);
+ analyze_dkmounts(dp, dp->d_mount);
+ }
+}
+
+
+/*
+ * Check that all static mounts make sense and
+ * that the source volumes exist.
+ */
+static void
+analyze_mounts(host *hp)
+{
+ qelem *q = hp->h_mount;
+ fsmount *fp;
+ int netbootp = 0;
+
+ ITER(fp, fsmount, q) {
+ char *p;
+ char *nn = strdup(fp->f_volname);
+ int req;
+ dict_ent *de = (dict_ent *) NULL;
+ int found = 0;
+ int matched = 0;
+
+ if (ISSET(fp->f_mask, FM_DIRECT)) {
+ found = 1;
+ matched = 1;
+ } else
+ do {
+ p = 0;
+ de = find_volname(nn);
+ log("Mount: %s (trying %s)", fp->f_volname, nn);
+
+ if (de) {
+ found = 1;
+
+ /*
+ * Check that the from field is really exporting
+ * the filesystem requested.
+ * LBL: If fake mount, then don't care about
+ * consistency check.
+ */
+ if (ISSET(fp->f_mask, FM_FROM) && !ISSET(fp->f_mask, FM_DIRECT)) {
+ dict_data *dd;
+ fsi_mount *mp2 = 0;
+
+ ITER(dd, dict_data, &de->de_q) {
+ fsi_mount *mp = (fsi_mount *) dd->dd_data;
+
+ if (STREQ(mp->m_dk->d_host->h_hostname, fp->f_from)) {
+ mp2 = mp;
+ break;
+ }
+ }
+
+ if (mp2) {
+ fp->f_ref = mp2;
+ matched = 1;
+ break;
+ }
+ } else {
+ matched = 1;
+ break;
+ }
+ }
+ p = strrchr(nn, '/');
+ if (p)
+ *p = 0;
+ } while (de && p);
+ XFREE(nn);
+
+ if (!found) {
+ lerror(fp->f_ioloc, "volname %s unknown", fp->f_volname);
+ } else if (matched) {
+
+ fixup_required_mount_info(fp, de);
+ req = ~fp->f_mask & FM_REQUIRED;
+ if (req) {
+ show_required(fp->f_ioloc, req, fp->f_volname, hp->h_hostname,
+ fsmount_strings);
+ } else if (STREQ(fp->f_localname, "/")) {
+ hp->h_netroot = fp;
+ netbootp |= FM_NETROOT;
+ } else if (STREQ(fp->f_localname, "swap")) {
+ hp->h_netswap = fp;
+ netbootp |= FM_NETSWAP;
+ }
+
+ } else {
+ lerror(fp->f_ioloc, "volname %s not exported from %s", fp->f_volname,
+ fp->f_from ? fp->f_from : "anywhere");
+ }
+ }
+
+ if (netbootp && (netbootp != FM_NETBOOT))
+ lerror(hp->h_ioloc, "network booting requires both root and swap areas");
+}
+
+
+void
+analyze_hosts(qelem *q)
+{
+ host *hp;
+
+ show_area_being_processed("analyze hosts", 5);
+
+ /*
+ * Check all drives
+ */
+ ITER(hp, host, q) {
+ log("disks on host %s", hp->h_hostname);
+ show_new("ana-host");
+ hp->h_hostpath = compute_hostpath(hp->h_hostname);
+
+ if (hp->h_disk_fs)
+ analyze_drives(hp);
+
+ }
+
+ show_area_being_processed("analyze mounts", 5);
+
+ /*
+ * Check static mounts
+ */
+ ITER(hp, host, q) {
+ log("mounts on host %s", hp->h_hostname);
+ show_new("ana-mount");
+ if (hp->h_mount)
+ analyze_mounts(hp);
+
+ }
+}
+
+
+/*
+ * Check an automount request
+ */
+static void
+analyze_automount(automount *ap)
+{
+ dict_ent *de = find_volname(ap->a_volname);
+
+ if (de) {
+ ap->a_mounted = de;
+ } else {
+ if (STREQ(ap->a_volname, ap->a_name))
+ lerror(ap->a_ioloc, "unknown volname %s automounted", ap->a_volname);
+ else
+ lerror(ap->a_ioloc, "unknown volname %s automounted on %s", ap->a_volname, ap->a_name);
+ }
+}
+
+
+static void
+analyze_automount_tree(qelem *q, char *pref, int lvl)
+{
+ automount *ap;
+
+ ITER(ap, automount, q) {
+ char nname[1024];
+
+ if (lvl > 0 || ap->a_mount)
+ if (ap->a_name[1] && strchr(ap->a_name + 1, '/'))
+ lerror(ap->a_ioloc, "not allowed '/' in a directory name");
+ sprintf(nname, "%s/%s", pref, ap->a_name);
+ XFREE(ap->a_name);
+ ap->a_name = strdup(nname[1] == '/' ? nname + 1 : nname);
+ log("automount point %s:", ap->a_name);
+ show_new("ana-automount");
+
+ if (ap->a_mount) {
+ analyze_automount_tree(ap->a_mount, ap->a_name, lvl + 1);
+ } else if (ap->a_hardwiredfs) {
+ log("\thardwired from %s to %s", ap->a_volname, ap->a_hardwiredfs);
+ } else if (ap->a_volname) {
+ log("\tautomount from %s", ap->a_volname);
+ analyze_automount(ap);
+ } else if (ap->a_symlink) {
+ log("\tsymlink to %s", ap->a_symlink);
+ } else {
+ ap->a_volname = strdup(ap->a_name);
+ log("\timplicit automount from %s", ap->a_volname);
+ analyze_automount(ap);
+ }
+ }
+}
+
+
+void
+analyze_automounts(qelem *q)
+{
+ auto_tree *tp;
+
+ show_area_being_processed("analyze automount", 5);
+
+ /*
+ * q is a list of automounts
+ */
+ ITER(tp, auto_tree, q)
+ analyze_automount_tree(tp->t_mount, "", 0);
+}
diff --git a/contrib/amd/fsinfo/fsi_data.h b/contrib/amd/fsinfo/fsi_data.h
new file mode 100644
index 000000000000..07b7b9e79816
--- /dev/null
+++ b/contrib/amd/fsinfo/fsi_data.h
@@ -0,0 +1,236 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: fsi_data.h,v 5.2.2.1 1992/02/09 15:09:53 jsp beta $
+ *
+ */
+
+typedef struct auto_tree auto_tree;
+typedef struct automount automount;
+typedef struct dict dict;
+typedef struct dict_data dict_data;
+typedef struct dict_ent dict_ent;
+typedef struct disk_fs disk_fs;
+typedef struct ether_if ether_if;
+typedef struct fsmount fsmount;
+typedef struct host host;
+typedef struct ioloc ioloc;
+typedef struct fsi_mount fsi_mount;
+
+
+/*
+ * Automount tree
+ */
+struct automount {
+ qelem a_q;
+ ioloc *a_ioloc;
+ char *a_name; /* Automount key */
+ char *a_volname; /* Equivalent volume to be referenced */
+ char *a_symlink; /* Symlink representation */
+ char *a_opts; /* opts for mounting */
+ char *a_hardwiredfs; /* hack to bypass bogus fs definitions */
+ qelem *a_mount; /* Tree representation */
+ dict_ent *a_mounted;
+};
+
+/*
+ * List of automount trees
+ */
+struct auto_tree {
+ qelem t_q;
+ ioloc *t_ioloc;
+ char *t_defaults;
+ qelem *t_mount;
+};
+
+/*
+ * A host
+ */
+struct host {
+ qelem q;
+ int h_mask;
+ ioloc *h_ioloc;
+ fsmount *h_netroot, *h_netswap;
+#define HF_HOST 0
+ char *h_hostname; /* The full name of the host */
+ char *h_lochost; /* The name of the host with local domains *
+ * * * stripped */
+ char *h_hostpath; /* The filesystem path to the host (cf * * *
+ * compute_hostpath) */
+#define HF_ETHER 1
+ qelem *h_ether;
+#define HF_CONFIG 2
+ qelem *h_config;
+#define HF_ARCH 3
+ char *h_arch;
+#define HF_CLUSTER 4
+ char *h_cluster;
+#define HF_OS 5
+ char *h_os;
+ qelem *h_disk_fs;
+ qelem *h_mount;
+};
+
+/*
+ * An ethernet interface
+ */
+struct ether_if {
+ qelem e_q;
+ int e_mask;
+ ioloc *e_ioloc;
+ char *e_if;
+#define EF_INADDR 0
+ struct in_addr e_inaddr;
+#define EF_NETMASK 1
+ u_long e_netmask;
+#define EF_HWADDR 2
+ char *e_hwaddr;
+};
+
+/*
+ * Disk filesystem structure.
+ *
+ * If the DF_* numbers are changed
+ * disk_fs_strings in analyze.c will
+ * need updating.
+ */
+struct disk_fs {
+ qelem d_q;
+ int d_mask;
+ ioloc *d_ioloc;
+ host *d_host;
+ char *d_mountpt;
+ char *d_dev;
+#define DF_FSTYPE 0
+ char *d_fstype;
+#define DF_OPTS 1
+ char *d_opts;
+#define DF_DUMPSET 2
+ char *d_dumpset;
+#define DF_PASSNO 3
+ int d_passno;
+#define DF_FREQ 4
+ int d_freq;
+#define DF_MOUNT 5
+ qelem *d_mount;
+#define DF_LOG 6
+ char *d_log;
+};
+
+#define DF_REQUIRED ((1<<DF_FSTYPE)|(1<<DF_OPTS)|(1<<DF_PASSNO)|(1<<DF_MOUNT))
+
+/*
+ * A mount tree
+ */
+struct fsi_mount {
+ qelem m_q;
+ ioloc *m_ioloc;
+ int m_mask;
+#define DM_VOLNAME 0
+ char *m_volname;
+#define DM_EXPORTFS 1
+ char *m_exportfs;
+#define DM_SEL 2
+ char *m_sel;
+ char *m_name;
+ int m_name_len;
+ fsi_mount *m_parent;
+ disk_fs *m_dk;
+ fsi_mount *m_exported;
+ qelem *m_mount;
+};
+
+/*
+ * Additional filesystem mounts
+ *
+ * If the FM_* numbers are changed
+ * disk_fs_strings in analyze.c will
+ * need updating.
+ */
+struct fsmount {
+ qelem f_q;
+ fsi_mount *f_ref;
+ ioloc *f_ioloc;
+ int f_mask;
+#define FM_LOCALNAME 0
+ char *f_localname;
+#define FM_VOLNAME 1
+ char *f_volname;
+#define FM_FSTYPE 2
+ char *f_fstype;
+#define FM_OPTS 3
+ char *f_opts;
+#define FM_FROM 4
+ char *f_from;
+#define FM_DIRECT 5
+};
+
+#define FM_REQUIRED ((1<<FM_VOLNAME)|(1<<FM_FSTYPE)|(1<<FM_OPTS)|(1<<FM_FROM)|(1<<FM_LOCALNAME))
+#define FM_NETROOT 0x01
+#define FM_NETSWAP 0x02
+#define FM_NETBOOT (FM_NETROOT|FM_NETSWAP)
+
+#define DICTHASH 5
+struct dict_ent {
+ dict_ent *de_next;
+ char *de_key;
+ int de_count;
+ qelem de_q;
+};
+
+/*
+ * Dictionaries ...
+ */
+struct dict_data {
+ qelem dd_q;
+ char *dd_data;
+};
+
+struct dict {
+ dict_ent *de[DICTHASH];
+};
+
+/*
+ * Source text location for error reports
+ */
+struct ioloc {
+ int i_line;
+ char *i_file;
+};
diff --git a/contrib/amd/fsinfo/fsi_dict.c b/contrib/amd/fsinfo/fsi_dict.c
new file mode 100644
index 000000000000..f9e686a0f3f1
--- /dev/null
+++ b/contrib/amd/fsinfo/fsi_dict.c
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: fsi_dict.c,v 5.2.2.1 1992/02/09 15:09:43 jsp beta $
+ *
+ */
+
+/*
+ * Dictionary support
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <fsi_data.h>
+#include <fsinfo.h>
+
+
+static int
+dict_hash(char *k)
+{
+ u_int h;
+
+ for (h = 0; *k; h += *k++) ;
+ return h % DICTHASH;
+}
+
+
+dict *
+new_dict(void)
+{
+ dict *dp = CALLOC(struct dict);
+
+ return dp;
+}
+
+
+static void
+dict_add_data(dict_ent *de, char *v)
+{
+ dict_data *dd = CALLOC(struct dict_data);
+
+ dd->dd_data = v;
+ ins_que(&dd->dd_q, de->de_q.q_back);
+ de->de_count++;
+}
+
+
+static dict_ent *
+new_dict_ent(char *k)
+{
+ dict_ent *de = CALLOC(struct dict_ent);
+
+ de->de_key = k;
+ init_que(&de->de_q);
+ return de;
+}
+
+
+dict_ent *
+dict_locate(dict *dp, char *k)
+{
+ dict_ent *de = dp->de[dict_hash(k)];
+
+ while (de && !STREQ(de->de_key, k))
+ de = de->de_next;
+ return de;
+}
+
+
+void
+dict_add(dict *dp, char *k, char *v)
+{
+ dict_ent *de = dict_locate(dp, k);
+
+ if (!de) {
+ dict_ent **dep = &dp->de[dict_hash(k)];
+ de = new_dict_ent(k);
+ de->de_next = *dep;
+ *dep = de;
+ }
+ dict_add_data(de, v);
+}
+
+
+int
+dict_iter(dict *dp, int (*fn) (qelem *))
+{
+ int i;
+ int errors = 0;
+
+ for (i = 0; i < DICTHASH; i++) {
+ dict_ent *de = dp->de[i];
+ while (de) {
+ errors += (*fn) (&de->de_q);
+ de = de->de_next;
+ }
+ }
+ return errors;
+}
diff --git a/contrib/amd/fsinfo/fsi_gram.y b/contrib/amd/fsinfo/fsi_gram.y
new file mode 100644
index 000000000000..b325c77d0ca6
--- /dev/null
+++ b/contrib/amd/fsinfo/fsi_gram.y
@@ -0,0 +1,419 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: fsi_gram.y,v 5.2.2.1 1992/02/09 15:09:35 jsp beta $
+ *
+ */
+
+%{
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <fsi_data.h>
+#include <fsinfo.h>
+
+extern qelem *list_of_hosts, *list_of_automounts;
+%}
+
+%union {
+ auto_tree *a;
+ disk_fs *d;
+ ether_if *e;
+ host *h;
+ qelem *q;
+ char *s;
+ fsi_mount *m;
+ fsmount *f;
+}
+
+%token tARCH
+%token tAS
+%token tAUTOMOUNT
+%token tCLUSTER
+%token tCONFIG
+%token tDUMPSET
+%token tEQ
+%token tNFSEQ
+%token tEXPORTFS
+%token tFREQ
+%token tFROM
+%token tFS
+%token tFSTYPE
+%token tHWADDR
+%token tINADDR
+%token tHOST
+%token tLOCALHOST
+%token tLOG
+%token tMOUNT
+%token tNETMASK
+%token tNETIF
+%token tVOLNAME
+%token tOPTS
+%token tOS
+%token tPASSNO
+%token tDIRECT
+%token tSEL
+%token <s> tSTR
+
+%start list_of_hosts
+
+%type <a> automount
+%type <q> automount_tree
+%type <e> ether_attr
+%type <m> dir_tree_info
+%type <d> filesystem fs_info_list
+%type <h> host host_attr host_attr_list
+%type <q> list_of_hosts list_of_filesystems list_of_mounts dir_tree
+%type <f> localinfo_list
+%type <s> opt_auto_opts
+
+%%
+
+list_of_hosts :
+ /* empty */
+ { $$ = new_que(); }
+
+ | list_of_hosts host
+ { if ($2) ins_que((qelem *) $2, list_of_hosts->q_back);
+ $$ = $1; }
+
+ | list_of_hosts automount
+ { if ($2) ins_que((qelem *) $2, list_of_automounts->q_back);
+ $$ = $1; }
+ ;
+
+/*
+ * A new host:
+ *
+ * host foo.domain
+ */
+host :
+ tHOST host_attr list_of_filesystems list_of_mounts
+ { $$ = $2; $$->h_disk_fs = $3; $$->h_mount = $4; }
+
+ | error tHOST host_attr list_of_filesystems list_of_mounts
+ { $$ = $3; $$->h_disk_fs = $4; $$->h_mount = $5; }
+
+ ;
+
+host_attr :
+ tSTR
+ { $$ = new_host(); set_host($$, HF_HOST, $1); }
+
+ | '{' host_attr_list '}' tSTR
+ { $$ = $2; set_host($$, HF_HOST, $4); }
+
+ ;
+
+host_attr_list :
+ /* empty */
+ { $$ = new_host(); }
+
+ | host_attr_list tNETIF tSTR '{' ether_attr '}'
+ { if ($5) {
+ $5->e_if = $3;
+ $$ = $1; set_host($$, HF_ETHER, (char *) $5); }
+ }
+
+ | host_attr_list tCONFIG tSTR
+ { $$ = $1; set_host($$, HF_CONFIG, $3); }
+
+ | host_attr_list tARCH '=' tSTR
+ { $$ = $1; set_host($$, HF_ARCH, $4); }
+
+ | host_attr_list tOS '=' tSTR
+ { $$ = $1; set_host($$, HF_OS, $4); }
+
+ | host_attr_list tCLUSTER '=' tSTR
+ { $$ = $1; set_host($$, HF_CLUSTER, $4); }
+
+ | host_attr_list error '=' tSTR
+ { yyerror("unknown host attribute"); }
+ ;
+
+ether_attr :
+ /* empty */
+ { $$ = new_ether_if(); }
+
+ | ether_attr tINADDR '=' tSTR
+ { $$ = $1; set_ether_if($$, EF_INADDR, $4); }
+ | ether_attr tNETMASK '=' tSTR
+ { $$ = $1; set_ether_if($$, EF_NETMASK, $4); }
+ | ether_attr tHWADDR '=' tSTR
+ { $$ = $1; set_ether_if($$, EF_HWADDR, $4); }
+ ;
+
+/*
+ * A new automount tree:
+ *
+ * automount /mountpoint { ... }
+ */
+automount :
+ tAUTOMOUNT opt_auto_opts automount_tree
+ { if ($3) {
+ $$ = new_auto_tree($2, $3);
+ } else {
+ $$ = 0;
+ }
+ }
+
+ | tAUTOMOUNT error
+ { $$ = 0; }
+ ;
+
+opt_auto_opts :
+ /* empty */
+ { $$ = strdup(""); }
+
+ | tOPTS tSTR
+ { $$ = $2; }
+ ;
+
+list_of_filesystems :
+ /* empty */
+ { $$ = 0; }
+
+ | list_of_filesystems filesystem
+ { if ($2) {
+ if ($1)
+ $$ = $1;
+ else
+ $$ = new_que();
+ ins_que(&$2->d_q, $$->q_back);
+ } else {
+ $$ = $1;
+ }
+ }
+ ;
+
+/*
+ * A new filesystem:
+ *
+ * fs /dev/whatever { ... }
+ */
+filesystem :
+ tFS tSTR '{' fs_info_list '}'
+ { $4->d_dev = $2; $$ = $4; }
+
+ | tFS error '}'
+ { $$ = (disk_fs *) 0; }
+ ;
+
+/*
+ * Per-filesystem information:
+ *
+ * fstype - the type of the filesystem (4.2, nfs, swap, export)
+ * opts - the mount options ("rw,grpid")
+ * passno - fsck pass number
+ * freq - dump frequency
+ * dumpset - tape set for filesystem dumps
+ * mount - where to mount this filesystem
+ * log - log device
+ */
+fs_info_list :
+ /* empty */
+ { $$ = new_disk_fs(); }
+
+ | fs_info_list tFSTYPE '=' tSTR
+ { $$ = $1; set_disk_fs($$, DF_FSTYPE, $4); }
+
+ | fs_info_list tOPTS '=' tSTR
+ { $$ = $1; set_disk_fs($$, DF_OPTS, $4); }
+
+ | fs_info_list tPASSNO '=' tSTR
+ { $$ = $1; set_disk_fs($$, DF_PASSNO, $4); }
+
+ | fs_info_list tFREQ '=' tSTR
+ { $$ = $1; set_disk_fs($$, DF_FREQ, $4); }
+
+ | fs_info_list tMOUNT dir_tree
+ { $$ = $1; set_disk_fs($$, DF_MOUNT, (char *) $3); }
+
+ | fs_info_list tDUMPSET '=' tSTR
+ { $$ = $1; set_disk_fs($$, DF_DUMPSET, $4); }
+
+ | fs_info_list tLOG '=' tSTR
+ { $$ = $1; set_disk_fs($$, DF_LOG, $4); }
+
+ | fs_info_list error '=' tSTR
+ { yyerror("unknown filesystem attribute"); }
+ ;
+
+/*
+ * An automount tree:
+ *
+ * name = "volname" name is a reference to volname
+ * name -> "string" name is a link to "string"
+ * name nfsalias "string" name is a link to "string", string parsed as NFS
+ * pathname.
+ * name { ... } name is an automount tree
+ */
+automount_tree :
+ /* empty */
+ { $$ = 0; }
+
+ | automount_tree tSTR opt_auto_opts '=' tSTR
+ { automount *a = new_automount($2);
+ a->a_volname = $5;
+ a->a_opts = $3;
+ if ($1)
+ $$ = $1;
+ else
+ $$ = new_que();
+ ins_que(&a->a_q, $$->q_back);
+ }
+ | automount_tree tSTR opt_auto_opts tNFSEQ tSTR
+ { automount *a = new_automount($2);
+ a->a_hardwiredfs = $5;
+ a->a_opts = $3;
+ if ($1)
+ $$ = $1;
+ else
+ $$ = new_que();
+ ins_que(&a->a_q, $$->q_back);
+ }
+
+ | automount_tree tSTR tEQ tSTR
+ { automount *a = new_automount($2);
+ a->a_symlink = $4;
+ if ($1)
+ $$ = $1;
+ else
+ $$ = new_que();
+ ins_que(&a->a_q, $$->q_back);
+ }
+
+ | automount_tree tSTR opt_auto_opts '{' automount_tree '}'
+ { automount *a = new_automount($2);
+ a->a_mount = $5;
+ a->a_opts = $3;
+ if ($1)
+ $$ = $1;
+ else
+ $$ = new_que();
+ ins_que(&a->a_q, $$->q_back);
+ }
+ ;
+
+dir_tree :
+ /* empty */
+ { $$ = 0; }
+
+ | dir_tree tSTR '{' dir_tree_info dir_tree '}'
+ { $4->m_mount = $5;
+ $4->m_name = $2;
+ if ($2[0] != '/' && $2[1] && strchr($2+1, '/'))
+ yyerror("not allowed '/' in a directory name");
+ if ($1)
+ $$ = $1;
+ else
+ $$ = new_que();
+ ins_que(&$4->m_q, $$->q_back);
+ }
+ ;
+
+dir_tree_info :
+ /* empty */
+ { $$ = new_mount(); }
+
+ | dir_tree_info tEXPORTFS tSTR
+ { $$ = $1; set_mount($$, DM_EXPORTFS, $3); }
+
+ | dir_tree_info tVOLNAME tSTR
+ { $$ = $1; set_mount($$, DM_VOLNAME, $3); }
+
+ | dir_tree_info tSEL tSTR
+ { $$ = $1; set_mount($$, DM_SEL, $3); }
+
+ | dir_tree_info error '=' tSTR
+ { yyerror("unknown directory attribute"); }
+ ;
+
+/*
+ * Additional mounts on a host
+ *
+ * mount "volname" ...
+ */
+list_of_mounts :
+ /* empty */
+ { $$ = 0; }
+
+ | list_of_mounts tMOUNT tSTR localinfo_list
+ { set_fsmount($4, FM_VOLNAME, $3);
+ if ($1)
+ $$ = $1;
+ else
+ $$ = new_que();
+ ins_que(&$4->f_q, $$->q_back);
+ }
+ ;
+
+/*
+ * Mount info:
+ *
+ * from "hostname" - obtain the object from the named host
+ * as "string" - where to mount, if different from the volname
+ * opts "string" - mount options
+ * fstype "type" - type of filesystem mount, if not nfs
+ * direct - mount entry, no need to create ad-hoc hosts file
+ */
+localinfo_list :
+ /* empty */
+ { $$ = new_fsmount(); }
+
+ | localinfo_list tDIRECT
+ { $$ = $1; set_fsmount($$, FM_DIRECT, ""); }
+
+ | localinfo_list tAS tSTR
+ { $$ = $1; set_fsmount($$, FM_LOCALNAME, $3); }
+
+ | localinfo_list tFROM tSTR
+ { $$ = $1; set_fsmount($$, FM_FROM, $3); }
+
+ | localinfo_list tFSTYPE tSTR
+ { $$ = $1; set_fsmount($$, FM_FSTYPE, $3); }
+
+ | localinfo_list tOPTS tSTR
+ { $$ = $1; set_fsmount($$, FM_OPTS, $3); }
+
+ | localinfo_list error '=' tSTR
+ { yyerror("unknown mount attribute"); }
+ ;
diff --git a/contrib/amd/fsinfo/fsi_lex.l b/contrib/amd/fsinfo/fsi_lex.l
new file mode 100644
index 000000000000..98b1976f745e
--- /dev/null
+++ b/contrib/amd/fsinfo/fsi_lex.l
@@ -0,0 +1,270 @@
+%{
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: fsi_lex.l,v 5.2.2.1 1992/02/09 15:09:36 jsp beta $
+ *
+ */
+
+/*
+ * Lexical analyzer for fsinfo.
+ * TODO: Needs rewriting.
+ */
+
+#ifdef FLEX_SCANNER
+static int yylineno;
+# define INIT_STATE { \
+ switch ((yy_start - 1) / 2) { \
+ case 0: \
+ BEGIN F; \
+ break; \
+ } \
+}
+
+#else /* not FLEX_SCANNER */
+
+/*
+ * Using old lex...
+ */
+# define INIT_STATE { \
+ switch (yybgin - yysvec - 1) { \
+ case 0: \
+ BEGIN F; \
+ break; \
+ } \
+}
+
+#endif /* end FLEX_SCANNER */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+/*
+ * Some systems include a definition for the macro ECHO in <sys/ioctl.h>,
+ * and their (bad) version of lex defines it too at the very beginning of
+ * the generated lex.yy.c file (before it can be easily undefined),
+ * resulting in a conflict. So undefine it here before needed.
+ * Luckily, it does not appear that this macro is actually used in the rest
+ * of the generated lex.yy.c file.
+ */
+#ifdef ECHO
+# undef ECHO
+#endif /* ECHO */
+#include <am_defs.h>
+#include <fsi_data.h>
+#include <fsinfo.h>
+#include <fsi_gram.h>
+/* and once again undefine this, just in case */
+#ifdef ECHO
+# undef ECHO
+#endif /* ECHO */
+
+/*
+ * There are some things that need to be defined only if useing GNU flex.
+ * These must not be defined if using standard lex
+ */
+#ifdef FLEX_SCANNER
+# ifndef ECHO
+# define ECHO (void) fwrite( yytext, yyleng, 1, yyout )
+# endif /* not ECHO */
+#endif /* FLEX_SCANNER */
+
+/*
+ * some systems such as DU-4.x have a different GNU flex in /usr/bin
+ * which automatically generates yywrap macros and symbols. So I must
+ * distinguish between them and when yywrap is actually needed.
+ */
+#ifndef yywrap
+int yywrap(void);
+#endif /* not yywrap */
+
+YYSTYPE yylval;
+static char *fsi_filename;
+static char *optr;
+static char ostr[1024];
+static int find_resword(char *);
+static int quoted;
+
+struct r {
+ char *rw;
+ int tok;
+} rr[] = {
+ { "->", tEQ },
+ { "arch", tARCH },
+ { "as", tAS },
+ { "automount", tAUTOMOUNT },
+ { "cluster", tCLUSTER },
+ { "config", tCONFIG },
+ { "direct", tDIRECT },
+ { "dumpset", tDUMPSET },
+ { "exportfs", tEXPORTFS },
+ { "freq", tFREQ },
+ { "from", tFROM },
+ { "fs", tFS },
+ { "fstype", tFSTYPE },
+ { "host", tHOST },
+ { "hwaddr", tHWADDR },
+ { "inaddr", tINADDR },
+ { "localhost", tLOCALHOST },
+ { "log", tLOG },
+ { "mount", tMOUNT },
+ { "netif", tNETIF },
+ { "netmask", tNETMASK },
+ { "nfsalias", tNFSEQ },
+ { "opts", tOPTS },
+ { "os", tOS },
+ { "passno", tPASSNO },
+ { "sel", tSEL },
+ { "volname", tVOLNAME },
+ { 0, 0 },
+};
+#define NRES_WORDS (sizeof(rr)/sizeof(rr[0])-1)
+
+%}
+
+%start F Q
+
+%%
+ INIT_STATE; /* witchcraft */
+
+<F>[^ \t\n"={}]+ { return find_resword(yytext); } /* dummy " */
+<F>[ \t] ;
+<F>"\n" { yylineno++; }
+<F>[={}] { return *yytext; }
+
+<F>\" { BEGIN Q; optr = ostr; quoted = 1; }
+<Q>\n { yylineno++; yyerror("\" expected"); BEGIN F; }
+<Q>\\b { *optr++ = '\b'; /* escape */ }
+<Q>\\t { *optr++ = '\t'; /* escape */ }
+<Q>\\\" { *optr++ = '\"'; /* escape */ }
+<Q>\\\\ { *optr++ = '\\'; /* escape */ }
+<Q>\\\n { yylineno++; /* continue */ }
+<Q>\\r { *optr++ = '\r'; /* escape */ }
+<Q>\\n { *optr++ = '\n'; /* escape */ }
+<Q>\\f { *optr++ = '\f'; /* escape */ }
+<Q>"\\ " { *optr++ = ' '; /* force space */ }
+<Q>\\. { yyerror("Unknown \\ sequence"); }
+<Q>([ \t]|"\\\n"){2,} { char *p = (char *) yytext-1; while ((p = strchr(p+1, '\n'))) yylineno++; }
+<Q>\" { BEGIN F; quoted = 0;
+ *optr = '\0';
+ yylval.s = strdup(ostr);
+ return tSTR;
+ }
+<Q>. { *optr++ = *yytext; }
+
+%%
+
+
+static int
+find_resword(char *s)
+{
+ int tok = 0;
+ int l = 0, m = NRES_WORDS/2, h = NRES_WORDS-1;
+ int rc = 0;
+
+ m = NRES_WORDS/2;
+
+#define FSTRCMP(p, q) ((*(p) == *(q)) ? strcmp((p)+1, (q)+1) : *(p) - *(q))
+
+ while ((l <= h) && (rc = FSTRCMP(s, rr[m].rw))) {
+ if (rc < 0)
+ h = m - 1;
+ else
+ l = m + 1;
+ m = (h + l) / 2;
+ }
+
+ if (rc == 0)
+ tok = rr[m].tok;
+
+ switch (tok) {
+ case tLOCALHOST:
+ s = "${host}";
+ /* fall through... */
+ case 0:
+ yylval.s = strdup(s);
+ tok = tSTR;
+ /* fall through... */
+ default:
+ return tok;
+ }
+}
+
+
+int
+yyerror(char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ col_cleanup(0);
+ fprintf(stderr, "%s:%d: ", fsi_filename ? fsi_filename : "/dev/stdin", yylineno);
+ fprintf(stderr, fmt, ap);
+ fputc('\n', stderr);
+ parse_errors++;
+ va_end(ap);
+ return 0;
+
+ /* this call will never happen */
+ unput(0);
+}
+
+
+ioloc *
+current_location(void)
+{
+ ioloc *ip = CALLOC(struct ioloc);
+ ip->i_line = yylineno;
+ ip->i_file = fsi_filename;
+ return ip;
+}
+
+
+/*
+ * some systems such as DU-4.x have a different GNU flex in /usr/bin
+ * which automatically generates yywrap macros and symbols. So I must
+ * distinguish between them and when yywrap is actually needed.
+ */
+#ifndef yywrap
+int yywrap(void)
+{
+ return 1;
+}
+#endif /* not yywrap */
diff --git a/contrib/amd/fsinfo/fsi_util.c b/contrib/amd/fsinfo/fsi_util.c
new file mode 100644
index 000000000000..d69c4593b49e
--- /dev/null
+++ b/contrib/amd/fsinfo/fsi_util.c
@@ -0,0 +1,693 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989 The Regents of the University of California.
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: fsi_util.c,v 5.2.2.1 1992/02/09 15:09:39 jsp beta $
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <fsi_data.h>
+#include <fsinfo.h>
+
+/* static variables */
+static int show_range = 10;
+static int col = 0;
+static int total_shown = 0;
+static int total_mmm = 8;
+
+
+static int
+col_output(int len)
+{
+ int wrapped = 0;
+
+ col += len;
+ if (col > 77) {
+ fputc('\n', stdout);
+ col = len;
+ wrapped = 1;
+ }
+ return wrapped;
+}
+
+
+static void
+show_total(void)
+{
+ if (total_mmm != -show_range + 1) {
+ char n[8];
+ int len;
+
+ if (total_mmm < 0)
+ fputc('*', stdout);
+ sprintf(n, "%d", total_shown);
+ len = strlen(n);
+ if (col_output(len))
+ fputc(' ', stdout);
+ fputs(n, stdout);
+ fflush(stdout);
+ total_mmm = -show_range;
+ }
+}
+
+
+void
+col_cleanup(int eoj)
+{
+ if (verbose < 0)
+ return;
+ if (eoj) {
+ show_total();
+ fputs(")]", stdout);
+ }
+ if (col) {
+ fputc('\n', stdout);
+ col = 0;
+ }
+}
+
+
+/*
+ * Lots of ways of reporting errors...
+ */
+void
+error(char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ col_cleanup(0);
+ fprintf(stderr, "%s: Error, ", progname);
+ fprintf(stderr, fmt, ap);
+ fputc('\n', stderr);
+ errors++;
+ va_end(ap);
+}
+
+
+void
+lerror(ioloc *l, char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ col_cleanup(0);
+ fprintf(stderr, "%s:%d: ", l->i_file, l->i_line);
+ fprintf(stderr, fmt, ap);
+ fputc('\n', stderr);
+ errors++;
+ va_end(ap);
+}
+
+
+void
+lwarning(ioloc *l, char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ col_cleanup(0);
+ fprintf(stderr, "%s:%d: ", l->i_file, l->i_line);
+ fprintf(stderr, fmt, ap);
+ fputc('\n', stderr);
+ va_end(ap);
+}
+
+
+void
+fatal(char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ col_cleanup(1);
+ fprintf(stderr, "%s: Fatal, ", progname);
+ fprintf(stderr, fmt, ap);
+ fputc('\n', stderr);
+ va_end(ap);
+ exit(1);
+}
+
+
+/*
+ * Debug log
+ */
+void
+log(char *fmt, ...)
+{
+ va_list ap;
+
+ if (verbose > 0) {
+ va_start(ap, fmt);
+ fputc('#', stdout);
+ fprintf(stdout, "%s: ", progname);
+ fprintf(stdout, fmt, ap);
+ putc('\n', stdout);
+ va_end(ap);
+ }
+}
+
+
+void
+info_hdr(FILE *ef, char *info)
+{
+ fprintf(ef, "# *** NOTE: This file contains %s info\n", info);
+}
+
+
+void
+gen_hdr(FILE *ef, char *hn)
+{
+ fprintf(ef, "# *** NOTE: Only for use on %s\n", hn);
+}
+
+
+static void
+make_banner(FILE *fp)
+{
+ time_t t = time((time_t *) 0);
+ char *cp = ctime(&t);
+
+ fprintf(fp,
+ "\
+# *** This file was automatically generated -- DO NOT EDIT HERE ***\n\
+# \"%s\" run by %s@%s on %s\
+#\n\
+",
+ progname, username, hostname, cp);
+}
+
+
+void
+show_new(char *msg)
+{
+ if (verbose < 0)
+ return;
+
+ total_shown++;
+ if (total_mmm > show_range) {
+ show_total();
+ } else if (total_mmm == 0) {
+ fputc('*', stdout);
+ fflush(stdout);
+ col += 1;
+ }
+ total_mmm++;
+}
+
+
+void
+show_area_being_processed(char *area, int n)
+{
+ static char *last_area = 0;
+
+ if (verbose < 0)
+ return;
+ if (last_area) {
+ if (total_shown)
+ show_total();
+ fputs(")", stdout);
+ col += 1;
+ }
+
+ if (!last_area || !STREQ(area, last_area)) {
+ if (last_area) {
+ col_cleanup(0);
+ total_shown = 0;
+ total_mmm = show_range + 1;
+ }
+ (void) col_output(strlen(area) + 2);
+ fprintf(stdout, "[%s", area);
+ last_area = area;
+ }
+
+ fputs(" (", stdout);
+ col += 2;
+ show_range = n;
+ total_mmm = n + 1;
+
+ fflush(stdout);
+}
+
+
+/*
+ * Open a file with the given prefix and name
+ */
+FILE *
+pref_open(char *pref, char *hn, void (*hdr) (FILE *, char *), char *arg)
+{
+ char p[MAXPATHLEN];
+ FILE *ef;
+
+ sprintf(p, "%s%s", pref, hn);
+ log("Writing %s info for %s to %s", pref, hn, p);
+ ef = fopen(p, "w");
+ if (ef) {
+ (*hdr) (ef, arg);
+ make_banner(ef);
+ } else {
+ error("can't open %s for writing", p);
+ }
+
+ return ef;
+}
+
+
+int
+pref_close(FILE *fp)
+{
+ return fclose(fp) == 0;
+}
+
+
+/*
+ * Determine where Amd would automount the host/volname pair
+ */
+void
+compute_automount_point(char *buf, host *hp, char *vn)
+{
+ sprintf(buf, "%s/%s%s", autodir, hp->h_lochost, vn);
+}
+
+
+char *
+xcalloc(int i, int s)
+{
+ char *p = (char *) calloc(i, (unsigned) s);
+
+ if (!p)
+ fatal("Out of memory");
+ return p;
+}
+
+
+/*
+ * Data constructors..
+ */
+automount *
+new_automount(char *name)
+{
+ automount *ap = CALLOC(struct automount);
+
+ ap->a_ioloc = current_location();
+ ap->a_name = name;
+ ap->a_volname = 0;
+ ap->a_mount = 0;
+ ap->a_opts = 0;
+ show_new("automount");
+ return ap;
+}
+
+
+auto_tree *
+new_auto_tree(char *def, qelem *ap)
+{
+ auto_tree *tp = CALLOC(struct auto_tree);
+
+ tp->t_ioloc = current_location();
+ tp->t_defaults = def;
+ tp->t_mount = ap;
+ show_new("auto_tree");
+ return tp;
+}
+
+
+host *
+new_host(void)
+{
+ host *hp = CALLOC(struct host);
+
+ hp->h_ioloc = current_location();
+ hp->h_mask = 0;
+ show_new("host");
+ return hp;
+}
+
+
+void
+set_host(host *hp, int k, char *v)
+{
+ int m = 1 << k;
+
+ if (hp->h_mask & m) {
+ yyerror("host field \"%s\" already set", host_strings[k]);
+ return;
+ }
+ hp->h_mask |= m;
+
+ switch (k) {
+
+ case HF_HOST:{
+ char *p = strdup(v);
+ dict_ent *de = dict_locate(dict_of_hosts, v);
+
+ if (de)
+ yyerror("duplicate host %s!", v);
+ else
+ dict_add(dict_of_hosts, v, (char *) hp);
+ hp->h_hostname = v;
+ domain_strip(p, hostname);
+ if (strchr(p, '.') != 0)
+ XFREE(p);
+ else
+ hp->h_lochost = p;
+ }
+ break;
+
+ case HF_CONFIG:{
+ qelem *q;
+ qelem *vq = (qelem *) v;
+
+ hp->h_mask &= ~m;
+ if (hp->h_config)
+ q = hp->h_config;
+ else
+ q = hp->h_config = new_que();
+ ins_que(vq, q->q_back);
+ }
+ break;
+
+ case HF_ETHER:{
+ qelem *q;
+ qelem *vq = (qelem *) v;
+
+ hp->h_mask &= ~m;
+ if (hp->h_ether)
+ q = hp->h_ether;
+ else
+ q = hp->h_ether = new_que();
+ ins_que(vq, q->q_back);
+ }
+ break;
+
+ case HF_ARCH:
+ hp->h_arch = v;
+ break;
+
+ case HF_OS:
+ hp->h_os = v;
+ break;
+
+ case HF_CLUSTER:
+ hp->h_cluster = v;
+ break;
+
+ default:
+ abort();
+ break;
+ }
+}
+
+
+ether_if *
+new_ether_if(void)
+{
+ ether_if *ep = CALLOC(struct ether_if);
+
+ ep->e_mask = 0;
+ ep->e_ioloc = current_location();
+ show_new("ether_if");
+ return ep;
+}
+
+
+void
+set_ether_if(ether_if *ep, int k, char *v)
+{
+ int m = 1 << k;
+
+ if (ep->e_mask & m) {
+ yyerror("netif field \"%s\" already set", ether_if_strings[k]);
+ return;
+ }
+ ep->e_mask |= m;
+
+ switch (k) {
+
+ case EF_INADDR:{
+ ep->e_inaddr.s_addr = inet_addr(v);
+ if (ep->e_inaddr.s_addr == (u_long) - 1)
+ yyerror("malformed IP dotted quad: %s", v);
+ XFREE(v);
+ }
+ break;
+
+ case EF_NETMASK:{
+ u_long nm = 0;
+
+ if ((sscanf(v, "0x%lx", &nm) == 1 || sscanf(v, "%lx", &nm) == 1) && nm != 0)
+ ep->e_netmask = htonl(nm);
+ else
+ yyerror("malformed netmask: %s", v);
+ XFREE(v);
+ }
+ break;
+
+ case EF_HWADDR:
+ ep->e_hwaddr = v;
+ break;
+
+ default:
+ abort();
+ break;
+ }
+}
+
+
+void
+set_disk_fs(disk_fs *dp, int k, char *v)
+{
+ int m = 1 << k;
+
+ if (dp->d_mask & m) {
+ yyerror("fs field \"%s\" already set", disk_fs_strings[k]);
+ return;
+ }
+ dp->d_mask |= m;
+
+ switch (k) {
+
+ case DF_FSTYPE:
+ dp->d_fstype = v;
+ break;
+
+ case DF_OPTS:
+ dp->d_opts = v;
+ break;
+
+ case DF_DUMPSET:
+ dp->d_dumpset = v;
+ break;
+
+ case DF_LOG:
+ dp->d_log = v;
+ break;
+
+ case DF_PASSNO:
+ dp->d_passno = atoi(v);
+ XFREE(v);
+ break;
+
+ case DF_FREQ:
+ dp->d_freq = atoi(v);
+ XFREE(v);
+ break;
+
+ case DF_MOUNT:
+ dp->d_mount = &((fsi_mount *) v)->m_q;
+ break;
+
+ default:
+ abort();
+ break;
+ }
+}
+
+
+disk_fs *
+new_disk_fs(void)
+{
+ disk_fs *dp = CALLOC(struct disk_fs);
+
+ dp->d_ioloc = current_location();
+ show_new("disk_fs");
+ return dp;
+}
+
+
+void
+set_mount(fsi_mount *mp, int k, char *v)
+{
+ int m = 1 << k;
+
+ if (mp->m_mask & m) {
+ yyerror("mount tree field \"%s\" already set", mount_strings[k]);
+ return;
+ }
+ mp->m_mask |= m;
+
+ switch (k) {
+
+ case DM_VOLNAME:
+ dict_add(dict_of_volnames, v, (char *) mp);
+ mp->m_volname = v;
+ break;
+
+ case DM_EXPORTFS:
+ mp->m_exportfs = v;
+ break;
+
+ case DM_SEL:
+ mp->m_sel = v;
+ break;
+
+ default:
+ abort();
+ break;
+ }
+}
+
+
+fsi_mount *
+new_mount(void)
+{
+ fsi_mount *fp = CALLOC(struct fsi_mount);
+
+ fp->m_ioloc = current_location();
+ show_new("mount");
+ return fp;
+}
+
+
+void
+set_fsmount(fsmount *fp, int k, char *v)
+{
+ int m = 1 << k;
+
+ if (fp->f_mask & m) {
+ yyerror("mount field \"%s\" already set", fsmount_strings[k]);
+ return;
+ }
+ fp->f_mask |= m;
+
+ switch (k) {
+
+ case FM_LOCALNAME:
+ fp->f_localname = v;
+ break;
+
+ case FM_VOLNAME:
+ fp->f_volname = v;
+ break;
+
+ case FM_FSTYPE:
+ fp->f_fstype = v;
+ break;
+
+ case FM_OPTS:
+ fp->f_opts = v;
+ break;
+
+ case FM_FROM:
+ fp->f_from = v;
+ break;
+
+ case FM_DIRECT:
+ break;
+
+ default:
+ abort();
+ break;
+ }
+}
+
+
+fsmount *
+new_fsmount(void)
+{
+ fsmount *fp = CALLOC(struct fsmount);
+
+ fp->f_ioloc = current_location();
+ show_new("fsmount");
+ return fp;
+}
+
+
+void
+init_que(qelem *q)
+{
+ q->q_forw = q->q_back = q;
+}
+
+
+qelem *
+new_que(void)
+{
+ qelem *q = CALLOC(qelem);
+
+ init_que(q);
+ return q;
+}
+
+
+void
+ins_que(qelem *elem, qelem *pred)
+{
+ qelem *p;
+
+ p = pred->q_forw;
+ elem->q_back = pred;
+ elem->q_forw = p;
+ pred->q_forw = elem;
+ p->q_back = elem;
+}
+
+
+void
+rem_que(qelem *elem)
+{
+ qelem *p, *p2;
+
+ p = elem->q_forw;
+ p2 = elem->q_back;
+
+ p2->q_forw = p;
+ p->q_back = p2;
+}
diff --git a/contrib/amd/fsinfo/fsinfo.8 b/contrib/amd/fsinfo/fsinfo.8
new file mode 100644
index 000000000000..5a4eec50cd39
--- /dev/null
+++ b/contrib/amd/fsinfo/fsinfo.8
@@ -0,0 +1,101 @@
+.\"
+.\" Copyright (c) 1997-1998 Erez Zadok
+.\" Copyright (c) 1993 Jan-Simon Pendry.
+.\" Copyright (c) 1993
+.\" The Regents of the University of California. 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.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgment:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+.\"
+.\" from: @(#)fsinfo.8 8.1 (Berkeley) 6/28/93
+.\" $Id: fsinfo.8,v 1.2 1994/06/13 20:50:18 mycroft Exp $
+.\"
+.TH FSINFO 8 "June 28, 1993"
+.SH NAME
+fsinfo \- co-ordinate site-wide filesystem information
+.SH SYNOPSIS
+.B fsinfo
+[
+.B \-v
+] [
+.B \-a
+.I autodir
+] [
+.B \-b
+.I bootparams
+] [
+.B \-d
+.I dumpsets
+] [
+.B \-e
+.I exports
+] [
+.B \-f
+.I fstabs
+] [
+.B \-h
+.I hostname
+] [
+.B \-m
+.I automounts
+] [
+.B \-I
+.I dir
+] [
+.B \-D
+.I string[=string]]
+] [
+.B \-U
+.I string[=string]]
+]
+.I config
+.I ...
+.SH DESCRIPTION
+The
+.B fsinfo
+utility takes a set of system configuration information, and generates
+a co-ordinated set of
+.I amd
+,
+.I mount
+and
+.I mountd
+configuration files.
+.PP
+The
+.B fsinfo
+command is fully described in the document
+.I "Amd - The 4.4BSD Automounter"
+.SH "SEE ALSO"
+.BR amd (8),
+.BR mount (8),
+.BR mountd (8).
+.SH HISTORY
+The
+.B fsinfo
+command first appeared in 4.4BSD.
diff --git a/contrib/amd/fsinfo/fsinfo.c b/contrib/amd/fsinfo/fsinfo.c
new file mode 100644
index 000000000000..e2632bff4a22
--- /dev/null
+++ b/contrib/amd/fsinfo/fsinfo.c
@@ -0,0 +1,293 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: fsinfo.c,v 5.2.2.1 1992/02/09 15:09:33 jsp beta $
+ *
+ */
+
+/*
+ * fsinfo
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <fsi_data.h>
+#include <fsinfo.h>
+#include <fsi_gram.h>
+
+/* globals */
+char **g_argv;
+char *autodir = "/a";
+char *progname;
+char *username;
+char hostname[MAXHOSTNAMELEN + 1];
+char idvbuf[1024];
+dict *dict_of_hosts;
+dict *dict_of_volnames;
+int errors;
+int file_io_errors;
+int parse_errors;
+int verbose;
+qelem *list_of_automounts;
+qelem *list_of_hosts;
+
+/*
+ * Output file prefixes
+ */
+char *bootparams_pref;
+char *dumpset_pref;
+char *exportfs_pref;
+char *fstab_pref;
+char *mount_pref;
+
+/* dummy variables */
+int orig_umask, foreground, debug_flags;
+pid_t mypid;
+serv_state amd_state;
+
+
+/*
+ * Argument cracking...
+ */
+static void
+fsi_get_args(int c, char *v[])
+{
+ int ch;
+ int usage = 0;
+ char *iptr = idvbuf;
+
+ /*
+ * Determine program name
+ */
+ if (v[0]) {
+ progname = strrchr(v[0], '/');
+ if (progname && progname[1])
+ progname++;
+ else
+ progname = v[0];
+ }
+
+ if (!progname)
+ progname = "fsinfo";
+
+ while ((ch = getopt(c, v, "a:b:d:e:f:h:m:D:U:I:qv")) != EOF)
+
+ switch (ch) {
+
+ case 'a':
+ autodir = optarg;
+ break;
+
+ case 'b':
+ if (bootparams_pref)
+ fatal("-b option specified twice");
+ bootparams_pref = optarg;
+ break;
+
+ case 'd':
+ if (dumpset_pref)
+ fatal("-d option specified twice");
+ dumpset_pref = optarg;
+ break;
+
+ case 'h':
+ strncpy(hostname, optarg, sizeof(hostname) - 1);
+ break;
+
+ case 'e':
+ if (exportfs_pref)
+ fatal("-e option specified twice");
+ exportfs_pref = optarg;
+ break;
+
+ case 'f':
+ if (fstab_pref)
+ fatal("-f option specified twice");
+ fstab_pref = optarg;
+ break;
+
+ case 'm':
+ if (mount_pref)
+ fatal("-m option specified twice");
+ mount_pref = optarg;
+ break;
+
+ case 'q':
+ verbose = -1;
+ break;
+
+ case 'v':
+ verbose = 1;
+ break;
+
+ case 'I':
+ case 'D':
+ case 'U':
+ sprintf(iptr, "-%c%s ", ch, optarg);
+ iptr += strlen(iptr);
+ break;
+
+ default:
+ usage++;
+ break;
+ }
+
+ if (c != optind) {
+ g_argv = v + optind - 1;
+ if (yywrap())
+ fatal("Cannot read any input files");
+ } else {
+ usage++;
+ }
+
+ if (usage) {
+ fprintf(stderr,
+ "\
+Usage: %s [-v] [-a autodir] [-h hostname] [-b bootparams] [-d dumpsets]\n\
+\t[-e exports] [-f fstabs] [-m automounts]\n\
+\t[-I dir] [-D|-U string[=string]] config ...\n", progname);
+ exit(1);
+ }
+
+ if (g_argv[0])
+ log("g_argv[0] = %s", g_argv[0]);
+ else
+ log("g_argv[0] = (nil)");
+}
+
+
+/*
+ * Determine username of caller
+ */
+static char *
+find_username(void)
+{
+ char *u = getlogin();
+
+ if (!u) {
+ struct passwd *pw = getpwuid(getuid());
+ if (pw)
+ u = pw->pw_name;
+ }
+
+ if (!u)
+ u = getenv("USER");
+ if (!u)
+ u = getenv("LOGNAME");
+ if (!u)
+ u = "root";
+
+ return strdup(u);
+}
+
+
+/*
+ * MAIN
+ */
+int
+main(int argc, char *argv[])
+{
+ /*
+ * Process arguments
+ */
+ fsi_get_args(argc, argv);
+
+ /*
+ * If no hostname given then use the local name
+ */
+ if (!*hostname && gethostname(hostname, sizeof(hostname)) < 0) {
+ perror("gethostname");
+ exit(1);
+ }
+
+ /*
+ * Get the username
+ */
+ username = find_username();
+
+ /*
+ * New hosts and automounts
+ */
+ list_of_hosts = new_que();
+ list_of_automounts = new_que();
+
+ /*
+ * New dictionaries
+ */
+ dict_of_volnames = new_dict();
+ dict_of_hosts = new_dict();
+
+ /*
+ * Parse input
+ */
+ show_area_being_processed("read config", 11);
+ if (yyparse())
+ errors = 1;
+ errors += file_io_errors + parse_errors;
+
+ if (errors == 0) {
+ /*
+ * Do semantic analysis of input
+ */
+ analyze_hosts(list_of_hosts);
+ analyze_automounts(list_of_automounts);
+ }
+
+ /*
+ * Give up if errors
+ */
+ if (errors == 0) {
+ /*
+ * Output data files
+ */
+
+ write_atab(list_of_automounts);
+ write_bootparams(list_of_hosts);
+ write_dumpset(list_of_hosts);
+ write_exportfs(list_of_hosts);
+ write_fstab(list_of_hosts);
+ }
+ col_cleanup(1);
+
+ exit(errors);
+ return errors; /* should never reach here */
+}
diff --git a/contrib/amd/fsinfo/fsinfo.h b/contrib/amd/fsinfo/fsinfo.h
new file mode 100644
index 000000000000..c02b67362f7c
--- /dev/null
+++ b/contrib/amd/fsinfo/fsinfo.h
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: fsinfo.h,v 5.2.2.1 1992/02/09 15:09:51 jsp beta $
+ *
+ */
+
+extern FILE *pref_open(char *pref, char *hn, void (*hdr) (FILE *, char *), char *arg);
+extern auto_tree *new_auto_tree(char *, qelem *);
+extern automount *new_automount(char *);
+extern char **g_argv;
+extern char *autodir;
+extern char *bootparams_pref;
+extern char *disk_fs_strings[];
+extern char *dumpset_pref;
+extern char *ether_if_strings[];
+extern char *exportfs_pref;
+extern char *fsmount_strings[];
+extern char *fstab_pref;
+extern char *host_strings[];
+extern char *mount_pref;
+extern char *mount_strings[];
+extern char *progname;
+extern char *username;
+extern char *xcalloc(int, int);
+extern char hostname[];
+extern char idvbuf[];
+extern dict *dict_of_hosts;
+extern dict *dict_of_volnames;
+extern dict *new_dict(void);
+extern dict_ent *dict_locate(dict *, char *);
+extern disk_fs *new_disk_fs(void);
+extern ether_if *new_ether_if(void);
+extern fsmount *new_fsmount(void);
+extern host *new_host(void);
+extern int dict_iter(dict *, int (*)(qelem *));
+extern int errors;
+extern int file_io_errors;
+extern int parse_errors;
+extern int pref_close(FILE *fp);
+extern int verbose;
+extern ioloc *current_location(void);
+extern fsi_mount *new_mount(void);
+extern qelem *new_que(void);
+extern void analyze_automounts(qelem *);
+extern void analyze_hosts(qelem *);
+extern void compute_automount_point(char *, host *, char *);
+extern void dict_add(dict *, char *, char *);
+extern void error(char *fmt, ...);
+extern void fatal(char *fmt, ...);
+extern void gen_hdr(FILE *ef, char *hn);
+extern void info_hdr(FILE *ef, char *info);
+extern void init_que(qelem *);
+extern void ins_que(qelem *, qelem *);
+extern void lerror(ioloc *l, char *fmt, ...);
+extern void log(char *fmt, ...);
+extern void lwarning(ioloc *l, char *fmt, ...);
+extern void rem_que(qelem *);
+extern void set_disk_fs(disk_fs *, int, char *);
+extern void set_fsmount(fsmount *, int, char *);
+extern void set_mount(fsi_mount *, int, char *);
+extern void show_area_being_processed(char *area, int n);
+extern void show_new(char *msg);
+extern void warning(void);
+
+extern int yyerror(char *fmt, ...);
+extern void domain_strip(char *otherdom, char *localdom);
+/*
+ * some systems such as DU-4.x have a different GNU flex in /usr/bin
+ * which automatically generates yywrap macros and symbols. So I must
+ * distinguish between them and when yywrap is actually needed.
+ */
+#ifndef yywrap
+extern int yywrap(void);
+#endif /* not yywrap */
+extern int yyparse(void);
+extern int write_atab(qelem *q);
+extern int write_bootparams(qelem *q);
+extern int write_dumpset(qelem *q);
+extern int write_exportfs(qelem *q);
+extern int write_fstab(qelem *q);
+extern void col_cleanup(int eoj);
+extern void set_host(host *hp, int k, char *v);
+extern void set_ether_if(ether_if *ep, int k, char *v);
+extern int yylex(void);
+
+
+#define BITSET(m,b) ((m) |= (1<<(b)))
+#define AM_FIRST(ty, q) ((ty *) ((q)->q_forw))
+#define HEAD(ty, q) ((ty *) q)
+#define ISSET(m,b) ((m) & (1<<(b)))
+#define ITER(v, ty, q) for ((v) = AM_FIRST(ty,(q)); (v) != HEAD(ty,(q)); (v) = NEXT(ty,(v)))
+#define AM_LAST(ty, q) ((ty *) ((q)->q_back))
+#define NEXT(ty, q) ((ty *) (((qelem *) q)->q_forw))
diff --git a/contrib/amd/fsinfo/wr_atab.c b/contrib/amd/fsinfo/wr_atab.c
new file mode 100644
index 000000000000..be870c043560
--- /dev/null
+++ b/contrib/amd/fsinfo/wr_atab.c
@@ -0,0 +1,334 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: wr_atab.c,v 5.2.2.1 1992/02/09 15:09:44 jsp beta $
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <fsi_data.h>
+#include <fsinfo.h>
+
+
+/*
+ * Write a sequence of automount mount map entries
+ */
+static int
+write_amount_info(FILE *af, automount *ap, int sk)
+{
+ int errors = 0;
+
+ if (ap->a_mount) {
+ /*
+ * A pseudo-directory.
+ * This can also be a top-level directory, in which
+ * case the type:=auto is not wanted...
+ *
+ * type:=auto;fs:=${map};pref:=whatever/
+ */
+ automount *ap2;
+ if (strlen(ap->a_name) > sk) {
+ fprintf(af, "%s type:=auto;fs:=${map};pref:=%s/\n",
+ ap->a_name + sk, ap->a_name + sk);
+ }
+ ITER(ap2, automount, ap->a_mount)
+ errors += write_amount_info(af, ap2, sk);
+ } else if (ap->a_hardwiredfs) {
+
+ /*
+ * A hardwired filesystem "hostname:path"
+ * rhost:=hostname;rfs:=path
+ */
+ char *key = ap->a_name + sk;
+ char *hostname = ap->a_hardwiredfs;
+ char *path = strrchr(hostname, (int) ':');
+
+ if (path == NULL) {
+ fprintf(stderr, "%s: %s not an NFS filesystem\n", ap->a_name, ap->a_hardwiredfs);
+ errors++;
+ } else {
+ *path = '\0';
+ path++;
+
+ /*
+ * Output the map key
+ */
+ fputs(key, af);
+ fprintf(af, " rhost:=%s", hostname);
+ fprintf(af, ";rfs:=%s", path);
+ if (ap->a_opts && !STREQ(ap->a_opts, "")) {
+ fprintf(af, ";%s", ap->a_opts);
+ }
+ fputc('\n', af);
+ path--;
+ *path = ':';
+ }
+ } else if (ap->a_mounted) {
+
+ /*
+ * A mounted partition
+ * type:=link [ link entries ] type:=nfs [ nfs entries ]
+ */
+ dict_data *dd;
+ dict_ent *de = ap->a_mounted;
+ int done_type_link = 0;
+ char *key = ap->a_name + sk;
+
+ /*
+ * Output the map key
+ */
+ fputs(key, af);
+
+ /*
+ * First output any Link locations that would not
+ * otherwise be correctly mounted. These refer
+ * to filesystem which are not mounted in the same
+ * place which the automounter would use.
+ */
+ ITER(dd, dict_data, &de->de_q) {
+ fsi_mount *mp = (fsi_mount *) dd->dd_data;
+ /*
+ * If the mount point and the exported volname are the
+ * same then this filesystem will be recognised by
+ * the restart code - so we don't need to put out a
+ * special rule for it.
+ */
+ if (mp->m_dk->d_host->h_lochost) {
+ char amountpt[1024];
+ compute_automount_point(amountpt, mp->m_dk->d_host, mp->m_exported->m_volname);
+ if (!STREQ(mp->m_dk->d_mountpt, amountpt)) {
+ /*
+ * ap->a_volname is the name of the aliased volume
+ * mp->m_name is the mount point of the filesystem
+ * mp->m_volname is the volume name of the filesystems
+ */
+
+ /*
+ * Find length of key and volume names
+ */
+ int avlen = strlen(ap->a_volname);
+ int mnlen = strlen(mp->m_volname);
+
+ /*
+ * Make sure a -type:=link is output once
+ */
+ if (!done_type_link) {
+ done_type_link = 1;
+ fputs(" -type:=link", af);
+ }
+
+ /*
+ * Output a selector for the hostname,
+ * the device from which to mount and
+ * where to mount. This will correspond
+ * to the values output for the fstab.
+ */
+ if (mp->m_dk->d_host->h_lochost)
+ fprintf(af, " host==%s", mp->m_dk->d_host->h_lochost);
+ else
+ fprintf(af, " hostd==%s", mp->m_dk->d_host->h_hostname);
+ fprintf(af, ";fs:=%s", mp->m_name);
+
+ /*
+ * ... and a sublink if needed
+ */
+ if (mnlen < avlen) {
+ char *sublink = ap->a_volname + mnlen + 1;
+ fprintf(af, "/%s", sublink);
+ }
+ fputs(" ||", af);
+ }
+ }
+ }
+
+ /*
+ * Next do the NFS locations
+ */
+ if (done_type_link)
+ fputs(" -", af);
+
+ ITER(dd, dict_data, &de->de_q) {
+ fsi_mount *mp = (fsi_mount *) dd->dd_data;
+ int namelen = mp->m_name_len;
+ int exp_namelen = mp->m_exported->m_name_len;
+ int volnlen = strlen(ap->a_volname);
+ int mvolnlen = strlen(mp->m_volname);
+
+ fputc(' ', af);
+
+ /*
+ * Output any selectors
+ */
+ if (mp->m_sel)
+ fprintf(af, "%s;", mp->m_sel);
+
+ /*
+ * Print host and volname of exported filesystem
+ */
+ fprintf(af, "rhost:=%s",
+ mp->m_dk->d_host->h_lochost ?
+ mp->m_dk->d_host->h_lochost :
+ mp->m_dk->d_host->h_hostname);
+ fprintf(af, ";rfs:=%s", mp->m_exported->m_volname);
+ if (ap->a_opts && !STREQ(ap->a_opts, "")) {
+ fprintf(af, ";%s", ap->a_opts);
+ }
+
+ /*
+ * Now determine whether a sublink is required.
+ */
+ if (exp_namelen < namelen || mvolnlen < volnlen) {
+ char sublink[1024];
+ sublink[0] = '\0';
+ if (exp_namelen < namelen) {
+ strcat(sublink, mp->m_name + exp_namelen + 1);
+ if (mvolnlen < volnlen)
+ strcat(sublink, "/");
+ }
+ if (mvolnlen < volnlen)
+ strcat(sublink, ap->a_volname + mvolnlen + 1);
+
+ fprintf(af, ";sublink:=%s", sublink);
+ }
+ }
+ fputc('\n', af);
+ } else if (ap->a_symlink) {
+
+ /*
+ * A specific link.
+ *
+ * type:=link;fs:=whatever
+ */
+ fprintf(af, "%s type:=link;fs:=%s\n", ap->a_name + sk, ap->a_symlink);
+ }
+
+ return errors;
+}
+
+
+/*
+ * Write a single automount configuration file
+ */
+static int
+write_amount( qelem *q, char *def)
+{
+ automount *ap;
+ int errors = 0;
+ int direct = 0;
+
+ /*
+ * Output all indirect maps
+ */
+ ITER(ap, automount, q) {
+ FILE *af;
+ char *p;
+
+ /*
+ * If there is no a_mount node then this is really
+ * a direct mount, so just keep a count and continue.
+ * Direct mounts are output into a special file during
+ * the second pass below.
+ */
+ if (!ap->a_mount) {
+ direct++;
+ continue;
+ }
+
+ p = strrchr(ap->a_name, '/');
+ if (!p)
+ p = ap->a_name;
+ else
+ p++;
+
+ af = pref_open(mount_pref, p, gen_hdr, ap->a_name);
+ if (af) {
+ show_new(ap->a_name);
+ fputs("/defaults ", af);
+ if (*def)
+ fprintf(af, "%s;", def);
+ fputs("type:=nfs\n", af);
+ errors += write_amount_info(af, ap, strlen(ap->a_name) + 1);
+ errors += pref_close(af);
+ }
+ }
+
+ /*
+ * Output any direct map entries which were found during the
+ * previous pass over the data.
+ */
+ if (direct) {
+ FILE *af = pref_open(mount_pref, "direct.map", info_hdr, "direct mount");
+
+ if (af) {
+ show_new("direct mounts");
+ fputs("/defaults ", af);
+ if (*def)
+ fprintf(af, "%s;", def);
+ fputs("type:=nfs\n", af);
+ ITER(ap, automount, q)
+ if (!ap->a_mount)
+ errors += write_amount_info(af, ap, 1);
+ errors += pref_close(af);
+ }
+ }
+ return errors;
+}
+
+
+/*
+ * Write all the needed automount configuration files
+ */
+int
+write_atab(qelem *q)
+{
+ int errors = 0;
+
+ if (mount_pref) {
+ auto_tree *tp;
+ show_area_being_processed("write automount", 5);
+ ITER(tp, auto_tree, q)
+ errors += write_amount(tp->t_mount, tp->t_defaults);
+ }
+
+ return errors;
+}
diff --git a/contrib/amd/fsinfo/wr_bparam.c b/contrib/amd/fsinfo/wr_bparam.c
new file mode 100644
index 000000000000..fb4479c4286c
--- /dev/null
+++ b/contrib/amd/fsinfo/wr_bparam.c
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: wr_bparam.c,v 5.2.2.1 1992/02/09 15:09:46 jsp beta $
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <fsi_data.h>
+#include <fsinfo.h>
+
+
+/*
+ * Write a host/path in NFS format
+ */
+static int
+write_nfsname(FILE *ef, fsmount *fp, char *hn)
+{
+ int errors = 0;
+ char *h = strdup(fp->f_ref->m_dk->d_host->h_hostname);
+
+ domain_strip(h, hn);
+ fprintf(ef, "%s:%s", h, fp->f_volname);
+ XFREE(h);
+ return errors;
+}
+
+
+/*
+ * Write a bootparams entry for a host
+ */
+static int
+write_boot_info(FILE *ef, host *hp)
+{
+ int errors = 0;
+
+ fprintf(ef, "%s\troot=", hp->h_hostname);
+ errors += write_nfsname(ef, hp->h_netroot, hp->h_hostname);
+ fputs(" swap=", ef);
+ errors += write_nfsname(ef, hp->h_netswap, hp->h_hostname);
+ fputs("\n", ef);
+
+ return 0;
+}
+
+
+/*
+ * Output a bootparams file
+ */
+int
+write_bootparams(qelem *q)
+{
+ int errors = 0;
+
+ if (bootparams_pref) {
+ FILE *ef = pref_open(bootparams_pref, "bootparams", info_hdr, "bootparams");
+ if (ef) {
+ host *hp;
+ ITER(hp, host, q)
+ if (hp->h_netroot && hp->h_netswap)
+ errors += write_boot_info(ef, hp);
+ errors += pref_close(ef);
+ } else {
+ errors++;
+ }
+ }
+
+ return errors;
+}
diff --git a/contrib/amd/fsinfo/wr_dumpset.c b/contrib/amd/fsinfo/wr_dumpset.c
new file mode 100644
index 000000000000..b33858dd7b96
--- /dev/null
+++ b/contrib/amd/fsinfo/wr_dumpset.c
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: wr_dumpset.c,v 5.2.2.1 1992/02/09 15:09:47 jsp beta $
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <fsi_data.h>
+#include <fsinfo.h>
+
+
+static int
+write_dumpset_info(FILE *ef, qelem *q)
+{
+ int errors = 0;
+ disk_fs *dp;
+
+ ITER(dp, disk_fs, q) {
+ if (dp->d_dumpset) {
+ fprintf(ef, "%s\t%s:%-30s\t# %s\n",
+ dp->d_dumpset,
+ dp->d_host->h_lochost ?
+ dp->d_host->h_lochost :
+ dp->d_host->h_hostname,
+ dp->d_mountpt,
+ dp->d_dev);
+ }
+ }
+ return errors;
+}
+
+
+int
+write_dumpset(qelem *q)
+{
+ int errors = 0;
+
+ if (dumpset_pref) {
+ FILE *ef = pref_open(dumpset_pref, "dumpsets", info_hdr, "exabyte dumpset");
+ if (ef) {
+ host *hp;
+
+ ITER(hp, host, q) {
+ if (hp->h_disk_fs) {
+ errors += write_dumpset_info(ef, hp->h_disk_fs);
+ }
+ }
+ errors += pref_close(ef);
+ } else {
+ errors++;
+ }
+ }
+
+ return errors;
+}
diff --git a/contrib/amd/fsinfo/wr_exportfs.c b/contrib/amd/fsinfo/wr_exportfs.c
new file mode 100644
index 000000000000..56d9bd5ac14a
--- /dev/null
+++ b/contrib/amd/fsinfo/wr_exportfs.c
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: wr_exportfs.c,v 5.2.2.1 1992/02/09 15:09:48 jsp beta $
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <fsi_data.h>
+#include <fsinfo.h>
+
+
+static int
+write_export_info(FILE *ef, qelem *q, int errors)
+{
+ fsi_mount *mp;
+
+ ITER(mp, fsi_mount, q) {
+ if (mp->m_mask & (1 << DM_EXPORTFS))
+ fprintf(ef, "%s\t%s\n", mp->m_volname, mp->m_exportfs);
+ if (mp->m_mount)
+ errors += write_export_info(ef, mp->m_mount, 0);
+ }
+
+ return errors;
+}
+
+
+static int
+write_dkexports(FILE *ef, qelem *q)
+{
+ int errors = 0;
+ disk_fs *dp;
+
+ ITER(dp, disk_fs, q) {
+ if (dp->d_mount)
+ errors += write_export_info(ef, dp->d_mount, 0);
+ }
+
+ return errors;
+}
+
+
+int
+write_exportfs(qelem *q)
+{
+ int errors = 0;
+
+ if (exportfs_pref) {
+ host *hp;
+
+ show_area_being_processed("write exportfs", 5);
+ ITER(hp, host, q) {
+ if (hp->h_disk_fs) {
+ FILE *ef = pref_open(exportfs_pref, hp->h_hostname, gen_hdr, hp->h_hostname);
+ if (ef) {
+ show_new(hp->h_hostname);
+ errors += write_dkexports(ef, hp->h_disk_fs);
+ errors += pref_close(ef);
+ } else {
+ errors++;
+ }
+ }
+ }
+ }
+
+ return errors;
+}
diff --git a/contrib/amd/fsinfo/wr_fstab.c b/contrib/amd/fsinfo/wr_fstab.c
new file mode 100644
index 000000000000..49cbfc435119
--- /dev/null
+++ b/contrib/amd/fsinfo/wr_fstab.c
@@ -0,0 +1,342 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: wr_fstab.c,v 5.2.2.1 1992/02/09 15:09:49 jsp beta $
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <fsi_data.h>
+#include <fsinfo.h>
+
+#define GENERIC_OS_NAME "generic"
+
+/* forward definitions */
+static void write_aix1_dkfstab(FILE *ef, disk_fs *dp);
+static void write_aix1_dkrmount(FILE *ef, char *hn, fsmount *fp);
+static void write_aix3_dkfstab(FILE *ef, disk_fs *dp);
+static void write_aix3_dkrmount(FILE *ef, char *hn, fsmount *fp);
+static int write_dkfstab(FILE *ef, qelem *q, void (*output) (FILE *, disk_fs *));
+static int write_dkrmount(FILE *ef, qelem *q, char *hn, void (*output) (FILE *, char *, fsmount *));
+static void write_generic_dkfstab(FILE *ef, disk_fs *dp);
+static void write_generic_dkrmount(FILE *ef, char *hn, fsmount *fp);
+static void write_ultrix_dkfstab(FILE *ef, disk_fs *dp);
+static void write_ultrix_dkrmount(FILE *ef, char *hn, fsmount *fp);
+
+/* ----------------------------------------------- */
+
+static struct os_fstab_type {
+ char *os_name;
+ void (*op_fstab) (FILE *ef, disk_fs *dp);
+ void (*op_mount) (FILE *ef, char *hn, fsmount *fp);
+} os_tabs[] = {
+
+ {
+ "aix1", write_aix1_dkfstab, write_aix1_dkrmount
+ }, /* AIX 1 */
+ {
+ "aix3", write_aix3_dkfstab, write_aix3_dkrmount
+ }, /* AIX 3 */
+ {
+ "generic", write_generic_dkfstab, write_generic_dkrmount
+ }, /* Generic */
+ {
+ "u2_0", write_ultrix_dkfstab, write_ultrix_dkrmount
+ }, /* Ultrix */
+ {
+ "u3_0", write_ultrix_dkfstab, write_ultrix_dkrmount
+ }, /* Ultrix */
+ {
+ "u4_0", write_ultrix_dkfstab, write_ultrix_dkrmount
+ }, /* Ultrix */
+ {
+ 0, 0, 0
+ }
+};
+
+
+/* ---------- AIX 1 ------------------------------ */
+
+/*
+ * AIX 1 format
+ */
+static void
+write_aix1_dkfstab(FILE *ef, disk_fs *dp)
+{
+ char *hp = strdup(dp->d_host->h_hostname);
+ char *p = strchr(hp, '.');
+
+ if (p)
+ *p = '\0';
+
+ fprintf(ef, "\n%s:\n\tdev = %s\n\tvfs = %s\n\ttype = %s\n\tlog = %s\n\tvol = %s\n\topts = %s\n\tmount = true\n\tcheck = true\n\tfree = false\n",
+ dp->d_mountpt,
+ dp->d_dev,
+ dp->d_fstype,
+ dp->d_fstype,
+ dp->d_log,
+ dp->d_mountpt,
+ dp->d_opts);
+ XFREE(hp);
+}
+
+
+static void
+write_aix1_dkrmount(FILE *ef, char *hn, fsmount *fp)
+{
+ char *h = strdup(fp->f_ref->m_dk->d_host->h_hostname);
+ char *hp = strdup(h);
+ char *p = strchr(hp, '.');
+
+ if (p)
+ *p = '\0';
+ domain_strip(h, hn);
+ fprintf(ef, "\n%s:\n\tsite = %s\n\tdev = %s:%s\n\tvfs = %s\n\ttype = %s\n\tvol = %s\n\topts = %s\n\tmount = true\n\tcheck = true\n\tfree = false\n",
+ fp->f_localname,
+ hp,
+ h,
+ fp->f_volname,
+ fp->f_fstype,
+ fp->f_fstype,
+ fp->f_localname,
+ fp->f_opts);
+
+ XFREE(hp);
+ XFREE(h);
+}
+
+
+/* ---------- AIX 3 ------------------------------ */
+
+/*
+ * AIX 3 format
+ */
+static void
+write_aix3_dkfstab(FILE *ef, disk_fs *dp)
+{
+ if (STREQ(dp->d_fstype, "jfs") &&
+ NSTREQ(dp->d_dev, "/dev/", 5) &&
+ !dp->d_log)
+ error("aix 3 needs a log device for journalled filesystem (jfs) mounts");
+
+ fprintf(ef, "\n%s:\n\tdev = %s\n\tvfs = %s\n\ttype = %s\n\tlog = %s\n\tvol = %s\n\topts = %s\n\tmount = true\n\tcheck = true\n\tfree = false\n",
+ dp->d_mountpt,
+ dp->d_dev,
+ dp->d_fstype,
+ dp->d_fstype,
+ dp->d_log,
+ dp->d_mountpt,
+ dp->d_opts);
+}
+
+
+static void
+write_aix3_dkrmount(FILE *ef, char *hn, fsmount *fp)
+{
+ char *h = strdup(fp->f_ref->m_dk->d_host->h_hostname);
+
+ domain_strip(h, hn);
+ fprintf(ef, "\n%s:\n\tdev = %s:%s\n\tvfs = %s\n\ttype = %s\n\tvol = %s\n\topts = %s\n\tmount = true\n\tcheck = true\n\tfree = false\n",
+ fp->f_localname,
+ h,
+ fp->f_volname,
+ fp->f_fstype,
+ fp->f_fstype,
+ fp->f_localname,
+ fp->f_opts);
+
+ XFREE(h);
+}
+
+
+/* ---------- Ultrix ----------------------------- */
+
+static void
+write_ultrix_dkfstab(FILE *ef, disk_fs *dp)
+{
+ fprintf(ef, "%s:%s:%s:%s:%d:%d\n",
+ dp->d_dev,
+ dp->d_mountpt,
+ dp->d_fstype,
+ dp->d_opts,
+ dp->d_freq,
+ dp->d_passno);
+}
+
+
+static void
+write_ultrix_dkrmount(FILE *ef, char *hn, fsmount *fp)
+{
+ char *h = strdup(fp->f_ref->m_dk->d_host->h_hostname);
+
+ domain_strip(h, hn);
+ fprintf(ef, "%s@%s:%s:%s:%s:0:0\n",
+ fp->f_volname,
+ h,
+ fp->f_localname,
+ fp->f_fstype,
+ fp->f_opts);
+ XFREE(h);
+}
+
+
+/* ---------- Generic ---------------------------- */
+
+/*
+ * Generic (BSD, SunOS, HPUX) format
+ */
+static void
+write_generic_dkfstab(FILE *ef, disk_fs *dp)
+{
+ fprintf(ef, "%s %s %s %s %d %d\n",
+ dp->d_dev,
+ dp->d_mountpt,
+ dp->d_fstype,
+ dp->d_opts,
+ dp->d_freq,
+ dp->d_passno);
+}
+
+
+static void
+write_generic_dkrmount(FILE *ef, char *hn, fsmount *fp)
+{
+ char *h;
+
+ if (fp->f_ref) {
+ h = strdup(fp->f_ref->m_dk->d_host->h_hostname);
+ } else {
+ h = strdup(fp->f_from);
+ }
+ domain_strip(h, hn);
+ fprintf(ef, "%s:%s %s %s %s 0 0\n",
+ h,
+ fp->f_volname,
+ fp->f_localname,
+ fp->f_fstype,
+ fp->f_opts);
+ XFREE(h);
+}
+
+
+static struct os_fstab_type *
+find_fstab_type(host *hp)
+{
+ struct os_fstab_type *op = 0;
+ char *os_name = 0;
+
+again:;
+ if (os_name == 0) {
+ if (ISSET(hp->h_mask, HF_OS))
+ os_name = hp->h_os;
+ else
+ os_name = GENERIC_OS_NAME;
+ }
+ for (op = os_tabs; op->os_name; op++)
+ if (STREQ(os_name, op->os_name))
+ return op;
+
+ os_name = GENERIC_OS_NAME;
+ goto again;
+}
+
+
+static int
+write_dkfstab(FILE *ef, qelem *q, void (*output) (FILE *, disk_fs *))
+{
+ int errors = 0;
+ disk_fs *dp;
+
+ ITER(dp, disk_fs, q)
+ if (!STREQ(dp->d_fstype, "export"))
+ (*output) (ef, dp);
+
+ return errors;
+}
+
+
+static int
+write_dkrmount(FILE *ef, qelem *q, char *hn, void (*output) (FILE *, char *, fsmount *))
+{
+ int errors = 0;
+ fsmount *fp;
+
+ ITER(fp, fsmount, q)
+ (*output) (ef, hn, fp);
+
+ return errors;
+}
+
+
+int
+write_fstab(qelem *q)
+{
+ int errors = 0;
+
+ if (fstab_pref) {
+ host *hp;
+
+ show_area_being_processed("write fstab", 4);
+ ITER(hp, host, q) {
+ if (hp->h_disk_fs || hp->h_mount) {
+ FILE *ef = pref_open(fstab_pref, hp->h_hostname, gen_hdr, hp->h_hostname);
+ if (ef) {
+ struct os_fstab_type *op = find_fstab_type(hp);
+ show_new(hp->h_hostname);
+ if (hp->h_disk_fs)
+ errors += write_dkfstab(ef, hp->h_disk_fs, op->op_fstab);
+ else
+ log("No local disk mounts on %s", hp->h_hostname);
+
+ if (hp->h_mount)
+ errors += write_dkrmount(ef, hp->h_mount, hp->h_hostname, op->op_mount);
+
+ pref_close(ef);
+ }
+ } else {
+ error("no disk mounts on %s", hp->h_hostname);
+ }
+ }
+ }
+ return errors;
+}
diff --git a/contrib/amd/hlfsd/hlfsd.8 b/contrib/amd/hlfsd/hlfsd.8
new file mode 100644
index 000000000000..ecc7e3a0d189
--- /dev/null
+++ b/contrib/amd/hlfsd/hlfsd.8
@@ -0,0 +1,310 @@
+.\"
+.\" Copyright (c) 1997-1998 Erez Zadok
+.\" Copyright (c) 1989 Jan-Simon Pendry
+.\" Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+.\" Copyright (c) 1989 The Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Jan-Simon Pendry at Imperial College, London.
+.\"
+.\" 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.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgment:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+.\"
+.\" $Id: hlfsd.8,v 1.2 1993/09/14 22:29:10 ezk Exp ezk $
+.\"
+.\" HLFSD was written at Columbia University Computer Science Department, by
+.\" Erez Zadok <ezk@cs.columbia.edu> and Alexander Dupuy <dupuy@smarts.com>
+.\" It is distributed under the same terms and conditions as AMD.
+.\"
+.TH HLFSD 8 "14 September 1993"
+.SH NAME
+hlfsd \- home-link file system daemon
+.SH SYNOPSIS
+.B hlfsd
+[
+.B \-fhnpvC
+] [
+.BI \-a " alt_dir"
+] [
+.BI \-c " cache-interval"
+] [
+.BI \-g " group"
+] [
+.BI \-i " reload-interval"
+] [
+.BI \-l " logfile"
+] [
+.BI \-o " mount-options"
+] [
+.BI \-x " log-options"
+] [
+.BI \-D " debug-options"
+] [
+.BI \-P " password-file"
+]
+[
+.I linkname
+.RI [ " subdir " ]
+]
+.SH DESCRIPTION
+.B Hlfsd
+is a daemon which implements a filesystem containing a symbolic link to
+subdirectory within a user's home directory, depending on the user
+which accessed that link. It was primarily designed to redirect
+incoming mail to users' home directories, so that it can read from
+anywhere.
+.LP
+.B Hlfsd
+operates by mounting itself as an
+.SM NFS
+server for the directory containing
+.IR linkname ,
+which defaults to
+.BR /hlfs/home .
+Lookups within that directory are handled by
+.BR hlfsd ,
+which uses the password map to determine how to resolve the lookup.
+The directory will be created if it doesn't already exist. The symbolic link will be to the accessing user's home directory, with
+.I subdir
+appended to it. If not specified,
+.I subdir
+defaults to
+.BR .hlfsdir .
+This directory will also be created if it does not already exist.
+.LP
+A SIGTERM sent to
+.B hlfsd
+will cause it to shutdown. A SIGHUP will flush the internal
+caches, and reload the password map. It will also close and
+reopen the log file, to enable the original log file to be
+removed or rotated. A SIGUSR1 will cause it to dump its internal
+table of user IDs and home directories to the file
+.BR /usr/tmp/hlfsd.dump.XXXXXX .
+.SH OPTIONS
+.TP
+.BI \-a " alt_dir"
+Alternate directory. The name of the directory to which
+the symbolic link returned by
+.B hlfsd
+will point, if it cannot access the home directory of the user. This
+defaults to
+.BR /var/hlfs .
+This directory will be created if it doesn't exist. It is expected
+that either users will read these files, or the system administrators
+will run a script to resend this "lost mail" to its owner.
+.TP
+.BI \-c " cache-interval"
+Caching interval.
+.B Hlfsd
+will cache the validity of home directories for this interval, in
+seconds. Entries which have been verified within the last
+.I cache-interval
+seconds will not be verified again, since the operation could
+be expensive, and the entries are most likely still valid.
+After the interval has expired,
+.B hlfsd
+will re-verify the validity of the user's home directory, and
+reset the cache time-counter. The default value for
+.I cache-interval
+is 300 seconds (5 minutes).
+.TP
+.B \-f
+Force fast startup. This option tells
+.B hlfsd
+to skip startup-time consistency checks such as existence of mount
+directory, alternate spool directory, symlink to be hidden under the
+mount directory, their permissions and validity.
+.TP
+.BI \-g " group"
+Set the special group HLFS_GID to
+.IR group .
+Programs such as
+.B from
+or
+.BR comsat ,
+which access the mailboxes of other users) must be setgid HLFS_GID to
+work properly. The default group is "hlfs". If no group is provided,
+and there is no group "hlfs", this feature is disabled.
+.TP
+.B \-h
+Help. Print a brief help message, and exit.
+.TP
+.BI \-i " reload-interval"
+Map-reloading interval. Each
+.I reload-interval
+seconds,
+.B hlfsd
+will reload the password map.
+.B Hlfsd
+needs the password map for the UIDs and home directory pathnames.
+.B Hlfsd
+schedules a SIGALRM to reload the password maps. A SIGHUP sent to
+.B hlfsd
+will force it to reload the maps immediately. The default
+value for
+.I reload-interval
+is 900 seconds (15 minutes.)
+.TP
+.BI \-l " logfile"
+Specify a log file to which
+.B hlfsd
+will record events. If
+.I logfile
+is the string
+.B syslog
+then the log messages will be sent to the system log daemon by
+.IR syslog (3),
+using the LOG_DAEMON facility.
+This is also the default.
+.TP
+.B \-n
+No verify.
+.B Hlfsd
+will not verify the validity of the symbolic link it will be
+returning, or that the user's home directory contains
+sufficient disk-space for spooling. This can speed up
+.B hlfsd
+at the cost of possibly returning symbolic links to home
+directories which are not currently accessible or are full.
+By default,
+.B hlfsd
+validates the symbolic-link in the background.
+The
+.B \-n
+option overrides the meaning of the
+.B \-c
+option, since no caching is necessary.
+.TP
+.BI \-o " mount-options"
+Mount options. Mount options which
+.B hlfsd
+will use to mount itself on top of
+.I dirname.
+By default,
+.IR mount-options
+is set to "ro". If the system supports symbolic-link caching, default
+options are set to "ro,nocache".
+.TP
+.B \-p
+Print PID.
+Outputs the process-id of
+.B hlfsd
+to standard output where it can be saved into a file.
+.TP
+.B \-v
+Version. Displays version information to standard error.
+.TP
+.BI \-x " log-options"
+Specify run-time logging options. The options are a comma separated
+list chosen from: fatal, error, user, warn, info, map, stats, all.
+.TP
+.BI \-C
+Force
+.B hlfsd
+to run on systems that cannot turn off the NFS attribute-cache. Use of
+this option on those systems is discouraged, as it may result in loss
+or misdelivery of mail. The option is ignored on systems that can turn
+off the attribute-cache.
+.TP
+.BI \-D " log-options"
+Select from a variety of debugging options. Prefixing an
+option with the string
+.B no
+reverses the effect of that option. Options are cumulative.
+The most useful option is
+.BR all .
+Since this option is only used for debugging other options are not
+documented here. A fuller description is available in the program
+source. A SIGUSR1 sent to
+.B hlfsd
+will cause it to dump its internal password map to the file
+.BR /usr/tmp/hlfsd.dump.XXXXXX .
+.TP
+.BI \-P " password-file"
+Read the user-name, user-id, and home directory information from the file
+.I password-file.
+Normally,
+.B hlfsd
+will use
+.IR getpwent (3)
+to read the password database. This option allows you to override the
+default database, and is useful if you want to map users' mail files to a
+directory other than their home directory. Only the username, uid, and
+home-directory fields of the file
+.I password-file
+are read and checked. All other fields are ignored. The file
+.I password-file
+must otherwise be compliant with Unix System 7 colon-delimited format
+.IR passwd (4).
+.SH FILES
+.PD 0
+.TP 5
+.B /hlfs
+directory under which
+.B hlfsd
+mounts itself and manages the symbolic link
+.BR home .
+.TP 5
+.B .hlfsdir
+default sub-directory in the user's home directory, to which the
+.B home
+symbolic link returned by
+.B hlfsd
+points.
+.TP 5
+.B /var/hlfs
+directory to which
+.B home
+symbolic link returned by
+.B hlfsd
+points if it is unable to verify the that
+user's home directory is accessible.
+.SH "SEE ALSO"
+.BR amd (8),
+.BR automount (8),
+.BR cron(8),
+.BR getgrent (3),
+.BR getpwent (3),
+.BR mail(1),
+.BR mnttab (4),
+.BR mount (8),
+.BR mtab (5),
+.BR passwd (4),
+.BR sendmail (8),
+.BR umount (8).
+.LP
+.IR "HLFSD: Delivering Email to Your $HOME" ,
+in
+.IR "Proc. LISA-VII, The 7th Usenix System Administration Conference" ,
+November 1993.
+.SH AUTHORS
+Erez Zadok <ezk@cs.columbia.edu>, Computer Science Department,
+Columbia University, New York City, New York, USA, and
+Alexander Dupuy <dupuy@smarts.com>, System Management ARTS,
+White Plains, New York, USA.
diff --git a/contrib/amd/hlfsd/hlfsd.c b/contrib/amd/hlfsd/hlfsd.c
new file mode 100644
index 000000000000..388c65b2189d
--- /dev/null
+++ b/contrib/amd/hlfsd/hlfsd.c
@@ -0,0 +1,953 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: hlfsd.c,v 1.11 1994/11/06 00:19:52 ezk Exp ezk $
+ *
+ * HLFSD was written at Columbia University Computer Science Department, by
+ * Erez Zadok <ezk@cs.columbia.edu> and Alexander Dupuy <dupuy@cs.columbia.edu>
+ * It is being distributed under the same terms and conditions as amd does.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <hlfsd.h>
+
+/*
+ * STATIC VARIABLES:
+ */
+static RETSIGTYPE proceed(int);
+static RETSIGTYPE reaper(int);
+static RETSIGTYPE reload(int);
+static char *hlfs_group = DEFAULT_HLFS_GROUP;
+static char default_dir_name[] = DEFAULT_DIRNAME;
+static char *dir_name = default_dir_name;
+static int printpid = 0;
+static int stoplight = 0;
+static void hlfsd_init(void);
+static void usage(void);
+
+static struct itimerval reloadinterval = {
+ {DEFAULT_INTERVAL, 0},
+ {DEFAULT_INTERVAL, 0}
+};
+
+/*
+ * default mount options.
+ */
+static char default_mntopts[] = "ro,noac";
+
+/*
+ * GLOBALS:
+ */
+SVCXPRT *nfsxprt;
+char *alt_spooldir = ALT_SPOOLDIR;
+char *home_subdir = HOME_SUBDIR;
+char *logfile = DEFAULT_LOGFILE;
+char *passwdfile = NULL; /* alternate passwd file to use */
+char *progname;
+char *slinkname = 0;
+char hostname[MAXHOSTNAMELEN] = "localhost";
+int cache_interval = DEFAULT_CACHE_INTERVAL;
+int foreground = 1; /* This is the top-level server */
+gid_t hlfs_gid = (gid_t) INVALIDID;
+int masterpid = 0;
+int noverify = 0;
+int orig_umask;
+int serverpid = 0;
+nfstime startup;
+pid_t mypid; /* Current process id */
+serv_state amd_state;
+u_short nfs_port;
+
+/* symbol must be available always */
+#ifdef MOUNT_TABLE_ON_FILE
+char *mnttab_file_name = MNTTAB_FILE_NAME;
+#else /* not MOUNT_TABLE_ON_FILE */
+char *mnttab_file_name = NULL;
+#endif /* not MOUNT_TABLE_ON_FILE */
+
+#ifdef DEBUG
+int debug_flags = 0;
+#endif /* DEBUG */
+
+/* forward declarations */
+void hlfsd_going_down(int rc);
+
+
+static void
+usage(void)
+{
+ fprintf(stderr,
+ "Usage: %s [-Cfhnpv] [-a altdir] [-c cache-interval] [-g group]\n",
+ progname);
+ fprintf(stderr, "\t[-i interval] [-l logfile] [-o mntopts] [-P passwdfile]\n");
+ show_opts('x', xlog_opt);
+#ifdef DEBUG
+ show_opts('D', dbg_opt);
+#endif /* DEBUG */
+ fprintf(stderr, "\t[dir_name [subdir]]\n");
+ exit(2);
+}
+
+
+int
+main(int argc, char *argv[])
+{
+ char *dot;
+ char *mntopts = (char *) NULL;
+ char hostpid_fs[MAXHOSTNAMELEN + 1 + 16]; /* room for ":(pid###)" */
+ char progpid_fs[PROGNAMESZ + 1 + 11]; /* room for ":pid" */
+ char preopts[128];
+ int forcecache = 0;
+ int forcefast = 0;
+ int genflags = 0;
+ int opt, ret;
+ int opterrs = 0;
+ int retry;
+ int soNFS; /* NFS socket */
+ int s = -99;
+ mntent_t mnt;
+ nfs_args_t nfs_args;
+ am_nfs_handle_t anh;
+ struct dirent *direntry;
+ struct group *grp;
+ struct stat stmodes;
+ DIR *mountdir;
+ MTYPE_TYPE type = MOUNT_TYPE_NFS;
+
+#ifdef HAVE_SIGACTION
+ struct sigaction sa;
+#endif /* not HAVE_SIGACTION */
+
+#ifndef HAVE_TRANSPORT_TYPE_TLI
+ struct sockaddr_in localsocket;
+#endif /* not HAVE_TRANSPORT_TYPE_TLI */
+
+
+ /* get program name and truncate so we don't overflow progpid_fs */
+
+ if ((progname = strrchr(argv[0], '/')) != NULL)
+ progname++;
+ else
+ progname = argv[0];
+ if ((int) strlen(progname) > PROGNAMESZ) /* truncate to reasonable size */
+ progname[PROGNAMESZ] = '\0';
+
+ while ((opt = getopt(argc, argv, "a:c:CD:fg:hi:l:no:pP:x:v")) != EOF)
+ switch (opt) {
+
+ case 'a':
+ if (!optarg || optarg[0] != '/') {
+ printf("%s: invalid directory for -a: %s\n",
+ progname, optarg);
+ exit(3);
+ }
+ alt_spooldir = optarg;
+ break;
+
+ case 'c':
+ if (!atoi(optarg)) {
+ printf("%s: invalid interval for -c: %s\n",
+ progname, optarg);
+ exit(3);
+ }
+ cache_interval = atoi(optarg);
+ break;
+
+ case 'C':
+ forcecache++;
+ break;
+
+ case 'f':
+ forcefast++;
+ break;
+
+ case 'g':
+ hlfs_group = optarg;
+ break;
+
+ case 'i':
+ if (!atoi(optarg)) {
+ printf("%s: invalid interval for -i: %s\n",
+ progname, optarg);
+ exit(3);
+ }
+ reloadinterval.it_interval.tv_sec = atoi(optarg);
+ reloadinterval.it_value.tv_sec = atoi(optarg);
+ break;
+
+ case 'l':
+ logfile = optarg;
+ break;
+
+ case 'n':
+ noverify++;
+ break;
+
+ case 'o':
+ mntopts = optarg;
+ break;
+
+ case 'p':
+ printpid++;
+ break;
+
+ case 'P':
+ passwdfile = optarg;
+ break;
+
+ case 'v':
+ fprintf(stderr, "%s\n", HLFSD_VERSION);
+ exit(0);
+
+ case 'x':
+ opterrs += switch_option(optarg);
+ break;
+
+ case 'D':
+#ifdef DEBUG
+ opterrs += debug_option(optarg);
+#else /* not DEBUG */
+ fprintf(stderr, "%s: not compiled with DEBUG -- sorry.\n", progname);
+#endif /* not DEBUG */
+ break;
+
+ case 'h':
+ case '?':
+ opterrs++;
+ }
+
+ /* set some default debugging options */
+ if (xlog_level_init == ~0)
+ switch_option("");
+ /* need my pid before any dlog/plog */
+ mypid = getpid();
+#ifdef DEBUG
+ switch_option("debug");
+#endif /* DEBUG */
+
+/*
+ * Terminate if did not ask to forcecache (-C) and hlfsd would not be able
+ * to set the minimum cache intervals.
+ */
+#if !defined(MNT2_NFS_OPT_ACREGMIN) && !defined(MNT2_NFS_OPT_NOAC) && !defined(HAVE_FIELD_NFS_ARGS_T_ACREGMIN)
+ if (!forcecache) {
+ fprintf(stderr, "%s: will not be able to turn off attribute caches.\n", progname);
+ exit(1);
+ }
+#endif /* !defined(MNT2_NFS_OPT_ACREGMIN) && !defined(MNT2_NFS_OPT_NOAC) && !defined(HAVE_FIELD_NFS_ARGS_T_ACREGMIN) */
+
+
+ switch (argc - optind) {
+ case 2:
+ home_subdir = argv[optind + 1];
+ case 1:
+ dir_name = argv[optind];
+ case 0:
+ break;
+ default:
+ opterrs++;
+ }
+
+ if (opterrs)
+ usage();
+
+ /* ensure that only root can run hlfsd */
+ if (geteuid()) {
+ fprintf(stderr, "hlfsd can only be run as root\n");
+ exit(1);
+ }
+ setbuf(stdout, (char *) NULL);
+ umask(0);
+
+ /* find gid for hlfs_group */
+ if ((grp = getgrnam(hlfs_group)) == (struct group *) NULL) {
+ fprintf(stderr, "%s: cannot get gid for group \"%s\".\n",
+ progname, hlfs_group);
+ } else {
+ hlfs_gid = grp->gr_gid;
+ }
+
+ /* get hostname for logging and open log before we reset umask */
+ gethostname(hostname, MAXHOSTNAMELEN);
+ if ((dot = strchr(hostname, '.')) != NULL)
+ *dot = '\0';
+ if (logfile)
+ switch_to_logfile(logfile);
+ orig_umask = umask(0);
+
+#if defined(DEBUG) && !defined(MOUNT_TABLE_ON_FILE)
+ if (debug_flags & D_MTAB)
+ dlog("-D mtab option ignored");
+#endif /* defined(DEBUG) && !defined(MOUNT_TABLE_ON_FILE) */
+
+ /* avoid hanging on other NFS servers if started elsewhere */
+ if (chdir("/") < 0)
+ fatal("cannot chdir to /: %m");
+
+ if (geteuid() != 0)
+ fatal("must be root to mount filesystems");
+
+ /*
+ * dir_name must match "^(/.*)/([^/]+)$", and is split at last '/' with
+ * slinkname = `basename $dir_name` - requires dir_name be writable
+ */
+
+ if (dir_name[0] != '/'
+ || ((slinkname = strrchr(dir_name, '/')), *slinkname++ = '\0',
+ (dir_name[0] == '\0' || slinkname[0] == '\0'))) {
+ if (slinkname)
+ *--slinkname = '/';
+ printf("%s: invalid mount directory/link %s\n",
+ progname, dir_name);
+ exit(3);
+ }
+
+ clock_valid = 0; /* invalidate logging clock */
+
+ if (!forcefast) {
+ /* make sure mount point exists and is at least mode 555 */
+ if (stat(dir_name, &stmodes) < 0)
+ if (errno != ENOENT || mkdirs(dir_name, 0555) < 0
+ || stat(dir_name, &stmodes) < 0)
+ fatalerror(dir_name);
+
+ if ((stmodes.st_mode & 0555) != 0555) {
+ fprintf(stderr, "%s: directory %s not read/executable\n",
+ progname, dir_name);
+ plog(XLOG_WARNING, "directory %s not read/executable",
+ dir_name);
+ }
+
+ /* warn if extraneous stuff will be hidden by mount */
+ if ((mountdir = opendir(dir_name)) == NULL)
+ fatalerror(dir_name);
+
+ while ((direntry = readdir(mountdir)) != NULL) {
+ if (!NSTREQ(".", direntry->d_name, NAMLEN(direntry)) &&
+ !NSTREQ("..", direntry->d_name, NAMLEN(direntry)) &&
+ !NSTREQ(slinkname, direntry->d_name, NAMLEN(direntry)))
+ break;
+ }
+
+ if (direntry != NULL) {
+ fprintf(stderr, "%s: %s/%s will be hidden by mount\n",
+ progname, dir_name, direntry->d_name);
+ plog(XLOG_WARNING, "%s/%s will be hidden by mount\n",
+ dir_name, direntry->d_name);
+ }
+ closedir(mountdir);
+
+ /* make sure alternate spool dir exists */
+ if ((errno = mkdirs(alt_spooldir, OPEN_SPOOLMODE))) {
+ fprintf(stderr, "%s: cannot create alternate dir ",
+ progname);
+ perror(alt_spooldir);
+ plog(XLOG_ERROR, "cannot create alternate dir %s: %m",
+ alt_spooldir);
+ }
+ chmod(alt_spooldir, OPEN_SPOOLMODE);
+
+ /* create failsafe link to alternate spool directory */
+ slinkname[-1] = '/'; /* unsplit dir_name to include link */
+ if (lstat(dir_name, &stmodes) == 0 &&
+ (stmodes.st_mode & S_IFMT) != S_IFLNK) {
+ fprintf(stderr, "%s: failsafe %s not a symlink\n",
+ progname, dir_name);
+ plog(XLOG_WARNING, "failsafe %s not a symlink\n",
+ dir_name);
+ } else {
+ unlink(dir_name);
+
+ if (symlink(alt_spooldir, dir_name) < 0) {
+ fprintf(stderr,
+ "%s: cannot create failsafe symlink %s -> ",
+ progname, dir_name);
+ perror(alt_spooldir);
+ plog(XLOG_WARNING,
+ "cannot create failsafe symlink %s -> %s: %m",
+ dir_name, alt_spooldir);
+ }
+ }
+
+ slinkname[-1] = '\0'; /* resplit dir_name */
+ } /* end of "if (!forcefast) {" */
+
+ /*
+ * Register hlfsd as an nfs service with the portmapper.
+ */
+#ifdef HAVE_TRANSPORT_TYPE_TLI
+ ret = create_nfs_service(&soNFS, &nfs_port, &nfsxprt, nfs_program_2);
+#else /* not HAVE_TRANSPORT_TYPE_TLI */
+ ret = create_nfs_service(&soNFS, &nfs_port, &nfsxprt, nfs_program_2);
+#endif /* not HAVE_TRANSPORT_TYPE_TLI */
+ if (ret != 0)
+ fatal("cannot create NFS service");
+
+#ifdef HAVE_SIGACTION
+ sa.sa_handler = proceed;
+ sa.sa_flags = 0;
+ sigemptyset(&(sa.sa_mask));
+ sigaddset(&(sa.sa_mask), SIGUSR2);
+ sigaction(SIGUSR2, &sa, NULL);
+#else /* not HAVE_SIGACTION */
+ signal(SIGUSR2, proceed);
+#endif /* not HAVE_SIGACTION */
+
+ plog(XLOG_INFO, "Initializing hlfsd...");
+ hlfsd_init(); /* start up child (forking) to run svc_run */
+
+#ifdef HAVE_SIGACTION
+ sa.sa_handler = reaper;
+ sa.sa_flags = 0;
+ sigemptyset(&(sa.sa_mask));
+ sigaddset(&(sa.sa_mask), SIGCHLD);
+ sigaction(SIGCHLD, &sa, NULL);
+#else /* not HAVE_SIGACTION */
+ signal(SIGCHLD, reaper);
+#endif /* not HAVE_SIGACTION */
+
+#ifdef DEBUG
+ /*
+ * In the parent, if -D nodaemon (or -D daemon) , we don't need to
+ * set this signal handler.
+ */
+ amuDebug(D_DAEMON) {
+#endif /* DEBUG */
+ /* XXX: port to use pure svr4 signals */
+ s = -99;
+ while (stoplight != SIGUSR2) {
+ plog(XLOG_INFO, "parent waits for child to setup (stoplight=%d)", stoplight);
+ s = sigpause(0); /* wait for child to set up */
+ sleep(1);
+ }
+#ifdef DEBUG
+ }
+#endif /* DEBUG */
+
+ /*
+ * setup options to mount table (/etc/{mtab,mnttab}) entry
+ */
+ sprintf(hostpid_fs, "%s:(pid%d)", hostname, masterpid);
+ memset((char *) &mnt, 0, sizeof(mnt));
+ mnt.mnt_dir = dir_name; /* i.e., "/mail" */
+ mnt.mnt_fsname = hostpid_fs;
+ if (mntopts) {
+ mnt.mnt_opts = mntopts;
+ } else {
+ strcpy(preopts, default_mntopts);
+ /*
+ * Turn off all kinds of attribute and symlink caches as
+ * much as possible. Also make sure that mount does not
+ * show up to df.
+ */
+#ifdef MNTTAB_OPT_INTR
+ strcat(preopts, ",");
+ strcat(preopts, MNTTAB_OPT_INTR);
+#endif /* MNTTAB_OPT_INTR */
+#ifdef MNTTAB_OPT_IGNORE
+ strcat(preopts, ",");
+ strcat(preopts, MNTTAB_OPT_IGNORE);
+#endif /* MNTTAB_OPT_IGNORE */
+#ifdef MNT2_GEN_OPT_CACHE
+ strcat(preopts, ",nocache");
+#endif /* MNT2_GEN_OPT_CACHE */
+#ifdef MNT2_NFS_OPT_SYMTTL
+ strcat(preopts, ",symttl=0");
+#endif /* MNT2_NFS_OPT_SYMTTL */
+ mnt.mnt_opts = preopts;
+ }
+
+ /*
+ * Make sure that amd's top-level NFS mounts are hidden by default
+ * from df.
+ * If they don't appear to support the either the "ignore" mnttab
+ * option entry, or the "auto" one, set the mount type to "nfs".
+ */
+ mnt.mnt_type = HIDE_MOUNT_TYPE;
+ /* some systems don't have a mount type, but a mount flag */
+
+#ifndef HAVE_TRANSPORT_TYPE_TLI
+ amu_get_myaddress(&localsocket.sin_addr);
+ localsocket.sin_family = AF_INET;
+ localsocket.sin_port = htons(nfsxprt->xp_port);
+#endif /* not HAVE_TRANSPORT_TYPE_TLI */
+
+ /*
+ * Update hostname field.
+ * Make some name prog:pid (i.e., hlfsd:174) for hostname
+ */
+ sprintf(progpid_fs, "%s:%d", progname, masterpid);
+
+ /* Most kernels have a name length restriction. */
+ if ((int) strlen(progpid_fs) >= (int) MAXHOSTNAMELEN)
+ strcpy(progpid_fs + MAXHOSTNAMELEN - 3, "..");
+
+ genflags = compute_mount_flags(&mnt);
+
+ retry = hasmntval(&mnt, MNTTAB_OPT_RETRY);
+ if (retry <= 0)
+ retry = 1; /* XXX */
+
+ memmove(&anh.v2.fhs_fh, root_fhp, sizeof(*root_fhp));
+#ifdef HAVE_TRANSPORT_TYPE_TLI
+ compute_nfs_args(&nfs_args,
+ &mnt,
+ genflags,
+ nfsncp,
+ NULL, /* remote host IP addr is set below */
+ NFS_VERSION, /* version 2 */
+ "udp", /* XXX: shouldn't this be "udp"? */
+ &anh,
+ progpid_fs, /* host name for kernel */
+ hostpid_fs); /* filesystem name for kernel */
+ /*
+ * IMPORTANT: set the correct IP address AFTERWARDS. It cannot
+ * be done using the normal mechanism of compute_nfs_args(), because
+ * that one will allocate a new address and use NFS_SA_DREF() to copy
+ * parts to it, while assuming that the ip_addr passed is always
+ * a "struct sockaddr_in". That assumption is incorrect on TLI systems,
+ * because they define a special macro HOST_SELF which is DIFFERENT
+ * than localhost (127.0.0.1)!
+ */
+ nfs_args.addr = &nfsxprt->xp_ltaddr;
+#else /* not HAVE_TRANSPORT_TYPE_TLI */
+ compute_nfs_args(&nfs_args,
+ &mnt,
+ genflags,
+ &localsocket,
+ NFS_VERSION, /* version 2 */
+ "udp", /* XXX: shouldn't this be "udp"? */
+ &anh,
+ progpid_fs, /* host name for kernel */
+ hostpid_fs); /* filesystem name for kernel */
+#endif /* not HAVE_TRANSPORT_TYPE_TLI */
+
+ /*************************************************************************
+ * NOTE: while compute_nfs_args() works ok for regular NFS mounts *
+ * the toplvl one is not, and so some options must be corrected by hand *
+ * more carefully, *after* compute_nfs_args() runs. *
+ *************************************************************************/
+ compute_automounter_nfs_args(&nfs_args, &mnt);
+
+ clock_valid = 0; /* invalidate logging clock */
+
+/*
+ * The following code could be cleverly ifdef-ed, but I duplicated the
+ * mount_fs call three times for simplicity and readability.
+ */
+#ifdef DEBUG
+/*
+ * For some reason, this mount may have to be done in the background, if I am
+ * using -D nodebug. I suspect that the actual act of mounting requires
+ * calling to hlfsd itself to invoke one or more of its nfs calls, to stat
+ * /mail. That means that even if you say -D nodaemon, at least the mount
+ * of hlfsd itself on top of /mail will be done in the background.
+ * The other alternative I have is to run svc_run, but set a special
+ * signal handler to perform the mount in N seconds via some alarm.
+ * -Erez Zadok.
+ */
+ if (debug_flags & D_DAEMON) { /* asked for -D daemon */
+ plog(XLOG_INFO, "parent NFS mounting hlfsd service points");
+ if (mount_fs(&mnt, genflags, (caddr_t) &nfs_args, retry, type, 0, NULL, mnttab_file_name) < 0)
+ fatal("nfsmount: %m");
+ } else { /* asked for -D nodaemon */
+ if (fork() == 0) { /* child runs mount */
+ mypid = getpid();
+ foreground = 0;
+ plog(XLOG_INFO, "child NFS mounting hlfsd service points");
+ if (mount_fs(&mnt, genflags, (caddr_t) &nfs_args, retry, type, 0, NULL, mnttab_file_name) < 0) {
+ fatal("nfsmount: %m");
+ }
+ exit(0); /* all went well */
+ } else { /* fork failed or parent running */
+ plog(XLOG_INFO, "parent waiting 1sec for mount...");
+ }
+ }
+#else /* not DEBUG */
+ plog(XLOG_INFO, "normal NFS mounting hlfsd service points");
+ if (mount_fs(&mnt, genflags, (caddr_t) &nfs_args, retry, type, 2, "udp", mnttab_file_name) < 0)
+ fatal("nfsmount: %m");
+#endif /* not DEBUG */
+
+#ifdef HAVE_TRANSPORT_TYPE_TLI
+ /*
+ * XXX: this free_knetconfig() was not done for hlfsd before,
+ * and apparently there was a reason for it, but why? -Erez
+ */
+ free_knetconfig(nfs_args.knconf);
+ /*
+ * local automounter mounts do not allocate a special address, so
+ * no need to XFREE(nfs_args.addr) under TLI.
+ */
+#endif /* HAVE_TRANSPORT_TYPE_TLI */
+
+ if (printpid)
+ printf("%d\n", masterpid);
+
+ plog(XLOG_INFO, "hlfsd ready to serve");
+#ifdef DEBUG
+ /*
+ * If asked not to fork a daemon (-D nodaemon), then hlfsd_init()
+ * will not run svc_run. We must start svc_run here.
+ */
+ dlog("starting no-daemon debugging svc_run");
+ amuDebugNo(D_DAEMON)
+ svc_run();
+#endif /* DEBUG */
+
+ cleanup(0); /* should never happen here */
+ return (0); /* everything went fine? */
+}
+
+
+static void
+hlfsd_init(void)
+{
+ int child = 0;
+#ifdef HAVE_SIGACTION
+ struct sigaction sa;
+#endif /* HAVE_SIGACTION */
+
+ clock_valid = 0; /* invalidate logging clock */
+
+ /*
+ * Initialize file handles.
+ */
+ plog(XLOG_INFO, "initializing hlfsd file handles");
+ hlfsd_init_filehandles();
+
+#ifdef DEBUG
+ /*
+ * If -D daemon then we must fork.
+ */
+ amuDebug(D_DAEMON)
+#endif /* DEBUG */
+ child = fork();
+
+ if (child < 0)
+ fatal("fork: %m");
+
+ if (child != 0) { /* parent process - save child pid */
+ masterpid = child;
+ mypid = getpid(); /* for logging routines */
+ return;
+ }
+
+ /*
+ * CHILD CODE:
+ * initialize server
+ */
+
+ plog(XLOG_INFO, "initializing home directory database");
+ plt_init(); /* initialize database */
+ plog(XLOG_INFO, "home directory database initialized");
+
+ masterpid = serverpid = mypid = getpid(); /* for logging routines */
+
+ /*
+ * SIGALRM/SIGHUP: reload password database if timer expired
+ * or user sent HUP signal.
+ */
+#ifdef HAVE_SIGACTION
+ sa.sa_handler = reload;
+ sa.sa_flags = 0;
+ sigemptyset(&(sa.sa_mask));
+ sigaddset(&(sa.sa_mask), SIGALRM);
+ sigaddset(&(sa.sa_mask), SIGHUP);
+ sigaction(SIGALRM, &sa, NULL);
+ sigaction(SIGHUP, &sa, NULL);
+#else /* not HAVE_SIGACTION */
+ signal(SIGALRM, reload);
+ signal(SIGHUP, reload);
+#endif /* not HAVE_SIGACTION */
+
+ /*
+ * SIGTERM: cleanup and exit.
+ */
+#ifdef HAVE_SIGACTION
+ sa.sa_handler = cleanup;
+ sa.sa_flags = 0;
+ sigemptyset(&(sa.sa_mask));
+ sigaddset(&(sa.sa_mask), SIGTERM);
+ sigaction(SIGTERM, &sa, NULL);
+#else /* not HAVE_SIGACTION */
+ signal(SIGTERM, cleanup);
+#endif /* not HAVE_SIGACTION */
+
+ /*
+ * SIGCHLD: interlock sycronization and testing
+ */
+#ifdef HAVE_SIGACTION
+ sa.sa_handler = interlock;
+ sa.sa_flags = 0;
+ sigemptyset(&(sa.sa_mask));
+ sigaddset(&(sa.sa_mask), SIGCHLD);
+ sigaction(SIGCHLD, &sa, NULL);
+#else /* not HAVE_SIGACTION */
+ signal(SIGCHLD, interlock);
+#endif /* not HAVE_SIGACTION */
+
+ /*
+ * SIGUSR1: dump internal hlfsd maps/cache to file
+ */
+#ifdef HAVE_SIGACTION
+# if defined(DEBUG) || defined(DEBUG_PRINT)
+ sa.sa_handler = plt_print;
+# else /* not defined(DEBUG) || defined(DEBUG_PRINT) */
+ sa.sa_handler = SIG_IGN;
+# endif /* not defined(DEBUG) || defined(DEBUG_PRINT) */
+ sa.sa_flags = 0;
+ sigemptyset(&(sa.sa_mask));
+ sigaddset(&(sa.sa_mask), SIGUSR1);
+ sigaction(SIGUSR1, &sa, NULL);
+#else /* not HAVE_SIGACTION */
+# if defined(DEBUG) || defined(DEBUG_PRINT)
+ signal(SIGUSR1, plt_print);
+# else /* not defined(DEBUG) || defined(DEBUG_PRINT) */
+ signal(SIGUSR1, SIG_IGN);
+# endif /* not defined(DEBUG) || defined(DEBUG_PRINT) */
+#endif /* not HAVE_SIGACTION */
+
+ if (setitimer(ITIMER_REAL, &reloadinterval, (struct itimerval *) 0) < 0)
+ fatal("setitimer: %m");
+
+ gettimeofday((struct timeval *) &startup, (struct timezone *) 0);
+
+#ifdef DEBUG
+ /*
+ * If -D daemon, then start serving here in the child,
+ * and the parent will exit. But if -D nodaemon, then
+ * skip this code and make sure svc_run is entered elsewhere.
+ */
+ amuDebug(D_DAEMON) {
+#endif /* DEBUG */
+
+ /*
+ * Dissociate from the controlling terminal
+ */
+ amu_release_controlling_tty();
+
+ /*
+ * signal parent we are ready. parent should
+ * mount(2) and die.
+ */
+ if (kill(getppid(), SIGUSR2) < 0)
+ fatal("kill: %m");
+ plog(XLOG_INFO, "starting svc_run");
+ svc_run();
+ cleanup(0); /* should never happen, just in case */
+#ifdef DEBUG
+ } /* end of code that runs iff hlfsd daemonizes */
+#endif /* DEBUG */
+
+}
+
+
+static RETSIGTYPE
+proceed(int signum)
+{
+ stoplight = signum;
+}
+
+
+static RETSIGTYPE
+reload(int signum)
+{
+ int child;
+ int status;
+
+ clock_valid = 0; /* invalidate logging clock */
+
+ if (getpid() != masterpid)
+ return;
+
+ /*
+ * If received a SIGHUP, close and reopen the log file (so that it
+ * can be rotated)
+ */
+ if (signum == SIGHUP && logfile)
+ switch_to_logfile(logfile);
+
+ /*
+ * parent performs the reload, while the child continues to serve
+ * clients accessing the home dir link.
+ */
+ if ((child = fork()) > 0) {
+ serverpid = child; /* parent runs here */
+ mypid = getpid();
+
+ plt_init();
+
+ if (kill(child, SIGKILL) < 0) {
+ plog(XLOG_ERROR, "kill child: %m");
+ } else { /* wait for child to die before continue */
+ if (wait(&status) != child) {
+ /*
+ * I took out this line because it generates annoying output. It
+ * indicates a very small bug in hlfsd which is totally harmless.
+ * It causes hlfsd to work a bit harder than it should.
+ * Nevertheless, I intend on fixing it in a future release.
+ * -Erez Zadok <ezk@cs.columbia.edu>
+ */
+ /* plog(XLOG_ERROR, "unknown child"); */
+ }
+ }
+ serverpid = masterpid;
+ } else if (child < 0) {
+ plog(XLOG_ERROR, "unable to fork: %m");
+ } else {
+ /* let child handle requests while we reload */
+ serverpid = getpid();
+ mypid = getpid();
+ }
+}
+
+
+RETSIGTYPE
+cleanup(int signum)
+{
+ struct stat stbuf;
+ int umount_result;
+
+ clock_valid = 0; /* invalidate logging clock */
+
+#ifdef DEBUG
+ amuDebug(D_DAEMON)
+#endif /* DEBUG */
+ if (getpid() != masterpid)
+ return;
+
+#ifdef DEBUG
+ amuDebug(D_DAEMON)
+#endif /* DEBUG */
+ if (fork() != 0) {
+ masterpid = 0;
+ mypid = getpid();
+ return;
+ }
+ mypid = getpid();
+
+ for (;;) {
+ while ((umount_result = UMOUNT_FS(dir_name, mnttab_file_name)) == EBUSY) {
+#ifdef DEBUG
+ dlog("cleanup(): umount delaying for 10 seconds");
+#endif /* DEBUG */
+ sleep(10);
+ }
+ if (stat(dir_name, &stbuf) == 0 && stbuf.st_ino == ROOTID) {
+ plog(XLOG_ERROR, "unable to unmount %s", dir_name);
+ plog(XLOG_ERROR, "suspending, unmount before terminating");
+ kill(mypid, SIGSTOP);
+ continue; /* retry unmount */
+ }
+ break;
+ }
+
+#ifdef DEBUG
+ dlog("cleanup(): killing processes and terminating");
+ amuDebug(D_DAEMON)
+#endif /* DEBUG */
+ kill(masterpid, SIGKILL);
+
+#ifdef DEBUG
+ amuDebug(D_DAEMON)
+#endif /* DEBUG */
+ kill(serverpid, SIGKILL);
+
+ plog(XLOG_INFO, "hlfsd terminating with status 0\n");
+ exit(0);
+}
+
+
+static RETSIGTYPE
+reaper(int signum)
+{
+ int result;
+
+ if (wait(&result) == masterpid) {
+ exit(4);
+ }
+}
+
+
+void
+hlfsd_going_down(int rc)
+{
+ int mypid = getpid();
+
+ if (mypid == masterpid)
+ cleanup(0);
+ else if (mypid == serverpid)
+ kill(masterpid, SIGTERM);
+
+ exit(rc);
+}
+
+
+void
+fatal(char *mess)
+{
+ if (logfile && !STREQ(logfile, "stderr")) {
+ char lessmess[128];
+ int messlen;
+
+ messlen = strlen(mess);
+
+ if (!STREQ(&mess[messlen + 1 - sizeof(ERRM)], ERRM))
+ fprintf(stderr, "%s: %s\n", progname, mess);
+ else {
+ strcpy(lessmess, mess);
+ lessmess[messlen - 4] = '\0';
+
+ if (errno < sys_nerr)
+ fprintf(stderr, "%s: %s: %s\n", progname,
+ lessmess, sys_errlist[errno]);
+ else
+ fprintf(stderr, "%s: %s: Error %d\n",
+ progname, lessmess, errno);
+ }
+ }
+ plog(XLOG_FATAL, mess);
+
+ hlfsd_going_down(1);
+}
diff --git a/contrib/amd/hlfsd/hlfsd.h b/contrib/amd/hlfsd/hlfsd.h
new file mode 100644
index 000000000000..dec5d918bcc0
--- /dev/null
+++ b/contrib/amd/hlfsd/hlfsd.h
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: hlfs.h,v 1.9 1993/09/13 15:11:00 ezk Exp $
+ *
+ * HLFSD was written at Columbia University Computer Science Department, by
+ * Erez Zadok <ezk@cs.columbia.edu> and Alexander Dupuy <dupuy@cs.columbia.edu>
+ * It is being distributed under the same terms and conditions as amd does.
+ */
+
+#ifndef _HLFSD_HLFS_H
+#define _HLFSD_HLFS_H
+
+/*
+ * MACROS AND CONSTANTS:
+ */
+
+#define HLFSD_VERSION "hlfsd 1.1 (March 4, 1997-1998)"
+#define PERS_SPOOLMODE 0755
+#define OPEN_SPOOLMODE 01777
+#define DOTSTRING "."
+
+/*
+ * ROOTID and SLINKID are the fixed "faked" node IDs (inodes) for
+ * the '.' (also '..') and the one symlink within the hlfs.
+ * They must always be unique, and should never match what a UID
+ * could be.
+ * They used to be -1 and -2, respectively.
+ *
+ * I used to cast these to (uid_t) but it failed to compile
+ * with /opt/SUNWspro/bin/cc because uid_t is long, while struct fattr's
+ * uid field is u_int. Then it failed to compile on some linux systems
+ * which define uid_t to be unsigned short, so I used the lowest common
+ * size which is unsigned short.
+ */
+#ifdef EXPERIMENTAL_UID_SIZE
+#define UID_SHIFT 30
+# define ROOTID ((1 << UID_SHIFT) - 1)
+# define SLINKID ((1 << UID_SHIFT) - 2)
+# define INVALIDID ((1 << UID_SHIFT) - 3)
+#else /* not EXPERIMENTAL_UID_SIZE */
+/*
+ * XXX: this will cause problems to systems with UIDs greater than
+ * MAX_UNSIGNED_SHORT-3.
+ */
+# define ROOTID (((unsigned short) ~0) - 1)
+# define SLINKID (((unsigned short) ~0) - 2)
+# define INVALIDID (((unsigned short) ~0) - 3)
+#endif /* not EXPERIMENTAL_UID_SIZE */
+
+
+#define DOTCOOKIE 1
+#define DOTDOTCOOKIE 2
+#define SLINKCOOKIE 3
+
+#define ALT_SPOOLDIR "/var/hlfs" /* symlink to use if others fail */
+#define HOME_SUBDIR ".hlfsdir" /* dirname in user's home dir */
+#define DEFAULT_DIRNAME "/hlfs/home"
+#define DEFAULT_INTERVAL 900 /* secs b/t re-reads of the password maps */
+#define DEFAULT_CACHE_INTERVAL 300 /* secs during which assume a link is up */
+#define DEFAULT_HLFS_GROUP "hlfs" /* Group name for special hlfs_gid */
+
+#define PROGNAMESZ (MAXHOSTNAMELEN - 5)
+
+#ifdef HAVE_SYSLOG
+# define DEFAULT_LOGFILE "syslog"
+#else /* not HAVE)_SYSLOG */
+# define DEFAULT_LOGFILE 0
+#endif /* not HAVE)_SYSLOG */
+
+#define ERRM ": %m"
+#define fatalerror(str) \
+ (fatal (strcat (strnsave ((str), strlen ((str)) + sizeof (ERRM) - 1), ERRM)))
+
+/*
+ * TYPDEFS:
+ */
+typedef struct uid2home_t uid2home_t;
+typedef struct username2uid_t username2uid_t;
+
+
+/*
+ * STRUCTURES:
+ */
+struct uid2home_t {
+ uid_t uid; /* XXX: with or without UID_OFFSET? */
+ pid_t child;
+ char *home; /* really allocated */
+ char *uname; /* an xref ptr to username2uid_t->username */
+ u_long last_access_time;
+ int last_status; /* 0=used $HOME/.hlfsspool; !0=used alt dir */
+};
+
+struct username2uid_t {
+ char *username; /* really allocated */
+ uid_t uid; /* XXX: with or without UID_OFFSET? */
+ char *home; /* an xref ptr to uid2home_t->home */
+};
+
+/*
+ * EXTERNALS:
+ */
+extern RETSIGTYPE cleanup(int);
+extern RETSIGTYPE interlock(int);
+extern SVCXPRT *nfs_program_2_transp; /* For quick_reply() */
+extern SVCXPRT *nfsxprt;
+extern char *alt_spooldir;
+extern char *home_subdir;
+extern char *homedir(int);
+extern char *mailbox(int, char *);
+extern char *passwdfile;
+extern char *slinkname;
+extern char mboxfile[];
+extern gid_t hlfs_gid;
+extern int cache_interval;
+extern int noverify;
+extern int serverpid;
+extern int sys_nerr;
+extern int untab_index(char *username);
+extern am_nfs_fh *root_fhp;
+extern am_nfs_fh root;
+extern nfstime startup;
+extern uid2home_t *plt_search(int);
+extern username2uid_t *untab; /* user name table */
+extern void fatal(char *);
+extern void plt_init(void);
+extern void hlfsd_init_filehandles(void);
+
+#if defined(DEBUG) || defined(DEBUG_PRINT)
+extern void plt_dump(uid2home_t *, pid_t);
+extern void plt_print(int);
+#endif /* defined(DEBUG) || defined(DEBUG_PRINT) */
+
+#endif /* _HLFSD_HLFS_H */
diff --git a/contrib/amd/hlfsd/homedir.c b/contrib/amd/hlfsd/homedir.c
new file mode 100644
index 000000000000..d6df58e02b2d
--- /dev/null
+++ b/contrib/amd/hlfsd/homedir.c
@@ -0,0 +1,799 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: homedir.c,v 1.16 1993/09/13 15:11:00 ezk Exp $
+ *
+ * HLFSD was written at Columbia University Computer Science Department, by
+ * Erez Zadok <ezk@cs.columbia.edu> and Alexander Dupuy <dupuy@cs.columbia.edu>
+ * It is being distributed under the same terms and conditions as amd does.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <hlfsd.h>
+
+
+/*
+ * STATIC VARIABLES AND FUNCTIONS:
+ */
+static FILE *passwd_fp = NULL;
+static char pw_name[16], pw_dir[128];
+static int cur_pwtab_num = 0, max_pwtab_num = 0;
+static int hlfsd_diskspace(char *);
+static int hlfsd_stat(char *, struct stat *);
+static int passwd_line = 0;
+static int plt_reset(void);
+static struct passwd passwd_ent;
+static uid2home_t *lastchild;
+static uid2home_t *pwtab;
+static void delay(uid2home_t *, int);
+static void table_add(int, char *, char *);
+
+/* GLOBAL FUNCTIONS */
+char *homeof(char *username);
+int uidof(char *username);
+
+/* GLOBALS VARIABLES */
+char mboxfile[MAXPATHLEN];
+username2uid_t *untab; /* user name table */
+
+
+/*
+ * Return the home directory pathname for the user with uid "userid".
+ */
+char *
+homedir(int userid)
+{
+ static char linkval[MAXPATHLEN + 1];
+ static struct timeval tp;
+ uid2home_t *found;
+ char *homename;
+ struct stat homestat;
+
+ clock_valid = 0; /* invalidate logging clock */
+
+ if ((int) userid == 0) { /* force superuser to use "/" as home */
+ sprintf(linkval, "/%s", home_subdir);
+ return linkval;
+ }
+ if ((found = plt_search(userid)) == (uid2home_t *) NULL) {
+ return alt_spooldir; /* use alt spool for unknown uid */
+ }
+ homename = found->home;
+
+ if (homename[0] != '/' || homename[1] == '\0') {
+ found->last_status = 1;
+ return alt_spooldir; /* use alt spool for / or rel. home */
+ }
+ sprintf(linkval, "%s/%s", homename, home_subdir);
+
+ if (noverify) {
+ found->last_status = 0;
+ return linkval;
+ }
+
+ /*
+ * To optimize hlfsd, we don't actually check the validity of the
+ * symlink if it has been in checked in the last N seconds. It is
+ * very likely that the link, machine, and filesystem are still
+ * valid, as long as N is small. But if N ls large, that may not be
+ * true. That's why the default N is 5 minutes, but we allow the
+ * user to override this value via a command line option. Note that
+ * we do not update the last_access_time each time it is accessed,
+ * but only once every N seconds.
+ */
+ if (gettimeofday(&tp, (struct timezone *) NULL) < 0) {
+ tp.tv_sec = 0;
+ } else {
+ if ((tp.tv_sec - found->last_access_time) < cache_interval) {
+ if (found->last_status == 0) {
+ return linkval;
+ } else {
+ return alt_spooldir;
+ }
+ } else {
+ found->last_access_time = tp.tv_sec;
+ }
+ }
+
+#ifdef DEBUG
+ /*
+ * only run this forking code if asked for -D fork
+ * or if did not ask for -D nofork
+ */
+ amuDebug(D_FORK) {
+#endif /* DEBUG */
+ /* fork child to process request if none in progress */
+ if (found->child && kill(found->child, 0))
+ found->child = 0;
+
+ if (found->child)
+ delay(found, 5); /* wait a bit if in progress */
+ if (found->child) { /* better safe than sorry - maybe */
+ found->last_status = 1;
+ return alt_spooldir;
+ }
+ if ((found->child = fork()) < 0) {
+ found->last_status = 1;
+ return alt_spooldir;
+ }
+ if (found->child) { /* PARENT */
+#ifdef DEBUG
+ if (lastchild)
+ plog(XLOG_INFO, "cache spill uid = %d, pid = %d, home = %s",
+ lastchild->uid, lastchild->child,
+ lastchild->home);
+#endif /* DEBUG */
+ lastchild = found;
+ return (char *) NULL; /* return NULL to parent, so it can continue */
+ }
+#ifdef DEBUG
+ } /* end of Debug(D_FORK) */
+#endif /* DEBUG */
+
+ /*
+ * CHILD: (or parent if -D nofork)
+ *
+ * Check and create dir if needed.
+ * Check disk space and/or quotas too.
+ *
+ * We don't need to set the _last_status field of found after the fork
+ * in the child, b/c that information would be later determined in
+ * nfsproc_readlink_2() and the correct exit status would be returned
+ * to the parent upon SIGCHLD in interlock().
+ *
+ */
+ mypid = getpid(); /* for logging routines */
+ if (seteuid(userid) < 0) {
+ plog(XLOG_WARNING, "could not seteuid to %d: %m", userid);
+ return linkval;
+ }
+ if (hlfsd_stat(linkval, &homestat) < 0) {
+ if (errno == ENOENT) { /* make the spool dir if possible */
+ /* don't use recursive mkdirs here */
+ if (mkdir(linkval, PERS_SPOOLMODE) < 0) {
+ seteuid(0);
+ plog(XLOG_WARNING, "can't make directory %s: %m", linkval);
+ return alt_spooldir;
+ }
+ /* fall through to testing the disk space / quota */
+ } else { /* the home dir itself must not exist then */
+ seteuid(0);
+ plog(XLOG_WARNING, "bad link to %s: %m", linkval);
+ return alt_spooldir;
+ }
+ }
+
+ /*
+ * If gets here, then either the spool dir in the home dir exists,
+ * or it was just created. In either case, we now need to
+ * test if we can create a small file and write at least one
+ * byte into it. This will test that we have both enough inodes
+ * and disk blocks to spare, or they fall within the user's quotas too.
+ * We are still seteuid to the user at this point.
+ */
+ if (hlfsd_diskspace(linkval) < 0) {
+ seteuid(0);
+ plog(XLOG_WARNING, "no more space in %s: %m", linkval);
+ return alt_spooldir;
+ } else {
+ seteuid(0);
+ return linkval;
+ }
+}
+
+
+static int
+hlfsd_diskspace(char *path)
+{
+ char buf[MAXPATHLEN];
+ int fd, len;
+
+ clock_valid = 0; /* invalidate logging clock */
+
+ sprintf(buf, "%s/._hlfstmp_%lu", path, (long) getpid());
+ if ((fd = open(buf, O_RDWR | O_CREAT, 0600)) < 0) {
+ plog(XLOG_ERROR, "cannot open %s: %m", buf);
+ return -1;
+ }
+ len = strlen(buf);
+ if (write(fd, buf, len) < len) {
+ plog(XLOG_ERROR, "cannot write \"%s\" (%d bytes) to %s : %m", buf, len, buf);
+ close(fd);
+ unlink(buf); /* cleanup just in case */
+ return -1;
+ }
+ if (unlink(buf) < 0) {
+ plog(XLOG_ERROR, "cannot unlink %s : %m", buf);
+ }
+ close(fd);
+ return 0;
+}
+
+
+static int
+hlfsd_stat(char *path, struct stat *statp)
+{
+ if (stat(path, statp) < 0)
+ return -1;
+ else if (!S_ISDIR(statp->st_mode)) {
+ errno = ENOTDIR;
+ return -1;
+ }
+ return 0;
+}
+
+
+static void
+delay(uid2home_t *found, int secs)
+{
+ struct timeval tv;
+
+#ifdef DEBUG
+ if (found)
+ dlog("delaying on child %d for %d seconds", found->child, secs);
+#endif /* DEBUG */
+
+ tv.tv_usec = 0;
+
+ do {
+ tv.tv_sec = secs;
+ if (select(0, 0, 0, 0, &tv) == 0)
+ break;
+ } while (--secs && found->child);
+}
+
+
+/*
+ * This function is called when a child has terminated after
+ * servicing an nfs request. We need to check the exit status and
+ * update the last_status field of the requesting user.
+ */
+RETSIGTYPE
+interlock(int signum)
+{
+ int child;
+ uid2home_t *lostchild;
+ int status;
+
+#ifdef HAVE_WAITPID
+ while ((child = waitpid((pid_t) -1, &status, WNOHANG)) > 0) {
+#else /* not HAVE_WAITPID */
+ while ((child = wait3(&status, WNOHANG, (struct rusage *) 0)) > 0) {
+#endif /* not HAVE_WAITPID */
+
+ /* high chances this was the last child forked */
+ if (lastchild && lastchild->child == child) {
+ lastchild->child = 0;
+
+ if (WIFEXITED(status))
+ lastchild->last_status = WEXITSTATUS(status);
+ lastchild = (uid2home_t *) NULL;
+ } else {
+ /* and if not, we have to search for it... */
+ for (lostchild = pwtab; lostchild < &pwtab[cur_pwtab_num]; lostchild++) {
+ if (lostchild->child == child) {
+ if (WIFEXITED(status))
+ lostchild->last_status = WEXITSTATUS(status);
+ lostchild->child = 0;
+ break;
+ }
+ }
+ }
+ }
+}
+
+
+/*
+ * PASSWORD AND USERNAME LOOKUP TABLES FUNCTIONS
+ */
+
+/*
+ * get index of UserName table entry which matches username.
+ * must not return uid_t because we want to return a negative number.
+ */
+int
+untab_index(char *username)
+{
+ int max, min, mid, cmp;
+
+ max = cur_pwtab_num - 1;
+ min = 0;
+
+ do {
+ mid = (max + min) / 2;
+ cmp = strcmp(untab[mid].username, username);
+ if (cmp == 0) /* record found! */
+ return mid;
+ if (cmp > 0)
+ max = mid;
+ else
+ min = mid;
+ } while (max > min + 1);
+
+ if (STREQ(untab[max].username, username))
+ return max;
+ if (STREQ(untab[min].username, username))
+ return min;
+
+ /* if gets here then record was not found */
+ return -1;
+}
+
+
+/*
+ * Don't make this return a uid_t, because we need to return negative
+ * numbers as well (error codes.)
+ */
+int
+uidof(char *username)
+{
+ int idx;
+
+ if ((idx = untab_index(username)) < 0) /* not found */
+ return INVALIDID; /* an invalid user id */
+ return untab[idx].uid;
+}
+
+
+/*
+ * Don't make this return a uid_t, because we need to return negative
+ * numbers as well (error codes.)
+ */
+char *
+homeof(char *username)
+{
+ int idx;
+
+ if ((idx = untab_index(username)) < 0) /* not found */
+ return (char *) NULL; /* an invalid user id */
+ return untab[idx].home;
+}
+
+
+char *
+mailbox(int uid, char *username)
+{
+ char *home;
+
+ if (uid < 0)
+ return (char *) NULL; /* not found */
+
+ if ((home = homeof(username)) == (char *) NULL)
+ return (char *) NULL;
+ if (STREQ(home, "/"))
+ sprintf(mboxfile, "/%s/%s", home_subdir, username);
+ else
+ sprintf(mboxfile, "%s/%s/%s", home, home_subdir, username);
+ return mboxfile;
+}
+
+
+static int
+plt_compare_fxn(const voidp x, const voidp y)
+
+{
+ uid2home_t *i = (uid2home_t *) x;
+ uid2home_t *j = (uid2home_t *) y;
+
+ return i->uid - j->uid;
+}
+
+
+static int
+unt_compare_fxn(const voidp x, const voidp y)
+{
+ username2uid_t *i = (username2uid_t *) x;
+ username2uid_t *j = (username2uid_t *) y;
+
+ return strcmp(i->username, j->username);
+}
+
+
+/* perform initialization of user passwd database */
+static void
+hlfsd_setpwent(void)
+{
+ if (!passwdfile) {
+ setpwent();
+ return;
+ }
+
+ passwd_fp = fopen(passwdfile, "r");
+ if (!passwd_fp) {
+ plog(XLOG_ERROR, "unable to read passwd file %s: %m", passwdfile);
+ return;
+ }
+ plog(XLOG_INFO, "reading password entries from file %s", passwdfile);
+
+ passwd_line = 0;
+ memset((char *) &passwd_ent, 0, sizeof(struct passwd));
+ passwd_ent.pw_name = (char *) &pw_name;
+ passwd_ent.pw_dir = (char *) &pw_dir;
+}
+
+
+/* perform de-initialization of user passwd database */
+static void
+hlfsd_endpwent(void)
+{
+ if (!passwdfile) {
+ /*
+ * Don't actually run this because we will be making more passwd calls
+ * afterwards. On Solaris 2.5.1, making getpwent() calls after calling
+ * endpwent() results in a memory leak! (and no, even Purify didn't
+ * detect it...)
+ *
+ endpwent();
+ */
+ return;
+ }
+
+ if (passwd_fp) {
+ fclose(passwd_fp);
+ }
+}
+
+
+/* perform record reading/parsing of individual passwd database records */
+static struct passwd *
+hlfsd_getpwent(void)
+{
+ char buf[256], *cp;
+
+ /* check if to perform standard unix function */
+ if (!passwdfile) {
+ return getpwent();
+ }
+
+ clock_valid = 0; /* invalidate logging clock */
+
+ /* return here to read another entry */
+readent:
+
+ /* return NULL if reached end of file */
+ if (feof(passwd_fp))
+ return NULL;
+
+ pw_name[0] = pw_dir[0] = '\0';
+
+ /* read records */
+ buf[0] = '\0';
+ fgets(buf, 256, passwd_fp);
+ passwd_line++;
+ if (!buf || buf[0] == '\0')
+ goto readent;
+
+ /* read user name */
+ cp = strtok(buf, ":");
+ if (!cp || cp[0] == '\0') {
+ plog(XLOG_ERROR, "no user name on line %d of %s", passwd_line, passwdfile);
+ goto readent;
+ }
+ strcpy(pw_name, cp); /* will show up in passwd_ent.pw_name */
+
+ /* skip passwd */
+ strtok(NULL, ":");
+
+ /* read uid */
+ cp = strtok(NULL, ":");
+ if (!cp || cp[0] == '\0') {
+ plog(XLOG_ERROR, "no uid on line %d of %s", passwd_line, passwdfile);
+ goto readent;
+ }
+ passwd_ent.pw_uid = atoi(cp);
+
+ /* skip gid and gcos */
+ strtok(NULL, ":");
+ strtok(NULL, ":");
+
+ /* read home dir */
+ cp = strtok(NULL, ":");
+ if (!cp || cp[0] == '\0') {
+ plog(XLOG_ERROR, "no home dir on line %d of %s", passwd_line, passwdfile);
+ goto readent;
+ }
+ strcpy(pw_dir, cp); /* will show up in passwd_ent.pw_dir */
+
+ /* the rest of the fields are unimportant and not being considered */
+
+ plog(XLOG_USER, "hlfsd_getpwent: name=%s, uid=%d, dir=%s",
+ passwd_ent.pw_name, passwd_ent.pw_uid, passwd_ent.pw_dir);
+
+ return &passwd_ent;
+}
+
+
+/*
+ * read and hash the passwd file or NIS map
+ */
+void
+plt_init(void)
+{
+ struct passwd *pent_p;
+
+ if (plt_reset() < 0) /* could not reset table. skip. */
+ return;
+
+ plog(XLOG_INFO, "reading password map");
+
+ hlfsd_setpwent(); /* prepare to read passwd entries */
+ while ((pent_p = hlfsd_getpwent()) != (struct passwd *) NULL) {
+ table_add(pent_p->pw_uid, pent_p->pw_dir, pent_p->pw_name);
+ }
+ hlfsd_endpwent();
+
+ qsort((char *) pwtab, cur_pwtab_num, sizeof(uid2home_t),
+ plt_compare_fxn);
+ qsort((char *) untab, cur_pwtab_num, sizeof(username2uid_t),
+ unt_compare_fxn);
+
+ plog(XLOG_INFO, "password map read and sorted");
+}
+
+
+/*
+ * This is essentially so that we don't reset known good lookup tables when a
+ * YP server goes down.
+ */
+static int
+plt_reset(void)
+{
+ int i;
+
+ clock_valid = 0; /* invalidate logging clock */
+
+ hlfsd_setpwent();
+ if (hlfsd_getpwent() == (struct passwd *) NULL) {
+ hlfsd_endpwent();
+ return -1; /* did not reset table */
+ }
+ hlfsd_endpwent();
+
+ lastchild = (uid2home_t *) NULL;
+
+ if (max_pwtab_num > 0) /* was used already. cleanup old table */
+ for (i = 0; i < cur_pwtab_num; ++i) {
+ if (pwtab[i].home) {
+ XFREE(pwtab[i].home);
+ pwtab[i].home = (char *) NULL;
+ }
+ pwtab[i].uid = INVALIDID; /* not a valid uid (yet...) */
+ pwtab[i].child = (pid_t) 0;
+ pwtab[i].uname = (char *) NULL; /* only a ptr to untab[i].username */
+ if (untab[i].username) {
+ XFREE(untab[i].username);
+ untab[i].username = (char *) NULL;
+ }
+ untab[i].uid = INVALIDID; /* invalid uid */
+ untab[i].home = (char *) NULL; /* only a ptr to pwtab[i].home */
+ }
+ cur_pwtab_num = 0; /* zero current size */
+
+ return 0; /* resetting ok */
+}
+
+
+/*
+ * u: uid number
+ * h: home directory
+ * n: user ID name
+ */
+static void
+table_add(int u, char *h, char *n)
+{
+ int i;
+
+ clock_valid = 0; /* invalidate logging clock */
+
+ if (max_pwtab_num <= 0) { /* was never initialized */
+ max_pwtab_num = 1;
+ pwtab = (uid2home_t *) xmalloc(max_pwtab_num *
+ sizeof(uid2home_t));
+ memset((char *) &pwtab[0], 0, max_pwtab_num * sizeof(uid2home_t));
+ untab = (username2uid_t *) xmalloc(max_pwtab_num *
+ sizeof(username2uid_t));
+ memset((char *) &untab[0], 0, max_pwtab_num * sizeof(username2uid_t));
+ }
+
+ /* check if need more space. */
+ if (cur_pwtab_num + 1 > max_pwtab_num) {
+ /* need more space in table */
+ max_pwtab_num *= 2;
+ plog(XLOG_INFO, "reallocating table spaces to %d entries", max_pwtab_num);
+ pwtab = (uid2home_t *) xrealloc(pwtab,
+ sizeof(uid2home_t) * max_pwtab_num);
+ untab = (username2uid_t *) xrealloc(untab,
+ sizeof(username2uid_t) *
+ max_pwtab_num);
+ /* zero out newly added entries */
+ for (i=cur_pwtab_num; i<max_pwtab_num; ++i) {
+ memset((char *) &pwtab[i], 0, sizeof(uid2home_t));
+ memset((char *) &untab[i], 0, sizeof(username2uid_t));
+ }
+ }
+
+ /* do NOT add duplicate entries (this is an O(N^2) algorithm... */
+ for (i=0; i<cur_pwtab_num; ++i)
+ if (u == pwtab[i].uid && u != 0 ) {
+#ifdef DEBUG
+ dlog("ignoring duplicate home %s for uid %d (already %s)",
+ h, u, pwtab[i].home);
+#endif /* DEBUG */
+ return;
+ }
+
+ /* add new password entry */
+ pwtab[cur_pwtab_num].home = strdup(h);
+ pwtab[cur_pwtab_num].child = 0;
+ pwtab[cur_pwtab_num].last_access_time = 0;
+ pwtab[cur_pwtab_num].last_status = 0; /* assume best: used homedir */
+ pwtab[cur_pwtab_num].uid = u;
+
+ /* add new userhome entry */
+ untab[cur_pwtab_num].username = strdup(n);
+
+ /* just a second pointer */
+ pwtab[cur_pwtab_num].uname = untab[cur_pwtab_num].username;
+ untab[cur_pwtab_num].uid = u;
+ untab[cur_pwtab_num].home = pwtab[cur_pwtab_num].home; /* a ptr */
+
+ /* increment counter */
+ ++cur_pwtab_num;
+}
+
+
+/*
+ * return entry in lookup table
+ */
+uid2home_t *
+plt_search(int u)
+{
+ int max, min, mid;
+
+ /*
+ * empty table should not happen,
+ * but I have a bug with signals to trace...
+ */
+ if (pwtab == (uid2home_t *) NULL)
+ return (uid2home_t *) NULL;
+
+ max = cur_pwtab_num - 1;
+ min = 0;
+
+ do {
+ mid = (max + min) / 2;
+ if (pwtab[mid].uid == u) /* record found! */
+ return &pwtab[mid];
+ if (pwtab[mid].uid > u)
+ max = mid;
+ else
+ min = mid;
+ } while (max > min + 1);
+
+ if (pwtab[max].uid == u)
+ return &pwtab[max];
+ if (pwtab[min].uid == u)
+ return &pwtab[min];
+
+ /* if gets here then record was not found */
+ return (uid2home_t *) NULL;
+}
+
+
+#if defined(DEBUG) || defined(DEBUG_PRINT)
+void
+plt_print(int signum)
+{
+ FILE *dumpfile;
+ int dumpfd;
+ char dumptmp[] = "/usr/tmp/hlfsd.dump.XXXXXX";
+ int i;
+
+#ifdef HAVE_MKSTEMP
+ dumpfd = mkstemp(dumptmp);
+#else /* not HAVE_MKSTEMP */
+ mktemp(dumptmp);
+ if (!dumptmp) {
+ plot(XLOG_ERROR, "cannot create temporary dump file");
+ return;
+ }
+ dumpfd = open(dumptmp, O_RDONLY);
+#endif /* not HAVE_MKSTEMP */
+ if (dumpfd < 0) {
+ plog(XLOG_ERROR, "cannot open temporary dump file");
+ return;
+ }
+ if ((dumpfile = fdopen(dumpfd, "a")) != NULL) {
+ plog(XLOG_INFO, "dumping internal state to file %s", dumptmp);
+ fprintf(dumpfile, "\n\nNew plt_dump():\n");
+ for (i = 0; i < cur_pwtab_num; ++i)
+ fprintf(dumpfile,
+ "%4d %5lu %10lu %1d %4lu \"%s\" uname=\"%s\"\n",
+ i,
+ (long) pwtab[i].child,
+ pwtab[i].last_access_time,
+ pwtab[i].last_status,
+ (long) pwtab[i].uid,
+ pwtab[i].home,
+ pwtab[i].uname);
+ fprintf(dumpfile, "\nUserName table by plt_print():\n");
+ for (i = 0; i < cur_pwtab_num; ++i)
+ fprintf(dumpfile, "%4d : \"%s\" %4lu \"%s\"\n", i,
+ untab[i].username, (long) untab[i].uid, untab[i].home);
+ close(dumpfd);
+ fclose(dumpfile);
+ }
+}
+
+
+void
+plt_dump(uid2home_t *lastc, pid_t this)
+{
+ FILE *dumpfile;
+ int i;
+
+ if ((dumpfile = fopen("/var/tmp/hlfsdump", "a")) != NULL) {
+ fprintf(dumpfile, "\n\nNEW PLT_DUMP -- ");
+ fprintf(dumpfile, "lastchild->child=%d ",
+ (int) (lastc ? lastc->child : -999));
+ fprintf(dumpfile, ", child from wait3=%lu:\n", (long) this);
+ for (i = 0; i < cur_pwtab_num; ++i)
+ fprintf(dumpfile, "%4d %5lu: %4lu \"%s\" uname=\"%s\"\n", i,
+ (long) pwtab[i].child, (long) pwtab[i].uid,
+ pwtab[i].home, pwtab[i].uname);
+ fprintf(dumpfile, "\nUserName table by plt_dump():\n");
+ for (i = 0; i < cur_pwtab_num; ++i)
+ fprintf(dumpfile, "%4d : \"%s\" %4lu \"%s\"\n", i,
+ untab[i].username, (long) untab[i].uid, untab[i].home);
+ fprintf(dumpfile, "ezk: ent=%d, uid=%lu, home=\"%s\"\n",
+ untab_index("ezk"),
+ (long) untab[untab_index("ezk")].uid,
+ pwtab[untab[untab_index("ezk")].uid].home);
+ fprintf(dumpfile, "rezk: ent=%d, uid=%lu, home=\"%s\"\n",
+ untab_index("rezk"),
+ (long) untab[untab_index("rezk")].uid,
+ pwtab[untab[untab_index("rezk")].uid].home);
+ fclose(dumpfile);
+ }
+}
+#endif /* defined(DEBUG) || defined(DEBUG_PRINT) */
diff --git a/contrib/amd/hlfsd/nfs_prot_svc.c b/contrib/amd/hlfsd/nfs_prot_svc.c
new file mode 100644
index 000000000000..fc2e663ee42f
--- /dev/null
+++ b/contrib/amd/hlfsd/nfs_prot_svc.c
@@ -0,0 +1,250 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: nfs_prot_svc.c,v 5.2.2.1 1992/02/09 15:09:30 jsp beta $
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <hlfsd.h>
+
+/* EXTERNAL FUNCTIONS */
+extern voidp nfsproc_null_2_svc(voidp, struct svc_req *);
+extern nfsattrstat * nfsproc_getattr_2_svc(am_nfs_fh *, struct svc_req *);
+extern nfsattrstat * nfsproc_setattr_2_svc(nfssattrargs *, struct svc_req *);
+extern voidp nfsproc_root_2_svc(voidp, struct svc_req *);
+extern nfsdiropres * nfsproc_lookup_2_svc(nfsdiropargs *, struct svc_req *);
+extern nfsreadlinkres * nfsproc_readlink_2_svc(am_nfs_fh *, struct svc_req *);
+extern nfsreadres * nfsproc_read_2_svc(nfsreadargs *, struct svc_req *);
+extern voidp nfsproc_writecache_2_svc(voidp, struct svc_req *);
+extern nfsattrstat * nfsproc_write_2_svc(nfswriteargs *, struct svc_req *);
+extern nfsdiropres * nfsproc_create_2_svc(nfscreateargs *, struct svc_req *);
+extern nfsstat * nfsproc_remove_2_svc(nfsdiropargs *, struct svc_req *);
+extern nfsstat * nfsproc_rename_2_svc(nfsrenameargs *, struct svc_req *);
+extern nfsstat * nfsproc_link_2_svc(nfslinkargs *, struct svc_req *);
+extern nfsstat * nfsproc_symlink_2_svc(nfssymlinkargs *, struct svc_req *);
+extern nfsdiropres * nfsproc_mkdir_2_svc(nfscreateargs *, struct svc_req *);
+extern nfsstat * nfsproc_rmdir_2_svc(nfsdiropargs *, struct svc_req *);
+extern nfsreaddirres * nfsproc_readdir_2_svc(nfsreaddirargs *, struct svc_req *);
+extern nfsstatfsres * nfsproc_statfs_2_svc(am_nfs_fh *, struct svc_req *);
+
+/* GLOBALS */
+SVCXPRT *nfs_program_2_transp;
+
+/* TYPEDEFS */
+typedef char *(*nfssvcproc_t)(voidp, struct svc_req *);
+
+
+void
+nfs_program_2(struct svc_req *rqstp, SVCXPRT *transp)
+{
+ union {
+ am_nfs_fh nfsproc_getattr_2_arg;
+ nfssattrargs nfsproc_setattr_2_arg;
+ nfsdiropargs nfsproc_lookup_2_arg;
+ am_nfs_fh nfsproc_readlink_2_arg;
+ nfsreadargs nfsproc_read_2_arg;
+ nfswriteargs nfsproc_write_2_arg;
+ nfscreateargs nfsproc_create_2_arg;
+ nfsdiropargs nfsproc_remove_2_arg;
+ nfsrenameargs nfsproc_rename_2_arg;
+ nfslinkargs nfsproc_link_2_arg;
+ nfssymlinkargs nfsproc_symlink_2_arg;
+ nfscreateargs nfsproc_mkdir_2_arg;
+ nfsdiropargs nfsproc_rmdir_2_arg;
+ nfsreaddirargs nfsproc_readdir_2_arg;
+ am_nfs_fh nfsproc_statfs_2_arg;
+ } argument;
+ char *result;
+ xdrproc_t xdr_argument, xdr_result;
+ nfssvcproc_t local;
+
+ nfs_program_2_transp = NULL;
+
+ switch (rqstp->rq_proc) {
+
+ case NFSPROC_NULL:
+ xdr_argument = (xdrproc_t) xdr_void;
+ xdr_result = (xdrproc_t) xdr_void;
+ local = (nfssvcproc_t) nfsproc_null_2_svc;
+ break;
+
+ case NFSPROC_GETATTR:
+ xdr_argument = (xdrproc_t) xdr_nfs_fh;
+ xdr_result = (xdrproc_t) xdr_attrstat;
+ local = (nfssvcproc_t) nfsproc_getattr_2_svc;
+ break;
+
+ case NFSPROC_SETATTR:
+ xdr_argument = (xdrproc_t) xdr_sattrargs;
+ xdr_result = (xdrproc_t) xdr_attrstat;
+ local = (nfssvcproc_t) nfsproc_setattr_2_svc;
+ break;
+
+ case NFSPROC_ROOT:
+ xdr_argument = (xdrproc_t) xdr_void;
+ xdr_result = (xdrproc_t) xdr_void;
+ local = (nfssvcproc_t) nfsproc_root_2_svc;
+ break;
+
+ case NFSPROC_LOOKUP:
+ xdr_argument = (xdrproc_t) xdr_diropargs;
+ xdr_result = (xdrproc_t) xdr_diropres;
+ local = (nfssvcproc_t) nfsproc_lookup_2_svc;
+ /*
+ * Cheap way to pass transp down to afs_lookuppn so it can
+ * be stored in the am_node structure and later used for
+ * quick_reply().
+ */
+ nfs_program_2_transp = transp;
+ break;
+
+ case NFSPROC_READLINK:
+ xdr_argument = (xdrproc_t) xdr_nfs_fh;
+ xdr_result = (xdrproc_t) xdr_readlinkres;
+ local = (nfssvcproc_t) nfsproc_readlink_2_svc;
+ break;
+
+ case NFSPROC_READ:
+ xdr_argument = (xdrproc_t) xdr_readargs;
+ xdr_result = (xdrproc_t) xdr_readres;
+ local = (nfssvcproc_t) nfsproc_read_2_svc;
+ break;
+
+ case NFSPROC_WRITECACHE:
+ xdr_argument = (xdrproc_t) xdr_void;
+ xdr_result = (xdrproc_t) xdr_void;
+ local = (nfssvcproc_t) nfsproc_writecache_2_svc;
+ break;
+
+ case NFSPROC_WRITE:
+ xdr_argument = (xdrproc_t) xdr_writeargs;
+ xdr_result = (xdrproc_t) xdr_attrstat;
+ local = (nfssvcproc_t) nfsproc_write_2_svc;
+ break;
+
+ case NFSPROC_CREATE:
+ xdr_argument = (xdrproc_t) xdr_createargs;
+ xdr_result = (xdrproc_t) xdr_diropres;
+ local = (nfssvcproc_t) nfsproc_create_2_svc;
+ break;
+
+ case NFSPROC_REMOVE:
+ xdr_argument = (xdrproc_t) xdr_diropargs;
+ xdr_result = (xdrproc_t) xdr_nfsstat;
+ local = (nfssvcproc_t) nfsproc_remove_2_svc;
+ break;
+
+ case NFSPROC_RENAME:
+ xdr_argument = (xdrproc_t) xdr_renameargs;
+ xdr_result = (xdrproc_t) xdr_nfsstat;
+ local = (nfssvcproc_t) nfsproc_rename_2_svc;
+ break;
+
+ case NFSPROC_LINK:
+ xdr_argument = (xdrproc_t) xdr_linkargs;
+ xdr_result = (xdrproc_t) xdr_nfsstat;
+ local = (nfssvcproc_t) nfsproc_link_2_svc;
+ break;
+
+ case NFSPROC_SYMLINK:
+ xdr_argument = (xdrproc_t) xdr_symlinkargs;
+ xdr_result = (xdrproc_t) xdr_nfsstat;
+ local = (nfssvcproc_t) nfsproc_symlink_2_svc;
+ break;
+
+ case NFSPROC_MKDIR:
+ xdr_argument = (xdrproc_t) xdr_createargs;
+ xdr_result = (xdrproc_t) xdr_diropres;
+ local = (nfssvcproc_t) nfsproc_mkdir_2_svc;
+ break;
+
+ case NFSPROC_RMDIR:
+ xdr_argument = (xdrproc_t) xdr_diropargs;
+ xdr_result = (xdrproc_t) xdr_nfsstat;
+ local = (nfssvcproc_t) nfsproc_rmdir_2_svc;
+ break;
+
+ case NFSPROC_READDIR:
+ xdr_argument = (xdrproc_t) xdr_readdirargs;
+ xdr_result = (xdrproc_t) xdr_readdirres;
+ local = (nfssvcproc_t) nfsproc_readdir_2_svc;
+ break;
+
+ case NFSPROC_STATFS:
+ xdr_argument = (xdrproc_t) xdr_nfs_fh;
+ xdr_result = (xdrproc_t) xdr_statfsres;
+ local = (nfssvcproc_t) nfsproc_statfs_2_svc;
+ break;
+
+ default:
+ svcerr_noproc(transp);
+ return;
+ }
+
+ memset((char *) &argument, 0, sizeof(argument));
+ if (!svc_getargs(transp,
+ (XDRPROC_T_TYPE) xdr_argument,
+ (SVC_IN_ARG_TYPE) &argument)) {
+ plog(XLOG_ERROR,
+ "NFS xdr decode failed for %d %d %d",
+ rqstp->rq_prog, rqstp->rq_vers, rqstp->rq_proc);
+ svcerr_decode(transp);
+ return;
+ }
+ result = (*local) (&argument, rqstp);
+
+ nfs_program_2_transp = NULL;
+
+ if (result != NULL && !svc_sendreply(transp,
+ (XDRPROC_T_TYPE) xdr_result,
+ result)) {
+ svcerr_systemerr(transp);
+ }
+ if (!svc_freeargs(transp,
+ (XDRPROC_T_TYPE) xdr_argument,
+ (SVC_IN_ARG_TYPE) & argument)) {
+ plog(XLOG_FATAL, "unable to free rpc arguments in nfs_program_2");
+ going_down(1);
+ }
+}
diff --git a/contrib/amd/hlfsd/stubs.c b/contrib/amd/hlfsd/stubs.c
new file mode 100644
index 000000000000..2ead112882c0
--- /dev/null
+++ b/contrib/amd/hlfsd/stubs.c
@@ -0,0 +1,530 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: stubs.c,v 1.10 1993/09/13 15:11:00 ezk Exp $
+ *
+ * HLFSD was written at Columbia University Computer Science Department, by
+ * Erez Zadok <ezk@cs.columbia.edu> and Alexander Dupuy <dupuy@cs.columbia.edu>
+ * It is being distributed under the same terms and conditions as amd does.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <hlfsd.h>
+
+/*
+ * STATIC VARIABLES:
+ */
+static nfsfattr rootfattr = {NFDIR, 0040555, 2, 0, 0, 512, 512, 0,
+ 1, 0, ROOTID};
+static nfsfattr slinkfattr = {NFLNK, 0120777, 1, 0, 0, NFS_MAXPATHLEN, 512, 0,
+ (NFS_MAXPATHLEN + 1) / 512, 0, SLINKID};
+ /* user name file attributes */
+static nfsfattr un_fattr = {NFLNK, 0120777, 1, 0, 0, NFS_MAXPATHLEN, 512, 0,
+ (NFS_MAXPATHLEN + 1) / 512, 0, INVALIDID};
+static int getcreds(struct svc_req *, uid_t *, gid_t *);
+static int started;
+static am_nfs_fh slink;
+static am_nfs_fh un_fhandle;
+
+/*
+ * GLOBALS:
+ */
+am_nfs_fh root;
+am_nfs_fh *root_fhp = &root;
+
+
+/* initialize NFS file handles for hlfsd */
+void
+hlfsd_init_filehandles(void)
+{
+ u_int ui;
+
+ ui = ROOTID;
+ memcpy(root.fh_data, &ui, sizeof(ui));
+
+ ui = SLINKID;
+ memcpy(slink.fh_data, &ui, sizeof(ui));
+
+ ui = INVALIDID;
+ memcpy(un_fhandle.fh_data, &ui, sizeof(ui));
+}
+
+
+voidp
+nfsproc_null_2_svc(voidp argp, struct svc_req *rqstp)
+{
+ static char res;
+
+ return (voidp) &res;
+}
+
+
+/* compare if two filehandles are equal */
+static int
+eq_fh(const am_nfs_fh *fh1, const am_nfs_fh *fh2)
+{
+ return (!memcmp((char *) fh1, (char *) fh2, sizeof(am_nfs_fh)));
+}
+
+
+nfsattrstat *
+nfsproc_getattr_2_svc(am_nfs_fh *argp, struct svc_req *rqstp)
+{
+ static nfsattrstat res;
+ uid_t uid = (uid_t) INVALIDID;
+ gid_t gid = (gid_t) INVALIDID;
+
+ if (!started) {
+ started++;
+ rootfattr.na_ctime = startup;
+ rootfattr.na_mtime = startup;
+ slinkfattr.na_ctime = startup;
+ slinkfattr.na_mtime = startup;
+ un_fattr.na_ctime = startup;
+ un_fattr.na_mtime = startup;
+ }
+
+ if (eq_fh(argp, &root)) {
+ res.ns_status = NFS_OK;
+ res.ns_u.ns_attr_u = rootfattr;
+ } else if (eq_fh(argp, &slink)) {
+
+#ifndef MNT2_NFS_OPT_SYMTTL
+ /*
+ * This code is needed to defeat Solaris 2.4's (and newer) symlink
+ * values cache. It forces the last-modifed time of the symlink to be
+ * current. It is not needed if the O/S has an nfs flag to turn off the
+ * symlink-cache at mount time (such as Irix 5.x and 6.x). -Erez.
+ */
+ if (++slinkfattr.na_mtime.nt_useconds == 0)
+ ++slinkfattr.na_mtime.nt_seconds;
+#endif /* not MNT2_NFS_OPT_SYMTTL */
+
+ res.ns_status = NFS_OK;
+ res.ns_u.ns_attr_u = slinkfattr;
+ } else {
+
+ if (getcreds(rqstp, &uid, &gid) < 0) {
+ res.ns_status = NFSERR_STALE;
+ return &res;
+ }
+ if (gid != hlfs_gid) {
+ res.ns_status = NFSERR_STALE;
+ } else {
+ memset((char *) &uid, 0, sizeof(int));
+ uid = *(u_int *) argp->fh_data;
+ if (plt_search(uid) != (uid2home_t *) NULL) {
+ res.ns_status = NFS_OK;
+ un_fattr.na_fileid = uid;
+ res.ns_u.ns_attr_u = un_fattr;
+#ifdef DEBUG
+ dlog("nfs_getattr: succesful search for uid=%d, gid=%d", uid, gid);
+#endif /* DEBUG */
+ } else { /* not found */
+ res.ns_status = NFSERR_STALE;
+ }
+ }
+ }
+ return &res;
+}
+
+
+nfsattrstat *
+nfsproc_setattr_2_svc(nfssattrargs *argp, struct svc_req *rqstp)
+{
+ static nfsattrstat res = {NFSERR_ROFS};
+
+ return &res;
+}
+
+
+voidp
+nfsproc_root_2_svc(voidp argp, struct svc_req *rqstp)
+{
+ static char res;
+
+ return (voidp) &res;
+}
+
+
+nfsdiropres *
+nfsproc_lookup_2_svc(nfsdiropargs *argp, struct svc_req *rqstp)
+{
+ static nfsdiropres res;
+ int idx;
+ uid_t uid = (uid_t) INVALIDID;
+ gid_t gid = (gid_t) INVALIDID;
+
+ if (!started) {
+ started++;
+ rootfattr.na_ctime = startup;
+ rootfattr.na_mtime = startup;
+ slinkfattr.na_ctime = startup;
+ slinkfattr.na_mtime = startup;
+ un_fattr.na_ctime = startup;
+ un_fattr.na_mtime = startup;
+ }
+
+ if (eq_fh(&argp->da_fhandle, &slink)) {
+ res.dr_status = NFSERR_NOTDIR;
+ return &res;
+ }
+
+ if (eq_fh(&argp->da_fhandle, &root)) {
+ if (argp->da_name[0] == '.' &&
+ (argp->da_name[1] == '\0' ||
+ (argp->da_name[1] == '.' &&
+ argp->da_name[2] == '\0'))) {
+ res.dr_u.dr_drok_u.drok_fhandle = root;
+ res.dr_u.dr_drok_u.drok_attributes = rootfattr;
+ res.dr_status = NFS_OK;
+ return &res;
+ }
+
+ if (STREQ(argp->da_name, slinkname)) {
+ res.dr_u.dr_drok_u.drok_fhandle = slink;
+ res.dr_u.dr_drok_u.drok_attributes = slinkfattr;
+ res.dr_status = NFS_OK;
+ return &res;
+ }
+
+ if (getcreds(rqstp, &uid, &gid) < 0 || gid != hlfs_gid) {
+ res.dr_status = NFSERR_NOENT;
+ return &res;
+ }
+
+ /* if get's here, gid == hlfs_gid */
+ if ((idx = untab_index(argp->da_name)) < 0) {
+ res.dr_status = NFSERR_NOENT;
+ return &res;
+ } else { /* entry found and gid is permitted */
+ un_fattr.na_fileid = untab[idx].uid;
+ res.dr_u.dr_drok_u.drok_attributes = un_fattr;
+ memset((char *) &un_fhandle, 0, sizeof(am_nfs_fh));
+ *(u_int *) un_fhandle.fh_data = (u_int) untab[idx].uid;
+ strncpy((char *) &un_fhandle.fh_data[sizeof(int)],
+ untab[idx].username,
+ sizeof(am_nfs_fh) - sizeof(int));
+ res.dr_u.dr_drok_u.drok_fhandle = un_fhandle;
+ res.dr_status = NFS_OK;
+#ifdef DEBUG
+ dlog("nfs_lookup: succesful lookup for uid=%d, gid=%d: username=%s",
+ uid, gid, untab[idx].username);
+#endif /* DEBUG */
+ return &res;
+ }
+ } /* end of "if (eq_fh(argp->dir.data, root.data)) {" */
+
+ res.dr_status = NFSERR_STALE;
+ return &res;
+}
+
+static int
+getcreds(struct svc_req *rp, uid_t *u, gid_t *g)
+{
+ struct authunix_parms *aup = (struct authunix_parms *) NULL;
+#ifdef HAVE_RPC_AUTH_DES_H
+ struct authdes_cred *adp;
+#endif /* HAVE_RPC_AUTH_DES_H */
+
+ switch (rp->rq_cred.oa_flavor) {
+
+ case AUTH_UNIX:
+ aup = (struct authunix_parms *) rp->rq_clntcred;
+ *u = aup->aup_uid;
+ *g = aup->aup_gid;
+ break;
+
+#ifdef HAVE_RPC_AUTH_DES_H
+ case AUTH_DES:
+ adp = (struct authdes_cred *) rp->rq_clntcred;
+ *g = INVALIDID; /* some unknown group id */
+ if (sscanf(adp->adc_fullname.name, "unix.%lu@", u) == 1)
+ break;
+ /* fall through */
+#endif /* HAVE_RPC_AUTH_DES_H */
+
+ default:
+ *u = *g = INVALIDID; /* just in case */
+ svcerr_weakauth(nfsxprt);
+ return -1;
+ }
+
+ return 0; /* everything is ok */
+}
+
+
+nfsreadlinkres *
+nfsproc_readlink_2_svc(am_nfs_fh *argp, struct svc_req *rqstp)
+{
+ static nfsreadlinkres res;
+ uid_t userid = (uid_t) INVALIDID;
+ gid_t groupid = hlfs_gid + 1; /* anything not hlfs_gid */
+ int retval = 0;
+ char *path_val = (char *) NULL;
+ char *username;
+ static uid_t last_uid = (uid_t) INVALIDID;
+
+ if (eq_fh(argp, &root)) {
+ res.rlr_status = NFSERR_ISDIR;
+ } else if (eq_fh(argp, &slink)) {
+ if (getcreds(rqstp, &userid, &groupid) < 0)
+ return (nfsreadlinkres *) NULL;
+
+ gettimeofday((struct timeval *) &slinkfattr.na_atime, (struct timezone *) 0);
+
+ res.rlr_status = NFS_OK;
+ if (groupid == hlfs_gid) {
+ res.rlr_u.rlr_data_u = DOTSTRING;
+ } else if (!(res.rlr_u.rlr_data_u = path_val = homedir(userid))) {
+ /*
+ * parent process (fork in homedir()) continues
+ * processing, by getting a NULL returned as a
+ * "special". Child returns result.
+ */
+ return (nfsreadlinkres *) NULL;
+ }
+
+ } else { /* check if asked for user mailbox */
+
+ if (getcreds(rqstp, &userid, &groupid) < 0) {
+ return (nfsreadlinkres *) NULL;
+ }
+
+ if (groupid == hlfs_gid) {
+ memset((char *) &userid, 0, sizeof(int));
+ userid = *(u_int *) argp->fh_data;
+ username = (char *) &argp->fh_data[sizeof(int)];
+ if (!(res.rlr_u.rlr_data_u = mailbox(userid, username)))
+ return (nfsreadlinkres *) NULL;
+ } else {
+ res.rlr_status = NFSERR_STALE;
+ }
+ }
+
+ /* print info, but try to avoid repetitions */
+ if (userid != last_uid) {
+ plog(XLOG_USER, "mailbox for uid=%d, gid=%d is %s",
+ userid, groupid, (char *) res.rlr_u.rlr_data_u);
+ last_uid = userid;
+ }
+
+ /* I don't think will pass this if -D nofork */
+ if (serverpid == getpid())
+ return &res;
+
+ if (!svc_sendreply(nfsxprt, (XDRPROC_T_TYPE) xdr_readlinkres, (SVC_IN_ARG_TYPE) &res))
+ svcerr_systemerr(nfsxprt);
+
+ /*
+ * Child exists here. We need to determine which
+ * exist status to return. The exit status
+ * is gathered using wait() and determines
+ * if we returned $HOME/.hlfsspool or $ALTDIR. The parent
+ * needs this info so it can update the lookup table.
+ */
+ if (path_val && alt_spooldir && STREQ(path_val, alt_spooldir))
+ retval = 1; /* could not get real home dir (or uid 0 user) */
+ else
+ retval = 0;
+
+#ifdef DEBUG
+ /*
+ * If asked for -D nofork, then must return the value,
+ * NOT exit, or else the main hlfsd server exits.
+ * Bug where is that status information being collected?
+ */
+ amuDebugNo(D_FORK)
+ return &res;
+#endif /* DEBUG */
+
+ exit(retval);
+}
+
+
+nfsreadres *
+nfsproc_read_2_svc(nfsreadargs *argp, struct svc_req *rqstp)
+{
+ static nfsreadres res = {NFSERR_ACCES};
+
+ return &res;
+}
+
+
+voidp
+nfsproc_writecache_2_svc(voidp argp, struct svc_req *rqstp)
+{
+ static char res;
+
+ return (voidp) &res;
+}
+
+
+nfsattrstat *
+nfsproc_write_2_svc(nfswriteargs *argp, struct svc_req *rqstp)
+{
+ static nfsattrstat res = {NFSERR_ROFS};
+
+ return &res;
+}
+
+
+nfsdiropres *
+nfsproc_create_2_svc(nfscreateargs *argp, struct svc_req *rqstp)
+{
+ static nfsdiropres res = {NFSERR_ROFS};
+
+ return &res;
+}
+
+
+nfsstat *
+nfsproc_remove_2_svc(nfsdiropargs *argp, struct svc_req *rqstp)
+{
+ static nfsstat res = {NFSERR_ROFS};
+
+ return &res;
+}
+
+
+nfsstat *
+nfsproc_rename_2_svc(nfsrenameargs *argp, struct svc_req *rqstp)
+{
+ static nfsstat res = {NFSERR_ROFS};
+
+ return &res;
+}
+
+
+nfsstat *
+nfsproc_link_2_svc(nfslinkargs *argp, struct svc_req *rqstp)
+{
+ static nfsstat res = {NFSERR_ROFS};
+
+ return &res;
+}
+
+
+nfsstat *
+nfsproc_symlink_2_svc(nfssymlinkargs *argp, struct svc_req *rqstp)
+{
+ static nfsstat res = {NFSERR_ROFS};
+
+ return &res;
+}
+
+
+nfsdiropres *
+nfsproc_mkdir_2_svc(nfscreateargs *argp, struct svc_req *rqstp)
+{
+ static nfsdiropres res = {NFSERR_ROFS};
+
+ return &res;
+}
+
+
+nfsstat *
+nfsproc_rmdir_2_svc(nfsdiropargs *argp, struct svc_req *rqstp)
+{
+ static nfsstat res = {NFSERR_ROFS};
+
+ return &res;
+}
+
+
+nfsreaddirres *
+nfsproc_readdir_2_svc(nfsreaddirargs *argp, struct svc_req *rqstp)
+{
+ static nfsreaddirres res;
+ static nfsentry slinkent = {SLINKID, 0, {SLINKCOOKIE}};
+ static nfsentry dotdotent = {ROOTID, "..", {DOTDOTCOOKIE}, &slinkent};
+ static nfsentry dotent = {ROOTID, ".", {DOTCOOKIE}, &dotdotent};
+
+ slinkent.ne_name = slinkname;
+
+ if (eq_fh(&argp->rda_fhandle, &slink)) {
+ res.rdr_status = NFSERR_NOTDIR;
+ } else if (eq_fh(&argp->rda_fhandle, &root)) {
+ gettimeofday((struct timeval *) &rootfattr.na_atime, (struct timezone *) 0);
+
+ res.rdr_status = NFS_OK;
+ switch (argp->rda_cookie[0]) {
+ case 0:
+ res.rdr_u.rdr_reply_u.dl_entries = &dotent;
+ break;
+ case DOTCOOKIE:
+ res.rdr_u.rdr_reply_u.dl_entries = &dotdotent;
+ break;
+ case DOTDOTCOOKIE:
+ res.rdr_u.rdr_reply_u.dl_entries = &slinkent;
+ break;
+ case SLINKCOOKIE:
+ res.rdr_u.rdr_reply_u.dl_entries = (nfsentry *) 0;
+ break;
+ }
+ res.rdr_u.rdr_reply_u.dl_eof = TRUE;
+ } else {
+ res.rdr_status = NFSERR_STALE;
+ }
+ return &res;
+}
+
+
+nfsstatfsres *
+nfsproc_statfs_2_svc(am_nfs_fh *argp, struct svc_req *rqstp)
+{
+ static nfsstatfsres res = {NFS_OK};
+
+ res.sfr_u.sfr_reply_u.sfrok_tsize = 1024;
+ res.sfr_u.sfr_reply_u.sfrok_bsize = 1024;
+
+ /*
+ * Some "df" programs automatically assume that file systems
+ * with zero blocks are meta-filesystems served by automounters.
+ */
+ res.sfr_u.sfr_reply_u.sfrok_blocks = 0;
+ res.sfr_u.sfr_reply_u.sfrok_bfree = 0;
+ res.sfr_u.sfr_reply_u.sfrok_bavail = 0;
+
+ return &res;
+}
diff --git a/contrib/amd/include/am_compat.h b/contrib/amd/include/am_compat.h
new file mode 100644
index 000000000000..ccb3528beae6
--- /dev/null
+++ b/contrib/amd/include/am_compat.h
@@ -0,0 +1,260 @@
+/*
+ * am_compat.h:
+ *
+ * This file contains compatibility functions and macros, all of which
+ * should be auto-discovered, but for one reason or another (mostly
+ * brain-damage on the part of system designers and header files) they cannot.
+ *
+ * Each compatibility macro/function must include instructions on how/when
+ * it can be removed the am-utils code.
+ *
+ */
+
+#ifndef _AM_COMPAT_H
+# define _AM_COMPAT_H
+
+/*
+ * incomplete mount options definitions (sunos4, irix6, linux, etc.)
+ */
+
+
+/*
+ * Complete MNTTAB_OPT_* options based on MNT2_NFS_OPT_* mount options.
+ */
+#if defined(MNT2_NFS_OPT_ACDIRMAX) && !defined(MNTTAB_OPT_ACDIRMAX)
+# define MNTTAB_OPT_ACDIRMAX "acdirmax"
+#endif /* defined(MNT2_NFS_OPT_ACDIRMAX) && !defined(MNTTAB_OPT_ACDIRMAX) */
+
+#if defined(MNT2_NFS_OPT_ACDIRMIN) && !defined(MNTTAB_OPT_ACDIRMIN)
+# define MNTTAB_OPT_ACDIRMIN "acdirmin"
+#endif /* defined(MNT2_NFS_OPT_ACDIRMIN) && !defined(MNTTAB_OPT_ACDIRMIN) */
+
+#if defined(MNT2_NFS_OPT_ACREGMAX) && !defined(MNTTAB_OPT_ACREGMAX)
+# define MNTTAB_OPT_ACREGMAX "acregmax"
+#endif /* defined(MNT2_NFS_OPT_ACREGMAX) && !defined(MNTTAB_OPT_ACREGMAX) */
+
+#if defined(MNT2_NFS_OPT_ACREGMIN) && !defined(MNTTAB_OPT_ACREGMIN)
+# define MNTTAB_OPT_ACREGMIN "acregmin"
+#endif /* defined(MNT2_NFS_OPT_ACREGMIN) && !defined(MNTTAB_OPT_ACREGMIN) */
+
+#if !defined(MNTTAB_OPT_IGNORE)
+/* SunOS 4.1.x and others define "noauto" option, but not "auto" */
+# if defined(MNTTAB_OPT_NOAUTO) && !defined(MNTTAB_OPT_AUTO)
+# define MNTTAB_OPT_AUTO "auto"
+# endif /* defined(MNTTAB_OPT_NOAUTO) && !defined(MNTTAB_OPT_AUTO) */
+#endif /* !defined(MNTTAB_OPT_IGNORE) */
+
+#if defined(MNT2_NFS_OPT_NOAC) && !defined(MNTTAB_OPT_NOAC)
+# define MNTTAB_OPT_NOAC "noac"
+#endif /* defined(MNT2_NFS_OPT_NOAC) && !defined(MNTTAB_OPT_NOAC) */
+
+#if defined(MNT2_NFS_OPT_NOCONN) && !defined(MNTTAB_OPT_NOCONN)
+# define MNTTAB_OPT_NOCONN "noconn"
+# ifndef MNTTAB_OPT_CONN
+# define MNTTAB_OPT_CONN "conn"
+# endif /* MNTTAB_OPT_CONN */
+#endif /* defined(MNT2_NFS_OPT_NOCONN) && !defined(MNTTAB_OPT_NOCONN) */
+
+#if defined(MNT2_NFS_OPT_PGTHRESH) && !defined(MNTTAB_OPT_PGTHRESH)
+# define MNTTAB_OPT_PGTHRESH "pgthresh"
+#endif /* defined(MNT2_NFS_OPT_PGTHRESH) && !defined(MNTTAB_OPT_PGTHRESH) */
+
+#if defined(MNT2_NFS_OPT_RETRANS) && !defined(MNTTAB_OPT_RETRANS)
+# define MNTTAB_OPT_RETRANS "retrans"
+#endif /* defined(MNT2_NFS_OPT_RETRANS) && !defined(MNTTAB_OPT_RETRANS) */
+
+#if defined(MNT2_NFS_OPT_RSIZE) && !defined(MNTTAB_OPT_RSIZE)
+# define MNTTAB_OPT_RSIZE "rsize"
+#endif /* defined(MNT2_NFS_OPT_RSIZE) && !defined(MNTTAB_OPT_RSIZE) */
+
+#if defined(MNT2_NFS_OPT_SOFT) && !defined(MNTTAB_OPT_SOFT)
+# define MNTTAB_OPT_SOFT "soft"
+# ifndef MNTTAB_OPT_HARD
+# define MNTTAB_OPT_HARD "hard"
+# endif /* not MNTTAB_OPT_HARD */
+#endif /* defined(MNT2_NFS_OPT_SOFT) && !defined(MNTTAB_OPT_SOFT) */
+
+#if defined(MNT2_NFS_OPT_TIMEO) && !defined(MNTTAB_OPT_TIMEO)
+# define MNTTAB_OPT_TIMEO "timeo"
+#endif /* defined(MNT2_NFS_OPT_TIMEO) && !defined(MNTTAB_OPT_TIMEO) */
+
+#if defined(MNT2_NFS_OPT_WSIZE) && !defined(MNTTAB_OPT_WSIZE)
+# define MNTTAB_OPT_WSIZE "wsize"
+#endif /* defined(MNT2_NFS_OPT_WSIZE) && !defined(MNTTAB_OPT_WSIZE) */
+
+#if defined(MNT2_NFS_OPT_MAXGRPS) && !defined(MNTTAB_OPT_MAXGROUPS)
+# define MNTTAB_OPT_MAXGROUPS "maxgroups"
+#endif /* defined(MNT2_NFS_OPT_MAXGRPS) && !defined(MNTTAB_OPT_MAXGROUPS) */
+
+/*
+ * Complete MNTTAB_OPT_* options based on MNT2_CDFS_OPT_* mount options.
+ */
+#if defined(MNT2_CDFS_OPT_DEFPERM) && !defined(MNTTAB_OPT_DEFPERM)
+# define MNTTAB_OPT_DEFPERM "defperm"
+#endif /* defined(MNT2_CDFS_OPT_DEFPERM) && !defined(MNTTAB_OPT_DEFPERM) */
+
+#if defined(MNT2_CDFS_OPT_NODEFPERM) && !defined(MNTTAB_OPT_NODEFPERM)
+# define MNTTAB_OPT_NODEFPERM "nodefperm"
+/*
+ * DEC OSF/1 V3.x/Digital UNIX V4.0 have M_NODEFPERM only, but
+ * both mnttab ops.
+ */
+# ifndef MNTTAB_OPT_DEFPERM
+# define MNTTAB_OPT_DEFPERM "defperm"
+# endif /* not MNTTAB_OPT_DEFPERM */
+#endif /* defined(MNT2_CDFS_OPT_NODEFPERM) && !defined(MNTTAB_OPT_NODEFPERM) */
+
+#if defined(MNT2_CDFS_OPT_NOVERSION) && !defined(MNTTAB_OPT_NOVERSION)
+# define MNTTAB_OPT_NOVERSION "noversion"
+#endif /* defined(MNT2_CDFS_OPT_NOVERSION) && !defined(MNTTAB_OPT_NOVERSION) */
+
+#if defined(MNT2_CDFS_OPT_RRIP) && !defined(MNTTAB_OPT_RRIP)
+# define MNTTAB_OPT_RRIP "rrip"
+#endif /* defined(MNT2_CDFS_OPT_RRIP) && !defined(MNTTAB_OPT_RRIP) */
+
+/*
+ * Complete MNTTAB_OPT_* options based on MNT2_GEN_OPT_* mount options.
+ */
+#if defined(MNT2_GEN_OPT_GRPID) && !defined(MNTTAB_OPT_GRPID)
+# define MNTTAB_OPT_GRPID "grpid"
+#endif /* defined(MNT2_GEN_OPT_GRPID) && !defined(MNTTAB_OPT_GRPID) */
+
+#if defined(MNT2_GEN_OPT_NOCACHE) && !defined(MNTTAB_OPT_NOCACHE)
+# define MNTTAB_OPT_NOCACHE "nocache"
+#endif /* defined(MNT2_GEN_OPT_NOCACHE) && !defined(MNTTAB_OPT_NOCACHE) */
+
+#if defined(MNT2_GEN_OPT_NOSUID) && !defined(MNTTAB_OPT_NOSUID)
+# define MNTTAB_OPT_NOSUID "nosuid"
+#endif /* defined(MNT2_GEN_OPT_NOSUID) && !defined(MNTTAB_OPT_NOSUID) */
+
+#if defined(MNT2_GEN_OPT_OVERLAY) && !defined(MNTTAB_OPT_OVERLAY)
+# define MNTTAB_OPT_OVERLAY "overlay"
+#endif /* defined(MNT2_GEN_OPT_OVERLAY) && !defined(MNTTAB_OPT_OVERLAY) */
+
+/*
+ * Complete MNTTAB_OPT_* options and their inverse based on MNT2_GEN_OPT_*
+ * options.
+ */
+#if defined(MNT2_GEN_OPT_NODEV) && !defined(MNTTAB_OPT_NODEV)
+# define MNTTAB_OPT_NODEV "nodev"
+/* this is missing under some versions of Linux */
+# ifndef MNTTAB_OPT_DEV
+# define MNTTAB_OPT_DEV "dev"
+# endif /* not MNTTAB_OPT_DEV */
+#endif /* defined(MNT2_GEN_OPT_NODEV) && !defined(MNTTAB_OPT_NODEV) */
+
+#if defined(MNT2_GEN_OPT_NOEXEC) && !defined(MNTTAB_OPT_NOEXEC)
+# define MNTTAB_OPT_NOEXEC "noexec"
+/* this is missing under some versions of Linux */
+# ifndef MNTTAB_OPT_EXEC
+# define MNTTAB_OPT_EXEC "exec"
+# endif /* not MNTTAB_OPT_EXEC */
+#endif /* defined(MNT2_GEN_OPT_NOEXEC) && !defined(MNTTAB_OPT_NOEXEC) */
+
+#if defined(MNT2_GEN_OPT_QUOTA) && !defined(MNTTAB_OPT_QUOTA)
+# define MNTTAB_OPT_QUOTA "quota"
+#endif /* defined(MNT2_GEN_OPT_QUOTA) && !defined(MNTTAB_OPT_QUOTA) */
+
+#if defined(MNT2_GEN_OPT_SYNC) && !defined(MNTTAB_OPT_SYNC)
+# define MNTTAB_OPT_SYNC "sync"
+#endif /* defined(MNT2_GEN_OPT_SYNC) && !defined(MNTTAB_OPT_SYNC) */
+
+
+/*
+ * Add missing MNTTAB_OPT_* options.
+ */
+#ifndef MNTTAB_OPT_ACTIMEO
+# define MNTTAB_OPT_ACTIMEO "actimeo"
+#endif /* not MNTTAB_OPT_ACTIMEO */
+
+#ifndef MNTTAB_OPT_INTR
+# define MNTTAB_OPT_INTR "intr"
+#endif /* not MNTTAB_OPT_INTR */
+
+#ifndef MNTTAB_OPT_PORT
+# define MNTTAB_OPT_PORT "port"
+#endif /* not MNTTAB_OPT_PORT */
+
+#ifndef MNTTAB_OPT_RETRANS
+# define MNTTAB_OPT_RETRANS "retrans"
+#endif /* not MNTTAB_OPT_RETRANS */
+
+#ifndef MNTTAB_OPT_RETRY
+# define MNTTAB_OPT_RETRY "retry"
+#endif /* not MNTTAB_OPT_RETRY */
+
+#ifndef MNTTAB_OPT_RO
+# define MNTTAB_OPT_RO "ro"
+#endif /* not MNTTAB_OPT_RO */
+
+#ifndef MNTTAB_OPT_RSIZE
+# define MNTTAB_OPT_RSIZE "rsize"
+#endif /* not MNTTAB_OPT_RSIZE */
+
+#ifndef MNTTAB_OPT_RW
+# define MNTTAB_OPT_RW "rw"
+#endif /* not MNTTAB_OPT_RW */
+
+#ifndef MNTTAB_OPT_TIMEO
+# define MNTTAB_OPT_TIMEO "timeo"
+#endif /* not MNTTAB_OPT_TIMEO */
+
+#ifndef MNTTAB_OPT_WSIZE
+# define MNTTAB_OPT_WSIZE "wsize"
+#endif /* not MNTTAB_OPT_WSIZE */
+
+
+/*
+ * Incomplete filesystem definitions (sunos4, irix6, solaris2)
+ */
+#if defined(HAVE_FS_CDFS) && defined(MOUNT_TYPE_CDFS) && !defined(MNTTYPE_CDFS)
+# define MNTTYPE_CDFS "hsfs"
+#endif /* defined(HAVE_FS_CDFS) && defined(MOUNT_TYPE_CDFS) && !defined(MNTTYPE_CDFS) */
+
+#ifndef cdfs_args_t
+/*
+ * Solaris has an HSFS filesystem, but does not define hsfs_args.
+ * XXX: the definition here for solaris is wrong, since under solaris,
+ * hsfs_args should be a single integer used as a bit-field for options.
+ * so this code has to be fixed later. -Erez.
+ */
+struct hsfs_args {
+ char *fspec; /* name of filesystem to mount */
+ int norrip;
+};
+# define cdfs_args_t struct hsfs_args
+# define HAVE_FIELD_CDFS_ARGS_T_NORRIP
+#endif /* not cdfs_args_t */
+
+/*
+ * if does not define struct pc_args, assume integer bit-field (irix6)
+ */
+#if defined(HAVE_FS_PCFS) && !defined(pcfs_args_t)
+# define pcfs_args_t u_int
+#endif /* defined(HAVE_FS_PCFS) && !defined(pcfs_args_t) */
+
+/*
+ * if does not define struct ufs_args, assume integer bit-field (linux)
+ */
+#if defined(HAVE_FS_UFS) && !defined(ufs_args_t)
+# define ufs_args_t u_int
+#endif /* defined(HAVE_FS_UFS) && !defined(ufs_args_t) */
+
+#if defined(HAVE_FS_AUTOFS) && defined(MOUNT_TYPE_AUTOFS) && !defined(MNTTYPE_AUTOFS)
+# define MNTTYPE_AUTOFS "autofs"
+#endif /* defined(HAVE_FS_AUTOFS) && defined(MOUNT_TYPE_AUTOFS) && !defined(MNTTYPE_AUTOFS) */
+
+/*
+ * If NFS3, then make sure that "proto" and "vers" mnttab options
+ * are available.
+ */
+#ifdef HAVE_FS_NFS3
+# ifndef MNTTAB_OPT_VERS
+# define MNTTAB_OPT_VERS "vers"
+# endif /* not MNTTAB_OPT_VERS */
+# ifndef MNTTAB_OPT_PROTO
+# define MNTTAB_OPT_PROTO "proto"
+# endif /* not MNTTAB_OPT_PROTO */
+#endif /* not HAVE_FS_NFS3 */
+
+#endif /* not _AM_COMPAT_H */
diff --git a/contrib/amd/include/am_defs.h b/contrib/amd/include/am_defs.h
new file mode 100644
index 000000000000..690d03305091
--- /dev/null
+++ b/contrib/amd/include/am_defs.h
@@ -0,0 +1,1320 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: am_defs.h,v 1.1 1996/01/13 23:23:39 ezk Exp ezk $
+ *
+ */
+
+/*
+ * Definitions that are not specific to the am-utils package, but
+ * are rather generic, and can be used elsewhere.
+ */
+
+#ifndef _AM_DEFS_H
+#define _AM_DEFS_H
+
+/*
+ * Actions to take if ANSI C.
+ */
+#if STDC_HEADERS
+# include <string.h>
+/* for function prototypes */
+# define P(x) x
+# define P_void void
+#else /* not STDC_HEADERS */
+/* empty function prototypes */
+# define P(x) ()
+# define P_void
+# ifndef HAVE_STRCHR
+# define strchr index
+# define strrchr rindex
+# endif /* not HAVE_STRCHR */
+char *strchr(), *strrchr(), *strdup();
+#endif /* not STDC_HEADERS */
+
+/*
+ * How to handle signals of any type
+ */
+#ifdef HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+#endif /* HAVE_SYS_WAIT_H */
+#ifndef WEXITSTATUS
+# define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
+#endif /* not WEXITSTATUS */
+#ifndef WIFEXITED
+# define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
+#endif /* not WIFEXITED */
+
+/*
+ * Actions to take regarding <time.h> and <sys/time.h>.
+ */
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else /* not TIME_WITH_SYS_TIME */
+# if HAVE_SYS_TIME_H
+# include <sys/time.h>
+# else /* not HAVE_SYS_TIME_H */
+# include <time.h>
+# endif /* not HAVE_SYS_TIME_H */
+#endif /* not TIME_WITH_SYS_TIME */
+
+/*
+ * Actions to take if <machine/endian.h> exists.
+ */
+#ifdef HAVE_MACHINE_ENDIAN_H
+# include <machine/endian.h>
+#endif /* HAVE_MACHINE_ENDIAN_H */
+
+/*
+ * Big-endian or little-endian?
+ */
+#ifdef WORDS_BIGENDIAN
+# define ARCH_ENDIAN "big"
+#else /* not WORDS_BIGENDIAN */
+# define ARCH_ENDIAN "little"
+#endif /* not WORDS_BIGENDIAN */
+
+/*
+ * Actions to take if HAVE_SYS_TYPES_H is defined.
+ */
+#if HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif /* HAVE_SYS_TYPES_H */
+
+/*
+ * Actions to take if HAVE_UNISTD_H is defined.
+ */
+#if HAVE_UNISTD_H
+# include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+
+/* after <unistd.h>, check if this is a POSIX.1 system */
+#ifdef _POSIX_VERSION
+/* Code for POSIX.1 systems. */
+#endif /* _POSIX_VERSION */
+
+/*
+ * Variable length argument lists.
+ * Must use only one of the two!
+ */
+#ifdef HAVE_STDARG_H
+# include <stdarg.h>
+/*
+ * On Solaris 2.6, <sys/varargs.h> is included in <sys/fs/autofs.h>
+ * So this ensures that only one is included.
+ */
+# ifndef _SYS_VARARGS_H
+# define _SYS_VARARGS_H
+# endif /* not _SYS_VARARGS_H */
+#else /* not HAVE_STDARG_H */
+# ifdef HAVE_VARARGS_H
+# include <varargs.h>
+# endif /* HAVE_VARARGS_H */
+#endif /* not HAVE_STDARG_H */
+
+/*
+ * Pick the right header file and macros for directory processing functions.
+ */
+#if HAVE_DIRENT_H
+# include <dirent.h>
+# define NAMLEN(dirent) strlen((dirent)->d_name)
+#else /* not HAVE_DIRENT_H */
+# define dirent direct
+# define NAMLEN(dirent) (dirent)->d_namlen
+# if HAVE_SYS_NDIR_H
+# include <sys/ndir.h>
+# endif /* HAVE_SYS_NDIR_H */
+# if HAVE_SYS_DIR_H
+# include <sys/dir.h>
+# endif /* HAVE_SYS_DIR_H */
+# if HAVE_NDIR_H
+# include <ndir.h>
+# endif /* HAVE_NDIR_H */
+#endif /* not HAVE_DIRENT_H */
+
+/*
+ * Actions to take if HAVE_FCNTL_H is defined.
+ */
+#if HAVE_FCNTL_H
+# include <fcntl.h>
+#endif /* HAVE_FCNTL_H */
+
+/*
+ * Actions to take if HAVE_MEMORY_H is defined.
+ */
+#if HAVE_MEMORY_H
+# include <memory.h>
+#endif /* HAVE_MEMORY_H */
+
+/*
+ * Actions to take if HAVE_SYS_FILE_H is defined.
+ */
+#if HAVE_SYS_FILE_H
+# include <sys/file.h>
+#endif /* HAVE_SYS_FILE_H */
+
+/*
+ * Actions to take if HAVE_SYS_IOCTL_H is defined.
+ */
+#if HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif /* HAVE_SYS_IOCTL_H */
+
+/*
+ * Actions to take if HAVE_SYSLOG_H or HAVE_SYS_SYSLOG_H is defined.
+ */
+#ifdef HAVE_SYSLOG_H
+# include <syslog.h>
+#else /* not HAVE_SYSLOG_H */
+# if HAVE_SYS_SYSLOG_H
+# include <sys/syslog.h>
+# endif /* HAVE_SYS_SYSLOG_H */
+#endif /* HAVE_SYSLOG_H */
+
+/*
+ * Actions to take if <sys/param.h> exists.
+ */
+#ifdef HAVE_SYS_PARAM_H
+# include <sys/param.h>
+#endif /* HAVE_SYS_PARAM_H */
+
+/*
+ * Actions to take if <sys/socket.h> exists.
+ */
+#ifdef HAVE_SYS_SOCKET_H
+# include <sys/socket.h>
+#endif /* HAVE_SYS_SOCKET_H */
+
+/*
+ * Actions to take if <rpc/rpc.h> exists.
+ */
+#ifdef HAVE_RPC_RPC_H
+/*
+ * Turn on PORTMAP, so that additional header files would get included
+ * and the important definition for UDPMSGSIZE is included too.
+ */
+# ifndef PORTMAP
+# define PORTMAP
+# endif /* not PORTMAP */
+# include <rpc/rpc.h>
+# ifndef XDRPROC_T_TYPE
+typedef bool_t (*xdrproc_t) __P ((XDR *, __ptr_t, ...));
+# endif /* not XDRPROC_T_TYPE */
+#endif /* HAVE_RPC_RPC_H */
+
+/*
+ * Actions to take if <rpc/types.h> exists.
+ */
+#ifdef HAVE_RPC_TYPES_H
+# include <rpc/types.h>
+#endif /* HAVE_RPC_TYPES_H */
+
+/*
+ * Actions to take if <rpc/xdr.h> exists.
+ */
+/* Prevent multiple inclusion on Ultrix 4 */
+#if defined(HAVE_RPC_XDR_H) && !defined(__XDR_HEADER__)
+# include <rpc/xdr.h>
+#endif /* defined(HAVE_RPC_XDR_H) && !defined(__XDR_HEADER__) */
+
+/*
+ * Actions to take if <malloc.h> exists.
+ */
+#ifdef HAVE_MALLOC_H
+# include <malloc.h>
+#endif /* HAVE_MALLOC_H */
+
+/*
+ * Actions to take if <mntent.h> exists.
+ */
+#ifdef HAVE_MNTENT_H
+/* some systems need <stdio.h> before <mntent.h> is included */
+# ifdef HAVE_STDIO_H
+# include <stdio.h>
+# endif /* HAVE_STDIO_H */
+# include <mntent.h>
+#endif /* HAVE_MNTENT_H */
+
+/*
+ * Actions to take if <sys/errno.h> exists.
+ */
+#ifdef HAVE_SYS_ERRNO_H
+# include <sys/errno.h>
+extern int errno;
+#endif /* HAVE_SYS_ERRNO_H */
+
+/*
+ * Actions to take if <sys/fsid.h> exists.
+ */
+#ifdef HAVE_SYS_FSID_H
+# include <sys/fsid.h>
+#endif /* HAVE_SYS_FSID_H */
+
+/*
+ * Actions to take if <sys/utsname.h> exists.
+ */
+#ifdef HAVE_SYS_UTSNAME_H
+# include <sys/utsname.h>
+#endif /* HAVE_SYS_UTSNAME_H */
+
+/*
+ * Actions to take if <sys/mntent.h> exists.
+ */
+#ifdef HAVE_SYS_MNTENT_H
+# include <sys/mntent.h>
+#endif /* HAVE_SYS_MNTENT_H */
+
+/*
+ * Actions to take if <ndbm.h> exists.
+ * Should be included before <rpcsvc/yp_prot.h> because on some systems
+ * like Linux, it also defines "struct datum".
+ */
+#ifdef HAVE_NDBM_H
+# include <ndbm.h>
+# ifndef DATUM
+/* ensure that struct datum is not included again from <rpcsvc/yp_prot.h> */
+# define DATUM
+# endif /* not DATUM */
+#endif /* HAVE_NDBM_H */
+
+/*
+ * Actions to take if <net/errno.h> exists.
+ */
+#ifdef HAVE_NET_ERRNO_H
+# include <net/errno.h>
+#endif /* HAVE_NET_ERRNO_H */
+
+/*
+ * Actions to take if <net/if.h> exists.
+ */
+#ifdef HAVE_NET_ROUTE_H
+# include <net/route.h>
+#endif /* HAVE_NET_ROUTE_H */
+
+/*
+ * Actions to take if <net/if.h> exists.
+ */
+#ifdef HAVE_SYS_MBUF_H
+# include <sys/mbuf.h>
+/*
+ * OSF4 (DU-4.0) defines m_next and m_data also in <sys/mount> so I must
+ # undefine them here to avoid conflicts.
+ */
+# ifdef m_next
+# undef m_next
+# endif /* m_next */
+# ifdef m_data
+# undef m_data
+# endif /* m_data */
+/*
+ * AIX 3 defines MFREE and m_flags also in <sys/mount>.
+ */
+# ifdef m_flags
+# undef m_flags
+# endif /* m_flags */
+# ifdef MFREE
+# undef MFREE
+# endif /* MFREE */
+#endif /* HAVE_SYS_MBUF_H */
+
+/*
+ * Actions to take if <net/if.h> exists.
+ */
+#ifdef HAVE_NET_IF_H
+# include <net/if.h>
+#endif /* HAVE_NET_IF_H */
+
+/*
+ * Actions to take if <netdb.h> exists.
+ */
+#ifdef HAVE_NETDB_H
+# include <netdb.h>
+#endif /* HAVE_NETDB_H */
+
+/*
+ * Actions to take if <netdir.h> exists.
+ */
+#ifdef HAVE_NETDIR_H
+# include <netdir.h>
+#endif /* HAVE_NETDIR_H */
+
+/*
+ * Actions to take if <net/if_var.h> exists.
+ */
+#ifdef HAVE_NET_IF_VAR_H
+# include <net/if_var.h>
+#endif /* HAVE_NET_IF_VAR_H */
+
+/*
+ * Actions to take if <netinet/if_ether.h> exists.
+ */
+#ifdef HAVE_NETINET_IF_ETHER_H
+# include <netinet/if_ether.h>
+#endif /* HAVE_NETINET_IF_ETHER_H */
+
+/*
+ * Actions to take if <netinet/in.h> exists.
+ */
+#ifdef HAVE_NETINET_IN_H
+# include <netinet/in.h>
+#endif /* HAVE_NETINET_IN_H */
+
+/*
+ * Actions to take if <rpcsvc/yp_prot.h> exists.
+ */
+#ifdef HAVE_RPCSVC_YP_PROT_H
+# include <rpcsvc/yp_prot.h>
+#endif /* HAVE_RPCSVC_YP_PROT_H */
+
+/*
+ * Actions to take if <rpcsvc/ypclnt.h> exists.
+ */
+#ifdef HAVE_RPCSVC_YPCLNT_H
+# include <rpcsvc/ypclnt.h>
+#endif /* HAVE_RPCSVC_YPCLNT_H */
+
+/*
+ * Actions to take if <sys/ucred.h> exists.
+ */
+#ifdef HAVE_SYS_UCRED_H
+# include <sys/ucred.h>
+#endif /* HAVE_SYS_UCRED_H */
+
+
+/*
+ * Actions to take if <sys/mount.h> exists.
+ */
+#ifdef HAVE_SYS_MOUNT_H
+/*
+ * Some operating systems must define these variables to get
+ * NFS and other definitions included.
+ */
+# ifndef NFSCLIENT
+# define NFSCLIENT
+# endif /* not NFSCLIENT */
+# ifndef NFS
+# define NFS
+# endif /* not NFS */
+# ifndef PCFS
+# define PCFS
+# endif /* not PCFS */
+# ifndef LOFS
+# define LOFS
+# endif /* not LOFS */
+# ifndef RFS
+# define RFS
+# endif /* not RFS */
+# ifndef MSDOSFS
+# define MSDOSFS
+# endif /* not MSDOSFS */
+# ifndef MFS
+# define MFS
+# endif /* not MFS */
+# ifndef CD9660
+# define CD9660
+# endif /* not CD9660 */
+# ifndef NFS
+# define NFS
+# endif /* not NFS */
+# include <sys/mount.h>
+#endif /* HAVE_SYS_MOUNT_H */
+
+#ifdef HAVE_SYS_VMOUNT_H
+# include <sys/vmount.h>
+#endif /* HAVE_SYS_VMOUNT_H */
+
+/*
+ * Actions to take if <linux/fs.h> exists.
+ */
+#ifdef HAVE_LINUX_FS_H
+/*
+ * There's a conflict of definitions on redhat alpha linux between
+ * <netinet/in.h> and <linux/fs.h>.
+ */
+# ifdef HAVE_SOCKETBITS_H
+/* conflicts with <socketbits.h> */
+# define _LINUX_SOCKET_H
+# undef BLKFLSBUF
+# undef BLKGETSIZE
+# undef BLKRAGET
+# undef BLKRASET
+# undef BLKROGET
+# undef BLKROSET
+# undef BLKRRPART
+# undef MS_MGC_VAL
+# undef MS_RMT_MASK
+/* conflicts with <waitflags.h> */
+# undef WNOHANG
+# undef WUNTRACED
+/* conflicts with <statfsbuf.h> */
+# define _SYS_STATFS_H
+# endif /* HAVE_SOCKETBITS_H */
+# include <linux/fs.h>
+#endif /* HAVE_LINUX_FS_H */
+
+#ifdef HAVE_CDFS_CDFS_MOUNT_H
+# include <cdfs/cdfs_mount.h>
+#endif /* HAVE_CDFS_CDFS_MOUNT_H */
+
+#ifdef HAVE_CDFS_CDFSMOUNT_H
+# include <cdfs/cdfsmount.h>
+#endif /* HAVE_CDFS_CDFSMOUNT_H */
+
+/*
+ * Actions to take if <linux/auto_fs.h> exists.
+ */
+#ifdef HAVE_LINUX_AUTO_FS_H
+# include <linux/auto_fs.h>
+#endif /* HAVE_LINUX_AUTO_FS_H */
+
+/*
+ * Actions to take if <sys/fs/autofs.h> exists.
+ */
+#ifdef HAVE_SYS_FS_AUTOFS_H
+# include <sys/fs/autofs.h>
+#endif /* HAVE_SYS_FS_AUTOFS_H */
+
+/*
+ * Actions to take if <sys/fs/autofs_prot.h> exists.
+ */
+#ifdef HAVE_SYS_FS_AUTOFS_PROT_H
+# include <sys/fs/autofs_prot.h>
+#endif /* HAVE_SYS_FS_AUTOFS_PROT_H */
+
+/*
+ * NFS PROTOCOL HEADER FILES:
+ */
+
+/*
+ * Actions to take if <nfs/export.h> exists.
+ */
+#ifdef HAVE_NFS_EXPORT_H
+# include <nfs/export.h>
+#endif /* HAVE_NFS_EXPORT_H */
+
+/****************************************************************************
+ ** IMPORTANT!!! **
+ ** We always include am-util's amu_nfs_prot.h. **
+ ** That is actually defined in "conf/nfs_prot/nfs_prot_${host_os_name}.h" **
+ ****************************************************************************/
+#include <amu_nfs_prot.h>
+
+/*
+ * DO NOT INCLUDE THESE FILES:
+ * They conflicts with other NFS headers and are generally not needed.
+ */
+#ifdef DO_NOT_INCLUDE
+# ifdef HAVE_NFS_NFS_CLNT_H
+# include <nfs/nfs_clnt.h>
+# endif /* HAVE_NFS_NFS_CLNT_H */
+# ifdef HAVE_LINUX_NFS_H
+# include <linux/nfs.h>
+# endif /* HAVE_LINUX_NFS_H */
+#endif /* DO NOT INCLUDE */
+
+/*
+ * Actions to take if one of the nfs headers exists.
+ */
+#ifdef HAVE_NFS_NFS_GFS_H
+# include <nfs/nfs_gfs.h>
+#endif /* HAVE_NFS_NFS_GFS_H */
+#ifdef HAVE_NFS_MOUNT_H
+# include <nfs/mount.h>
+#endif /* HAVE_NFS_MOUNT_H */
+#ifdef HAVE_NFS_NFS_MOUNT_H_off
+/* broken on netxtep3 (includes non-existing headers) */
+# include <nfs/nfs_mount.h>
+#endif /* HAVE_NFS_NFS_MOUNT_H */
+#ifdef HAVE_NFS_PATHCONF_H
+# include <nfs/pathconf.h>
+#endif /* HAVE_NFS_PATHCONF_H */
+#ifdef HAVE_SYS_FS_NFS_MOUNT_H
+# include <sys/fs/nfs/mount.h>
+#endif /* HAVE_SYS_FS_NFS_MOUNT_H */
+#ifdef HAVE_SYS_FS_NFS_NFS_CLNT_H
+# include <sys/fs/nfs/nfs_clnt.h>
+#endif /* HAVE_SYS_FS_NFS_NFS_CLNT_H */
+#ifdef HAVE_SYS_FS_NFS_CLNT_H
+# include <sys/fs/nfs_clnt.h>
+#endif /* HAVE_SYS_FS_NFS_CLNT_H */
+#ifdef HAVE_LINUX_NFS_MOUNT_H
+# include <linux/nfs_mount.h>
+#endif /* HAVE_LINUX_NFS_MOUNT_H */
+
+/*
+ * Actions to take if <pwd.h> exists.
+ */
+#ifdef HAVE_PWD_H
+# include <pwd.h>
+#endif /* HAVE_PWD_H */
+
+/*
+ * Actions to take if <hesiod.h> exists.
+ */
+#ifdef HAVE_HESIOD_H
+# include <hesiod.h>
+#endif /* HAVE_HESIOD_H */
+
+/*
+ * Actions to take if <lber.h> exists.
+ * This header file is required before <ldap.h> can be included.
+ */
+#ifdef HAVE_LBER_H
+# include <lber.h>
+#endif /* HAVE_LBER_H */
+
+/*
+ * Actions to take if <ldap.h> exists.
+ */
+#ifdef HAVE_LDAP_H
+# include <ldap.h>
+#endif /* HAVE_LDAP_H */
+
+/*
+ * Actions to take if <arpa/nameser.h> exists.
+ * Should be included before <resolv.h>.
+ */
+#ifdef HAVE_ARPA_NAMESER_H
+# ifdef NOERROR
+# undef NOERROR
+# endif /* NOERROR */
+/*
+ * Conflicts with <sys/tpicommon.h> which is included from <sys/tiuser.h>
+ * on Solaris 2.6 systems. So undefine it first.
+ */
+# ifdef T_UNSPEC
+# undef T_UNSPEC
+# endif /* T_UNSPEC */
+# include <arpa/nameser.h>
+#endif /* HAVE_ARPA_NAMESER_H */
+
+/*
+ * Actions to take if <arpa/inet.h> exists.
+ */
+#ifdef HAVE_ARPA_INET_H
+# include <arpa/inet.h>
+#endif /* HAVE_ARPA_INET_H */
+
+/*
+ * Actions to take if <resolv.h> exists.
+ */
+#ifdef HAVE_RESOLV_H
+# include <resolv.h>
+#endif /* HAVE_RESOLV_H */
+
+/*
+ * Actions to take if <sys/uio.h> exists.
+ */
+#ifdef HAVE_SYS_UIO_H
+# include <sys/uio.h>
+#endif /* HAVE_SYS_UIO_H */
+
+/*
+ * Actions to take if <sys/fs/cachefs_fs.h> exists.
+ */
+#ifdef HAVE_SYS_FS_CACHEFS_FS_H
+# include <sys/fs/cachefs_fs.h>
+#endif /* HAVE_SYS_FS_CACHEFS_FS_H */
+
+/*
+ * Actions to take if <sys/fs/pc_fs.h> exists.
+ */
+#ifdef HAVE_SYS_FS_PC_FS_H
+# include <sys/fs/pc_fs.h>
+#endif /* HAVE_SYS_FS_PC_FS_H */
+
+/*
+ * Actions to take if <msdosfs/msdosfsmount.h> exists.
+ */
+#ifdef HAVE_MSDOSFS_MSDOSFSMOUNT_H
+# include <msdosfs/msdosfsmount.h>
+#endif /* HAVE_MSDOSFS_MSDOSFSMOUNT_H */
+
+/*
+ * Actions to take if <sys/fs/tmp.h> exists.
+ */
+#ifdef HAVE_SYS_FS_TMP_H
+# include <sys/fs/tmp.h>
+#endif /* HAVE_SYS_FS_TMP_H */
+
+/*
+ * Actions to take if <sys/fs/ufs_mount.h> exists.
+ */
+#ifdef HAVE_SYS_FS_UFS_MOUNT_H
+# include <sys/fs/ufs_mount.h>
+#endif /* HAVE_SYS_FS_UFS_MOUNT_H */
+
+/*
+ * Actions to take if <sys/fs/efs_clnt.h> exists.
+ */
+#ifdef HAVE_SYS_FS_EFS_CLNT_H
+# include <sys/fs/efs_clnt.h>
+#endif /* HAVE_SYS_FS_EFS_CLNT_H */
+
+/*
+ * Actions to take if <sys/fs/xfs_clnt.h> exists.
+ */
+#ifdef HAVE_SYS_FS_XFS_CLNT_H
+# include <sys/fs/xfs_clnt.h>
+#endif /* HAVE_SYS_FS_XFS_CLNT_H */
+
+/*
+ * Actions to take if <assert.h> exists.
+ */
+#ifdef HAVE_ASSERT_H
+# include <assert.h>
+#endif /* HAVE_ASSERT_H */
+
+/*
+ * Actions to take if <cfs.h> exists.
+ */
+#ifdef HAVE_CFS_H
+# include <cfs.h>
+#endif /* HAVE_CFS_H */
+
+/*
+ * Actions to take if <cluster.h> exists.
+ */
+#ifdef HAVE_CLUSTER_H
+# include <cluster.h>
+#endif /* HAVE_CLUSTER_H */
+
+/*
+ * Actions to take if <ctype.h> exists.
+ */
+#ifdef HAVE_CTYPE_H
+# include <ctype.h>
+#endif /* HAVE_CTYPE_H */
+
+/*
+ * Actions to take if <errno.h> exists.
+ */
+#ifdef HAVE_ERRNO_H
+# include <errno.h>
+#endif /* HAVE_ERRNO_H */
+
+/*
+ * Actions to take if <grp.h> exists.
+ */
+#ifdef HAVE_GRP_H
+# include <grp.h>
+#endif /* HAVE_GRP_H */
+
+/*
+ * Actions to take if <hsfs/hsfs.h> exists.
+ */
+#ifdef HAVE_HSFS_HSFS_H
+# include <hsfs/hsfs.h>
+#endif /* HAVE_HSFS_HSFS_H */
+
+/*
+ * Actions to take if <cdfs/cdfsmount.h> exists.
+ */
+#ifdef HAVE_CDFS_CDFSMOUNT_H
+# include <cdfs/cdfsmount.h>
+#endif /* HAVE_CDFS_CDFSMOUNT_H */
+
+/*
+ * Actions to take if <isofs/cd9660/cd9660_mount.h> exists.
+ */
+#ifdef HAVE_ISOFS_CD9660_CD9660_MOUNT_H
+# include <isofs/cd9660/cd9660_mount.h>
+#endif /* HAVE_ISOFS_CD9660_CD9660_MOUNT_H */
+
+/*
+ * Actions to take if <mount.h> exists.
+ */
+#ifdef HAVE_MOUNT_H
+# include <mount.h>
+#endif /* HAVE_MOUNT_H */
+
+/*
+ * Actions to take if <nsswitch.h> exists.
+ */
+#ifdef HAVE_NSSWITCH_H
+# include <nsswitch.h>
+#endif /* HAVE_NSSWITCH_H */
+
+/*
+ * Actions to take if <rpc/auth_des.h> exists.
+ */
+#ifdef HAVE_RPC_AUTH_DES_H
+# include <rpc/auth_des.h>
+#endif /* HAVE_RPC_AUTH_DES_H */
+
+/*
+ * Actions to take if <rpc/pmap_clnt.h> exists.
+ */
+#ifdef HAVE_RPC_PMAP_CLNT_H
+# include <rpc/pmap_clnt.h>
+#endif /* HAVE_RPC_PMAP_CLNT_H */
+
+/*
+ * Actions to take if <rpc/pmap_prot.h> exists.
+ */
+#ifdef HAVE_RPC_PMAP_PROT_H
+# include <rpc/pmap_prot.h>
+#endif /* HAVE_RPC_PMAP_PROT_H */
+
+
+/*
+ * Actions to take if <rpcsvc/mount.h> exists.
+ * AIX does not protect against this file doubly included,
+ * so I have to do my own protection here.
+ */
+#ifdef HAVE_RPCSVC_MOUNT_H
+# ifndef _RPCSVC_MOUNT_H
+# include <rpcsvc/mount.h>
+# endif /* not _RPCSVC_MOUNT_H */
+#endif /* HAVE_RPCSVC_MOUNT_H */
+
+/*
+ * Actions to take if <rpcsvc/nis.h> exists.
+ */
+#ifdef HAVE_RPCSVC_NIS_H
+# include <rpcsvc/nis.h>
+#endif /* HAVE_RPCSVC_NIS_H */
+
+/*
+ * Actions to take if <setjmp.h> exists.
+ */
+#ifdef HAVE_SETJMP_H
+# include <setjmp.h>
+#endif /* HAVE_SETJMP_H */
+
+/*
+ * Actions to take if <signal.h> exists.
+ */
+#ifdef HAVE_SIGNAL_H
+# include <signal.h>
+#endif /* HAVE_SIGNAL_H */
+
+/*
+ * Actions to take if <string.h> exists.
+ */
+#ifdef HAVE_STRING_H
+# include <string.h>
+#endif /* HAVE_STRING_H */
+
+/*
+ * Actions to take if <strings.h> exists.
+ */
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif /* HAVE_STRINGS_H */
+
+/*
+ * Actions to take if <sys/config.h> exists.
+ */
+#ifdef HAVE_SYS_CONFIG_H
+# include <sys/config.h>
+#endif /* HAVE_SYS_CONFIG_H */
+
+/*
+ * Actions to take if <sys/dg_mount.h> exists.
+ */
+#ifdef HAVE_SYS_DG_MOUNT_H
+# include <sys/dg_mount.h>
+#endif /* HAVE_SYS_DG_MOUNT_H */
+
+/*
+ * Actions to take if <sys/fs_types.h> exists.
+ */
+#ifdef HAVE_SYS_FS_TYPES_H
+/*
+ * Define KERNEL here to avoid multiple definitions of gt_names[] on
+ * Ultrix 4.3.
+ */
+# define KERNEL
+# include <sys/fs_types.h>
+# undef KERNEL
+#endif /* HAVE_SYS_FS_TYPES_H */
+
+/*
+ * Actions to take if <sys/fstyp.h> exists.
+ */
+#ifdef HAVE_SYS_FSTYP_H
+# include <sys/fstyp.h>
+#endif /* HAVE_SYS_FSTYP_H */
+
+/*
+ * Actions to take if <sys/lock.h> exists.
+ */
+#ifdef HAVE_SYS_LOCK_H
+# include <sys/lock.h>
+#endif /* HAVE_SYS_LOCK_H */
+
+/*
+ * Actions to take if <sys/machine.h> exists.
+ */
+#ifdef HAVE_SYS_MACHINE_H
+# include <sys/machine.h>
+#endif /* HAVE_SYS_MACHINE_H */
+
+/*
+ * Actions to take if <sys/mntctl.h> exists.
+ */
+#ifdef HAVE_SYS_MNTCTL_H
+# include <sys/mntctl.h>
+#endif /* HAVE_SYS_MNTCTL_H */
+
+/*
+ * Actions to take if <sys/mnttab.h> exists.
+ */
+#ifdef HAVE_SYS_MNTTAB_H
+# include <sys/mnttab.h>
+#endif /* HAVE_SYS_MNTTAB_H */
+
+/*
+ * Actions to take if <mnttab.h> exists.
+ * Do not include it if MNTTAB is already defined because it probably
+ * came from <sys/mnttab.h> and we do not want conflicting definitions.
+ */
+#if defined(HAVE_MNTTAB_H) && !defined(MNTTAB)
+# include <mnttab.h>
+#endif /* defined(HAVE_MNTTAB_H) && !defined(MNTTAB) */
+
+/*
+ * Actions to take if <netconfig.h> exists.
+ */
+#ifdef HAVE_NETCONFIG_H
+# include <netconfig.h>
+/* Some systems (Solaris 2.5.1) don't declare this external */
+extern char *nc_sperror(void);
+#endif /* HAVE_NETCONFIG_H */
+
+/*
+ * Actions to take if <sys/netconfig.h> exists.
+ */
+#ifdef HAVE_SYS_NETCONFIG_H
+# include <sys/netconfig.h>
+#endif /* HAVE_SYS_NETCONFIG_H */
+
+/*
+ * Actions to take if <sys/pathconf.h> exists.
+ */
+#ifdef HAVE_SYS_PATHCONF_H
+# include <sys/pathconf.h>
+#endif /* HAVE_SYS_PATHCONF_H */
+
+/*
+ * Actions to take if <sys/resource.h> exists.
+ */
+#ifdef HAVE_SYS_RESOURCE_H
+# include <sys/resource.h>
+#endif /* HAVE_SYS_RESOURCE_H */
+
+/*
+ * Actions to take if <sys/sema.h> exists.
+ */
+#ifdef HAVE_SYS_SEMA_H
+# include <sys/sema.h>
+#endif /* HAVE_SYS_SEMA_H */
+
+/*
+ * Actions to take if <sys/signal.h> exists.
+ */
+#ifdef HAVE_SYS_SIGNAL_H
+# include <sys/signal.h>
+#endif /* HAVE_SYS_SIGNAL_H */
+
+/*
+ * Actions to take if <sys/sockio.h> exists.
+ */
+#ifdef HAVE_SYS_SOCKIO_H
+# include <sys/sockio.h>
+#endif /* HAVE_SYS_SOCKIO_H */
+
+/*
+ * Actions to take if <sys/syscall.h> exists.
+ */
+#ifdef HAVE_SYS_SYSCALL_H
+# include <sys/syscall.h>
+#endif /* HAVE_SYS_SYSCALL_H */
+
+/*
+ * Actions to take if <sys/syslimits.h> exists.
+ */
+#ifdef HAVE_SYS_SYSLIMITS_H
+# include <sys/syslimits.h>
+#endif /* HAVE_SYS_SYSLIMITS_H */
+
+/*
+ * Actions to take if <tiuser.h> exists.
+ */
+#ifdef HAVE_TIUSER_H
+/*
+ * Some systems like AIX have multiple definitions for T_NULL and othersd
+ * that are defined first in <arpa/nameser.h>.
+ */
+# ifdef HAVE_ARPA_NAMESER_H
+# ifdef T_NULL
+# undef T_NULL
+# endif /* T_NULL */
+# ifdef T_UNSPEC
+# undef T_UNSPEC
+# endif /* T_UNSPEC */
+# ifdef T_IDLE
+# undef T_IDLE
+# endif /* T_IDLE */
+# endif /* HAVE_ARPA_NAMESER_H */
+# include <tiuser.h>
+#endif /* HAVE_TIUSER_H */
+
+/*
+ * Actions to take if <sys/tiuser.h> exists.
+ */
+#ifdef HAVE_SYS_TIUSER_H
+# include <sys/tiuser.h>
+#endif /* HAVE_SYS_TIUSER_H */
+
+/*
+ * Actions to take if <sys/statfs.h> exists.
+ */
+#ifdef HAVE_SYS_STATFS_H
+# include <sys/statfs.h>
+#endif /* HAVE_SYS_STATFS_H */
+
+/*
+ * Actions to take if <sys/vfs.h> exists.
+ */
+#ifdef HAVE_SYS_VFS_H
+# include <sys/vfs.h>
+#endif /* HAVE_SYS_VFS_H */
+
+/*
+ * Actions to take if <sys/vmount.h> exists.
+ */
+#ifdef HAVE_SYS_VMOUNT_H
+# include <sys/vmount.h>
+#endif /* HAVE_SYS_VMOUNT_H */
+
+/*
+ * Actions to take if <ufs/ufs_mount.h> exists.
+ */
+#ifdef HAVE_UFS_UFS_MOUNT_H
+# include <ufs/ufs_mount.h>
+#endif /* HAVE_UFS_UFS_MOUNT_H */
+
+/*
+ * Are S_ISDIR, S_ISREG, et al broken? If not, include <sys/stat.h>.
+ * Turned off the not using sys/stat.h based on if the macros are
+ * "broken", because they incorrectly get reported as broken on
+ * ncr2.
+ */
+#ifndef STAT_MACROS_BROKEN_notused
+/*
+ * RedHat Linux 4.2 (alpha) has a problem in the headers that causes
+ * dupicate definitions, and also some other nasty bugs. Upgrade to Redhat
+ * 5.0!
+ */
+# ifdef HAVE_SYS_STAT_H
+/* avoid duplicates or conflicts with <linux/stat.h> (RedHat alpha linux) */
+# if defined(S_IFREG) && defined(HAVE_STATBUF_H)
+# include <statbuf.h>
+# undef S_IFBLK
+# undef S_IFCHR
+# undef S_IFDIR
+# undef S_IFIFO
+# undef S_IFLNK
+# undef S_IFMT
+# undef S_IFREG
+# undef S_IFSOCK
+# undef S_IRGRP
+# undef S_IROTH
+# undef S_IRUSR
+# undef S_IRWXG
+# undef S_IRWXO
+# undef S_IRWXU
+# undef S_ISBLK
+# undef S_ISCHR
+# undef S_ISDIR
+# undef S_ISFIFO
+# undef S_ISGID
+# undef S_ISLNK
+# undef S_ISREG
+# undef S_ISSOCK
+# undef S_ISUID
+# undef S_ISVTX
+# undef S_IWGRP
+# undef S_IWOTH
+# undef S_IWUSR
+# undef S_IXGRP
+# undef S_IXOTH
+# undef S_IXUSR
+# endif /* defined(S_IFREG) && defined(HAVE_STATBUF_H) */
+# include <sys/stat.h>
+# endif /* HAVE_SYS_STAT_H */
+#endif /* not STAT_MACROS_BROKEN_notused */
+
+/*
+ * Actions to take if <stdio.h> exists.
+ */
+#ifdef HAVE_STDIO_H
+# include <stdio.h>
+#endif /* HAVE_STDIO_H */
+
+/*
+ * Actions to take if <stdlib.h> exists.
+ */
+#ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+#endif /* HAVE_STDLIB_H */
+
+/*
+ * Actions to take if <regex.h> exists.
+ */
+#ifdef HAVE_REGEX_H
+# include <regex.h>
+#endif /* HAVE_REGEX_H */
+
+
+/****************************************************************************/
+/*
+ * Specific macros we're looking for.
+ */
+#ifndef HAVE_MEMSET
+# ifdef HAVE_BZERO
+# define memset(ptr, val, len) bzero((ptr), (len))
+# else /* not HAVE_BZERO */
+# error Cannot find either memset or bzero!
+# endif /* not HAVE_BZERO */
+#endif /* not HAVE_MEMSET */
+
+#ifndef HAVE_MEMMOVE
+# ifdef HAVE_BCOPY
+# define memmove(to, from, len) bcopy((from), (to), (len))
+# else /* not HAVE_BCOPY */
+# error Cannot find either memmove or bcopy!
+# endif /* not HAVE_BCOPY */
+#endif /* not HAVE_MEMMOVE */
+
+/*
+ * memcmp() is more problematic:
+ * Systems that don't have it, but have bcmp(), will use bcmp() instead.
+ * Those that have it, but it is bad (SunOS 4 doesn't handle
+ * 8 bit comparisons correctly), will get to use am_memcmp().
+ * Otherwise if you have memcmp() and it is good, use it.
+ */
+#ifdef HAVE_MEMCMP
+# ifdef HAVE_BAD_MEMCMP
+# define memcmp am_memcmp
+extern int am_memcmp(const voidp s1, const voidp s2, size_t len);
+# endif /* HAVE_BAD_MEMCMP */
+#else /* not HAVE_MEMCMP */
+# ifdef HAVE_BCMP
+# define memcmp(a, b, len) bcmp((a), (b), (len))
+# endif /* HAVE_BCMP */
+#endif /* not HAVE_MEMCMP */
+
+#ifndef HAVE_SETEUID
+# ifdef HAVE_SETRESUID
+# define seteuid(x) setresuid(-1,(x),-1)
+# else /* not HAVE_SETRESUID */
+# error Cannot find either seteuid or setresuid!
+# endif /* not HAVE_SETRESUID */
+#endif /* not HAVE_SETEUID */
+
+/*
+ * Define type of mntent_t.
+ * Defaults to struct mntent, else struct mnttab. If neither is found, and
+ * the operating system does keep not mount tables in the kernel, then flag
+ * it as an error. If neither is found and the OS keeps mount tables in the
+ * kernel, then define our own version of mntent; it will be needed for amd
+ * to keep its own internal version of the mount tables.
+ */
+#ifdef HAVE_STRUCT_MNTENT
+typedef struct mntent mntent_t;
+#else /* not HAVE_STRUCT_MNTENT */
+# ifdef HAVE_STRUCT_MNTTAB
+typedef struct mnttab mntent_t;
+/* map struct mnttab field names to struct mntent field names */
+# define mnt_fsname mnt_special
+# define mnt_dir mnt_mountp
+# define mnt_opts mnt_mntopts
+# define mnt_type mnt_fstype
+# else /* not HAVE_STRUCT_MNTTAB */
+# ifdef MOUNT_TABLE_ON_FILE
+# error Could not find definition for struct mntent or struct mnttab!
+# else /* not MOUNT_TABLE_ON_FILE */
+typedef struct _am_mntent {
+ char *mnt_fsname; /* name of mounted file system */
+ char *mnt_dir; /* file system path prefix */
+ char *mnt_type; /* MNTTAB_TYPE_* */
+ char *mnt_opts; /* MNTTAB_OPT_* */
+ int mnt_freq; /* dump frequency, in days */
+ int mnt_passno; /* pass number on parallel fsck */
+} mntent_t;
+# endif /* not MOUNT_TABLE_ON_FILE */
+# endif /* not HAVE_STRUCT_MNTTAB */
+#endif /* not HAVE_STRUCT_MNTENT */
+
+
+/*
+ * Complete external definitions missing from some systems.
+ */
+
+#ifndef HAVE_EXTERN_SYS_ERRLIST
+extern const char * const sys_errlist[];
+#endif /* not HAVE_EXTERN_SYS_ERRLIST */
+
+#ifndef HAVE_EXTERN_OPTARG
+extern char *optarg;
+extern int optind;
+#endif /* not HAVE_EXTERN_OPTARG */
+
+#if defined(HAVE_CLNT_SPERRNO) && !defined(HAVE_EXTERN_CLNT_SPERRNO)
+extern char *clnt_sperrno(const enum clnt_stat num);
+#endif /* defined(HAVE_CLNT_SPERRNO) && !defined(HAVE_EXTERN_CLNT_SPERRNO) */
+
+#ifndef HAVE_EXTERN_FREE
+extern void free(voidp);
+#endif /* not HAVE_EXTERN_FREE */
+
+#if defined(HAVE_GET_MYADDRESS) && !defined(HAVE_EXTERN_GET_MYADDRESS)
+extern void get_myaddress(struct sockaddr_in *addr);
+#endif /* defined(HAVE_GET_MYADDRESS) && !defined(HAVE_EXTERN_GET_MYADDRESS) */
+
+#if defined(HAVE_GETDOMAINNAME) && !defined(HAVE_EXTERN_GETDOMAINNAME)
+# if defined(HAVE_MAP_NIS) || defined(HAVE_MAP_NISPLUS)
+extern int getdomainname(char *name, int namelen);
+# endif /* defined(HAVE_MAP_NIS) || defined(HAVE_MAP_NISPLUS) */
+#endif /* defined(HAVE_GETDOMAINNAME) && !defined(HAVE_EXTERN_GETDOMAINNAME) */
+
+#if defined(HAVE_GETDTABLESIZE) && !defined(HAVE_EXTERN_GETDTABLESIZE)
+extern int getdtablesize(void);
+#endif /* defined(HAVE_GETDTABLESIZE) && !defined(HAVE_EXTERN_GETDTABLESIZE) */
+
+#if defined(HAVE_GETHOSTNAME) && !defined(HAVE_EXTERN_GETHOSTNAME)
+extern int gethostname(char *name, int namelen);
+#endif /* defined(HAVE_GETHOSTNAME) && !defined(HAVE_EXTERN_GETHOSTNAME) */
+
+#if defined(HAVE_GETPAGESIZE) && !defined(HAVE_EXTERN_GETPAGESIZE)
+extern int getpagesize(void);
+#endif /* defined(HAVE_GETPAGESIZE) && !defined(HAVE_EXTERN_GETPAGESIZE) */
+
+#ifndef HAVE_EXTERN_GETWD
+extern char *getwd(char *s);
+#endif /* not HAVE_EXTERN_GETWD */
+
+#ifndef HAVE_EXTERN_INNETGR
+extern int innetgr(char *, char *, char *, char *);
+#endif /* not HAVE_EXTERN_INNETGR */
+
+#if defined(HAVE_MKSTEMP) && !defined(HAVE_EXTERN_MKSTEMP)
+extern int mkstemp(char *);
+#endif /* defined(HAVE_MKSTEMP) && !defined(HAVE_EXTERN_MKSTEMP) */
+
+#ifndef HAVE_EXTERN_SBRK
+extern caddr_t sbrk(int incr);
+#endif /* not HAVE_EXTERN_SBRK */
+
+#ifndef HAVE_EXTERN_STRCASECMP
+/*
+ * define this extern even if function does not exist, for it will
+ * be filled in by libamu/strcasecmp.c
+ */
+extern int strcasecmp(const char *s1, const char *s2);
+#endif /* not HAVE_EXTERN_STRCASECMP */
+
+#ifndef HAVE_EXTERN_STRDUP
+/*
+ * define this extern even if function does not exist, for it will
+ * be filled in by libamu/strdup.c
+ */
+extern char *strdup(const char *s);
+#endif /* not HAVE_EXTERN_STRDUP */
+
+#if defined(HAVE_STRSTR) && !defined(HAVE_EXTERN_STRSTR)
+extern char *strstr(const char *s1, const char *s2);
+#endif /* defined(HAVE_STRSTR) && !defined(HAVE_EXTERN_STRSTR) */
+
+#if defined(HAVE_USLEEP) && !defined(HAVE_EXTERN_USLEEP)
+extern int usleep(u_int useconds);
+#endif /* defined(HAVE_USLEEP) && !defined(HAVE_EXTERN_USLEEP) */
+
+#ifndef HAVE_EXTERN_UALARM
+extern u_int ualarm(u_int usecs, u_int interval);
+#endif /* not HAVE_EXTERN_UALARM */
+
+#if defined(HAVE_WAIT3) && !defined(HAVE_EXTERN_WAIT3)
+extern int wait3(int *statusp, int options, struct rusage *rusage);
+#endif /* defined(HAVE_WAIT3) && !defined(HAVE_EXTERN_WAIT3) */
+
+#ifndef HAVE_EXTERN_XDR_OPAQUE_AUTH
+extern bool_t xdr_opaque_auth(XDR *, struct opaque_auth *);
+#endif /* not HAVE_EXTERN_XDR_OPAQUE_AUTH */
+
+#ifndef HAVE_EXTERN_GETLOGIN
+extern char *getlogin(void);
+#endif /* not HAVE_EXTERN_GETLOGIN */
+
+/****************************************************************************/
+/*
+ * amd-specific header files.
+ */
+#ifdef THIS_HEADER_FILE_IS_INCLUDED_ABOVE
+# include <amu_nfs_prot.h>
+#endif /* THIS_HEADER_FILE_IS_INCLUDED_ABOVE */
+#include <am_utils.h>
+#include <amq_defs.h>
+#include <aux_conf.h>
+/* compatibilty with old amd, while autoconfistating it */
+#include <am_compat.h>
+
+
+/****************************************************************************/
+/*
+ * External defintions that depend on other macros available (or not)
+ * and those are probably declared in any of the above headers.
+ */
+
+#ifndef HAVE_HASMNTOPT
+extern char *hasmntopt(mntent_t *mnt, char *opt);
+#endif /* not HAVE_HASMNTOPT */
+
+/*
+ * include definitions of all possible xdr functions that are otherwise
+ * not defined elsewhere.
+ */
+#include <am_xdr_func.h>
+
+#endif /* not _AM_DEFS_H */
diff --git a/contrib/amd/include/am_utils.h b/contrib/amd/include/am_utils.h
new file mode 100644
index 000000000000..f4e0ccfcddbf
--- /dev/null
+++ b/contrib/amd/include/am_utils.h
@@ -0,0 +1,956 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: am_utils.h,v 1.1 1996/01/13 23:23:39 ezk Exp ezk $
+ *
+ */
+
+/*
+ * Definitions that are specific to the am-utils package.
+ */
+
+#ifndef _AM_UTILS_H
+#define _AM_UTILS_H
+
+
+/**************************************************************************/
+/*** MACROS ***/
+/**************************************************************************/
+
+/*
+ * General macros.
+ */
+#ifndef FALSE
+# define FALSE 0
+#endif /* not FALSE */
+#ifndef TRUE
+# define TRUE 1
+#endif /* not TRUE */
+#ifndef MAX
+# define MAX(a, b) ((a) > (b) ? (a) : (b))
+#endif /* not MAX */
+#ifndef MIN
+# define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif /* not MIN */
+
+#define ONE_HOUR (60 * 60) /* One hour in seconds */
+
+#ifndef MAXHOSTNAMELEN
+# ifdef HOSTNAMESZ
+# define MAXHOSTNAMELEN HOSTNAMESZ
+# else /* not HOSTNAMESZ */
+# define MAXHOSTNAMELEN 64
+# endif /* not HOSTNAMESZ */
+#endif /* not MAXHOSTNAMELEN */
+
+/*
+ * String comparison macros
+ */
+#define STREQ(s1, s2) (strcmp((s1), (s2)) == 0)
+#define STRCEQ(s1, s2) (strcasecmp((s1), (s2)) == 0)
+#define NSTREQ(s1, s2, n) (strncmp((s1), (s2), (n)) == 0)
+#define FSTREQ(s1, s2) ((*(s1) == *(s2)) && STREQ((s1),(s2)))
+
+/*
+ * Logging options/flags
+ */
+#define XLOG_FATAL 0x0001
+#define XLOG_ERROR 0x0002
+#define XLOG_USER 0x0004
+#define XLOG_WARNING 0x0008
+#define XLOG_INFO 0x0010
+#define XLOG_DEBUG 0x0020
+#define XLOG_MAP 0x0040
+#define XLOG_STATS 0x0080
+#define XLOG_DEFSTR "all,nomap,nostats" /* Default log options */
+#define XLOG_ALL (XLOG_FATAL|XLOG_ERROR|XLOG_USER|XLOG_WARNING|XLOG_INFO|XLOG_MAP|XLOG_STATS)
+
+#define clocktime() (clock_valid ? clock_valid : time(&clock_valid))
+
+#ifndef ROOT_MAP
+# define ROOT_MAP "\"root\""
+#endif /* not ROOT_MAP */
+
+#define NO_SUBNET "notknown" /* default subnet name for no subnet */
+#define NEXP_AP (1022) /* gdmr: was 254 */
+#define NEXP_AP_MARGIN (128)
+#define MAX_READDIR_ENTRIES 16
+
+/*
+ * Linked list macros
+ */
+#define AM_FIRST(ty, q) ((ty *) ((q)->q_forw))
+#define AM_LAST(ty, q) ((ty *) ((q)->q_back))
+#define NEXT(ty, q) ((ty *) (((qelem *) q)->q_forw))
+#define PREV(ty, q) ((ty *) (((qelem *) q)->q_back))
+#define HEAD(ty, q) ((ty *) q)
+#define ITER(v, ty, q) \
+ for ((v) = AM_FIRST(ty,(q)); (v) != HEAD(ty,(q)); (v) = NEXT(ty,(v)))
+
+/* allocate anything of type ty */
+#define ALLOC(ty) ((ty *) xmalloc(sizeof(ty)))
+#define CALLOC(ty) ((ty *) xcalloc(1, sizeof(ty)))
+
+/* converting am-filehandles to mount-points */
+#define fh_to_mp2(fhp, rp) fh_to_mp3(fhp, rp, VLOOK_CREATE)
+
+/*
+ * Systems which have the mount table in a file need to read it before
+ * they can perform an unmount() system call.
+ */
+#define UMOUNT_FS(dir, mtb_name) umount_fs(dir, mtb_name)
+/* imported via $srcdir/conf/umount/umount_*.c */
+extern int umount_fs(char *fs_name, const char *mnttabname);
+
+/*
+ * macros for automounter vfs/vnode operations.
+ */
+#define VLOOK_CREATE 0x1
+#define VLOOK_DELETE 0x2
+#define FS_DIRECTORY 0x0001 /* This looks like a dir, not a link */
+#define FS_MBACKGROUND 0x0002 /* Should background this mount */
+#define FS_NOTIMEOUT 0x0004 /* Don't bother with timeouts */
+#define FS_MKMNT 0x0008 /* Need to make the mount point */
+#define FS_UBACKGROUND 0x0010 /* Unmount in background */
+#define FS_BACKGROUND (FS_MBACKGROUND|FS_UBACKGROUND)
+#define FS_DISCARD 0x0020 /* Discard immediately on last reference */
+#define FS_AMQINFO 0x0040 /* Amq is interested in this fs type */
+
+/*
+ * macros for struct fserver.
+ */
+#define FSF_VALID 0x0001 /* Valid information available */
+#define FSF_DOWN 0x0002 /* This fileserver is thought to be down */
+#define FSF_ERROR 0x0004 /* Permanent error has occured */
+#define FSF_WANT 0x0008 /* Want a wakeup call */
+#define FSF_PINGING 0x0010 /* Already doing pings */
+#define FSRV_ISDOWN(fs) (((fs)->fs_flags & (FSF_DOWN|FSF_VALID)) == (FSF_DOWN|FSF_VALID))
+#define FSRV_ISUP(fs) (((fs)->fs_flags & (FSF_DOWN|FSF_VALID)) == (FSF_VALID))
+
+/*
+ * macros for struct mntfs (list of mounted filesystems)
+ */
+#define MFF_MOUNTED 0x0001 /* Node is mounted */
+#define MFF_MOUNTING 0x0002 /* Mount is in progress */
+#define MFF_UNMOUNTING 0x0004 /* Unmount is in progress */
+#define MFF_RESTART 0x0008 /* Restarted node */
+#define MFF_MKMNT 0x0010 /* Delete this node's am_mount */
+#define MFF_ERROR 0x0020 /* This node failed to mount */
+#define MFF_LOGDOWN 0x0040 /* Logged that this mount is down */
+#define MFF_RSTKEEP 0x0080 /* Don't timeout this filesystem - restarted */
+#define MFF_WANTTIMO 0x0100 /* Need a timeout call when not busy */
+#ifdef HAVE_AM_FS_NFSL
+# define MFF_NFSLINK 0x0200 /* nfsl type, and deemed a link */
+#endif /* HAVE_AM_FS_NFSL */
+
+/*
+ * macros for struct am_node (map of auto-mount points).
+ */
+#define AMF_NOTIMEOUT 0x0001 /* This node never times out */
+#define AMF_ROOT 0x0002 /* This is a root node */
+#ifdef HAVE_FS_AUTOFS
+# define AMF_AUTOFS 0x0004 /* this node is of type autofs */
+#endif /* HAVE_FS_AUTOFS */
+
+/*
+ * The following values can be tuned...
+ */
+#define ALLOWED_MOUNT_TIME 40 /* 40s for a mount */
+#define AM_TTL (5 * 60) /* Default cache period */
+#define AM_TTL_W (2 * 60) /* Default unmount interval */
+#define AM_PINGER 30 /* NFS ping interval for live systems */
+#define AMFS_AUTO_TIMEO 8 /* Default amfs_auto timeout - .8s */
+
+/*
+ * default amfs_auto retrans - 1/10th seconds
+ */
+#define AMFS_AUTO_RETRANS ((ALLOWED_MOUNT_TIME*10+5*gopt.amfs_auto_timeo)/gopt.amfs_auto_timeo * 2)
+
+/*
+ * RPC-related macros.
+ */
+#define RPC_XID_PORTMAP 0
+#define RPC_XID_MOUNTD 1
+#define RPC_XID_NFSPING 2
+#define RPC_XID_MASK (0x0f) /* 16 id's for now */
+#define MK_RPC_XID(type_id, uniq) ((type_id) | ((uniq) << 4))
+
+/*
+ * What level of AMD are we backward compatible with?
+ * This only applies to externally visible characteristics.
+ * Rev.Minor.Branch.Patch (2 digits each)
+ */
+#define AMD_COMPAT 5000000 /* 5.0 */
+
+/*
+ * Error to return if remote host is not available.
+ * Try, in order, "host down", "host unreachable", "invalid argument".
+ */
+#ifdef EHOSTDOWN
+# define AM_ERRNO_HOST_DOWN EHOSTDOWN
+# else /* not EHOSTDOWN */
+# ifdef EHOSTUNREACH
+# define AM_ERRNO_HOST_DOWN EHOSTUNREACH
+# else /* not EHOSTUNREACH */
+# define AM_ERRNO_HOST_DOWN EINVAL
+# endif /* not EHOSTUNREACH */
+#endif /* not EHOSTDOWN */
+
+
+/**************************************************************************/
+/*** STRUCTURES AND TYPEDEFS ***/
+/**************************************************************************/
+
+/* some typedefs must come first */
+typedef char *amq_string;
+typedef struct mntfs mntfs;
+typedef struct am_opts am_opts;
+typedef struct am_ops am_ops;
+typedef struct am_node am_node;
+typedef struct _qelem qelem;
+typedef struct mntlist mntlist;
+typedef struct fserver fserver;
+
+/*
+ * Linked list
+ * (the name 'struct qelem' conflicts with linux's unistd.h)
+ */
+struct _qelem {
+ qelem *q_forw;
+ qelem *q_back;
+};
+
+/*
+ * Option tables
+ */
+struct opt_tab {
+ char *opt;
+ int flag;
+};
+
+/*
+ * Server states
+ */
+typedef enum {
+ Start,
+ Run,
+ Finishing,
+ Quit,
+ Done
+} serv_state;
+
+/*
+ * Options
+ */
+struct am_opts {
+ char *fs_glob; /* Smashed copy of global options */
+ char *fs_local; /* Expanded copy of local options */
+ char *fs_mtab; /* Mount table entry */
+ /* Other options ... */
+ char *opt_dev;
+ char *opt_delay;
+ char *opt_dir;
+ char *opt_fs;
+ char *opt_group;
+ char *opt_mount;
+ char *opt_opts;
+ char *opt_remopts;
+ char *opt_pref;
+ char *opt_autopref;
+ char *opt_cache;
+ char *opt_rfs;
+ char *opt_rhost;
+ char *opt_sublink;
+ char *opt_type;
+ char *opt_unmount;
+ char *opt_user;
+ char *opt_maptype; /* map type: file, nis, hesiod, etc. */
+ char *opt_cachedir; /* cache directory */
+ char *opt_addopts; /* options to add to opt_opts */
+};
+
+/*
+ * List of mounted filesystems
+ */
+struct mntfs {
+ qelem mf_q; /* List of mounted filesystems */
+ am_ops *mf_ops; /* Operations on this mountpoint */
+ am_opts *mf_fo; /* File opts */
+ char *mf_mount; /* "/a/kiska/home/kiska" */
+ char *mf_info; /* Mount info */
+ char *mf_auto; /* Automount opts */
+ char *mf_mopts; /* FS mount opts */
+ char *mf_remopts; /* Remote FS mount opts */
+ fserver *mf_server; /* File server */
+ int mf_flags; /* Flags MFF_* */
+ int mf_error; /* Error code from background mount */
+ int mf_refc; /* Number of references to this node */
+ int mf_cid; /* Callout id */
+ void (*mf_prfree) (voidp); /* Free private space */
+ voidp mf_private; /* Private - per-fs data */
+};
+
+/*
+ * File Handle
+ *
+ * This is interpreted by indexing the exported array
+ * by fhh_id.
+ *
+ * The whole structure is mapped onto a standard fhandle_t
+ * when transmitted.
+ */
+struct am_fh {
+ int fhh_pid; /* process id */
+ int fhh_id; /* map id */
+ int fhh_gen; /* generation number */
+};
+
+/*
+ * Multi-protocol NFS file handle
+ */
+union am_nfs_handle {
+ /* placeholder for V4 file handle */
+#ifdef HAVE_FS_NFS3
+ struct mountres3 v3; /* NFS version 3 handle */
+#endif /* HAVE_FS_NFS3 */
+ struct fhstatus v2; /* NFS version 2 handle */
+};
+typedef union am_nfs_handle am_nfs_handle_t;
+
+/*
+ * automounter vfs/vnode operations.
+ */
+typedef char *(*vfs_match) (am_opts *);
+typedef int (*vfs_init) (mntfs *);
+typedef int (*vmount_fs) (am_node *);
+typedef int (*vfmount_fs) (mntfs *);
+typedef int (*vumount_fs) (am_node *);
+typedef int (*vfumount_fs) (mntfs *);
+typedef am_node *(*vlookuppn) (am_node *, char *, int *, int);
+typedef int (*vreaddir) (am_node *, nfscookie, nfsdirlist *, nfsentry *, int);
+typedef am_node *(*vreadlink) (am_node *, int *);
+typedef void (*vmounted) (mntfs *);
+typedef void (*vumounted) (am_node *);
+typedef fserver *(*vffserver) (mntfs *);
+
+struct am_ops {
+ char *fs_type; /* type of filesystems "nfsx" */
+ vfs_match fs_match; /* fxn: match */
+ vfs_init fs_init; /* fxn: initialization */
+ vmount_fs mount_fs; /* fxn: mount vnode */
+ vfmount_fs fmount_fs; /* fxn: mount VFS */
+ vumount_fs umount_fs; /* fxn: unmount vnode */
+ vfumount_fs fumount_fs; /* fxn: unmount VFS */
+ vlookuppn lookuppn; /* fxn: lookup path-name */
+ vreaddir readdir; /* fxn: read directory */
+ vreadlink readlink; /* fxn: read link */
+ vmounted mounted; /* fxn: after-mount extra actions */
+ vumounted umounted; /* fxn: after-umount extra actions */
+ vffserver ffserver; /* fxn: find a file server */
+ int fs_flags; /* filesystem flags FS_* */
+};
+
+typedef int (*task_fun) (voidp);
+typedef void (*cb_fun) (int, int, voidp);
+typedef void (*fwd_fun) P((voidp, int, struct sockaddr_in *,
+ struct sockaddr_in *, voidp, int));
+
+/*
+ * List of mount table entries
+ */
+struct mntlist {
+ struct mntlist *mnext;
+ mntent_t *mnt;
+};
+
+/*
+ * Mount map
+ */
+typedef struct mnt_map mnt_map;
+
+/*
+ * Per-mountpoint statistics
+ */
+struct am_stats {
+ time_t s_mtime; /* Mount time */
+ u_short s_uid; /* Uid of mounter */
+ int s_getattr; /* Count of getattrs */
+ int s_lookup; /* Count of lookups */
+ int s_readdir; /* Count of readdirs */
+ int s_readlink; /* Count of readlinks */
+ int s_statfs; /* Count of statfs */
+};
+typedef struct am_stats am_stats;
+
+/*
+ * System statistics
+ */
+struct amd_stats {
+ int d_drops; /* Dropped requests */
+ int d_stale; /* Stale NFS handles */
+ int d_mok; /* Succesful mounts */
+ int d_merr; /* Failed mounts */
+ int d_uerr; /* Failed unmounts */
+};
+extern struct amd_stats amd_stats;
+
+/*
+ * List of fileservers
+ */
+struct fserver {
+ qelem fs_q; /* List of fileservers */
+ int fs_refc; /* Number of references to this node */
+ char *fs_host; /* Normalized hostname of server */
+ struct sockaddr_in *fs_ip; /* Network address of server */
+ int fs_cid; /* Callout id */
+ int fs_pinger; /* Ping (keepalive) interval */
+ int fs_flags; /* Flags */
+ char *fs_type; /* File server type */
+ u_long fs_version; /* NFS version of server (2, 3, etc.)*/
+ char *fs_proto; /* NFS protocol of server (tcp, udp, etc.) */
+ voidp fs_private; /* Private data */
+ void (*fs_prfree) (voidp); /* Free private data */
+};
+
+/*
+ * Map of auto-mount points.
+ */
+struct am_node {
+ int am_mapno; /* Map number */
+ mntfs *am_mnt; /* Mounted filesystem */
+ char *am_name; /* "kiska": name of this node */
+ char *am_path; /* "/home/kiska": path of this node's mount point */
+ char *am_link; /* "/a/kiska/home/kiska/this/that": link to sub-dir */
+ am_node *am_parent; /* Parent of this node */
+ am_node *am_ysib; /* Younger sibling of this node */
+ am_node *am_osib; /* Older sibling of this node */
+ am_node *am_child; /* First child of this node */
+ nfsattrstat am_attr; /* File attributes */
+#define am_fattr am_attr.ns_u.ns_attr_u
+ int am_flags; /* Boolean flags AMF_* */
+ int am_error; /* Specific mount error */
+ time_t am_ttl; /* Time to live */
+ int am_timeo_w; /* Wait interval */
+ int am_timeo; /* Timeout interval */
+ u_int am_gen; /* Generation number */
+ char *am_pref; /* Mount info prefix */
+ am_stats am_stats; /* Statistics gathering */
+ SVCXPRT *am_transp; /* Info for quick reply */
+};
+
+
+/**************************************************************************/
+/*** EXTERNALS ***/
+/**************************************************************************/
+
+/*
+ * Useful constants
+ */
+extern char *mnttab_file_name; /* Mount table */
+extern char *cpu; /* "CPU type" */
+extern char *endian; /* "big" */
+extern char *hostdomain; /* "southseas.nz" */
+extern char copyright[]; /* Copyright info */
+extern char hostd[]; /* "kiska.southseas.nz" */
+extern char pid_fsname[]; /* kiska.southseas.nz:(pid%d) */
+extern char version[]; /* Version info */
+
+/*
+ * Global variables.
+ */
+extern AUTH *nfs_auth; /* Dummy uthorisation for remote servers */
+extern FILE *logfp; /* Log file */
+extern am_node **exported_ap; /* List of nodes */
+extern am_node *root_node; /* Node for "root" */
+extern char *PrimNetName; /* Name of primary connected network */
+extern char *PrimNetNum; /* Name of primary connected network */
+extern char *SubsNetName; /* Name of subsidiary connected network */
+extern char *SubsNetNum; /* Name of subsidiary connected network */
+extern char *progname; /* "amd"|"mmd" */
+extern char hostname[]; /* "kiska" */
+extern int first_free_map; /* First free node */
+extern int foreground; /* Foreground process */
+extern int immediate_abort; /* Should close-down unmounts be retried */
+extern int last_used_map; /* Last map being used for mounts */
+extern int orig_umask; /* umask() on startup */
+extern int task_notify_todo; /* Task notifier needs running */
+extern int xlog_level; /* Logging level */
+extern int xlog_level_init;
+extern pid_t mypid; /* Current process id */
+extern serv_state amd_state; /* Should we go now */
+extern struct in_addr myipaddr; /* (An) IP address of this host */
+extern struct opt_tab xlog_opt[];
+extern time_t clock_valid; /* Clock needs recalculating */
+extern time_t do_mapc_reload; /* Flush & reload mount map cache */
+extern time_t next_softclock; /* Time to call softclock() */
+extern u_short nfs_port; /* Our NFS service port */
+
+/*
+ * Global routines
+ */
+extern CLIENT *get_mount_client(char *unused_host, struct sockaddr_in *sin, struct timeval *tv, int *sock, u_long mnt_version);
+extern RETSIGTYPE sigchld(int);
+extern SVCXPRT *nfsxprt;
+extern am_node *efs_lookuppn(am_node *, char *, int *, int);
+extern am_node *exported_ap_alloc(void);
+extern am_node *fh_to_mp(am_nfs_fh *);
+extern am_node *fh_to_mp3(am_nfs_fh *, int *, int);
+extern am_node *find_mf(mntfs *);
+extern am_node *next_map(int *);
+extern am_node *root_ap(char *, int);
+extern am_ops *ops_match(am_opts *, char *, char *, char *, char *, char *);
+extern bool_t xdr_amq_string(XDR *xdrs, amq_string *objp);
+extern bool_t xdr_dirpath(XDR *xdrs, dirpath *objp);
+extern char **strsplit(char *, int, int);
+extern char *expand_key(char *);
+extern char *get_version_string(void);
+extern char *inet_dquad(char *, u_long);
+extern char *print_wires(void);
+extern char *str3cat(char *, char *, char *, char *);
+extern char *strealloc(char *, char *);
+extern char *strip_selectors(char *, char *);
+extern char *strnsave(const char *, int);
+extern fserver *dup_srvr(fserver *);
+extern int amu_close(int fd);
+extern int background(void);
+extern int bind_resv_port(int, u_short *);
+extern int cmdoption(char *, struct opt_tab *, int *);
+extern int compute_mount_flags(mntent_t *);
+extern int efs_readdir(am_node *, nfscookie, nfsdirlist *, nfsentry *, int);
+extern int eval_fs_opts(am_opts *, char *, char *, char *, char *, char *);
+extern int fwd_init(void);
+extern int fwd_packet(int, voidp, int, struct sockaddr_in *, struct sockaddr_in *, voidp, fwd_fun);
+extern int get_amd_program_number(void);
+extern int hasmntval(mntent_t *, char *);
+extern int is_network_member(const char *net);
+extern int islocalnet(u_long);
+extern int make_nfs_auth(void);
+extern int make_rpc_packet(char *, int, u_long, struct rpc_msg *, voidp, XDRPROC_T_TYPE, AUTH *);
+extern int mapc_keyiter(mnt_map *, void(*)(char *, voidp), voidp);
+extern int mapc_search(mnt_map *, char *, char **);
+extern int mkdirs(char *, int);
+extern int mount_auto_node(char *, voidp);
+extern int mount_automounter(int);
+extern int mount_exported(void);
+extern int mount_fs(mntent_t *, int, caddr_t, int, MTYPE_TYPE, u_long, const char *, const char *);
+extern int mount_node(am_node *);
+extern int nfs_srvr_port(fserver *, u_short *, voidp);
+extern int pickup_rpc_reply(voidp, int, voidp, XDRPROC_T_TYPE);
+extern int root_keyiter(void(*)(char *, voidp), voidp);
+extern int softclock(void);
+extern int switch_option(char *);
+extern int switch_to_logfile(char *);
+extern int timeout(u_int, void (*fn)(voidp), voidp);
+extern int valid_key(char *);
+extern mnt_map *mapc_find(char *, char *, const char *);
+extern mntfs *dup_mntfs(mntfs *);
+extern mntfs *find_mntfs(am_ops *, am_opts *, char *, char *, char *, char *, char *);
+extern mntfs *new_mntfs(void);
+extern mntfs *realloc_mntfs(mntfs *, am_ops *, am_opts *, char *, char *, char *, char *, char *);
+extern mntlist *read_mtab(char *, const char *);
+extern struct sockaddr_in *amu_svc_getcaller(SVCXPRT *xprt);
+extern time_t time(time_t *);
+extern void am_mounted(am_node *);
+extern void am_unmounted(am_node *);
+extern void amq_program_1(struct svc_req *rqstp, SVCXPRT * transp);
+extern void amu_get_myaddress(struct in_addr *iap);
+extern void amu_release_controlling_tty(void);
+extern void compute_automounter_nfs_args(nfs_args_t *nap, mntent_t *mntp);
+extern void deslashify(char *);
+extern void discard_mntlist(mntlist *mp);
+extern void do_task_notify(void);
+extern void flush_mntfs(void);
+extern void flush_nfs_fhandle_cache(fserver *);
+extern void forcibly_timeout_mp(am_node *);
+extern void free_map(am_node *);
+extern void free_mntfs(voidp);
+extern void free_mntlist(mntlist *);
+extern void free_opts(am_opts *);
+extern void free_srvr(fserver *);
+extern void fwd_reply(void);
+extern void get_args(int argc, char *argv[]);
+extern void getwire(char **name1, char **number1);
+extern void going_down(int);
+extern void host_normalize(char **);
+extern void init_map(am_node *, char *);
+extern void ins_que(qelem *, qelem *);
+extern void insert_am(am_node *, am_node *);
+extern void make_root_node(void);
+extern void map_flush_srvr(fserver *);
+extern void mapc_add_kv(mnt_map *, char *, char *);
+extern void mapc_free(voidp);
+extern void mapc_reload(void);
+extern void mapc_showtypes(char *buf);
+extern void mk_fattr(am_node *, nfsftype);
+extern void mnt_free(mntent_t *);
+extern void mp_to_fh(am_node *, am_nfs_fh *);
+extern void new_ttl(am_node *);
+extern void nfs_program_2(struct svc_req *rqstp, SVCXPRT *transp);
+extern void normalize_slash(char *);
+extern void ops_showamfstypes(char *buf);
+extern void ops_showfstypes(char *outbuf);
+extern void plog(int, char *,...);
+extern void rem_que(qelem *);
+extern void reschedule_timeout_mp(void);
+extern void restart(void);
+extern void rmdirs(char *);
+extern void rpc_msg_init(struct rpc_msg *, u_long, u_long, u_long);
+extern void run_task(task_fun, voidp, cb_fun, voidp);
+extern void sched_task(cb_fun, voidp, voidp);
+extern void set_amd_program_number(int program);
+extern void show_opts(int ch, struct opt_tab *);
+extern void show_rcs_info(const char *, char *);
+extern void srvrlog(fserver *, char *);
+extern void timeout_mp(voidp);
+extern void umount_exported(void);
+extern void unregister_amq(void);
+extern void untimeout(int);
+extern void wakeup(voidp);
+extern void wakeup_srvr(fserver *);
+extern void wakeup_task(int, int, voidp);
+extern voidp xmalloc(int);
+extern voidp xrealloc(voidp, int);
+extern voidp xzalloc(int);
+extern u_long get_nfs_version(char *host, struct sockaddr_in *sin, u_long nfs_version, const char *proto);
+
+
+#ifdef MOUNT_TABLE_ON_FILE
+extern void rewrite_mtab(mntlist *, const char *);
+extern void unlock_mntlist(void);
+extern void write_mntent(mntent_t *, const char *);
+#endif /* MOUNT_TABLE_ON_FILE */
+
+#if defined(HAVE_SYSLOG_H) || defined(HAVE_SYS_SYSLOG_H)
+extern int syslogging;
+#endif /* defined(HAVE_SYSLOG_H) || defined(HAVE_SYS_SYSLOG_H) */
+
+#ifdef HAVE_TRANSPORT_TYPE_TLI
+
+extern void compute_nfs_args(nfs_args_t *nap, mntent_t *mntp, int genflags, struct netconfig *nfsncp, struct sockaddr_in *ip_addr, u_long nfs_version, char *nfs_proto, am_nfs_handle_t *fhp, char *host_name, char *fs_name);
+extern int create_amq_service(int *udp_soAMQp, SVCXPRT **udp_amqpp, struct netconfig **udp_amqncpp, int *tcp_soAMQp, SVCXPRT **tcp_amqpp, struct netconfig **tcp_amqncpp);
+extern int create_nfs_service(int *soNFSp, u_short *nfs_portp, SVCXPRT **nfs_xprtp, void (*dispatch_fxn)(struct svc_req *rqstp, SVCXPRT *transp));
+extern int get_knetconfig(struct knetconfig **kncpp, struct netconfig *in_ncp, char *nc_protoname);
+extern struct netconfig *nfsncp;
+extern void free_knetconfig(struct knetconfig *kncp);
+
+#else /* not HAVE_TRANSPORT_TYPE_TLI */
+
+extern void compute_nfs_args(nfs_args_t *nap, mntent_t *mntp, int genflags, struct sockaddr_in *ip_addr, u_long nfs_version, char *nfs_proto, am_nfs_handle_t *fhp, char *host_name, char *fs_name);
+extern enum clnt_stat pmap_ping(struct sockaddr_in *address);
+extern int create_amq_service(int *udp_soAMQp, SVCXPRT **udp_amqpp, int *tcp_soAMQp, SVCXPRT **tcp_amqpp);
+extern int create_nfs_service(int *soNFSp, u_short *nfs_portp, SVCXPRT **nfs_xprtp, void (*dispatch_fxn)(struct svc_req *rqstp, SVCXPRT *transp));
+
+#endif /* not HAVE_TRANSPORT_TYPE_TLI */
+
+#ifndef HAVE_FIELD_STRUCT_FHSTATUS_FHS_FH
+# define fhs_fh fhstatus_u.fhs_fhandle
+#endif /* not HAVE_FIELD_STRUCT_FHSTATUS_FHS_FH */
+
+
+/**************************************************************************/
+/*** Generic file-system types, implemented as part of the native O/S. ***/
+/**************************************************************************/
+
+/*
+ * Loopback File System
+ * Many systems can't support this, and in any case most of the
+ * functionality is available with Symlink FS.
+ */
+#ifdef HAVE_FS_LOFS
+extern am_ops lofs_ops;
+#endif /* HAVE_FS_LOFS */
+
+/*
+ * CD-ROM File System (CD-ROM)
+ * (HSFS: High Sierra F/S on some machines)
+ * Many systems can't support this, and in any case most of the
+ * functionality is available with program FS.
+ */
+#ifdef HAVE_FS_CDFS
+extern am_ops cdfs_ops;
+#endif /* HAVE_FS_CDFS */
+
+/*
+ * PC File System (MS-DOS)
+ * Many systems can't support this, and in any case most of the
+ * functionality is available with program FS.
+ */
+#ifdef HAVE_FS_PCFS
+extern am_ops pcfs_ops;
+#endif /* HAVE_FS_PCFS */
+
+/*
+ * Caching File System (Solaris)
+ */
+#ifdef HAVE_FS_CACHEFS
+extern am_ops cachefs_ops;
+#endif /* HAVE_FS_CACHEFS */
+
+/*
+ * Network File System
+ * Good, slow, NFS V.2.
+ */
+#ifdef HAVE_FS_NFS
+extern am_ops nfs_ops; /* NFS */
+extern fserver *find_nfs_srvr (mntfs *);
+extern int nfs_fmount(mntfs *mf);
+extern int nfs_fumount(mntfs *mf);
+extern int nfs_init(mntfs *mf);
+extern qelem nfs_srvr_list;
+extern void nfs_umounted(am_node *mp);
+#endif /* HAVE_FS_NFS */
+
+
+/*
+ * Network File System: the new generation
+ * NFS V.3
+ */
+#ifdef HAVE_FS_NFS3
+# ifndef NFS_VERSION3
+# define NFS_VERSION3 ((u_int) 3)
+# endif /* not NFS_VERSION3 */
+#endif /* HAVE_FS_NFS3 */
+
+/*
+ * Un*x File System
+ * Normal local disk file system.
+ */
+#ifdef HAVE_FS_UFS
+extern am_ops ufs_ops; /* Un*x file system */
+#endif /* HAVE_FS_UFS */
+
+
+/**************************************************************************/
+/*** Automounter file-system types, implemented by amd. ***/
+/**************************************************************************/
+
+/*
+ * Automount File System
+ */
+#ifdef HAVE_AM_FS_AUTO
+extern am_ops amfs_auto_ops; /* Automount file system (this!) */
+extern am_ops amfs_toplvl_ops; /* Top-level automount file system */
+extern am_ops amfs_root_ops; /* Root file system */
+extern qelem amfs_auto_srvr_list;
+extern am_node *amfs_auto_lookuppn(am_node *mp, char *fname, int *error_return, int op);
+extern am_node *next_nonerror_node(am_node *xp);
+extern char *amfs_auto_match(am_opts *fo);
+extern fserver *find_amfs_auto_srvr(mntfs *);
+extern int amfs_auto_readdir(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, int count);
+extern int amfs_auto_umount(am_node *mp);
+extern int amfs_auto_fmount(am_node *mp);
+extern int amfs_auto_fumount(am_node *mp);
+#endif /* HAVE_AM_FS_AUTO */
+
+/*
+ * Toplvl Automount File System
+ */
+#ifdef HAVE_AM_FS_TOPLVL
+extern am_ops amfs_toplvl_ops; /* Toplvl Automount file system */
+extern int amfs_toplvl_mount(am_node *mp);
+extern int amfs_toplvl_umount(am_node *mp);
+extern void amfs_toplvl_mounted(mntfs *mf);
+#endif /* HAVE_AM_FS_TOPLVL */
+
+/*
+ * Direct Automount File System
+ */
+#ifdef HAVE_AM_FS_DIRECT
+extern am_ops amfs_direct_ops; /* Direct Automount file system (this too) */
+#endif /* HAVE_AM_FS_DIRECT */
+
+/*
+ * Error File System
+ */
+#ifdef HAVE_AM_FS_ERROR
+extern am_ops amfs_error_ops; /* Error file system */
+extern am_node *amfs_error_lookuppn(am_node *mp, char *fname, int *error_return, int op);
+extern int amfs_error_readdir(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, int count);
+#endif /* HAVE_AM_FS_ERROR */
+
+/*
+ * Inheritance File System
+ */
+#ifdef HAVE_AM_FS_INHERIT
+extern am_ops amfs_inherit_ops; /* Inheritance file system */
+#endif /* HAVE_AM_FS_INHERIT */
+
+/*
+ * NFS mounts with local existence check.
+ */
+#ifdef HAVE_AM_FS_NFSL
+extern am_ops amfs_nfsl_ops; /* NFSL */
+#endif /* HAVE_AM_FS_NFSL */
+
+/*
+ * Multi-nfs mounts.
+ */
+#ifdef HAVE_AM_FS_NFSX
+extern am_ops amfs_nfsx_ops; /* NFSX */
+#endif /* HAVE_AM_FS_NFSX */
+
+/*
+ * NFS host - a whole tree.
+ */
+#ifdef HAVE_AM_FS_HOST
+extern am_ops amfs_host_ops; /* NFS host */
+#endif /* HAVE_AM_FS_HOST */
+
+/*
+ * Program File System
+ * This is useful for things like RVD.
+ */
+#ifdef HAVE_AM_FS_PROGRAM
+extern am_ops amfs_program_ops; /* Program File System */
+#endif /* HAVE_AM_FS_PROGRAM */
+
+/*
+ * Symbolic-link file system.
+ * A "filesystem" which is just a symbol link.
+ */
+#ifdef HAVE_AM_FS_LINK
+extern am_ops amfs_link_ops; /* Symlink FS */
+extern int amfs_link_fmount(mntfs *mf);
+#endif /* HAVE_AM_FS_LINK */
+
+/*
+ * Symbolic-link file syste, which also checks that the target of
+ * the symlink exists.
+ * A "filesystem" which is just a symbol link.
+ */
+#ifdef HAVE_AM_FS_LINKX
+extern am_ops amfs_linkx_ops; /* Symlink FS with existence check */
+#endif /* HAVE_AM_FS_LINKX */
+
+/*
+ * Union file system
+ */
+#ifdef HAVE_AM_FS_UNION
+extern am_ops amfs_union_ops; /* Union FS */
+#endif /* HAVE_AM_FS_UNION */
+
+/*
+ * Autofs file system
+ */
+#ifdef HAVE_FS_AUTOFS
+extern am_ops autofs_ops; /* (Sun) Autofs FS */
+#endif /* HAVE_FS_AUTOFS */
+
+
+/**************************************************************************/
+/*** DEBUGGING ***/
+/**************************************************************************/
+
+/*
+ * DEBUGGING:
+ */
+#ifdef DEBUG
+
+# define D_ALL (~0)
+# define D_DAEMON 0x0001 /* Enter daemon mode */
+# define D_TRACE 0x0002 /* Do protocol trace */
+# define D_FULL 0x0004 /* Do full trace */
+# define D_MTAB 0x0008 /* Use local mtab */
+# define D_AMQ 0x0010 /* Register amq program */
+# define D_STR 0x0020 /* Debug string munging */
+# ifdef DEBUG_MEM
+# define D_MEM 0x0040 /* Trace memory allocations */
+# endif /* DEBUG_MEM */
+# define D_FORK 0x0080 /* Fork server */
+ /* info service specific debugging (hesiod, nis, etc) */
+# define D_INFO 0x0100
+
+/*
+ * Normally, don't enter daemon mode, and don't register amq
+ */
+# ifdef DEBUG_MEM
+# define D_TEST (~(D_DAEMON|D_MEM|D_STR))
+# else /* not DEBUG_MEM */
+# define D_TEST (~(D_DAEMON|D_STR))
+# endif /* not DEBUG_MEM */
+
+# define amuDebug(x) if (debug_flags & (x))
+# define dlog amuDebug(D_FULL) dplog
+# define amuDebugNo(x) if (!(debug_flags & (x)))
+
+/* debugging mount-table file to use */
+# ifndef DEBUG_MNTTAB
+# define DEBUG_MNTTAB "./mnttab"
+# endif /* not DEBUG_MNTTAB */
+
+# ifdef DEBUG_MEM
+/*
+ * If debugging memory, then call a special freeing function that logs
+ * more info, and resets the pointer to NULL so it cannot be used again.
+ */
+# define XFREE(x) dxfree(__FILE__,__LINE__,x)
+extern void dxfree(char *file, int line, voidp ptr);
+extern void malloc_verify(void);
+# else /* not DEBUG_MEM */
+/*
+ * If regular debugging, then free the pointer and reset to NULL.
+ * This should remain so for as long as am-utils is in alpha/beta testing.
+ */
+# define XFREE(x) do { free((voidp)x); x = NULL;} while (0)
+# endif /* not DEBUG_MEM */
+
+/* functions that depend solely on debugging */
+extern void print_nfs_args(const nfs_args_t *nap, u_long nfs_version);
+
+#else /* not DEBUG */
+
+/*
+ * if not debugging, then simple perform free, and don't bother
+ * resetting the pointer.
+ */
+# define XFREE(x) free(x)
+
+#endif /* not DEBUG */
+
+extern int debug_flags; /* Debug options */
+extern int debug_option (char *);
+extern struct opt_tab dbg_opt[];
+extern void dplog(char *fmt, ...);
+
+/**************************************************************************/
+/*** MISC (stuff left to autoconfiscate) ***/
+/**************************************************************************/
+
+#endif /* not _AM_UTILS_H */
diff --git a/contrib/amd/include/am_xdr_func.h b/contrib/amd/include/am_xdr_func.h
new file mode 100644
index 000000000000..936400280a9f
--- /dev/null
+++ b/contrib/amd/include/am_xdr_func.h
@@ -0,0 +1,203 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: am_xdr_func.c,v 5.2.2.1 1992/02/09 15:08:40 ezk beta $
+ *
+ */
+
+/*
+ * Definitions of all possible xdr functions that are otherwise
+ * not defined elsewhere.
+ */
+
+#ifndef _AM_XDR_FUNC_H
+#define _AM_XDR_FUNC_H
+
+#ifndef HAVE_XDR_ATTRSTAT
+bool_t xdr_attrstat(XDR *xdrs, nfsattrstat *objp);
+#endif /* not HAVE_XDR_ATTRSTAT */
+#ifndef HAVE_XDR_CREATEARGS
+bool_t xdr_createargs(XDR *xdrs, nfscreateargs *objp);
+#endif /* not HAVE_XDR_CREATEARGS */
+#ifndef HAVE_XDR_DIRLIST
+bool_t xdr_dirlist(XDR *xdrs, nfsdirlist *objp);
+#endif /* not HAVE_XDR_DIRLIST */
+#ifndef HAVE_XDR_DIROPARGS
+bool_t xdr_diropargs(XDR *xdrs, nfsdiropargs *objp);
+#endif /* not HAVE_XDR_DIROPARGS */
+#ifndef HAVE_XDR_DIROPOKRES
+bool_t xdr_diropokres(XDR *xdrs, nfsdiropokres *objp);
+#endif /* not HAVE_XDR_DIROPOKRES */
+#ifndef HAVE_XDR_DIROPRES
+bool_t xdr_diropres(XDR *xdrs, nfsdiropres *objp);
+#endif /* not HAVE_XDR_DIROPRES */
+#ifndef HAVE_XDR_DIRPATH
+bool_t xdr_dirpath(XDR *xdrs, dirpath *objp);
+#endif /* not HAVE_XDR_DIRPATH */
+#ifndef HAVE_XDR_ENTRY
+bool_t xdr_entry(XDR *xdrs, nfsentry *objp);
+#endif /* not HAVE_XDR_ENTRY */
+#ifndef HAVE_XDR_EXPORTNODE
+bool_t xdr_exportnode(XDR *xdrs, exportnode *objp);
+#endif /* not HAVE_XDR_EXPORTNODE */
+#ifndef HAVE_XDR_EXPORTS
+bool_t xdr_exports(XDR *xdrs, exports *objp);
+#endif /* not HAVE_XDR_EXPORTS */
+#ifndef HAVE_XDR_FATTR
+bool_t xdr_fattr(XDR *xdrs, nfsfattr *objp);
+#endif /* not HAVE_XDR_FATTR */
+#ifndef HAVE_XDR_FHANDLE
+bool_t xdr_fhandle(XDR *xdrs, fhandle objp);
+#endif /* not HAVE_XDR_FHANDLE */
+#ifndef HAVE_XDR_FHSTATUS
+bool_t xdr_fhstatus(XDR *xdrs, fhstatus *objp);
+#endif /* not HAVE_XDR_FHSTATUS */
+#ifndef HAVE_XDR_FILENAME
+bool_t xdr_filename(XDR *xdrs, filename *objp);
+#endif /* not HAVE_XDR_FILENAME */
+#ifndef HAVE_XDR_FTYPE
+bool_t xdr_ftype(XDR *xdrs, nfsftype *objp);
+#endif /* not HAVE_XDR_FTYPE */
+#ifndef HAVE_XDR_GROUPNODE
+bool_t xdr_groupnode(XDR *xdrs, groupnode *objp);
+#endif /* not HAVE_XDR_GROUPNODE */
+#ifndef HAVE_XDR_GROUPS
+bool_t xdr_groups(XDR *xdrs, groups objp);
+#endif /* not HAVE_XDR_GROUPS */
+#ifndef HAVE_XDR_LINKARGS
+bool_t xdr_linkargs(XDR *xdrs, nfslinkargs *objp);
+#endif /* not HAVE_XDR_LINKARGS */
+#ifndef HAVE_XDR_MOUNTBODY
+bool_t xdr_mountbody(XDR *xdrs, mountbody *objp);
+#endif /* not HAVE_XDR_MOUNTBODY */
+#ifndef HAVE_XDR_MOUNTLIST
+bool_t xdr_mountlist(XDR *xdrs, mountlist *objp);
+#endif /* not HAVE_XDR_MOUNTLIST */
+
+/*
+ * NFS3 XDR FUNCTIONS:
+ */
+#if defined(HAVE_FS_NFS3) && !defined(HAVE_XDR_MOUNTRES3)
+bool_t xdr_fhandle3(XDR *xdrs, fhandle3 *objp);
+bool_t xdr_mountstat3(XDR *xdrs, mountstat3 *objp);
+bool_t xdr_mountres3_ok(XDR *xdrs, mountres3_ok *objp);
+bool_t xdr_mountres3(XDR *xdrs, mountres3 *objp);
+#endif /* defined(HAVE_FS_NFS3) && !defined(HAVE_XDR_MOUNTRES3) */
+
+#ifndef HAVE_XDR_NAME
+bool_t xdr_name(XDR *xdrs, name *objp);
+#endif /* not HAVE_XDR_NAME */
+#ifndef HAVE_XDR_NFS_FH
+bool_t xdr_nfs_fh(XDR *xdrs, am_nfs_fh *objp);
+#endif /* not HAVE_XDR_NFS_FH */
+#ifndef HAVE_XDR_NFSCOOKIE
+bool_t xdr_nfscookie(XDR *xdrs, nfscookie objp);
+#endif /* not HAVE_XDR_NFSCOOKIE */
+#ifndef HAVE_XDR_NFSPATH
+bool_t xdr_nfspath(XDR *xdrs, nfspath *objp);
+#endif /* not HAVE_XDR_NFSPATH */
+#ifndef HAVE_XDR_NFSSTAT
+bool_t xdr_nfsstat(XDR *xdrs, nfsstat *objp);
+#endif /* not HAVE_XDR_NFSSTAT */
+#ifndef HAVE_XDR_NFSTIME
+bool_t xdr_nfstime(XDR *xdrs, nfstime *objp);
+#endif /* not HAVE_XDR_NFSTIME */
+#ifndef HAVE_XDR_POINTER
+bool_t xdr_pointer(register XDR *xdrs, char **objpp, u_int obj_size, XDRPROC_T_TYPE xdr_obj);
+#endif /* not HAVE_XDR_POINTER */
+#ifndef HAVE_XDR_READARGS
+bool_t xdr_readargs(XDR *xdrs, nfsreadargs *objp);
+#endif /* not HAVE_XDR_READARGS */
+#ifndef HAVE_XDR_READDIRARGS
+bool_t xdr_readdirargs(XDR *xdrs, nfsreaddirargs *objp);
+#endif /* not HAVE_XDR_READDIRARGS */
+#ifndef HAVE_XDR_READDIRRES
+bool_t xdr_readdirres(XDR *xdrs, nfsreaddirres *objp);
+#endif /* not HAVE_XDR_READDIRRES */
+#ifndef HAVE_XDR_READLINKRES
+bool_t xdr_readlinkres(XDR *xdrs, nfsreadlinkres *objp);
+#endif /* not HAVE_XDR_READLINKRES */
+#ifndef HAVE_XDR_READOKRES
+bool_t xdr_readokres(XDR *xdrs, nfsreadokres *objp);
+#endif /* not HAVE_XDR_READOKRES */
+#ifndef HAVE_XDR_READRES
+bool_t xdr_readres(XDR *xdrs, nfsreadres *objp);
+#endif /* not HAVE_XDR_READRES */
+#ifndef HAVE_XDR_RENAMEARGS
+bool_t xdr_renameargs(XDR *xdrs, nfsrenameargs *objp);
+#endif /* not HAVE_XDR_RENAMEARGS */
+#ifndef HAVE_XDR_SATTR
+bool_t xdr_sattr(XDR *xdrs, nfssattr *objp);
+#endif /* not HAVE_XDR_SATTR */
+#ifndef HAVE_XDR_SATTRARGS
+bool_t xdr_sattrargs(XDR *xdrs, nfssattrargs *objp);
+#endif /* not HAVE_XDR_SATTRARGS */
+#ifndef HAVE_XDR_STATFSOKRES
+bool_t xdr_statfsokres(XDR *xdrs, nfsstatfsokres *objp);
+#endif /* not HAVE_XDR_STATFSOKRES */
+#ifndef HAVE_XDR_STATFSRES
+bool_t xdr_statfsres(XDR *xdrs, nfsstatfsres *objp);
+#endif /* not HAVE_XDR_STATFSRES */
+#ifndef HAVE_XDR_SYMLINKARGS
+bool_t xdr_symlinkargs(XDR *xdrs, nfssymlinkargs *objp);
+#endif /* not HAVE_XDR_SYMLINKARGS */
+#ifndef HAVE_XDR_WRITEARGS
+bool_t xdr_writeargs(XDR *xdrs, nfswriteargs *objp);
+#endif /* not HAVE_XDR_WRITEARGS */
+
+/*
+ * AUTOFS XDR FUNCTIONS:
+ */
+#ifdef HAVE_FS_AUTOFS
+# ifndef HAVE_XDR_MNTREQUEST
+bool_t xdr_mntrequest(XDR *xdrs, mntrequest *objp);
+# endif /* not HAVE_XDR_MNTREQUEST */
+# ifndef HAVE_XDR_MNTRES
+bool_t xdr_mntres(XDR *xdrs, mntres *objp);
+# endif /* not HAVE_XDR_MNTRES */
+# ifndef HAVE_XDR_UMNTREQUEST
+bool_t xdr_umntrequest(XDR *xdrs, umntrequest *objp);
+# endif /* not HAVE_XDR_UMNTREQUEST */
+# ifndef HAVE_XDR_UMNTRES
+bool_t xdr_umntres(XDR *xdrs, umntres *objp);
+# endif /* not HAVE_XDR_UMNTRES */
+#endif /* HAVE_FS_AUTOFS */
+
+#endif /* not _AM_XDR_FUNC_H */
diff --git a/contrib/amd/include/amq_defs.h b/contrib/amd/include/amq_defs.h
new file mode 100644
index 000000000000..e2f941bfa027
--- /dev/null
+++ b/contrib/amd/include/amq_defs.h
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: amq_defs.h,v 1.1 1996/01/13 23:23:39 ezk Exp ezk $
+ *
+ */
+
+#ifndef _AMQ_DEFS_H
+#define _AMQ_DEFS_H
+
+/*
+ * MACROS
+ */
+#ifndef AMQ_SIZE
+# define AMQ_SIZE 16384
+#endif /* not AMQ_SIZE */
+#define AMQ_STRLEN 1024
+#define AMQ_PROGRAM ((u_long)300019)
+#define AMQ_VERSION ((u_long)1)
+#define AMQPROC_NULL ((u_long)0)
+#define AMQPROC_MNTTREE ((u_long)1)
+#define AMQPROC_UMNT ((u_long)2)
+#define AMQPROC_STATS ((u_long)3)
+#define AMQPROC_EXPORT ((u_long)4)
+#define AMQPROC_SETOPT ((u_long)5)
+#define AMQPROC_GETMNTFS ((u_long)6)
+#define AMQPROC_MOUNT ((u_long)7)
+#define AMQPROC_GETVERS ((u_long)8)
+#define AMQPROC_GETPID ((u_long)9)
+
+/*
+ * TYPEDEFS
+ */
+typedef long *time_type;
+typedef struct amq_mount_info amq_mount_info;
+typedef struct amq_mount_stats amq_mount_stats;
+typedef struct amq_mount_tree amq_mount_tree;
+typedef struct amq_setopt amq_setopt;
+typedef amq_mount_tree *amq_mount_tree_p;
+
+/*
+ * STRUCTURES:
+ */
+struct amq_mount_tree {
+ amq_string mt_mountinfo;
+ amq_string mt_directory;
+ amq_string mt_mountpoint;
+ amq_string mt_type;
+ time_type mt_mounttime;
+ u_short mt_mountuid;
+ int mt_getattr;
+ int mt_lookup;
+ int mt_readdir;
+ int mt_readlink;
+ int mt_statfs;
+ struct amq_mount_tree *mt_next;
+ struct amq_mount_tree *mt_child;
+};
+
+struct amq_mount_info {
+ amq_string mi_type;
+ amq_string mi_mountpt;
+ amq_string mi_mountinfo;
+ amq_string mi_fserver;
+ int mi_error;
+ int mi_refc;
+ int mi_up;
+};
+
+typedef struct {
+ u_int amq_mount_info_list_len;
+ amq_mount_info *amq_mount_info_list_val;
+} amq_mount_info_list;
+
+typedef struct {
+ u_int amq_mount_tree_list_len;
+ amq_mount_tree_p *amq_mount_tree_list_val;
+} amq_mount_tree_list;
+
+struct amq_mount_stats {
+ int as_drops;
+ int as_stale;
+ int as_mok;
+ int as_merr;
+ int as_uerr;
+};
+
+enum amq_opt {
+ AMOPT_DEBUG = 0,
+ AMOPT_LOGFILE = 1,
+ AMOPT_XLOG = 2,
+ AMOPT_FLUSHMAPC = 3
+};
+typedef enum amq_opt amq_opt; /* enum typedefs should be after enum */
+
+struct amq_setopt {
+ amq_opt as_opt;
+ amq_string as_str;
+};
+
+/*
+ * EXTERNALS:
+ *
+ * external definitions for amqproc_*_1() have been moved off to private
+ * headers in lib/amu.h, amd/amd.h, etc. They have to be private since the
+ * same named functions appear in different places with different prototypes
+ * an functionality.
+ */
+extern bool_t xdr_amq_mount_info(XDR *xdrs, amq_mount_info *objp);
+extern bool_t xdr_amq_mount_info_list(XDR *xdrs, amq_mount_info_list *objp);
+extern bool_t xdr_amq_mount_stats(XDR *xdrs, amq_mount_stats *objp);
+extern bool_t xdr_amq_mount_tree(XDR *xdrs, amq_mount_tree *objp);
+extern bool_t xdr_amq_mount_tree_list(XDR *xdrs, amq_mount_tree_list *objp);
+extern bool_t xdr_amq_mount_tree_p(XDR *xdrs, amq_mount_tree_p *objp);
+extern bool_t xdr_amq_opt(XDR *xdrs, amq_opt *objp);
+extern bool_t xdr_amq_setopt(XDR *xdrs, amq_setopt *objp);
+extern bool_t xdr_pri_free(XDRPROC_T_TYPE xdr_args, caddr_t args_ptr);
+extern bool_t xdr_time_type(XDR *xdrs, time_type *objp);
+
+#endif /* not _AMQ_DEFS_H */
diff --git a/contrib/amd/libamu/amu.h b/contrib/amd/libamu/amu.h
new file mode 100644
index 000000000000..f69b99ed31dc
--- /dev/null
+++ b/contrib/amd/libamu/amu.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: amu.h,v 1.1 1996/01/13 23:23:39 ezk Exp ezk $
+ *
+ */
+
+#ifndef _AMU_H
+#define _AMU_H
+
+/*
+ * Decide what maximum level of NFS server to try and mount with.
+ */
+#ifdef HAVE_FS_NFS3
+# define NFS_VERS_MAX NFS_VERSION3
+#else /* not HAVE_FS_NFS3 */
+# define NFS_VERS_MAX NFS_VERSION
+#endif /* not HAVE_FS_NFS3 */
+
+/* some systems like ncr2 do not define this in <rpcsvc/mount.h> */
+#ifndef MNTPATHLEN
+# define MNTPATHLEN 1024
+#endif /* not MNTPATHLEN */
+#ifndef MNTNAMLEN
+# define MNTNAMLEN 255
+#endif /* not MNTNAMLEN */
+
+/*
+ * external definitions for building libamu.a
+ */
+extern voidp amqproc_null_1(voidp argp, CLIENT *rqstp);
+extern amq_mount_tree_p *amqproc_mnttree_1(amq_string *argp, CLIENT *rqstp);
+extern voidp amqproc_umnt_1(amq_string *argp, CLIENT *rqstp);
+extern amq_mount_stats *amqproc_stats_1(voidp argp, CLIENT *rqstp);
+extern amq_mount_tree_list *amqproc_export_1(voidp argp, CLIENT *rqstp);
+extern int *amqproc_setopt_1(amq_setopt *argp, CLIENT *rqstp);
+extern amq_mount_info_list *amqproc_getmntfs_1(voidp argp, CLIENT *rqstp);
+extern int *amqproc_mount_1(voidp argp, CLIENT *rqstp);
+extern amq_string *amqproc_getvers_1(voidp argp, CLIENT *rqstp);
+
+#endif /* not _AMU_H */
diff --git a/contrib/amd/libamu/hasmntopt.c b/contrib/amd/libamu/hasmntopt.c
new file mode 100644
index 000000000000..7cf9167b2baf
--- /dev/null
+++ b/contrib/amd/libamu/hasmntopt.c
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+n * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: hasmntopt.c,v 5.2.2.2 1992/05/31 16:35:45 jsp Exp $
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amu.h>
+
+#ifndef MNTMAXSTR
+# define MNTMAXSTR 128
+#endif /* not MNTMAXSTR */
+
+
+/*
+ * Some systems don't provide these to the user,
+ * but amd needs them, so...
+ *
+ * From: Piete Brooks <pb@cl.cam.ac.uk>
+ */
+
+static char *
+nextmntopt(char **p)
+{
+ char *cp = *p;
+ char *rp;
+
+ /*
+ * Skip past white space
+ */
+ while (*cp && isspace(*cp))
+ cp++;
+
+ /*
+ * Word starts here
+ */
+ rp = cp;
+
+ /*
+ * Scan to send of string or separator
+ */
+ while (*cp && *cp != ',')
+ cp++;
+
+ /*
+ * If separator found the overwrite with nul char.
+ */
+ if (*cp) {
+ *cp = '\0';
+ cp++;
+ }
+
+ /*
+ * Return value for next call
+ */
+ *p = cp;
+ return rp;
+}
+
+/*
+ * replacement for hasmntopt if the system does not have it.
+ */
+char *
+hasmntopt(mntent_t *mnt, char *opt)
+{
+ char t[MNTMAXSTR];
+ char *f;
+ char *o = t;
+ int l = strlen(opt);
+
+ strcpy(t, mnt->mnt_opts);
+
+ while (*(f = nextmntopt(&o)))
+ if (NSTREQ(opt, f, l))
+ return f - t + mnt->mnt_opts;
+
+ return 0;
+}
diff --git a/contrib/amd/libamu/misc_rpc.c b/contrib/amd/libamu/misc_rpc.c
new file mode 100644
index 000000000000..b6d23cd9e535
--- /dev/null
+++ b/contrib/amd/libamu/misc_rpc.c
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: misc_rpc.c,v 5.2.2.1 1992/02/09 15:08:40 jsp beta $
+ *
+ */
+
+/*
+ * Additions to Sun RPC.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amu.h>
+
+/*
+ * Some systems renamed _seterr_reply to __seterr_reply (with two
+ * leading underscores)
+ */
+#if !defined(HAVE__SETERR_REPLY) && defined(HAVE___SETERR_REPLY)
+# define _seterr_reply __seterr_reply
+#endif /* !defined(HAVE__SETERR_REPLY) && defined(HAVE___SETERR_REPLY) */
+
+
+void
+rpc_msg_init(struct rpc_msg *mp, u_long prog, u_long vers, u_long proc)
+{
+ /*
+ * Initialise the message
+ */
+ memset((voidp) mp, 0, sizeof(*mp));
+ mp->rm_xid = 0;
+ mp->rm_direction = CALL;
+ mp->rm_call.cb_rpcvers = RPC_MSG_VERSION;
+ mp->rm_call.cb_prog = prog;
+ mp->rm_call.cb_vers = vers;
+ mp->rm_call.cb_proc = proc;
+}
+
+
+/*
+ * Field reply to call to mountd
+ */
+int
+pickup_rpc_reply(voidp pkt, int len, voidp where, XDRPROC_T_TYPE where_xdr)
+{
+ XDR reply_xdr;
+ int ok;
+ struct rpc_err err;
+ struct rpc_msg reply_msg;
+ int error = 0;
+
+ /* memset((voidp) &err, 0, sizeof(err)); */
+ memset((voidp) &reply_msg, 0, sizeof(reply_msg));
+ memset((voidp) &reply_xdr, 0, sizeof(reply_xdr));
+
+ reply_msg.acpted_rply.ar_results.where = (caddr_t) where;
+ reply_msg.acpted_rply.ar_results.proc = where_xdr;
+
+ xdrmem_create(&reply_xdr, pkt, len, XDR_DECODE);
+
+ ok = xdr_replymsg(&reply_xdr, &reply_msg);
+ if (!ok) {
+ error = EIO;
+ goto drop;
+ }
+ _seterr_reply(&reply_msg, &err);
+ if (err.re_status != RPC_SUCCESS) {
+ error = EIO;
+ goto drop;
+ }
+
+drop:
+ if (reply_msg.rm_reply.rp_stat == MSG_ACCEPTED &&
+ reply_msg.acpted_rply.ar_verf.oa_base) {
+ reply_xdr.x_op = XDR_FREE;
+ (void) xdr_opaque_auth(&reply_xdr,
+ &reply_msg.acpted_rply.ar_verf);
+ }
+ xdr_destroy(&reply_xdr);
+
+ return error;
+}
+
+
+int
+make_rpc_packet(char *buf, int buflen, u_long proc, struct rpc_msg *mp, voidp arg, XDRPROC_T_TYPE arg_xdr, AUTH *auth)
+{
+ XDR msg_xdr;
+ int len;
+
+ xdrmem_create(&msg_xdr, buf, buflen, XDR_ENCODE);
+
+ /*
+ * Basic protocol header
+ */
+ if (!xdr_callhdr(&msg_xdr, mp))
+ return -EIO;
+
+ /*
+ * Called procedure number
+ */
+ if (!xdr_enum(&msg_xdr, (enum_t *) & proc))
+ return -EIO;
+
+ /*
+ * Authorization
+ */
+ if (!AUTH_MARSHALL(auth, &msg_xdr))
+ return -EIO;
+
+ /*
+ * Arguments
+ */
+ if (!(*arg_xdr) (&msg_xdr, arg))
+ return -EIO;
+
+ /*
+ * Determine length
+ */
+ len = xdr_getpos(&msg_xdr);
+
+ /*
+ * Throw away xdr
+ */
+ xdr_destroy(&msg_xdr);
+
+ return len;
+}
diff --git a/contrib/amd/libamu/mount_fs.c b/contrib/amd/libamu/mount_fs.c
new file mode 100644
index 000000000000..948b18df35ac
--- /dev/null
+++ b/contrib/amd/libamu/mount_fs.c
@@ -0,0 +1,892 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: mount_fs.c,v 5.2.2.2 1992/05/31 16:35:45 jsp Exp $
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amu.h>
+
+
+/* ensure that mount table options are delimited by a comma */
+#define append_opts(old, new) { \
+ if (*(old) != '\0') strcat(old, ","); \
+ strcat(old, new); }
+
+/*
+ * Standard mount flags
+ */
+struct opt_tab mnt_flags[] =
+{
+#if defined(MNT2_GEN_OPT_RDONLY) && defined(MNTTAB_OPT_RO)
+ {MNTTAB_OPT_RO, MNT2_GEN_OPT_RDONLY},
+#endif /* defined(MNT2_GEN_OPT_RDONLY) && defined(MNTTAB_OPT_RO) */
+
+#if defined(MNT2_GEN_OPT_NOCACHE) && defined(MNTTAB_OPT_NOCACHE)
+ {MNTTAB_OPT_NOCACHE, MNT2_GEN_OPT_NOCACHE},
+#endif /* defined(MNT2_GEN_OPT_NOCACHE) && defined(MNTTAB_OPT_NOCACHE) */
+
+ /* the "grpid" mount option can be offered as generic of NFS */
+#ifdef MNTTAB_OPT_GRPID
+# ifdef MNT2_GEN_OPT_GRPID
+ {MNTTAB_OPT_GRPID, MNT2_GEN_OPT_GRPID},
+# endif /* MNT2_GEN_OPT_GRPID */
+# ifdef MNT2_NFS_OPT_GRPID
+ {MNTTAB_OPT_GRPID, MNT2_NFS_OPT_GRPID},
+# endif /* MNT2_NFS_OPT_GRPID */
+#endif /* MNTTAB_OPT_GRPID */
+
+#if defined(MNT2_GEN_OPT_MULTI) && defined(MNTTAB_OPT_MULTI)
+ {MNTTAB_OPT_MULTI, MNT2_GEN_OPT_MULTI},
+#endif /* defined(MNT2_GEN_OPT_MULTI) && defined(MNTTAB_OPT_MULTI) */
+
+#if defined(MNT2_GEN_OPT_NODEV) && defined(MNTTAB_OPT_NODEV)
+ {MNTTAB_OPT_NODEV, MNT2_GEN_OPT_NODEV},
+#endif /* defined(MNT2_GEN_OPT_NODEV) && defined(MNTTAB_OPT_NODEV) */
+
+#if defined(MNT2_GEN_OPT_NOEXEC) && defined(MNTTAB_OPT_NOEXEC)
+ {MNTTAB_OPT_NOEXEC, MNT2_GEN_OPT_NOEXEC},
+#endif /* defined(MNT2_GEN_OPT_NOEXEC) && defined(MNTTAB_OPT_NOEXEC) */
+
+#if defined(MNT2_GEN_OPT_NOSUB) && defined(MNTTAB_OPT_NOSUB)
+ {MNTTAB_OPT_NOSUB, MNT2_GEN_OPT_NOSUB},
+#endif /* defined(MNT2_GEN_OPT_NOSUB) && defined(MNTTAB_OPT_NOSUB) */
+
+#if defined(MNT2_GEN_OPT_NOSUID) && defined(MNTTAB_OPT_NOSUID)
+ {MNTTAB_OPT_NOSUID, MNT2_GEN_OPT_NOSUID},
+#endif /* defined(MNT2_GEN_OPT_NOSUID) && defined(MNTTAB_OPT_NOSUID) */
+
+#if defined(MNT2_GEN_OPT_SYNC) && defined(MNTTAB_OPT_SYNC)
+ {MNTTAB_OPT_SYNC, MNT2_GEN_OPT_SYNC},
+#endif /* defined(MNT2_GEN_OPT_SYNC) && defined(MNTTAB_OPT_SYNC) */
+
+#if defined(MNT2_GEN_OPT_OVERLAY) && defined(MNTTAB_OPT_OVERLAY)
+ {MNTTAB_OPT_OVERLAY, MNT2_GEN_OPT_OVERLAY},
+#endif /* defined(MNT2_GEN_OPT_OVERLAY) && defined(MNTTAB_OPT_OVERLAY) */
+
+ {0, 0}
+};
+
+
+int
+compute_mount_flags(mntent_t *mntp)
+{
+ struct opt_tab *opt;
+ int flags;
+
+ /* start: this must come first */
+#ifdef MNT2_GEN_OPT_NEWTYPE
+ flags = MNT2_GEN_OPT_NEWTYPE;
+#else /* not MNT2_GEN_OPT_NEWTYPE */
+ /* Not all machines have MNT2_GEN_OPT_NEWTYPE (HP-UX 9.01) */
+ flags = 0;
+#endif /* not MNT2_GEN_OPT_NEWTYPE */
+
+#if defined(MNT2_GEN_OPT_OVERLAY) && defined(MNTTAB_OPT_OVERLAY)
+ /*
+ * Overlay this amd mount (presumably on another amd which died
+ * before and left the machine hung). This will allow a new amd or
+ * hlfsd to be remounted on top of another one.
+ */
+ if (hasmntopt(mntp, MNTTAB_OPT_OVERLAY)) {
+ flags |= MNT2_GEN_OPT_OVERLAY;
+ plog(XLOG_INFO, "using an overlay mount");
+ }
+#endif /* defined(MNT2_GEN_OVERLAY) && defined(MNTOPT_OVERLAY) */
+
+ /*
+ * Crack basic mount options
+ */
+ for (opt = mnt_flags; opt->opt; opt++) {
+ flags |= hasmntopt(mntp, opt->opt) ? opt->flag : 0;
+ }
+
+ return flags;
+}
+
+
+int
+mount_fs(mntent_t *mnt, int flags, caddr_t mnt_data, int retry, MTYPE_TYPE type, u_long nfs_version, const char *nfs_proto, const char *mnttabname)
+{
+ int error = 0;
+#ifdef MOUNT_TABLE_ON_FILE
+# ifdef MNTTAB_OPT_DEV
+ struct stat stb;
+# endif /* MNTTAB_OPT_DEV */
+ char *zopts = NULL, *xopts = NULL;
+# if defined(MNTTAB_OPT_DEV) || (defined(HAVE_FS_NFS3) && defined(MNTTAB_OPT_VERS)) || defined(MNTTAB_OPT_PROTO)
+ char optsbuf[48];
+# endif /* defined(MNTTAB_OPT_DEV) || (defined(HAVE_FS_NFS3) && defined(MNTTAB_OPT_VERS)) || defined(MNTTAB_OPT_PROTO) */
+#endif /* MOUNT_TABLE_ON_FILE */
+#ifdef DEBUG
+ char buf[80]; /* buffer for sprintf */
+#endif /* DEBUG */
+
+#ifdef DEBUG
+ sprintf(buf, "%s%s%s",
+ "%s fstype ", MTYPE_PRINTF_TYPE, " (%s) flags %#x (%s)");
+ dlog(buf, mnt->mnt_dir, type, mnt->mnt_type, flags, mnt->mnt_opts);
+#endif /* DEBUG */
+
+again:
+ clock_valid = 0;
+
+ error = MOUNT_TRAP(type, mnt, flags, mnt_data);
+
+ if (error < 0) {
+ plog(XLOG_ERROR, "%s: mount: %m", mnt->mnt_dir);
+ /*
+ * The following code handles conditions which shouldn't
+ * occur. They are possible either because amd screws up
+ * in preparing for the mount, or because some human
+ * messed with the mount point. Both have been known to
+ * happen. -- stolcke 2/22/95
+ */
+ if (errno == ENOENT) {
+ /*
+ * Occasionally the mount point vanishes, probably
+ * due to some race condition. Just recreate it
+ * as necessary.
+ */
+ errno = mkdirs(mnt->mnt_dir, 0555);
+ if (errno != 0 && errno != EEXIST)
+ plog(XLOG_ERROR, "%s: mkdirs: %m", mnt->mnt_dir);
+ else {
+ plog(XLOG_WARNING, "extra mkdirs required for %s",
+ mnt->mnt_dir);
+ error = MOUNT_TRAP(type, mnt, flags, mnt_data);
+ }
+ } else if (errno == EBUSY) {
+ /*
+ * Also, sometimes unmount isn't called, e.g., because
+ * our mountlist is garbled. This leaves old mount
+ * points around which need to be removed before we
+ * can mount something new in their place.
+ */
+ errno = umount_fs(mnt->mnt_dir, mnttabname);
+ if (errno != 0)
+ plog(XLOG_ERROR, "%s: umount: %m", mnt->mnt_dir);
+ else {
+ plog(XLOG_WARNING, "extra umount required for %s",
+ mnt->mnt_dir);
+ error = MOUNT_TRAP(type, mnt, flags, mnt_data);
+ }
+ }
+ }
+
+ if (error < 0 && --retry > 0) {
+ sleep(1);
+ goto again;
+ }
+ if (error < 0) {
+ return errno;
+ }
+
+#ifdef MOUNT_TABLE_ON_FILE
+ /*
+ * Allocate memory for options:
+ * dev=..., vers={2,3}, proto={tcp,udp}
+ */
+ zopts = (char *) xmalloc(strlen(mnt->mnt_opts) + 48);
+
+ /* copy standard options */
+ xopts = mnt->mnt_opts;
+
+ strcpy(zopts, xopts);
+
+# ifdef MNTTAB_OPT_DEV
+ /* add the extra dev= field to the mount table */
+ if (lstat(mnt->mnt_dir, &stb) == 0) {
+ if (sizeof(stb.st_dev) == 2) /* e.g. SunOS 4.1 */
+ sprintf(optsbuf, "%s=%04lx",
+ MNTTAB_OPT_DEV, (u_long) stb.st_dev & 0xffff);
+ else /* e.g. System Vr4 */
+ sprintf(optsbuf, "%s=%08lx",
+ MNTTAB_OPT_DEV, (u_long) stb.st_dev);
+ append_opts(zopts, optsbuf);
+ }
+# endif /* MNTTAB_OPT_DEV */
+
+# if defined(HAVE_FS_NFS3) && defined(MNTTAB_OPT_VERS)
+ /*
+ * add the extra vers={2,3} field to the mount table,
+ * unless already specified by user
+ */
+ if (nfs_version == NFS_VERSION3 &&
+ hasmntval(mnt, MNTTAB_OPT_VERS) != NFS_VERSION3) {
+ sprintf(optsbuf, "%s=%d", MNTTAB_OPT_VERS, NFS_VERSION3);
+ append_opts(zopts, optsbuf);
+ }
+# endif /* defined(HAVE_FS_NFS3) && defined(MNTTAB_OPT_VERS) */
+
+# ifdef MNTTAB_OPT_PROTO
+ /*
+ * add the extra proto={tcp,udp} field to the mount table,
+ * unless already specified by user.
+ */
+ if (nfs_proto && !hasmntopt(mnt, MNTTAB_OPT_PROTO)) {
+ sprintf(optsbuf, "%s=%s", MNTTAB_OPT_PROTO, nfs_proto);
+ append_opts(zopts, optsbuf);
+ }
+# endif /* MNTTAB_OPT_PROTO */
+
+ /* finally, store the options into the mount table structure */
+ mnt->mnt_opts = zopts;
+
+ /*
+ * Additional fields in mntent_t
+ * are fixed up here
+ */
+# ifdef HAVE_FIELD_MNTENT_T_MNT_CNODE
+ mnt->mnt_cnode = 0;
+# endif /* HAVE_FIELD_MNTENT_T_MNT_CNODE */
+
+# ifdef HAVE_FIELD_MNTENT_T_MNT_RO
+ mnt->mnt_ro = (hasmntopt(mnt, MNTTAB_OPT_RO) != NULL);
+# endif /* HAVE_FIELD_MNTENT_T_MNT_RO */
+
+# ifdef HAVE_FIELD_MNTENT_T_MNT_TIME
+# ifdef HAVE_FIELD_MNTENT_T_MNT_TIME_STRING
+ { /* allocate enough space for a long */
+ char *str = (char *) xmalloc(13 * sizeof(char));
+ sprintf(str, "%ld", time((time_t *) NULL));
+ mnt->mnt_time = str;
+ }
+# else /* not HAVE_FIELD_MNTENT_T_MNT_TIME_STRING */
+ mnt->mnt_time = time((time_t *) NULL);
+# endif /* not HAVE_FIELD_MNTENT_T_MNT_TIME_STRING */
+# endif /* HAVE_FIELD_MNTENT_T_MNT_TIME */
+
+ write_mntent(mnt, mnttabname);
+
+# ifdef MNTTAB_OPT_DEV
+ if (xopts) {
+ XFREE(mnt->mnt_opts);
+ mnt->mnt_opts = xopts;
+ }
+# endif /* MNTTAB_OPT_DEV */
+#endif /* MOUNT_TABLE_ON_FILE */
+
+ return 0;
+}
+
+
+/*
+ * Fill in the many possible fields and flags of struct nfs_args.
+ *
+ * nap: pre-allocted structure to fill in.
+ * mntp: mount entry structure (includes options)
+ * genflags: generic mount flags already determined
+ * nfsncp: (TLI only) netconfig entry for this NFS mount
+ * ip_addr: IP address of file server
+ * nfs_version: 2, 3, (4 in the future), or 0 if unknown
+ * nfs_proto: "udp", "tcp", or NULL.
+ * fhp: file handle structure pointer
+ * host_name: name of remote NFS host
+ * fs_name: remote file system name to mount
+ */
+void
+#ifdef HAVE_TRANSPORT_TYPE_TLI
+compute_nfs_args(nfs_args_t *nap, mntent_t *mntp, int genflags, struct netconfig *nfsncp, struct sockaddr_in *ip_addr, u_long nfs_version, char *nfs_proto, am_nfs_handle_t *fhp, char *host_name, char *fs_name)
+#else /* not HAVE_TRANSPORT_TYPE_TLI */
+compute_nfs_args(nfs_args_t *nap, mntent_t *mntp, int genflags, struct sockaddr_in *ip_addr, u_long nfs_version, char *nfs_proto, am_nfs_handle_t *fhp, char *host_name, char *fs_name)
+#endif /* not HAVE_TRANSPORT_TYPE_TLI */
+{
+ int acval = 0;
+#ifdef HAVE_FS_NFS3
+ static am_nfs_fh3 fh3; /* static, b/c gcc on aix corrupts stack */
+#endif /* HAVE_FS_NFS3 */
+
+ /* initialize just in case */
+ memset((voidp) nap, 0, sizeof(nfs_args_t));
+
+ /************************************************************************/
+ /*** FILEHANDLE DATA AND LENGTH ***/
+ /************************************************************************/
+#ifdef HAVE_FS_NFS3
+ if (nfs_version == NFS_VERSION3) {
+ memset((voidp) &fh3, 0, sizeof(am_nfs_fh3));
+ fh3.fh3_length = fhp->v3.mountres3_u.mountinfo.fhandle.fhandle3_len;
+ memmove(fh3.fh3_u.data,
+ fhp->v3.mountres3_u.mountinfo.fhandle.fhandle3_val,
+ fh3.fh3_length);
+
+# if defined(HAVE_FIELD_NFS_ARGS_T_FHSIZE) || defined(HAVE_FIELD_NFS_ARGS_T_FH_LEN)
+ /*
+ * Some systems (Irix/bsdi3) have a separate field in nfs_args for
+ * the length of the file handle for NFS V3. They insist that
+ * the file handle set in nfs_args be plain bytes, and not
+ * include the length field.
+ */
+ NFS_FH_DREF(nap->NFS_FH_FIELD, &(fh3.fh3_u.data));
+# else /* not defined(HAVE_FIELD_NFS_ARGS_T_FHSIZE) || defined(HAVE_FIELD_NFS_ARGS_T_FH_LEN) */
+ NFS_FH_DREF(nap->NFS_FH_FIELD, &fh3);
+# endif /* not defined(HAVE_FIELD_NFS_ARGS_T_FHSIZE) || defined(HAVE_FIELD_NFS_ARGS_T_FH_LEN) */
+# ifdef MNT2_NFS_OPT_NFSV3
+ nap->flags |= MNT2_NFS_OPT_NFSV3;
+# endif /* MNT2_NFS_OPT_NFSV3 */
+ } else
+#endif /* HAVE_FS_NFS3 */
+ NFS_FH_DREF(nap->NFS_FH_FIELD, &(fhp->v2.fhs_fh));
+
+#ifdef HAVE_FIELD_NFS_ARGS_T_FHSIZE
+# ifdef HAVE_FS_NFS3
+ if (nfs_version == NFS_VERSION3)
+ nap->fhsize = fh3.fh3_length;
+ else
+# endif /* HAVE_FS_NFS3 */
+ nap->fhsize = FHSIZE;
+#endif /* HAVE_FIELD_NFS_ARGS_T_FHSIZE */
+
+ /* this is the version of the nfs_args structure, not of NFS! */
+#ifdef HAVE_FIELD_NFS_ARGS_T_FH_LEN
+# ifdef HAVE_FS_NFS3
+ if (nfs_version == NFS_VERSION3)
+ nap->fh_len = fh3.fh3_length;
+ else
+# endif /* HAVE_FS_NFS3 */
+ nap->fh_len = FHSIZE;
+#endif /* HAVE_FIELD_NFS_ARGS_T_FH_LEN */
+
+ /************************************************************************/
+ /*** HOST NAME ***/
+ /************************************************************************/
+ NFS_HN_DREF(nap->hostname, host_name);
+#ifdef MNT2_NFS_OPT_HOSTNAME
+ nap->flags |= MNT2_NFS_OPT_HOSTNAME;
+#endif /* MNT2_NFS_OPT_HOSTNAME */
+
+ /************************************************************************/
+ /*** ATTRIBUTE CACHES ***/
+ /************************************************************************/
+ /*
+ * acval is set to 0 at the top of the function. If actimeo mount option
+ * exists and defined in mntopts, then it acval is set to it.
+ * If the value is non-zero, then we set all attribute cache fields to it.
+ * If acval is zero, it means it was never defined in mntopts or the
+ * actimeo mount option does not exist, in which case we check for
+ * individual mount options per attribute cache.
+ * Regardless of the value of acval, mount flags are set based directly
+ * on the values of the attribute caches.
+ */
+#ifdef MNTTAB_OPT_ACTIMEO
+ acval = hasmntval(mntp, MNTTAB_OPT_ACTIMEO); /* attr cache timeout (sec) */
+#endif /* MNTTAB_OPT_ACTIMEO */
+
+ if (acval) {
+#ifdef HAVE_FIELD_NFS_ARGS_T_ACREGMIN
+ nap->acregmin = acval; /* min ac timeout for reg files (sec) */
+ nap->acregmax = acval; /* max ac timeout for reg files (sec) */
+#endif /* HAVE_FIELD_NFS_ARGS_T_ACREGMIN */
+#ifdef HAVE_FIELD_NFS_ARGS_T_ACDIRMIN
+ nap->acdirmin = acval; /* min ac timeout for dirs (sec) */
+ nap->acdirmax = acval; /* max ac timeout for dirs (sec) */
+#endif /* HAVE_FIELD_NFS_ARGS_T_ACDIRMIN */
+ } else {
+#ifdef MNTTAB_OPT_ACREGMIN
+ nap->acregmin = hasmntval(mntp, MNTTAB_OPT_ACREGMIN);
+#endif /* MNTTAB_OPT_ACREGMIN */
+#ifdef MNTTAB_OPT_ACREGMAX
+ nap->acregmax = hasmntval(mntp, MNTTAB_OPT_ACREGMAX);
+#endif /* MNTTAB_OPT_ACREGMAX */
+#ifdef MNTTAB_OPT_ACDIRMIN
+ nap->acdirmin = hasmntval(mntp, MNTTAB_OPT_ACDIRMIN);
+#endif /* MNTTAB_OPT_ACDIRMIN */
+#ifdef MNTTAB_OPT_ACDIRMAX
+ nap->acdirmax = hasmntval(mntp, MNTTAB_OPT_ACDIRMAX);
+#endif /* MNTTAB_OPT_ACDIRMAX */
+ } /* end of "if (acval)" statement */
+
+#ifdef MNT2_NFS_OPT_ACREGMIN
+ if (nap->acregmin)
+ nap->flags |= MNT2_NFS_OPT_ACREGMIN;
+#endif /* MNT2_NFS_OPT_ACREGMIN */
+#ifdef MNT2_NFS_OPT_ACREGMAX
+ if (nap->acregmax)
+ nap->flags |= MNT2_NFS_OPT_ACREGMAX;
+#endif /* MNT2_NFS_OPT_ACREGMAX */
+#ifdef MNT2_NFS_OPT_ACDIRMIN
+ if (nap->acdirmin)
+ nap->flags |= MNT2_NFS_OPT_ACDIRMIN;
+#endif /* MNT2_NFS_OPT_ACDIRMIN */
+#ifdef MNT2_NFS_OPT_ACDIRMAX
+ if (nap->acdirmax)
+ nap->flags |= MNT2_NFS_OPT_ACDIRMAX;
+#endif /* MNT2_NFS_OPT_ACDIRMAX */
+
+#ifdef MNTTAB_OPT_NOAC /* don't cache attributes */
+ if (hasmntopt(mntp, MNTTAB_OPT_NOAC) != NULL)
+ nap->flags |= MNT2_NFS_OPT_NOAC;
+#endif /* MNTTAB_OPT_NOAC */
+
+ /************************************************************************/
+ /*** IP ADDRESS OF REMOTE HOST ***/
+ /************************************************************************/
+ if (ip_addr) {
+#ifdef HAVE_TRANSPORT_TYPE_TLI
+ nap->addr = ALLOC(struct netbuf); /* free()'ed at end of mount_nfs_fh() */
+#endif /* HAVE_TRANSPORT_TYPE_TLI */
+ NFS_SA_DREF(nap, ip_addr);
+ }
+
+ /************************************************************************/
+ /*** NFS PROTOCOL (UDP, TCP) AND VERSION ***/
+ /************************************************************************/
+#ifdef MNT2_NFS_OPT_TCP
+ if (nfs_proto && STREQ(nfs_proto, "tcp"))
+ nap->flags |= MNT2_NFS_OPT_TCP;
+#endif /* MNT2_NFS_OPT_TCP */
+
+#ifdef HAVE_FIELD_NFS_ARGS_T_SOTYPE
+ /* bsdi3 uses this */
+ if (nfs_proto) {
+ if (STREQ(nfs_proto, "tcp"))
+ nap->sotype = SOCK_STREAM;
+ else if (STREQ(nfs_proto, "udp"))
+ nap->sotype = SOCK_DGRAM;
+ }
+#endif /* HAVE_FIELD_NFS_ARGS_T_SOTYPE */
+
+#ifdef HAVE_FIELD_NFS_ARGS_T_PROTO
+ nap->proto = 0; /* bsdi3 sets this field to zero */
+# ifdef IPPROTO_TCP
+ if (nfs_proto) {
+ if (STREQ(nfs_proto, "tcp")) /* AIX 4.2.x needs this */
+ nap->proto = IPPROTO_TCP;
+ else if (STREQ(nfs_proto, "udp"))
+ nap->proto = IPPROTO_UDP;
+ }
+# endif /* IPPROTO_TCP */
+#endif /* HAVE_FIELD_NFS_ARGS_T_SOTYPE */
+
+#ifdef HAVE_FIELD_NFS_ARGS_T_VERSION
+# ifdef NFS_ARGSVERSION
+ nap->version = NFS_ARGSVERSION; /* BSDI 3.0 and OpenBSD 2.2 */
+# endif /* NFS_ARGSVERSION */
+# ifdef DG_MOUNT_NFS_VERSION
+ nap->version = DG_MOUNT_NFS_VERSION; /* dg-ux */
+# endif /* DG_MOUNT_NFS_VERSION */
+#endif /* HAVE_FIELD_NFS_ARGS_VERSION */
+
+ /************************************************************************/
+ /*** OTHER NFS SOCKET RELATED OPTIONS AND FLAGS ***/
+ /************************************************************************/
+#ifdef MNT2_NFS_OPT_NOCONN
+ /* check if user specified to use unconnected or connected sockets */
+ if (hasmntopt(mntp, MNTTAB_OPT_NOCONN) != NULL)
+ nap->flags |= MNT2_NFS_OPT_NOCONN;
+ else if (hasmntopt(mntp, MNTTAB_OPT_CONN) != NULL)
+ nap->flags &= ~MNT2_NFS_OPT_NOCONN;
+ else {
+ /*
+ * Some OSs want you to set noconn always. Some want you to always turn
+ * it off. Others want you to turn it on/off only if NFS V.3 is used.
+ * And all of that changes from revision to another. This is
+ * particularly true of OpenBSD, NetBSD, and FreeBSD. So, rather than
+ * attempt to auto-detect this, I'm forced to "fix" it in the individual
+ * conf/nfs_prot/nfs_prot_*.h files.
+ */
+# ifdef USE_UNCONNECTED_NFS_SOCKETS
+ nap->flags |= MNT2_NFS_OPT_NOCONN;
+ plog(XLOG_WARNING, "noconn option exists, and was turned ON! (May cause NFS hangs on some systems...)");
+# endif /* USE_UNCONNECTED_NFS_SOCKETS */
+# ifdef USE_CONNECTED_NFS_SOCKETS
+ nap->flags &= ~MNT2_NFS_OPT_NOCONN;
+ plog(XLOG_WARNING, "noconn option exists, and was turned OFF! (May cause NFS hangs on some systems...)");
+# endif /* USE_CONNECTED_NFS_SOCKETS */
+ }
+#endif /* MNT2_NFS_OPT_NOCONN */
+
+#ifdef MNT2_NFS_OPT_RESVPORT
+# ifdef MNTTAB_OPT_RESVPORT
+ if (hasmntopt(mntp, MNTTAB_OPT_RESVPORT) != NULL)
+ nap->flags |= MNT2_NFS_OPT_RESVPORT;
+# else /* not MNTTAB_OPT_RESVPORT */
+ nap->flags |= MNT2_NFS_OPT_RESVPORT;
+# endif /* not MNTTAB_OPT_RESVPORT */
+#endif /* MNT2_NFS_OPT_RESVPORT */
+
+ /************************************************************************/
+ /*** OTHER FLAGS AND OPTIONS ***/
+ /************************************************************************/
+
+#ifdef HAVE_TRANSPORT_TYPE_TLI
+ /* set up syncaddr field */
+ nap->syncaddr = (struct netbuf *) NULL;
+
+ /* set up knconf field */
+ if (get_knetconfig(&nap->knconf, nfsncp, nfs_proto) < 0) {
+ plog(XLOG_FATAL, "cannot fill knetconfig structure for nfs_args");
+ going_down(1);
+ }
+ /* update the flags field for knconf */
+ nap->flags |= MNT2_NFS_OPT_KNCONF;
+#endif /* HAVE_TRANSPORT_TYPE_TLI */
+
+#ifdef MNT2_NFS_OPT_FSNAME
+ nap->fsname = fs_name;
+ nap->flags |= MNT2_NFS_OPT_FSNAME;
+#endif /* MNT2_NFS_OPT_FSNAME */
+
+ nap->rsize = hasmntval(mntp, MNTTAB_OPT_RSIZE);
+#ifdef MNT2_NFS_OPT_RSIZE
+ if (nap->rsize)
+ nap->flags |= MNT2_NFS_OPT_RSIZE;
+#endif /* MNT2_NFS_OPT_RSIZE */
+
+ nap->wsize = hasmntval(mntp, MNTTAB_OPT_WSIZE);
+#ifdef MNT2_NFS_OPT_WSIZE
+ if (nap->wsize)
+ nap->flags |= MNT2_NFS_OPT_WSIZE;
+#endif /* MNT2_NFS_OPT_WSIZE */
+
+ nap->timeo = hasmntval(mntp, MNTTAB_OPT_TIMEO);
+#ifdef MNT2_NFS_OPT_TIMEO
+ if (nap->timeo)
+ nap->flags |= MNT2_NFS_OPT_TIMEO;
+#endif /* MNT2_NFS_OPT_TIMEO */
+
+ nap->retrans = hasmntval(mntp, MNTTAB_OPT_RETRANS);
+#ifdef MNT2_NFS_OPT_RETRANS
+ if (nap->retrans)
+ nap->flags |= MNT2_NFS_OPT_RETRANS;
+#endif /* MNT2_NFS_OPT_RETRANS */
+
+#ifdef MNT2_NFS_OPT_BIODS
+ if ((nap->biods = hasmntval(mntp, MNTTAB_OPT_BIODS)))
+ nap->flags |= MNT2_NFS_OPT_BIODS;
+#endif /* MNT2_NFS_OPT_BIODS */
+
+ if (hasmntopt(mntp, MNTTAB_OPT_SOFT) != NULL)
+ nap->flags |= MNT2_NFS_OPT_SOFT;
+
+#ifdef MNT2_NFS_OPT_SPONGY
+ if (hasmntopt(mntp, MNTTAB_OPT_SPONGY) != NULL) {
+ nap->flags |= MNT2_NFS_OPT_SPONGY;
+ if (nap->flags & MNT2_NFS_OPT_SOFT) {
+ plog(XLOG_USER, "Mount opts soft and spongy are incompatible - soft ignored");
+ nap->flags &= ~MNT2_NFS_OPT_SOFT;
+ }
+ }
+#endif /* MNT2_NFS_OPT_SPONGY */
+
+#if defined(MNT2_GEN_OPT_RONLY) && defined(MNT2_NFS_OPT_RONLY)
+ /* Ultrix has separate generic and NFS ro flags */
+ if (genflags & MNT2_GEN_OPT_RONLY)
+ nap->flags |= MNT2_NFS_OPT_RONLY;
+#endif /* defined(MNT2_GEN_OPT_RONLY) && defined(MNT2_NFS_OPT_RONLY) */
+
+#ifdef MNTTAB_OPT_INTR
+ if (hasmntopt(mntp, MNTTAB_OPT_INTR) != NULL)
+ /*
+ * Either turn on the "allow interrupts" option, or
+ * turn off the "disallow interrupts" option"
+ */
+# ifdef MNT2_NFS_OPT_INT
+ nap->flags |= MNT2_NFS_OPT_INT;
+# endif /* MNT2_NFS_OPT_INT */
+# ifdef MNT2_NFS_OPT_NOINT
+ nap->flags &= ~MNT2_NFS_OPT_NOINT;
+# endif /* MNT2_NFS_OPT_NOINT */
+#endif /* MNTTAB_OPT_INTR */
+
+#ifdef MNTTAB_OPT_NODEVS
+ if (hasmntopt(mntp, MNTTAB_OPT_NODEVS) != NULL)
+ nap->flags |= MNT2_NFS_OPT_NODEVS;
+#endif /* MNTTAB_OPT_NODEVS */
+
+#ifdef MNTTAB_OPT_COMPRESS
+ if (hasmntopt(mntp, MNTTAB_OPT_COMPRESS) != NULL)
+ nap->flags |= MNT2_NFS_OPT_COMPRESS;
+#endif /* MNTTAB_OPT_COMPRESS */
+
+#ifdef MNTTAB_OPT_PRIVATE /* mount private, single-client tree */
+ if (hasmntopt(mntp, MNTTAB_OPT_PRIVATE) != NULL)
+ nap->flags |= MNT2_NFS_OPT_PRIVATE;
+#endif /* MNTTAB_OPT_PRIVATE */
+
+#ifdef MNTTAB_OPT_SYMTTL /* symlink cache time-to-live */
+ if ((nap->symttl = hasmntval(mntp, MNTTAB_OPT_SYMTTL)))
+ nap->flags |= MNT2_NFS_OPT_SYMTTL;
+#endif /* MNTTAB_OPT_SYMTTL */
+
+#ifdef MNT2_NFS_OPT_PGTHRESH /* paging threshold */
+ if ((nap->pg_thresh = hasmntval(mntp, MNTTAB_OPT_PGTHRESH)))
+ nap->flags |= MNT2_NFS_OPT_PGTHRESH;
+#endif /* MNT2_NFS_OPT_PGTHRESH */
+
+#if defined(MNT2_NFS_OPT_NOCTO) && defined(MNTTAB_OPT_NOCTO)
+ if (hasmntopt(mntp, MNTTAB_OPT_NOCTO) != NULL)
+ nap->flags |= MNT2_NFS_OPT_NOCTO;
+#endif /* defined(MNT2_NFS_OPT_NOCTO) && defined(MNTTAB_OPT_NOCTO) */
+
+#if defined(MNT2_NFS_OPT_POSIX) && defined(MNTTAB_OPT_POSIX)
+ if (hasmntopt(mntp, MNTTAB_OPT_POSIX) != NULL) {
+ nap->flags |= MNT2_NFS_OPT_POSIX;
+ nap->pathconf = NULL;
+ }
+#endif /* MNT2_NFS_OPT_POSIX && MNTTAB_OPT_POSIX */
+
+#if defined(MNT2_NFS_OPT_MAXGRPS) && defined(MNTTAB_OPT_MAXGROUPS)
+ nap->maxgrouplist = hasmntval(mntp, MNTTAB_OPT_MAXGROUPS);
+ if (nap->maxgrouplist != NULL)
+ nap->flags |= MNT2_NFS_OPT_MAXGRPS;
+#endif /* defined(MNT2_NFS_OPT_MAXGRPS) && defined(MNTTAB_OPT_MAXGROUPS) */
+
+#ifdef HAVE_FIELD_NFS_ARGS_T_OPTSTR
+ nap->optstr = mntp->mnt_opts;
+#endif /* HAVE_FIELD_NFS_ARGS_T_OPTSTR */
+
+ /************************************************************************/
+ /*** FINAL ACTIONS ***/
+ /************************************************************************/
+
+#ifdef HAVE_FIELD_NFS_ARGS_T_GFS_FLAGS
+ /* Ultrix stores generic flags in nfs_args.gfs_flags. */
+ nap->gfs_flags = genflags;
+#endif /* HAVE_FIELD_NFS_ARGS_T_FLAGS */
+
+ return; /* end of compute_nfs_args() function */
+}
+
+
+/*
+ * Fill in special values for flags and fields of nfs_args, for an
+ * automounter NFS mount.
+ */
+void
+compute_automounter_nfs_args(nfs_args_t *nap, mntent_t *mntp)
+{
+#ifdef MNT2_NFS_OPT_SYMTTL
+ /*
+ * Don't let the kernel cache symbolic links we generate, or else lookups
+ * will bypass amd and fail to remount stuff as needed.
+ */
+ plog(XLOG_INFO, "turning on NFS option symttl and setting value to %d", 0);
+ nap->flags |= MNT2_NFS_OPT_SYMTTL;
+ nap->symttl = 0;
+#endif /* MNT2_NFS_OPT_SYMTTL */
+
+ /*
+ * This completes the flags for the HIDE_MOUNT_TYPE code above.
+ * Some systems don't have a mount type, but a mount flag.
+ */
+#ifdef MNT2_NFS_OPT_AUTO
+ nap->flags |= MNT2_NFS_OPT_AUTO;
+#endif /* MNT2_NFS_OPT_AUTO */
+#ifdef MNT2_NFS_OPT_IGNORE
+ nap->flags |= MNT2_NFS_OPT_IGNORE;
+#endif /* MNT2_NFS_OPT_IGNORE */
+
+#ifdef MNT2_NFS_OPT_DUMBTIMR
+ /*
+ * Don't let the kernel start computing throughput of Amd The numbers will
+ * be meaningless because of the way Amd does mount retries.
+ */
+ plog(XLOG_INFO, "%s: disabling nfs congestion window", mntp->mnt_dir);
+ nap->flags |= MNT2_NFS_OPT_DUMBTIMR;
+#endif /* MNT2_NFS_OPT_DUMBTIMR */
+
+#ifdef MNT2_NFS_OPT_NOAC
+ /*
+ * Don't cache attributes - they are changing under the kernel's feet.
+ * For example, IRIX5.2 will dispense with nfs lookup calls and hand stale
+ * filehandles to getattr unless we disable attribute caching on the
+ * automount points.
+ */
+ nap->flags |= MNT2_NFS_OPT_NOAC;
+#else /* not MNT2_NFS_OPT_NOAC */
+ /*
+ * Setting these to 0 results in an error on some systems, which is why
+ * it's better to use "noac" if possible.
+ */
+# if defined(MNT2_NFS_OPT_ACREGMIN) && defined(MNT2_NFS_OPT_ACREGMAX)
+ nap->acregmin = nap->acregmax = 0; /* XXX: was 1, but why? */
+ nap->flags |= MNT2_NFS_OPT_ACREGMIN | MNT2_NFS_OPT_ACREGMAX;
+# endif /* defined(MNT2_NFS_OPT_ACREGMIN) && defined(MNT2_NFS_OPT_ACREGMAX) */
+# if defined(MNT2_NFS_OPT_ACDIRMIN) && defined(MNT2_NFS_OPT_ACDIRMAX)
+ nap->acdirmin = nap->acdirmax = 0; /* XXX: was 1, but why? */
+ nap->flags |= MNT2_NFS_OPT_ACDIRMIN | MNT2_NFS_OPT_ACDIRMAX;
+# endif /* defined(MNT2_NFS_OPT_ACDIRMIN) && defined(MNT2_NFS_OPT_ACDIRMAX) */
+#endif /* not MNT2_NFS_OPT_NOAC */
+}
+
+
+#ifdef DEBUG
+/* get string version (in hex) of identifier */
+static char *
+get_hex_string(u_int len, const char *fhdata)
+{
+ int i;
+ static char buf[128]; /* better not go over it! */
+ char str[16];
+ short int arr[64];
+
+ if (!fhdata)
+ return NULL;
+ buf[0] = '\0';
+ memset(&arr[0], 0, (64 * sizeof(short int)));
+ memcpy(&arr[0], &fhdata[0], len);
+ for (i=0; i<len/sizeof(short int); i++) {
+ sprintf(str, "%04x", ntohs(arr[i]));
+ strcat(buf, str);
+ }
+ return buf;
+}
+
+
+/*
+ * print a subset of fields from "struct nfs_args" that are otherwise
+ * not being provided anywhere else.
+ */
+void
+print_nfs_args(const nfs_args_t *nap, u_long nfs_version)
+{
+ int fhlen = 32; /* default: NFS V.2 file handle length is 32 */
+#ifdef HAVE_TRANSPORT_TYPE_TLI
+ struct netbuf *nbp;
+ struct knetconfig *kncp;
+#else /* not HAVE_TRANSPORT_TYPE_TLI */
+ struct sockaddr_in *sap;
+#endif /* not HAVE_TRANSPORT_TYPE_TLI */
+
+ if (!nap) {
+ plog(XLOG_DEBUG, "NULL nfs_args!");
+ return;
+ }
+
+ /* override default file handle size */
+#ifdef FHSIZE
+ fhlen = FHSIZE;
+#endif /* FHSIZE */
+#ifdef NFS_FHSIZE
+ fhlen = NFS_FHSIZE;
+#endif /* NFS_FHSIZE */
+
+#ifdef HAVE_TRANSPORT_TYPE_TLI
+ nbp = nap->addr;
+ plog(XLOG_DEBUG, "NA->addr {netbuf} (maxlen=%d, len=%d) = \"%s\"",
+ nbp->maxlen, nbp->len,
+ get_hex_string(nbp->len, nbp->buf));
+ nbp = nap->syncaddr;
+ plog(XLOG_DEBUG, "NA->syncaddr {netbuf} 0x%x", (int) nbp);
+ kncp = nap->knconf;
+ plog(XLOG_DEBUG, "NA->knconf->semantics %lu", kncp->knc_semantics);
+ plog(XLOG_DEBUG, "NA->knconf->protofmly \"%s\"", kncp->knc_protofmly);
+ plog(XLOG_DEBUG, "NA->knconf->proto \"%s\"", kncp->knc_proto);
+ plog(XLOG_DEBUG, "NA->knconf->rdev %lu", kncp->knc_rdev);
+ /* don't print knconf->unused field */
+#else /* not HAVE_TRANSPORT_TYPE_TLI */
+ sap = (struct sockaddr_in *) &nap->addr;
+ plog(XLOG_DEBUG, "NA->addr {sockaddr_in} (len=%d) = \"%s\"",
+ sizeof(struct sockaddr_in),
+ get_hex_string(sizeof(struct sockaddr_in), (const char *)sap));
+#ifdef HAVE_FIELD_STRUCT_SOCKADDR_SA_LEN_off
+ plog(XLOG_DEBUG, "NA->addr.sin_len = \"%d\"", sap->sin_len);
+#endif /* HAVE_FIELD_STRUCT_SOCKADDR_SA_LEN */
+ plog(XLOG_DEBUG, "NA->addr.sin_family = \"%d\"", sap->sin_family);
+ plog(XLOG_DEBUG, "NA->addr.sin_port = \"%d\"", sap->sin_port);
+ plog(XLOG_DEBUG, "NA->addr.sin_addr = \"%s\"",
+ get_hex_string(sizeof(struct in_addr), (const char *) &sap->sin_addr));
+#endif /* not HAVE_TRANSPORT_TYPE_TLI */
+
+ plog(XLOG_DEBUG, "NA->hostname = \"%s\"", nap->hostname ? nap->hostname : "null");
+#ifdef MNT2_NFS_OPT_FSNAME
+ plog(XLOG_DEBUG, "NA->fsname = \"%s\"", nap->fsname ? nap->fsname : "null");
+#endif /* MNT2_NFS_OPT_FSNAME */
+
+#ifdef HAVE_FIELD_NFS_ARGS_T_FHSIZE
+ plog(XLOG_DEBUG, "NA->fhsize = %d", nap->fhsize);
+ fhlen = nap->fhsize;
+#endif /* HAVE_FIELD_NFS_ARGS_T_FHSIZE */
+#ifdef HAVE_FIELD_NFS_ARGS_T_FH_LEN
+ plog(XLOG_DEBUG, "NA->fh_len = %d", nap->fh_len);
+ fhlen = nap->fh_len;
+#endif /* HAVE_FIELD_NFS_ARGS_T_FH_LEN */
+
+ /*
+ * XXX: need to figure out how to correctly print file handles,
+ * since some times they are pointers, and sometimes the real structure
+ * is stored in nfs_args. Even if it is a pointer, it can be the actual
+ * char[] array, or a structure containing multiple fields.
+ */
+ plog(XLOG_DEBUG, "NA->filehandle = \"%s\"",
+ get_hex_string(fhlen, (const char *) &nap->NFS_FH_FIELD));
+
+#ifdef HAVE_FIELD_NFS_ARGS_T_SOTYPE
+ plog(XLOG_DEBUG, "NA->sotype = %d", nap->sotype);
+#endif /* HAVE_FIELD_NFS_ARGS_T_SOTYPE */
+#ifdef HAVE_FIELD_NFS_ARGS_T_PROTO
+ plog(XLOG_DEBUG, "NA->proto = %d", nap->proto);
+#endif /* HAVE_FIELD_NFS_ARGS_T_PROTO */
+#ifdef HAVE_FIELD_NFS_ARGS_T_VERSION
+ plog(XLOG_DEBUG, "NA->version = %d", nap->version);
+#endif /* HAVE_FIELD_NFS_ARGS_T_VERSION */
+
+ plog(XLOG_DEBUG, "NA->flags = 0x%x", nap->flags);
+
+ plog(XLOG_DEBUG, "NA->rsize = %d", nap->rsize);
+ plog(XLOG_DEBUG, "NA->wsize = %d", nap->wsize);
+ plog(XLOG_DEBUG, "NA->timeo = %d", nap->timeo);
+ plog(XLOG_DEBUG, "NA->retrans = %d", nap->retrans);
+
+#ifdef HAVE_FIELD_NFS_ARGS_T_ACREGMIN
+ plog(XLOG_DEBUG, "NA->acregmin = %d", nap->acregmin);
+ plog(XLOG_DEBUG, "NA->acregmax = %d", nap->acregmax);
+ plog(XLOG_DEBUG, "NA->acdirmin = %d", nap->acdirmin);
+ plog(XLOG_DEBUG, "NA->acdirmax = %d", nap->acdirmax);
+#endif /* HAVE_FIELD_NFS_ARGS_T_ACREGMIN */
+#ifdef MNTTAB_OPT_SYMTTL
+ plog(XLOG_DEBUG, "NA->symttl = %d", nap->symttl);
+#endif /* MNTTAB_OPT_SYMTTL */
+#ifdef MNTTAB_OPT_PG_THRESH
+ plog(XLOG_DEBUG, "NA->pg_thresh = %d", nap->pg_thresh);
+#endif /* MNTTAB_OPT_PG_THRESH */
+
+#ifdef MNT2_NFS_OPT_BIODS
+ plog(XLOG_DEBUG, "NA->biods = %d", nap->biods);
+#endif /* MNT2_NFS_OPT_BIODS */
+
+}
+#endif /* DEBUG */
diff --git a/contrib/amd/libamu/mtab.c b/contrib/amd/libamu/mtab.c
new file mode 100644
index 000000000000..20cae62c1734
--- /dev/null
+++ b/contrib/amd/libamu/mtab.c
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: mtab.c,v 5.2.2.1 1992/02/09 15:08:45 jsp beta $
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amu.h>
+
+
+/*
+ * Firewall /etc/mtab entries
+ */
+void
+mnt_free(mntent_t *mp)
+{
+ XFREE(mp->mnt_fsname);
+ XFREE(mp->mnt_dir);
+ XFREE(mp->mnt_type);
+ XFREE(mp->mnt_opts);
+
+#ifdef HAVE_FIELD_MNTENT_T_MNT_TIME
+# ifdef HAVE_FIELD_MNTENT_T_MNT_TIME_STRING
+ XFREE(mp->mnt_time);
+# endif /* HAVE_FIELD_MNTENT_T_MNT_TIME_STRING */
+#endif /* HAVE_FIELD_MNTENT_T_MNT_TIME */
+
+ XFREE(mp);
+}
+
+
+/*
+ * Discard memory allocated for mount list
+ */
+void
+discard_mntlist(mntlist *mp)
+{
+ mntlist *mp2;
+
+ while ((mp2 = mp)) {
+ mp = mp->mnext;
+ if (mp2->mnt)
+ mnt_free(mp2->mnt);
+ XFREE(mp2);
+ }
+}
+
+
+/*
+ * Throw away a mount list
+ */
+void
+free_mntlist(mntlist *mp)
+{
+ discard_mntlist(mp);
+#ifdef MOUNT_TABLE_ON_FILE
+ unlock_mntlist();
+#endif /* MOUNT_TABLE_ON_FILE */
+}
+
+
+/*
+ * Utility routine which determines the value of a
+ * numeric option in the mount options (such as port=%d).
+ * Returns 0 if the option is not specified.
+ */
+int
+hasmntval(mntent_t *mnt, char *opt)
+{
+ char *str = hasmntopt(mnt, opt);
+
+ if (str) {
+ char *eq = strchr(str, '=');
+ if (eq)
+ return atoi(eq + 1);
+ else
+ plog(XLOG_USER, "bad numeric option \"%s\" in \"%s\"", opt, str);
+ }
+ return 0;
+}
diff --git a/contrib/amd/libamu/nfs_prot_xdr.c b/contrib/amd/libamu/nfs_prot_xdr.c
new file mode 100644
index 000000000000..2098b5dfb3b1
--- /dev/null
+++ b/contrib/amd/libamu/nfs_prot_xdr.c
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: nfs_prot_xdr.c,v 5.2.2.1 1992/02/09 15:09:32 jsp beta $
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amu.h>
+
+
+bool_t
+xdr_amq_string(XDR *xdrs, amq_string *objp)
+{
+ if (!xdr_string(xdrs, objp, AMQ_STRLEN)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
diff --git a/contrib/amd/libamu/util.c b/contrib/amd/libamu/util.c
new file mode 100644
index 000000000000..1198347fffa2
--- /dev/null
+++ b/contrib/amd/libamu/util.c
@@ -0,0 +1,176 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: util.c,v 5.2.2.2 1992/03/07 17:52:06 jsp Exp $
+ *
+ */
+
+/*
+ * General Utilitiles.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amu.h>
+
+
+char *
+strnsave(const char *str, int len)
+{
+ char *sp = (char *) xmalloc(len + 1);
+ memmove(sp, str, len);
+ sp[len] = 0;
+
+ return sp;
+}
+
+
+/*
+ * Concatenate three strings and store in buffer pointed to
+ * by p, making p large enough to hold the strings
+ */
+char *
+str3cat(char *p, char *s1, char *s2, char *s3)
+{
+ int l1 = strlen(s1);
+ int l2 = strlen(s2);
+ int l3 = strlen(s3);
+
+ p = (char *) xrealloc(p, l1 + l2 + l3 + 1);
+ memmove(p, s1, l1);
+ memmove(p + l1, s2, l2);
+ memmove(p + l1 + l2, s3, l3 + 1);
+ return p;
+}
+
+
+/*
+ * Make all the directories in the path.
+ */
+int
+mkdirs(char *path, int mode)
+{
+ /*
+ * take a copy in case path is in readonly store
+ */
+ char *p2 = strdup(path);
+ char *sp = p2;
+ struct stat stb;
+ int error_so_far = 0;
+
+ /*
+ * Skip through the string make the directories.
+ * Mostly ignore errors - the result is tested at the end.
+ *
+ * This assumes we are root so that we can do mkdir in a
+ * mode 555 directory...
+ */
+ while ((sp = strchr(sp + 1, '/'))) {
+ *sp = '\0';
+ if (mkdir(p2, mode) < 0) {
+ error_so_far = errno;
+ } else {
+#ifdef DEBUG
+ dlog("mkdir(%s)", p2);
+#endif /* DEBUG */
+ }
+ *sp = '/';
+ }
+
+ if (mkdir(p2, mode) < 0) {
+ error_so_far = errno;
+ } else {
+#ifdef DEBUG
+ dlog("mkdir(%s)", p2);
+#endif /* DEBUG */
+ }
+
+ XFREE(p2);
+
+ return stat(path, &stb) == 0 &&
+ (stb.st_mode & S_IFMT) == S_IFDIR ? 0 : error_so_far;
+}
+
+
+/*
+ * Remove as many directories in the path as possible.
+ * Give up if the directory doesn't appear to have
+ * been created by Amd (not mode dr-x) or an rmdir
+ * fails for any reason.
+ */
+void
+rmdirs(char *dir)
+{
+ char *xdp = strdup(dir);
+ char *dp;
+
+ do {
+ struct stat stb;
+ /*
+ * Try to find out whether this was
+ * created by amd. Do this by checking
+ * for owner write permission.
+ */
+ if (stat(xdp, &stb) == 0 && (stb.st_mode & 0200) == 0) {
+ if (rmdir(xdp) < 0) {
+ if (errno != ENOTEMPTY &&
+ errno != EBUSY &&
+ errno != EEXIST &&
+ errno != EINVAL)
+ plog(XLOG_ERROR, "rmdir(%s): %m", xdp);
+ break;
+ } else {
+#ifdef DEBUG
+ dlog("rmdir(%s)", xdp);
+#endif /* DEBUG */
+ }
+ } else {
+ break;
+ }
+
+ dp = strrchr(xdp, '/');
+ if (dp)
+ *dp = '\0';
+ } while (dp && dp > xdp);
+
+ XFREE(xdp);
+}
diff --git a/contrib/amd/libamu/wire.c b/contrib/amd/libamu/wire.c
new file mode 100644
index 000000000000..f610f92485c7
--- /dev/null
+++ b/contrib/amd/libamu/wire.c
@@ -0,0 +1,404 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: wire.c,v 5.2.2.2 1992/06/07 18:06:46 jsp Exp jsp $
+ *
+ */
+
+/*
+ * This function returns the subnet (address&netmask) for the primary network
+ * interface. If the resulting address has an entry in the hosts file, the
+ * corresponding name is retuned, otherwise the address is returned in
+ * standard internet format.
+ * As a side-effect, a list of local IP/net address is recorded for use
+ * by the islocalnet() function.
+ *
+ * Derived from original by Paul Anderson (23/4/90)
+ * Updates from Dirk Grunwald (11/11/91)
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amu.h>
+
+
+/*
+ * List of locally connected networks
+ */
+typedef struct addrlist addrlist;
+struct addrlist {
+ addrlist *ip_next;
+ u_long ip_addr; /* address of network */
+ u_long ip_mask;
+ char *ip_net_num; /* number of network */
+ char *ip_net_name; /* name of network */
+};
+static addrlist *localnets = NULL;
+
+#if defined(IFF_LOCAL_LOOPBACK) && !defined(IFF_LOOPBACK)
+# define IFF_LOOPBACK IFF_LOCAL_LOOPBACK
+#endif /* defined(IFF_LOCAL_LOOPBACK) && !defined(IFF_LOOPBACK) */
+
+#if defined(HAVE_FIELD_STRUCT_IFREQ_IFR_ADDR) && defined(HAVE_FIELD_STRUCT_SOCKADDR_SA_LEN)
+# define SIZE(ifr) (MAX((ifr)->ifr_addr.sa_len, sizeof((ifr)->ifr_addr)) + sizeof(ifr->ifr_name))
+#else /* not defined(HAVE_FIELD_STRUCT_IFREQ_IFR_ADDR) && defined(HAVE_FIELD_STRUCT_SOCKADDR_SA_LEN) */
+# define SIZE(ifr) sizeof(struct ifreq)
+#endif /* not defined(HAVE_FIELD_STRUCT_IFREQ_IFR_ADDR) && defined(HAVE_FIELD_STRUCT_SOCKADDR_SA_LEN) */
+
+#define C(x) ((x) & 0xff)
+#define GFBUFLEN 1024
+#define clist (ifc.ifc_ifcu.ifcu_req)
+#define count (ifc.ifc_len/sizeof(struct ifreq))
+
+
+/* return malloc'ed buffer. caller must free it */
+char *
+print_wires(void)
+{
+ addrlist *al;
+ char s[256];
+ int i;
+ char *buf;
+ int bufcount = 0;
+ int buf_size = 1024;
+
+ buf = malloc(1024);
+ if (!buf)
+ return NULL;
+
+ if (!localnets) {
+ sprintf(buf, "No networks.\n");
+ return buf;
+ }
+ /* check if there's more than one network */
+ if (!localnets->ip_next) {
+ sprintf(buf,
+ "Network: wire=\"%s\" (netnumber=%s).\n",
+ localnets->ip_net_name, localnets->ip_net_num);
+ return buf;
+ }
+ buf[0] = '\0'; /* null out buffer before appending */
+ for (i = 1, al = localnets; al; al = al->ip_next, i++) {
+ sprintf(s, "Network %d: wire=\"%s\" (netnumber=%s).\n",
+ i, al->ip_net_name, al->ip_net_num);
+ bufcount += strlen(s);
+ if (bufcount > buf_size) {
+ buf_size *= 2;
+ buf = xrealloc(buf, buf_size);
+ }
+ strcat(buf, s);
+ }
+ return buf;
+}
+
+
+void
+getwire(char **name1, char **number1)
+{
+ struct hostent *hp;
+ struct netent *np;
+ struct ifconf ifc;
+ struct ifreq *ifr;
+ caddr_t cp, cplim;
+ u_long address, netmask, subnet;
+ char buf[GFBUFLEN], *s;
+ int fd = -1;
+ u_long net;
+ u_long mask;
+ u_long subnetshift;
+ char netNumberBuf[64];
+ addrlist *al = NULL, *tail = NULL;
+
+#ifndef SIOCGIFFLAGS
+ /* if cannot get interface flags, return nothing */
+ plog(XLOG_ERROR, "getwire unable to get interface flags");
+ localnets = NULL;
+ return;
+#endif /* not SIOCGIFFLAGS */
+
+ /*
+ * Get suitable socket
+ */
+ if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
+ goto out;
+
+ /*
+ * Fill in ifconf details
+ */
+ memset(&buf[0], 0, GFBUFLEN);
+ ifc.ifc_len = sizeof(buf);
+ ifc.ifc_buf = buf;
+
+ /*
+ * Get network interface configurations
+ */
+ if (ioctl(fd, SIOCGIFCONF, (caddr_t) & ifc) < 0)
+ goto out;
+
+ /*
+ * Upper bound on array
+ */
+ cplim = buf + ifc.ifc_len;
+
+ /*
+ * This is some magic to cope with both "traditional" and the
+ * new 4.4BSD-style struct sockaddrs. The new structure has
+ * variable length and a size field to support longer addresses.
+ * AF_LINK is a new definition for 4.4BSD.
+ */
+
+ /*
+ * Scan the list looking for a suitable interface
+ */
+ for (cp = buf; cp < cplim; cp += SIZE(ifr)) {
+ ifr = (struct ifreq *) cp;
+
+ if (ifr->ifr_addr.sa_family != AF_INET)
+ continue;
+ else
+ address = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr.s_addr;
+
+ /*
+ * Get interface flags
+ */
+ if (ioctl(fd, SIOCGIFFLAGS, (caddr_t) ifr) < 0)
+ continue;
+
+ /*
+ * If the interface is a loopback, or its not running
+ * then ignore it.
+ */
+#ifdef IFF_LOOPBACK
+ if ((ifr->ifr_flags & IFF_LOOPBACK) != 0)
+ continue;
+#endif /* IFF_LOOPBACK */
+ /*
+ * Fix for 0.0.0.0 loopback on SunOS 3.X which defines IFF_ROUTE
+ * instead of IFF_LOOPBACK.
+ */
+#ifdef IFF_ROUTE
+ if (ifr->ifr_flags == (IFF_UP|IFF_RUNNING))
+ continue;
+#endif /* IFF_ROUTE */
+
+ /* if the interface is not UP or not RUNNING, skip it */
+ if ((ifr->ifr_flags & IFF_RUNNING) == 0 ||
+ (ifr->ifr_flags & IFF_UP) == 0)
+ continue;
+
+ /*
+ * Get the netmask of this interface
+ */
+ if (ioctl(fd, SIOCGIFNETMASK, (caddr_t) ifr) < 0)
+ continue;
+
+ netmask = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr.s_addr;
+
+ /*
+ * Add interface to local network singly linked list
+ */
+ al = ALLOC(struct addrlist);
+ al->ip_addr = address;
+ al->ip_mask = netmask;
+ al->ip_net_name = NO_SUBNET; /* fill in a bit later */
+ al->ip_net_num = "0.0.0.0"; /* fill in a bit later */
+ al->ip_next = NULL;
+ /* append to the end of the list */
+ if (!localnets) {
+ localnets = tail = al;
+ tail->ip_next = NULL;
+ } else {
+ tail->ip_next = al;
+ tail = al;
+ }
+
+ /*
+ * Figure out the subnet's network address
+ */
+ subnet = address & netmask;
+
+#ifdef IN_CLASSA
+ subnet = htonl(subnet);
+
+ if (IN_CLASSA(subnet)) {
+ mask = IN_CLASSA_NET;
+ subnetshift = 8;
+ } else if (IN_CLASSB(subnet)) {
+ mask = IN_CLASSB_NET;
+ subnetshift = 8;
+ } else {
+ mask = IN_CLASSC_NET;
+ subnetshift = 4;
+ }
+
+ /*
+ * If there are more bits than the standard mask
+ * would suggest, subnets must be in use.
+ * Guess at the subnet mask, assuming reasonable
+ * width subnet fields.
+ * XXX: Or-in at least 1 byte's worth of 1s to make
+ * sure the top bits remain set.
+ */
+ while (subnet & ~mask)
+ mask = (mask >> subnetshift) | 0xff000000;
+
+ net = subnet & mask;
+ while ((mask & 1) == 0)
+ mask >>= 1, net >>= 1;
+
+ /*
+ * Now get a usable name.
+ * First use the network database,
+ * then the host database,
+ * and finally just make a dotted quad.
+ */
+ np = getnetbyaddr(net, AF_INET);
+
+ /* the network address has been masked off */
+ if ((subnet & 0xffffff) == 0) {
+ sprintf(netNumberBuf, "%lu", C(subnet >> 24));
+ } else if ((subnet & 0xffff) == 0) {
+ sprintf(netNumberBuf, "%lu.%lu",
+ C(subnet >> 24), C(subnet >> 16));
+ } else if ((subnet & 0xff) == 0) {
+ sprintf(netNumberBuf, "%lu.%lu.%lu",
+ C(subnet >> 24), C(subnet >> 16),
+ C(subnet >> 8));
+ } else {
+ sprintf(netNumberBuf, "%lu.%lu.%lu.%lu",
+ C(subnet >> 24), C(subnet >> 16),
+ C(subnet >> 8), C(subnet));
+ }
+
+ /* fill in network number (string) */
+ al->ip_net_num = strdup(netNumberBuf);
+
+#else /* not IN_CLASSA */
+ /* This is probably very wrong. */
+ np = getnetbyaddr(subnet, AF_INET);
+#endif /* not IN_CLASSA */
+
+ if (np)
+ s = np->n_name;
+ else {
+ subnet = address & netmask;
+ hp = gethostbyaddr((char *) &subnet, 4, AF_INET);
+ if (hp)
+ s = (char *) hp->h_name;
+ else
+ s = inet_dquad(buf, subnet);
+ }
+
+ /* fill in network name (string) */
+ al->ip_net_name = strdup(s);
+ }
+
+out:
+ if (fd >= 0)
+ close(fd);
+ if (localnets) {
+ *name1 = localnets->ip_net_name;
+ *number1 = localnets->ip_net_num;
+ } else {
+ *name1 = NO_SUBNET;
+ *number1 = "0.0.0.0";
+ }
+}
+
+
+/*
+ * Make a dotted quad from a 32bit IP address
+ * addr is in network byte order.
+ * sizeof(buf) needs to be at least 16.
+ */
+char *
+inet_dquad(char *buf, u_long addr)
+{
+ addr = ntohl(addr);
+ sprintf(buf, "%ld.%ld.%ld.%ld",
+ ((addr >> 24) & 0xff),
+ ((addr >> 16) & 0xff),
+ ((addr >> 8) & 0xff),
+ ((addr >> 0) & 0xff));
+ return buf;
+}
+
+
+/*
+ * Determine whether a network is on a local network
+ * (addr) is in network byte order.
+ */
+int
+islocalnet(u_long addr)
+{
+ addrlist *al;
+#ifdef DEBUG
+ char buf[16];
+#endif /* DEBUG */
+
+ for (al = localnets; al; al = al->ip_next)
+ if (((addr ^ al->ip_addr) & al->ip_mask) == 0)
+ return TRUE;
+
+#ifdef DEBUG
+ plog(XLOG_INFO, "%s is on a remote network", inet_dquad(buf, addr));
+#endif /* DEBUG */
+
+ return FALSE;
+}
+
+
+/*
+ * Determine whether a network name is one of the local networks
+ * of a host.
+ */
+int
+is_network_member(const char *net)
+{
+ addrlist *al;
+
+ for (al = localnets; al; al = al->ip_next)
+ if (STREQ(net, al->ip_net_name) || STREQ(net, al->ip_net_num))
+ return TRUE;
+
+ return FALSE;
+}
diff --git a/contrib/amd/libamu/xdr_func.c b/contrib/amd/libamu/xdr_func.c
new file mode 100644
index 000000000000..feafbae01e08
--- /dev/null
+++ b/contrib/amd/libamu/xdr_func.c
@@ -0,0 +1,1155 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: xdr_func.c,v 5.2.2.1 1992/02/09 15:08:40 jsp beta $
+ *
+ */
+
+/*
+ * Complete list of all possible xdr functions which may be needed.
+ */
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amu.h>
+
+/*
+ * MACROS:
+ */
+#ifdef HAVE_FS_AUTOFS
+# ifndef A_MAXNAME
+# define A_MAXNAME 255
+# endif /* not A_MAXNAME */
+# ifndef A_MAXOPTS
+# define A_MAXOPTS 255
+# endif /* not A_MAXOPTS */
+# ifndef A_MAXPATH
+# define A_MAXPATH 1024
+# endif /* not A_MAXPATH */
+#endif /* HAVE_FS_AUTOFS */
+
+/* forward definitions, are they needed? */
+extern bool_t xdr_exportnode(XDR *xdrs, exportnode *objp);
+extern bool_t xdr_groupnode(XDR *xdrs, groupnode *objp);
+extern bool_t xdr_name(XDR *xdrs, name *objp);
+
+
+#ifndef HAVE_XDR_ATTRSTAT
+bool_t
+xdr_attrstat(XDR *xdrs, nfsattrstat *objp)
+{
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "xdr_attrstat:");
+#endif /* DEBUG */
+
+ if (!xdr_nfsstat(xdrs, &objp->ns_status)) {
+ return (FALSE);
+ }
+ switch (objp->ns_status) {
+ case NFS_OK:
+ if (!xdr_fattr(xdrs, &objp->ns_u.ns_attr_u)) {
+ return (FALSE);
+ }
+ break;
+ default:
+ break;
+ }
+ return (TRUE);
+}
+#endif /* not HAVE_XDR_ATTRSTAT */
+
+
+#ifndef HAVE_XDR_CREATEARGS
+bool_t
+xdr_createargs(XDR *xdrs, nfscreateargs *objp)
+{
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "xdr_createargs:");
+#endif /* DEBUG */
+
+ if (!xdr_diropargs(xdrs, &objp->ca_where)) {
+ return (FALSE);
+ }
+ if (!xdr_sattr(xdrs, &objp->ca_attributes)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+#endif /* not HAVE_XDR_CREATEARGS */
+
+
+#ifndef HAVE_XDR_DIRLIST
+bool_t
+xdr_dirlist(XDR *xdrs, nfsdirlist *objp)
+{
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "xdr_dirlist:");
+#endif /* DEBUG */
+
+ if (!xdr_pointer(xdrs, (char **) &objp->dl_entries, sizeof(nfsentry), (XDRPROC_T_TYPE) xdr_entry)) {
+ return (FALSE);
+ }
+ if (!xdr_bool(xdrs, &objp->dl_eof)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+#endif /* not HAVE_XDR_DIRLIST */
+
+
+#ifndef HAVE_XDR_DIROPARGS
+bool_t
+xdr_diropargs(XDR *xdrs, nfsdiropargs *objp)
+{
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "xdr_diropargs:");
+#endif /* DEBUG */
+
+ if (!xdr_nfs_fh(xdrs, &objp->da_fhandle)) {
+ return (FALSE);
+ }
+ if (!xdr_filename(xdrs, &objp->da_name)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+#endif /* not HAVE_XDR_DIROPARGS */
+
+
+#ifndef HAVE_XDR_DIROPOKRES
+bool_t
+xdr_diropokres(XDR *xdrs, nfsdiropokres *objp)
+{
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "xdr_diropokres:");
+#endif /* DEBUG */
+
+ if (!xdr_nfs_fh(xdrs, &objp->drok_fhandle)) {
+ return (FALSE);
+ }
+ if (!xdr_fattr(xdrs, &objp->drok_attributes)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+#endif /* not HAVE_XDR_DIROPOKRES */
+
+
+#ifndef HAVE_XDR_DIROPRES
+bool_t
+xdr_diropres(XDR *xdrs, nfsdiropres *objp)
+{
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "xdr_diropres:");
+#endif /* DEBUG */
+
+ if (!xdr_nfsstat(xdrs, &objp->dr_status)) {
+ return (FALSE);
+ }
+ switch (objp->dr_status) {
+ case NFS_OK:
+ if (!xdr_diropokres(xdrs, &objp->dr_u.dr_drok_u)) {
+ return (FALSE);
+ }
+ break;
+ default:
+ break;
+ }
+ return (TRUE);
+}
+#endif /* not HAVE_XDR_DIROPRES */
+
+
+#ifndef HAVE_XDR_DIRPATH
+bool_t
+xdr_dirpath(XDR *xdrs, dirpath *objp)
+{
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "xdr_dirpath:");
+#endif /* DEBUG */
+
+ if (!xdr_string(xdrs, objp, MNTPATHLEN)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+#endif /* not HAVE_XDR_DIRPATH */
+
+
+#ifndef HAVE_XDR_ENTRY
+bool_t
+xdr_entry(XDR *xdrs, nfsentry *objp)
+{
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "xdr_entry:");
+#endif /* DEBUG */
+
+ if (!xdr_u_int(xdrs, &objp->ne_fileid)) {
+ return (FALSE);
+ }
+ if (!xdr_filename(xdrs, &objp->ne_name)) {
+ return (FALSE);
+ }
+ if (!xdr_nfscookie(xdrs, objp->ne_cookie)) {
+ return (FALSE);
+ }
+ if (!xdr_pointer(xdrs, (char **) &objp->ne_nextentry, sizeof(nfsentry), (XDRPROC_T_TYPE) xdr_entry)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+#endif /* not HAVE_XDR_ENTRY */
+
+
+#ifndef HAVE_XDR_EXPORTNODE
+bool_t
+xdr_exportnode(XDR *xdrs, exportnode *objp)
+{
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "xdr_exportnode:");
+#endif /* DEBUG */
+
+ if (!xdr_dirpath(xdrs, &objp->ex_dir)) {
+ return (FALSE);
+ }
+ /*
+ * This cast to (groups) is needed for Irix6. If you change it, it
+ * may produce a warning/error on other systems.
+ */
+ if (!xdr_groups(xdrs, (groups) &objp->ex_groups)) {
+ return (FALSE);
+ }
+ if (!xdr_exports(xdrs, &objp->ex_next)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+#endif /* not HAVE_XDR_EXPORTNODE */
+
+
+#ifndef HAVE_XDR_EXPORTS
+bool_t
+xdr_exports(XDR *xdrs, exports *objp)
+{
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "xdr_exports:");
+#endif /* DEBUG */
+
+ if (!xdr_pointer(xdrs, (char **) objp, sizeof(exportnode), (XDRPROC_T_TYPE) xdr_exportnode)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+#endif /* not HAVE_XDR_EXPORTS */
+
+
+#ifndef HAVE_XDR_FATTR
+bool_t
+xdr_fattr(XDR *xdrs, nfsfattr *objp)
+{
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "xdr_fattr:");
+#endif /* DEBUG */
+
+ if (!xdr_ftype(xdrs, &objp->na_type)) {
+ return (FALSE);
+ }
+ if (!xdr_u_int(xdrs, &objp->na_mode)) {
+ return (FALSE);
+ }
+ if (!xdr_u_int(xdrs, &objp->na_nlink)) {
+ return (FALSE);
+ }
+ if (!xdr_u_int(xdrs, &objp->na_uid)) {
+ return (FALSE);
+ }
+ if (!xdr_u_int(xdrs, &objp->na_gid)) {
+ return (FALSE);
+ }
+ if (!xdr_u_int(xdrs, &objp->na_size)) {
+ return (FALSE);
+ }
+ if (!xdr_u_int(xdrs, &objp->na_blocksize)) {
+ return (FALSE);
+ }
+ if (!xdr_u_int(xdrs, &objp->na_rdev)) {
+ return (FALSE);
+ }
+ if (!xdr_u_int(xdrs, &objp->na_blocks)) {
+ return (FALSE);
+ }
+ if (!xdr_u_int(xdrs, &objp->na_fsid)) {
+ return (FALSE);
+ }
+ if (!xdr_u_int(xdrs, &objp->na_fileid)) {
+ return (FALSE);
+ }
+ if (!xdr_nfstime(xdrs, &objp->na_atime)) {
+ return (FALSE);
+ }
+ if (!xdr_nfstime(xdrs, &objp->na_mtime)) {
+ return (FALSE);
+ }
+ if (!xdr_nfstime(xdrs, &objp->na_ctime)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+#endif /* not HAVE_XDR_FATTR */
+
+
+#ifndef HAVE_XDR_FHANDLE
+bool_t
+xdr_fhandle(XDR *xdrs, fhandle objp)
+{
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "xdr_fhandle:");
+#endif /* DEBUG */
+
+ if (!xdr_opaque(xdrs, objp, NFS_FHSIZE)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+#endif /* not HAVE_XDR_FHANDLE */
+
+
+#ifndef HAVE_XDR_FHSTATUS
+bool_t
+xdr_fhstatus(XDR *xdrs, fhstatus *objp)
+{
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "xdr_fhstatus:");
+#endif /* DEBUG */
+
+ if (!xdr_u_int(xdrs, &objp->fhs_status)) {
+ return (FALSE);
+ }
+ if (objp->fhs_status == 0 && !xdr_fhandle(xdrs, objp->fhs_fh)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+#endif /* not HAVE_XDR_FHSTATUS */
+
+
+#ifndef HAVE_XDR_FILENAME
+bool_t
+xdr_filename(XDR *xdrs, filename *objp)
+{
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "xdr_filename:");
+#endif /* DEBUG */
+
+ if (!xdr_string(xdrs, objp, NFS_MAXNAMLEN)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+#endif /* not HAVE_XDR_FILENAME */
+
+
+#ifndef HAVE_XDR_FTYPE
+bool_t
+xdr_ftype(XDR *xdrs, nfsftype *objp)
+{
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "xdr_ftype:");
+#endif /* DEBUG */
+
+ if (!xdr_enum(xdrs, (enum_t *) objp)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+#endif /* not HAVE_XDR_FTYPE */
+
+
+#ifndef HAVE_XDR_GROUPNODE
+bool_t
+xdr_groupnode(XDR *xdrs, groupnode *objp)
+{
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "xdr_groupnode:");
+#endif /* DEBUG */
+
+ if (!xdr_name(xdrs, &objp->gr_name)) {
+ return (FALSE);
+ }
+ /*
+ * This cast to (groups) is needed for Irix6. If you change it, it
+ * may produce a warning/error on other systems.
+ */
+ if (!xdr_groups(xdrs, (groups) &objp->gr_next)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+#endif /* not HAVE_XDR_GROUPNODE */
+
+
+#ifndef HAVE_XDR_GROUPS
+bool_t
+xdr_groups(XDR *xdrs, groups objp)
+{
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "xdr_groups:");
+#endif /* DEBUG */
+
+ if (!xdr_pointer(xdrs, (char **) objp, sizeof(groupnode), (XDRPROC_T_TYPE) xdr_groupnode)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+#endif /* not HAVE_XDR_GROUPS */
+
+
+#ifndef HAVE_XDR_LINKARGS
+bool_t
+xdr_linkargs(XDR *xdrs, nfslinkargs *objp)
+{
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "xdr_linkargs:");
+#endif /* DEBUG */
+
+ if (!xdr_nfs_fh(xdrs, &objp->la_fhandle)) {
+ return (FALSE);
+ }
+ if (!xdr_diropargs(xdrs, &objp->la_to)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+#endif /* not HAVE_XDR_LINKARGS */
+
+
+#ifndef HAVE_XDR_MOUNTBODY
+bool_t
+xdr_mountbody(XDR *xdrs, mountbody *objp)
+{
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "xdr_mountbody:");
+#endif /* DEBUG */
+
+ if (!xdr_name(xdrs, &objp->ml_hostname)) {
+ return (FALSE);
+ }
+ if (!xdr_dirpath(xdrs, &objp->ml_directory)) {
+ return (FALSE);
+ }
+ if (!xdr_mountlist(xdrs, &objp->ml_next)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+#endif /* not HAVE_XDR_MOUNTBODY */
+
+
+#ifndef HAVE_XDR_MOUNTLIST
+bool_t
+xdr_mountlist(XDR *xdrs, mountlist *objp)
+{
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "xdr_mountlist:");
+#endif /* DEBUG */
+
+ if (!xdr_pointer(xdrs, (char **) objp, sizeof(mountbody), (XDRPROC_T_TYPE) xdr_mountbody)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+#endif /* not HAVE_XDR_MOUNTLIST */
+
+
+#if defined(HAVE_FS_NFS3) && !defined(HAVE_XDR_MOUNTRES3)
+/*
+ * This ifdef is a hack: this whole file needs to be compiled
+ * only if the system has NFS V3 and does not have the xdr_mountres3
+ * function. Autoconf should pick this source file to compile only
+ * if these two conditions apply.
+ */
+
+bool_t
+xdr_fhandle3(XDR *xdrs, fhandle3 *objp)
+{
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "xdr_fhandle3:");
+#endif /* DEBUG */
+
+ if (!xdr_bytes(xdrs,
+ (char **) &objp->fhandle3_val,
+ (u_int *) &objp->fhandle3_len,
+ FHSIZE3))
+ return (FALSE);
+ return (TRUE);
+}
+
+
+bool_t
+xdr_mountstat3(XDR *xdrs, mountstat3 *objp)
+{
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "xdr_mountstat3:");
+#endif /* DEBUG */
+
+ if (!xdr_enum(xdrs, (enum_t *)objp))
+ return (FALSE);
+ return (TRUE);
+}
+
+
+bool_t
+xdr_mountres3_ok(XDR *xdrs, mountres3_ok *objp)
+{
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "xdr_mountres3_ok:");
+#endif /* DEBUG */
+
+ if (!xdr_fhandle3(xdrs, &objp->fhandle))
+ return (FALSE);
+ if (!xdr_array(xdrs,
+ (char **)&objp->auth_flavors.auth_flavors_val,
+ (u_int *) &objp->auth_flavors.auth_flavors_len,
+ ~0,
+ sizeof (int),
+ (xdrproc_t) xdr_int))
+ return (FALSE);
+ return (TRUE);
+}
+
+
+bool_t
+xdr_mountres3(XDR *xdrs, mountres3 *objp)
+{
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "xdr_mountres3:");
+#endif /* DEBUG */
+
+ if (!xdr_mountstat3(xdrs, &objp->fhs_status))
+ return (FALSE);
+
+ if (objp->fhs_status == 0) { /* 0 == MNT_OK or MNT3_OK */
+ if (!xdr_mountres3_ok(xdrs, &objp->mountres3_u.mountinfo))
+ return (FALSE);
+ }
+ return (TRUE);
+}
+#endif /* defined(HAVE_FS_NFS3) && !defined(HAVE_XDR_MOUNTRES3) */
+
+
+#ifndef HAVE_XDR_NAME
+bool_t
+xdr_name(XDR *xdrs, name *objp)
+{
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "xdr_name:");
+#endif /* DEBUG */
+
+ if (!xdr_string(xdrs, objp, MNTNAMLEN)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+#endif /* not HAVE_XDR_NAME */
+
+
+#ifndef HAVE_XDR_NFS_FH
+bool_t
+xdr_nfs_fh(XDR *xdrs, am_nfs_fh *objp)
+{
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "xdr_nfs_fh:");
+#endif /* DEBUG */
+
+ if (!xdr_opaque(xdrs, (caddr_t) objp->fh_data, NFS_FHSIZE)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+#endif /* not HAVE_XDR_NFS_FH */
+
+
+#ifndef HAVE_XDR_NFSCOOKIE
+bool_t
+xdr_nfscookie(XDR *xdrs, nfscookie objp)
+{
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "xdr_nfscookie:");
+#endif /* DEBUG */
+
+ if (!xdr_opaque(xdrs, objp, NFS_COOKIESIZE)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+#endif /* not HAVE_XDR_NFSCOOKIE */
+
+
+#ifndef HAVE_XDR_NFSPATH
+bool_t
+xdr_nfspath(XDR *xdrs, nfspath *objp)
+{
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "xdr_nfspath:");
+#endif /* DEBUG */
+
+ if (!xdr_string(xdrs, objp, NFS_MAXPATHLEN)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+#endif /* not HAVE_XDR_NFSPATH */
+
+
+#ifndef HAVE_XDR_NFSSTAT
+bool_t
+xdr_nfsstat(XDR *xdrs, nfsstat *objp)
+{
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "xdr_nfsstat:");
+#endif /* DEBUG */
+
+ if (!xdr_enum(xdrs, (enum_t *) objp)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+#endif /* not HAVE_XDR_NFSSTAT */
+
+
+#ifndef HAVE_XDR_NFSTIME
+bool_t
+xdr_nfstime(XDR *xdrs, nfstime *objp)
+{
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "xdr_nfstime:");
+#endif /* DEBUG */
+
+ if (!xdr_u_int(xdrs, (u_int *) &objp->nt_seconds)) {
+ return (FALSE);
+ }
+ if (!xdr_u_int(xdrs, (u_int *) &objp->nt_useconds)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+#endif /* not HAVE_XDR_NFSTIME */
+
+
+#ifndef HAVE_XDR_POINTER
+bool_t
+xdr_pointer(register XDR *xdrs, char **objpp, u_int obj_size, XDRPROC_T_TYPE xdr_obj)
+{
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "xdr_pointer:");
+#endif /* DEBUG */
+
+
+ bool_t more_data;
+
+ more_data = (*objpp != NULL);
+ if (!xdr_bool(xdrs, &more_data)) {
+ return (FALSE);
+ }
+ if (!more_data) {
+ *objpp = NULL;
+ return (TRUE);
+ }
+
+ return (xdr_reference(xdrs, objpp, obj_size, xdr_obj));
+}
+#endif /* not HAVE_XDR_POINTER */
+
+
+#ifndef HAVE_XDR_READARGS
+bool_t
+xdr_readargs(XDR *xdrs, nfsreadargs *objp)
+{
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "xdr_readargs:");
+#endif /* DEBUG */
+
+ if (!xdr_nfs_fh(xdrs, &objp->ra_fhandle)) {
+ return (FALSE);
+ }
+ if (!xdr_u_int(xdrs, &objp->ra_offset)) {
+ return (FALSE);
+ }
+ if (!xdr_u_int(xdrs, &objp->ra_count)) {
+ return (FALSE);
+ }
+ if (!xdr_u_int(xdrs, &objp->ra_totalcount)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+#endif /* not HAVE_XDR_READARGS */
+
+
+#ifndef HAVE_XDR_READDIRARGS
+bool_t
+xdr_readdirargs(XDR *xdrs, nfsreaddirargs *objp)
+{
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "xdr_readdirargs:");
+#endif /* DEBUG */
+
+ if (!xdr_nfs_fh(xdrs, &objp->rda_fhandle)) {
+ return (FALSE);
+ }
+ if (!xdr_nfscookie(xdrs, objp->rda_cookie)) {
+ return (FALSE);
+ }
+ if (!xdr_u_int(xdrs, &objp->rda_count)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+#endif /* not HAVE_XDR_READDIRARGS */
+
+
+#ifndef HAVE_XDR_READDIRRES
+bool_t
+xdr_readdirres(XDR *xdrs, nfsreaddirres *objp)
+{
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "xdr_readdirres:");
+#endif /* DEBUG */
+
+ if (!xdr_nfsstat(xdrs, &objp->rdr_status)) {
+ return (FALSE);
+ }
+ switch (objp->rdr_status) {
+ case NFS_OK:
+ if (!xdr_dirlist(xdrs, &objp->rdr_u.rdr_reply_u)) {
+ return (FALSE);
+ }
+ break;
+ default:
+ break;
+ }
+ return (TRUE);
+}
+#endif /* not HAVE_XDR_READDIRRES */
+
+
+#ifndef HAVE_XDR_READLINKRES
+bool_t
+xdr_readlinkres(XDR *xdrs, nfsreadlinkres *objp)
+{
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "xdr_readlinkres:");
+#endif /* DEBUG */
+
+ if (!xdr_nfsstat(xdrs, &objp->rlr_status)) {
+ return (FALSE);
+ }
+ switch (objp->rlr_status) {
+ case NFS_OK:
+ if (!xdr_nfspath(xdrs, &objp->rlr_u.rlr_data_u)) {
+ return (FALSE);
+ }
+ break;
+ default:
+ break;
+ }
+ return (TRUE);
+}
+#endif /* not HAVE_XDR_READLINKRES */
+
+
+#ifndef HAVE_XDR_READOKRES
+bool_t
+xdr_readokres(XDR *xdrs, nfsreadokres *objp)
+{
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "xdr_readokres:");
+#endif /* DEBUG */
+
+ if (!xdr_fattr(xdrs, &objp->raok_attributes)) {
+ return (FALSE);
+ }
+ if (!xdr_bytes(xdrs,
+ (char **) & objp->raok_u.raok_val_u,
+ (u_int *) & objp->raok_u.raok_len_u,
+ NFS_MAXDATA)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+#endif /* not HAVE_XDR_READOKRES */
+
+
+#ifndef HAVE_XDR_READRES
+bool_t
+xdr_readres(XDR *xdrs, nfsreadres *objp)
+{
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "xdr_readres:");
+#endif /* DEBUG */
+
+ if (!xdr_nfsstat(xdrs, &objp->rr_status)) {
+ return (FALSE);
+ }
+ switch (objp->rr_status) {
+ case NFS_OK:
+ if (!xdr_readokres(xdrs, &objp->rr_u.rr_reply_u)) {
+ return (FALSE);
+ }
+ break;
+ default:
+ break;
+ }
+ return (TRUE);
+}
+#endif /* not HAVE_XDR_READRES */
+
+
+#ifndef HAVE_XDR_RENAMEARGS
+bool_t
+xdr_renameargs(XDR *xdrs, nfsrenameargs *objp)
+{
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "xdr_renameargs:");
+#endif /* DEBUG */
+
+ if (!xdr_diropargs(xdrs, &objp->rna_from)) {
+ return (FALSE);
+ }
+ if (!xdr_diropargs(xdrs, &objp->rna_to)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+#endif /* not HAVE_XDR_RENAMEARGS */
+
+
+#ifndef HAVE_XDR_SATTR
+bool_t
+xdr_sattr(XDR *xdrs, nfssattr *objp)
+{
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "xdr_sattr:");
+#endif /* DEBUG */
+
+ if (!xdr_u_int(xdrs, &objp->sa_mode)) {
+ return (FALSE);
+ }
+ if (!xdr_u_int(xdrs, &objp->sa_uid)) {
+ return (FALSE);
+ }
+ if (!xdr_u_int(xdrs, &objp->sa_gid)) {
+ return (FALSE);
+ }
+ if (!xdr_u_int(xdrs, &objp->sa_size)) {
+ return (FALSE);
+ }
+ if (!xdr_nfstime(xdrs, &objp->sa_atime)) {
+ return (FALSE);
+ }
+ if (!xdr_nfstime(xdrs, &objp->sa_mtime)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+#endif /* not HAVE_XDR_SATTR */
+
+
+#ifndef HAVE_XDR_SATTRARGS
+bool_t
+xdr_sattrargs(XDR *xdrs, nfssattrargs *objp)
+{
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "xdr_sattrargs:");
+#endif /* DEBUG */
+
+ if (!xdr_nfs_fh(xdrs, &objp->sag_fhandle)) {
+ return (FALSE);
+ }
+ if (!xdr_sattr(xdrs, &objp->sag_attributes)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+#endif /* not HAVE_XDR_SATTRARGS */
+
+
+#ifndef HAVE_XDR_STATFSOKRES
+bool_t
+xdr_statfsokres(XDR *xdrs, nfsstatfsokres *objp)
+{
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "xdr_statfsokres:");
+#endif /* DEBUG */
+
+ if (!xdr_u_int(xdrs, &objp->sfrok_tsize)) {
+ return (FALSE);
+ }
+ if (!xdr_u_int(xdrs, &objp->sfrok_bsize)) {
+ return (FALSE);
+ }
+ if (!xdr_u_int(xdrs, &objp->sfrok_blocks)) {
+ return (FALSE);
+ }
+ if (!xdr_u_int(xdrs, &objp->sfrok_bfree)) {
+ return (FALSE);
+ }
+ if (!xdr_u_int(xdrs, &objp->sfrok_bavail)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+#endif /* not HAVE_XDR_STATFSOKRES */
+
+
+#ifndef HAVE_XDR_STATFSRES
+bool_t
+xdr_statfsres(XDR *xdrs, nfsstatfsres *objp)
+{
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "xdr_statfsres:");
+#endif /* DEBUG */
+
+ if (!xdr_nfsstat(xdrs, &objp->sfr_status)) {
+ return (FALSE);
+ }
+ switch (objp->sfr_status) {
+ case NFS_OK:
+ if (!xdr_statfsokres(xdrs, &objp->sfr_u.sfr_reply_u)) {
+ return (FALSE);
+ }
+ break;
+ default:
+ break;
+ }
+ return (TRUE);
+}
+#endif /* not HAVE_XDR_STATFSRES */
+
+
+#ifndef HAVE_XDR_SYMLINKARGS
+bool_t
+xdr_symlinkargs(XDR *xdrs, nfssymlinkargs *objp)
+{
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "xdr_symlinkargs:");
+#endif /* DEBUG */
+
+ if (!xdr_diropargs(xdrs, &objp->sla_from)) {
+ return (FALSE);
+ }
+ if (!xdr_nfspath(xdrs, &objp->sla_to)) {
+ return (FALSE);
+ }
+ if (!xdr_sattr(xdrs, &objp->sla_attributes)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+#endif /* not HAVE_XDR_SYMLINKARGS */
+
+
+#ifndef HAVE_XDR_WRITEARGS
+bool_t
+xdr_writeargs(XDR *xdrs, nfswriteargs *objp)
+{
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "xdr_writeargs:");
+#endif /* DEBUG */
+
+ if (!xdr_nfs_fh(xdrs, &objp->wra_fhandle)) {
+ return (FALSE);
+ }
+ if (!xdr_u_int(xdrs, &objp->wra_beginoffset)) {
+ return (FALSE);
+ }
+ if (!xdr_u_int(xdrs, &objp->wra_offset)) {
+ return (FALSE);
+ }
+ if (!xdr_u_int(xdrs, &objp->wra_totalcount)) {
+ return (FALSE);
+ }
+ if (!xdr_bytes(xdrs,
+ (char **) & objp->wra_u.wra_val_u,
+ (u_int *) & objp->wra_u.wra_len_u,
+ NFS_MAXDATA)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+#endif /* not HAVE_XDR_WRITEARGS */
+
+
+/*
+ * AUTOFS XDR FUNCTIONS:
+ */
+#ifdef HAVE_FS_AUTOFS
+# ifndef HAVE_XDR_MNTREQUEST
+bool_t
+xdr_mntrequest(XDR *xdrs, mntrequest *objp)
+{
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "xdr_mntrequest:");
+#endif /* DEBUG */
+
+ if (!xdr_string(xdrs, &objp->name, A_MAXNAME))
+ return (FALSE);
+
+ if (!xdr_string(xdrs, &objp->map, A_MAXNAME))
+ return (FALSE);
+
+ if (!xdr_string(xdrs, &objp->opts, A_MAXOPTS))
+ return (FALSE);
+
+ if (!xdr_string(xdrs, &objp->path, A_MAXPATH))
+ return (FALSE);
+
+ return (TRUE);
+}
+# endif /* not HAVE_XDR_MNTREQUEST */
+
+
+# ifndef HAVE_XDR_MNTRES
+bool_t
+xdr_mntres(XDR *xdrs, mntres *objp)
+{
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "xdr_mntres:");
+#endif /* DEBUG */
+
+ if (!xdr_int(xdrs, &objp->status))
+ return (FALSE);
+
+ return (TRUE);
+}
+# endif /* not HAVE_XDR_MNTRES */
+
+
+# ifndef HAVE_XDR_UMNTREQUEST
+bool_t
+xdr_umntrequest(XDR *xdrs, umntrequest *objp)
+{
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "xdr_umntrequest:");
+#endif /* DEBUG */
+
+ if (!xdr_int(xdrs, &objp->isdirect))
+ return (FALSE);
+
+ if (!xdr_u_int(xdrs, (u_int *) &objp->devid))
+ return (FALSE);
+
+#ifdef HAVE_FIELD_UMNTREQUEST_RDEVID
+ if (!xdr_u_long(xdrs, &objp->rdevid))
+ return (FALSE);
+#endif /* HAVE_FIELD_UMNTREQUEST_RDEVID */
+
+ if (!xdr_pointer(xdrs, (char **) &objp->next, sizeof(umntrequest), (XDRPROC_T_TYPE) xdr_umntrequest))
+ return (FALSE);
+
+ return (TRUE);
+}
+# endif /* not HAVE_XDR_UMNTREQUEST */
+
+
+# ifndef HAVE_XDR_UMNTRES
+bool_t
+xdr_umntres(XDR *xdrs, umntres *objp)
+{
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "xdr_mntres:");
+#endif /* DEBUG */
+
+ if (!xdr_int(xdrs, &objp->status))
+ return (FALSE);
+
+ return (TRUE);
+}
+# endif /* not HAVE_XDR_UMNTRES */
+#endif /* HAVE_FS_AUTOFS */
diff --git a/contrib/amd/libamu/xutil.c b/contrib/amd/libamu/xutil.c
new file mode 100644
index 000000000000..15d2ceea7224
--- /dev/null
+++ b/contrib/amd/libamu/xutil.c
@@ -0,0 +1,825 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: xutil.c,v 1.1 1997-1998/01/11 21:06:22 ezk Exp ezk $
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amu.h>
+
+FILE *logfp = stderr; /* Log errors to stderr initially */
+
+#ifdef HAVE_SYSLOG
+int syslogging;
+#endif /* HAVE_SYSLOG */
+int xlog_level = XLOG_ALL & ~XLOG_MAP & ~XLOG_STATS;
+int xlog_level_init = ~0;
+static int amd_program_number = AMQ_PROGRAM;
+
+time_t clock_valid = 0;
+time_t xclock_valid = 0;
+
+#ifdef DEBUG_MEM
+static int mem_bytes;
+static int orig_mem_bytes;
+#endif /* DEBUG_MEM */
+
+/* forward definitions */
+static void real_plog(int lvl, char *fmt, va_list vargs);
+
+#ifdef DEBUG
+/*
+ * List of debug options.
+ */
+struct opt_tab dbg_opt[] =
+{
+ {"all", D_ALL}, /* All */
+ {"amq", D_AMQ}, /* Register for AMQ program */
+ {"daemon", D_DAEMON}, /* Enter daemon mode */
+ {"fork", D_FORK}, /* Fork server (nofork = don't fork) */
+ {"full", D_FULL}, /* Program trace */
+ /* info service specific debugging (hesiod, nis, etc) */
+ {"info", D_INFO},
+# ifdef DEBUG_MEM
+ {"mem", D_MEM}, /* Trace memory allocations */
+# endif /* DEBUG_MEM */
+ {"mtab", D_MTAB}, /* Use local mtab file */
+ {"str", D_STR}, /* Debug string munging */
+ {"test", D_TEST}, /* Full debug - but no daemon */
+ {"trace", D_TRACE}, /* Protocol trace */
+ {0, 0}
+};
+#endif /* DEBUG */
+
+/*
+ * List of log options
+ */
+struct opt_tab xlog_opt[] =
+{
+ {"all", XLOG_ALL}, /* All messages */
+#ifdef DEBUG
+ {"debug", XLOG_DEBUG}, /* Debug messages */
+#endif /* DEBUG */ /* DEBUG */
+ {"error", XLOG_ERROR}, /* Non-fatal system errors */
+ {"fatal", XLOG_FATAL}, /* Fatal errors */
+ {"info", XLOG_INFO}, /* Information */
+ {"map", XLOG_MAP}, /* Map errors */
+ {"stats", XLOG_STATS}, /* Additional statistical information */
+ {"user", XLOG_USER}, /* Non-fatal user errors */
+ {"warn", XLOG_WARNING}, /* Warnings */
+ {"warning", XLOG_WARNING}, /* Warnings */
+ {0, 0}
+};
+
+
+voidp
+xmalloc(int len)
+{
+ voidp p;
+ int retries = 600;
+
+ /*
+ * Avoid malloc's which return NULL for malloc(0)
+ */
+ if (len == 0)
+ len = 1;
+
+ do {
+ p = (voidp) malloc((unsigned) len);
+ if (p) {
+#if defined(DEBUG) && defined(DEBUG_MEM)
+ amuDebug(D_MEM)
+ plog(XLOG_DEBUG, "Allocated size %d; block %#x", len, p);
+#endif /* defined(DEBUG) && defined(DEBUG_MEM) */
+ return p;
+ }
+ if (retries > 0) {
+ plog(XLOG_ERROR, "Retrying memory allocation");
+ sleep(1);
+ }
+ } while (--retries);
+
+ plog(XLOG_FATAL, "Out of memory");
+ going_down(1);
+
+ abort();
+
+ return 0;
+}
+
+
+/* like xmalloc, but zeros out the bytes */
+voidp
+xzalloc(int len)
+{
+ voidp p = xmalloc(len);
+
+ if (p)
+ memset(p, 0, len);
+ return p;
+}
+
+
+voidp
+xrealloc(voidp ptr, int len)
+{
+#if defined(DEBUG) && defined(DEBUG_MEM)
+ amuDebug(D_MEM) plog(XLOG_DEBUG, "Reallocated size %d; block %#x", len, ptr);
+#endif /* defined(DEBUG) && defined(DEBUG_MEM) */
+
+ if (len == 0)
+ len = 1;
+
+ if (ptr)
+ ptr = (voidp) realloc(ptr, (unsigned) len);
+ else
+ ptr = (voidp) xmalloc((unsigned) len);
+
+ if (!ptr) {
+ plog(XLOG_FATAL, "Out of memory in realloc");
+ going_down(1);
+ abort();
+ }
+ return ptr;
+}
+
+
+#if defined(DEBUG) && defined(DEBUG_MEM)
+void
+dxfree(char *file, int line, voidp ptr)
+{
+ amuDebug(D_MEM)
+ plog(XLOG_DEBUG, "Free in %s:%d: block %#x", file, line, ptr);
+ /* this is the only place that must NOT use XFREE()!!! */
+ free(ptr);
+ ptr = NULL; /* paranoid */
+}
+#endif /* defined(DEBUG) && defined(DEBUG_MEM) */
+
+
+#ifdef DEBUG_MEM
+static void
+checkup_mem(void)
+{
+ struct mallinfo mi = mallinfo();
+ u_long uordbytes = mi.uordblks * 4096;
+
+ if (mem_bytes != uordbytes) {
+ if (orig_mem_bytes == 0)
+ mem_bytes = orig_mem_bytes = uordbytes;
+ else {
+ fprintf(logfp, "%s[%ld]: ", progname, (long) mypid);
+ if (mem_bytes < uordbytes) {
+ fprintf(logfp, "ALLOC: %ld bytes", uordbytes - mem_bytes);
+ } else {
+ fprintf(logfp, "FREE: %ld bytes", mem_bytes - uordbytes);
+ }
+ mem_bytes = uordbytes;
+ fprintf(logfp, ", making %d missing\n", mem_bytes - orig_mem_bytes);
+ }
+ }
+ malloc_verify();
+}
+#endif /* DEBUG_MEM */
+
+
+/*
+ * Take a log format string and expand occurences of %m
+ * with the current error code taken from errno.
+ */
+static void
+expand_error(char *f, char *e)
+{
+ extern int sys_nerr;
+ char *p;
+ int error = errno;
+
+ for (p = f; (*e = *p); e++, p++) {
+ if (p[0] == '%' && p[1] == 'm') {
+ const char *errstr;
+ if (error < 0 || error >= sys_nerr)
+ errstr = NULL;
+ else
+ errstr = sys_errlist[error];
+ if (errstr)
+ strcpy(e, errstr);
+ else
+ sprintf(e, "Error %d", error);
+ e += strlen(e) - 1;
+ p++;
+ }
+ }
+}
+
+
+/*
+ * Output the time of day and hostname to the logfile
+ */
+static void
+show_time_host_and_name(int lvl)
+{
+ static time_t last_t = 0;
+ static char *last_ctime = 0;
+ time_t t = clocktime();
+ char *sev;
+
+ if (t != last_t) {
+ last_ctime = ctime(&t);
+ last_t = t;
+ }
+ switch (lvl) {
+ case XLOG_FATAL:
+ sev = "fatal:";
+ break;
+ case XLOG_ERROR:
+ sev = "error:";
+ break;
+ case XLOG_USER:
+ sev = "user: ";
+ break;
+ case XLOG_WARNING:
+ sev = "warn: ";
+ break;
+ case XLOG_INFO:
+ sev = "info: ";
+ break;
+ case XLOG_DEBUG:
+ sev = "debug:";
+ break;
+ case XLOG_MAP:
+ sev = "map: ";
+ break;
+ case XLOG_STATS:
+ sev = "stats:";
+ break;
+ default:
+ sev = "hmm: ";
+ break;
+ }
+ fprintf(logfp, "%15.15s %s %s[%ld]/%s ",
+ last_ctime + 4, hostname,
+ progname,
+ (long) mypid,
+ sev);
+}
+
+
+#ifdef DEBUG
+/*
+ * Switch on/off debug options
+ */
+int
+debug_option(char *opt)
+{
+ return cmdoption(opt, dbg_opt, &debug_flags);
+}
+
+
+void
+dplog(char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ real_plog(XLOG_DEBUG, fmt, ap);
+ va_end(ap);
+}
+#endif /* DEBUG */
+
+
+void
+plog(int lvl, char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ real_plog(lvl, fmt, ap);
+ va_end(ap);
+}
+
+
+static void
+real_plog(int lvl, char *fmt, va_list vargs)
+{
+ char msg[1024];
+ char efmt[1024];
+ char *ptr = msg;
+ static char last_msg[1024];
+ static int last_count = 0, last_lvl = 0;
+
+ if (!(xlog_level & lvl))
+ return;
+
+#ifdef DEBUG_MEM
+ checkup_mem();
+#endif /* DEBUG_MEM */
+
+ expand_error(fmt, efmt);
+
+ vsprintf(ptr, efmt, vargs);
+
+ ptr += strlen(ptr);
+ if (ptr[-1] == '\n')
+ *--ptr = '\0';
+
+#ifdef HAVE_SYSLOG
+ if (syslogging) {
+ switch (lvl) { /* from mike <mcooper@usc.edu> */
+ case XLOG_FATAL:
+ lvl = LOG_CRIT;
+ break;
+ case XLOG_ERROR:
+ lvl = LOG_ERR;
+ break;
+ case XLOG_USER:
+ lvl = LOG_WARNING;
+ break;
+ case XLOG_WARNING:
+ lvl = LOG_WARNING;
+ break;
+ case XLOG_INFO:
+ lvl = LOG_INFO;
+ break;
+ case XLOG_DEBUG:
+ lvl = LOG_DEBUG;
+ break;
+ case XLOG_MAP:
+ lvl = LOG_DEBUG;
+ break;
+ case XLOG_STATS:
+ lvl = LOG_INFO;
+ break;
+ default:
+ lvl = LOG_ERR;
+ break;
+ }
+ syslog(lvl, "%s", msg);
+ return;
+ }
+#endif /* HAVE_SYSLOG */
+
+ *ptr++ = '\n';
+ *ptr = '\0';
+
+ /*
+ * mimic syslog behavior: only write repeated strings if they differ
+ */
+ switch (last_count) {
+ case 0: /* never printed at all */
+ last_count = 1;
+ strncpy(last_msg, msg, 1024);
+ last_lvl = lvl;
+ show_time_host_and_name(lvl); /* mimic syslog header */
+ fwrite(msg, ptr - msg, 1, logfp);
+ fflush(logfp);
+ break;
+
+ case 1: /* item printed once, if same, don't repeat */
+ if (STREQ(last_msg, msg)) {
+ last_count++;
+ } else { /* last msg printed once, new one differs */
+ /* last_count remains at 1 */
+ strncpy(last_msg, msg, 1024);
+ last_lvl = lvl;
+ show_time_host_and_name(lvl); /* mimic syslog header */
+ fwrite(msg, ptr - msg, 1, logfp);
+ fflush(logfp);
+ }
+ break;
+
+ case 100:
+ /*
+ * Don't allow repetitions longer than 100, so you can see when something
+ * cycles like crazy.
+ */
+ show_time_host_and_name(last_lvl);
+ sprintf(last_msg, "last message repeated %d times\n", last_count);
+ fwrite(last_msg, strlen(last_msg), 1, logfp);
+ fflush(logfp);
+ last_count = 0; /* start from scratch */
+ break;
+
+ default: /* item repeated multiple times */
+ if (STREQ(last_msg, msg)) {
+ last_count++;
+ } else { /* last msg repeated+skipped, new one differs */
+ show_time_host_and_name(last_lvl);
+ sprintf(last_msg, "last message repeated %d times\n", last_count);
+ fwrite(last_msg, strlen(last_msg), 1, logfp);
+ strncpy(last_msg, msg, 1024);
+ last_count = 1;
+ last_lvl = lvl;
+ show_time_host_and_name(lvl); /* mimic syslog header */
+ fwrite(msg, ptr - msg, 1, logfp);
+ fflush(logfp);
+ }
+ break;
+ }
+
+}
+
+
+/*
+ * Display current debug options
+ */
+void
+show_opts(int ch, struct opt_tab *opts)
+{
+ int i;
+ int s = '{';
+
+ fprintf(stderr, "\t[-%c {no}", ch);
+ for (i = 0; opts[i].opt; i++) {
+ fprintf(stderr, "%c%s", s, opts[i].opt);
+ s = ',';
+ }
+ fputs("}]\n", stderr);
+}
+
+
+int
+cmdoption(char *s, struct opt_tab *optb, int *flags)
+{
+ char *p = s;
+ int errs = 0;
+
+ while (p && *p) {
+ int neg;
+ char *opt;
+ struct opt_tab *dp, *dpn = 0;
+
+ s = p;
+ p = strchr(p, ',');
+ if (p)
+ *p = '\0';
+
+ /* check for "no" prefix to options */
+ if (s[0] == 'n' && s[1] == 'o') {
+ opt = s + 2;
+ neg = 1;
+ } else {
+ opt = s;
+ neg = 0;
+ }
+
+ /*
+ * Scan the array of debug options to find the
+ * corresponding flag value. If it is found
+ * then set (or clear) the flag (depending on
+ * whether the option was prefixed with "no").
+ */
+ for (dp = optb; dp->opt; dp++) {
+ if (STREQ(opt, dp->opt))
+ break;
+ if (opt != s && !dpn && STREQ(s, dp->opt))
+ dpn = dp;
+ }
+
+ if (dp->opt || dpn) {
+ if (!dp->opt) {
+ dp = dpn;
+ neg = !neg;
+ }
+ if (neg)
+ *flags &= ~dp->flag;
+ else
+ *flags |= dp->flag;
+ } else {
+ /*
+ * This will log to stderr when parsing the command line
+ * since any -l option will not yet have taken effect.
+ */
+ plog(XLOG_USER, "option \"%s\" not recognised", s);
+ errs++;
+ }
+
+ /*
+ * Put the comma back
+ */
+ if (p)
+ *p++ = ',';
+ }
+
+ return errs;
+}
+
+
+/*
+ * Switch on/off logging options
+ */
+int
+switch_option(char *opt)
+{
+ int xl = xlog_level;
+ int rc = cmdoption(opt, xlog_opt, &xl);
+
+ if (rc) {
+ rc = EINVAL;
+ } else {
+ /*
+ * Keep track of initial log level, and
+ * don't allow options to be turned off.
+ */
+ if (xlog_level_init == ~0)
+ xlog_level_init = xl;
+ else
+ xl |= xlog_level_init;
+ xlog_level = xl;
+ }
+ return rc;
+}
+
+/*
+ * get syslog facility to use.
+ * logfile can be "syslog", "syslog:daemon", "syslog:local7", etc.
+ */
+static int
+get_syslog_facility(const char *logfile)
+{
+ char *facstr;
+
+ /* parse facility string */
+ facstr = strchr(logfile, ':');
+ if (!facstr) /* log file was "syslog" */
+ return LOG_DAEMON;
+ facstr++;
+ if (!facstr || facstr[0] == '\0') { /* log file was "syslog:" */
+ plog(XLOG_WARNING, "null syslog facility, using LOG_DAEMON");
+ return LOG_DAEMON;
+ }
+
+#ifdef LOG_KERN
+ if (STREQ(facstr, "kern"))
+ return LOG_KERN;
+#endif /* not LOG_KERN */
+#ifdef LOG_USER
+ if (STREQ(facstr, "user"))
+ return LOG_USER;
+#endif /* not LOG_USER */
+#ifdef LOG_MAIL
+ if (STREQ(facstr, "mail"))
+ return LOG_MAIL;
+#endif /* not LOG_MAIL */
+#ifdef LOG_DAEMON
+ if (STREQ(facstr, "daemon"))
+ return LOG_DAEMON;
+#endif /* not LOG_DAEMON */
+#ifdef LOG_AUTH
+ if (STREQ(facstr, "auth"))
+ return LOG_AUTH;
+#endif /* not LOG_AUTH */
+#ifdef LOG_SYSLOG
+ if (STREQ(facstr, "syslog"))
+ return LOG_SYSLOG;
+#endif /* not LOG_SYSLOG */
+#ifdef LOG_LPR
+ if (STREQ(facstr, "lpr"))
+ return LOG_LPR;
+#endif /* not LOG_LPR */
+#ifdef LOG_NEWS
+ if (STREQ(facstr, "news"))
+ return LOG_NEWS;
+#endif /* not LOG_NEWS */
+#ifdef LOG_UUCP
+ if (STREQ(facstr, "uucp"))
+ return LOG_UUCP;
+#endif /* not LOG_UUCP */
+#ifdef LOG_CRON
+ if (STREQ(facstr, "cron"))
+ return LOG_CRON;
+#endif /* not LOG_CRON */
+#ifdef LOG_LOCAL0
+ if (STREQ(facstr, "local0"))
+ return LOG_LOCAL0;
+#endif /* not LOG_LOCAL0 */
+#ifdef LOG_LOCAL1
+ if (STREQ(facstr, "local1"))
+ return LOG_LOCAL1;
+#endif /* not LOG_LOCAL1 */
+#ifdef LOG_LOCAL2
+ if (STREQ(facstr, "local2"))
+ return LOG_LOCAL2;
+#endif /* not LOG_LOCAL2 */
+#ifdef LOG_LOCAL3
+ if (STREQ(facstr, "local3"))
+ return LOG_LOCAL3;
+#endif /* not LOG_LOCAL3 */
+#ifdef LOG_LOCAL4
+ if (STREQ(facstr, "local4"))
+ return LOG_LOCAL4;
+#endif /* not LOG_LOCAL4 */
+#ifdef LOG_LOCAL5
+ if (STREQ(facstr, "local5"))
+ return LOG_LOCAL5;
+#endif /* not LOG_LOCAL5 */
+#ifdef LOG_LOCAL6
+ if (STREQ(facstr, "local6"))
+ return LOG_LOCAL6;
+#endif /* not LOG_LOCAL6 */
+#ifdef LOG_LOCAL7
+ if (STREQ(facstr, "local7"))
+ return LOG_LOCAL7;
+#endif /* not LOG_LOCAL7 */
+
+ /* didn't match anything else */
+ plog(XLOG_WARNING, "unknown syslog facility \"%s\", using LOG_DAEMON", facstr);
+ return LOG_DAEMON;
+}
+
+
+/*
+ * Change current logfile
+ */
+int
+switch_to_logfile(char *logfile)
+{
+ FILE *new_logfp = stderr;
+
+ if (logfile) {
+#ifdef HAVE_SYSLOG
+ syslogging = 0;
+#endif /* HAVE_SYSLOG */
+
+ if (STREQ(logfile, "/dev/stderr"))
+ new_logfp = stderr;
+ else if (NSTREQ(logfile, "syslog", strlen("syslog"))) {
+
+#ifdef HAVE_SYSLOG
+ syslogging = 1;
+ new_logfp = stderr;
+ openlog(progname,
+ LOG_PID
+# ifdef LOG_CONS
+ | LOG_CONS
+# endif /* LOG_CONS */
+# ifdef LOG_NOWAIT
+ | LOG_NOWAIT
+# endif /* LOG_NOWAIT */
+# ifdef LOG_DAEMON
+ , get_syslog_facility(logfile)
+# endif /* LOG_DAEMON */
+ );
+#else /* not HAVE_SYSLOG */
+ plog(XLOG_WARNING, "syslog option not supported, logging unchanged");
+#endif /* not HAVE_SYSLOG */
+
+ } else {
+ (void) umask(orig_umask);
+ new_logfp = fopen(logfile, "a");
+ umask(0);
+ }
+ }
+
+ /*
+ * If we couldn't open a new file, then continue using the old.
+ */
+ if (!new_logfp && logfile) {
+ plog(XLOG_USER, "%s: Can't open logfile: %m", logfile);
+ return 1;
+ }
+
+ /*
+ * Close the previous file
+ */
+ if (logfp && logfp != stderr)
+ (void) fclose(logfp);
+ logfp = new_logfp;
+
+ return 0;
+}
+
+
+void
+unregister_amq(void)
+{
+#ifdef DEBUG
+ amuDebug(D_AMQ)
+#endif /* DEBUG */
+ /* find which instance of amd to unregister */
+ pmap_unset(get_amd_program_number(), AMQ_VERSION);
+}
+
+
+void
+going_down(int rc)
+{
+ if (foreground) {
+ if (amd_state != Start) {
+ if (amd_state != Done)
+ return;
+ unregister_amq();
+ }
+ }
+ if (foreground) {
+ plog(XLOG_INFO, "Finishing with status %d", rc);
+ } else {
+#ifdef DEBUG
+ dlog("background process exiting with status %d", rc);
+#endif /* DEBUG */
+ }
+
+ exit(rc);
+}
+
+
+/* return the rpc program number under which amd was used */
+int
+get_amd_program_number(void)
+{
+ return amd_program_number;
+}
+
+
+/* set the rpc program number used for amd */
+void
+set_amd_program_number(int program)
+{
+ amd_program_number = program;
+}
+
+
+/*
+ * Release the controlling tty of the process pid.
+ *
+ * Algorithm: try these in order, if available, until one of them
+ * succeeds: setsid(), ioctl(fd, TIOCNOTTY, 0).
+ * Do not use setpgid(): on some OSs it may release the controlling tty,
+ * even if the man page does not mention it, but on other OSs it does not.
+ * Also avoid setpgrp(): it works on some systems, and on others it is
+ * identical to setpgid().
+ */
+void
+amu_release_controlling_tty(void)
+{
+#ifdef TIOCNOTTY
+ int fd;
+#endif /* TIOCNOTTY */
+
+#ifdef HAVE_SETSID
+ if (setsid() < 0) {
+ plog(XLOG_WARNING, "Could not release controlling tty using setsid(): %m");
+ } else {
+ plog(XLOG_INFO, "released controlling tty using setsid()");
+ return;
+ }
+#endif /* HAVE_SETSID */
+
+#ifdef TIOCNOTTY
+ fd = open("/dev/tty", O_RDWR);
+ if (fd < 0) {
+ /* not an error if already no controlling tty */
+ if (errno != ENXIO)
+ plog(XLOG_WARNING, "Could not open controlling tty: %m");
+ } else {
+ if (ioctl(fd, TIOCNOTTY, 0) < 0 && errno != ENOTTY)
+ plog(XLOG_WARNING, "Could not disassociate tty (TIOCNOTTY): %m");
+ else
+ plog(XLOG_INFO, "released controlling tty using ioctl(TIOCNOTTY)");
+ close(fd);
+ }
+ return;
+#endif /* not TIOCNOTTY */
+
+ plog(XLOG_ERROR, "unable to release controlling tty");
+}
diff --git a/contrib/amd/mk-amd-map/mk-amd-map.8 b/contrib/amd/mk-amd-map/mk-amd-map.8
new file mode 100644
index 000000000000..a6690b8afba9
--- /dev/null
+++ b/contrib/amd/mk-amd-map/mk-amd-map.8
@@ -0,0 +1,62 @@
+.\"
+.\" Copyright (c) 1997-1998 Erez Zadok
+.\" Copyright (c) 1993 Jan-Simon Pendry
+.\" Copyright (c) 1993
+.\" The Regents of the University of California. 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.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgment:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+.\"
+.\" from: @(#)mk-amd-map.8 8.1 (Berkeley) 6/28/93
+.\" $Id: mk-amd-map.8,v 1.3 1997/05/29 01:48:43 cgd Exp $
+.\"
+.TH MK-AMD-MAP 8 "June 28, 1993"
+.SH NAME
+.B mk-amd-map
+\- create database maps for Amd
+.SH SYNOPSIS
+.B mk-amd-map
+[
+.B \-p
+]
+.I mapname
+.SH DESCRIPTION
+.B mk-amd-map
+creates the database maps used by the keyed map lookups in
+amd(8).
+It reads input from the named file
+and outputs them to a correspondingly named
+hashed database.
+.TP
+.B \-p
+This
+option prints the map on standard output instead of generating
+a database. This is usually used to merge continuation lines
+into one physical line.
+.SH SEE ALSO
+.BR amd (8).
diff --git a/contrib/amd/mk-amd-map/mk-amd-map.c b/contrib/amd/mk-amd-map/mk-amd-map.c
new file mode 100644
index 000000000000..2b4154008b44
--- /dev/null
+++ b/contrib/amd/mk-amd-map/mk-amd-map.c
@@ -0,0 +1,356 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: mk-amd-map.c,v 5.2.2.1 1992/02/09 15:09:18 jsp beta $
+ */
+
+/*
+ * Convert a file map into an ndbm map
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+
+/* dummy variables */
+char *progname;
+char hostname[MAXHOSTNAMELEN];
+int orig_umask, foreground, debug_flags;
+pid_t mypid;
+serv_state amd_state;
+
+
+#ifdef HAVE_MAP_NDBM
+
+static int
+store_data(voidp db, char *k, char *v)
+{
+ datum key, val;
+
+ key.dptr = k;
+ val.dptr = v;
+ key.dsize = strlen(k) + 1;
+ val.dsize = strlen(v) + 1;
+ return dbm_store((DBM *) db, key, val, DBM_INSERT);
+}
+
+
+/*
+ * Read one line from file.
+ */
+static int
+read_line(char *buf, int size, FILE *fp)
+{
+ int done = 0;
+
+ do {
+ while (fgets(buf, size, fp)) {
+ int len = strlen(buf);
+
+ done += len;
+ if (len > 1 && buf[len - 2] == '\\' && buf[len - 1] == '\n') {
+ int ch;
+ buf += len - 2;
+ size -= len - 2;
+ *buf = '\n';
+ buf[1] = '\0';
+
+ /*
+ * Skip leading white space on next line
+ */
+ while ((ch = getc(fp)) != EOF && isascii(ch) && isspace(ch)) ;
+ (void) ungetc(ch, fp);
+ } else {
+ return done;
+ }
+ }
+ } while (size > 0 && !feof(fp));
+
+ return done;
+}
+
+
+/*
+ * Read through a map.
+ */
+static int
+read_file(FILE *fp, char *map, voidp db)
+{
+ char key_val[2048];
+ int chuck = 0;
+ int line_no = 0;
+ int errs = 0;
+
+ while (read_line(key_val, sizeof(key_val), fp)) {
+ char *kp;
+ char *cp;
+ char *hash;
+ int len = strlen(key_val);
+
+ line_no++;
+
+ /*
+ * Make sure we got the whole line
+ */
+ if (key_val[len - 1] != '\n') {
+ fprintf(stderr, "line %d in \"%s\" is too long", line_no, map);
+ chuck = 1;
+ } else {
+ key_val[len - 1] = '\0';
+ }
+
+ /*
+ * Strip comments
+ */
+ hash = strchr(key_val, '#');
+ if (hash)
+ *hash = '\0';
+
+ /*
+ * Find start of key
+ */
+ for (kp = key_val; *kp && isascii(*kp) && isspace((int)*kp); kp++) ;
+
+ /*
+ * Ignore blank lines
+ */
+ if (!*kp)
+ goto again;
+
+ /*
+ * Find end of key
+ */
+ for (cp = kp; *cp && (!isascii(*cp) || !isspace((int)*cp)); cp++) ;
+
+ /*
+ * Check whether key matches, or whether
+ * the entry is a wildcard entry.
+ */
+ if (*cp)
+ *cp++ = '\0';
+ while (*cp && isascii(*cp) && isspace((int)*cp))
+ cp++;
+ if (*kp == '+') {
+ fprintf(stderr, "Can't interpolate %s\n", kp);
+ errs++;
+ } else if (*cp) {
+ if (db) {
+ if (store_data(db, kp, cp) < 0) {
+ fprintf(stderr, "Could store %s -> %s\n", kp, cp);
+ errs++;
+ }
+ } else {
+ printf("%s\t%s\n", kp, cp);
+ }
+ } else {
+ fprintf(stderr, "%s: line %d has no value field", map, line_no);
+ errs++;
+ }
+
+ again:
+ /*
+ * If the last read didn't get a whole line then
+ * throw away the remainder before continuing...
+ */
+ if (chuck) {
+ while (fgets(key_val, sizeof(key_val), fp) &&
+ !strchr(key_val, '\n')) ;
+ chuck = 0;
+ }
+ }
+ return errs;
+}
+
+
+static int
+remove_file(char *f)
+{
+ if (unlink(f) < 0 && errno != ENOENT)
+ return -1;
+
+ return 0;
+}
+
+
+int
+main(int argc, char *argv[])
+{
+ FILE *mapf;
+ int mapfd = -1;
+ char *map;
+ int rc = 0;
+ DBM *mapd = NULL;
+ static char maptmp[] = "dbmXXXXXX";
+ char maptpag[16], maptdir[16];
+ char *mappag = (char *) NULL, *mapdir = (char *) NULL;
+ int len;
+ char *sl;
+ int printit = 0;
+ int usage = 0;
+ int ch;
+ extern int optind;
+
+ /* test options */
+ while ((ch = getopt(argc, argv, "p")) != EOF)
+ switch (ch) {
+ case 'p':
+ printit = 1;
+ break;
+ default:
+ usage++;
+ break;
+ }
+
+ if (usage || optind != (argc - 1)) {
+ fputs("Usage: mk-amd-map [-p] file-map\n", stderr);
+ exit(1);
+ }
+ map = argv[optind];
+
+ /* test if can get to the map directory */
+ sl = strrchr(map, '/');
+ if (sl) {
+ *sl = '\0';
+ if (chdir(map) < 0) {
+ fputs("Can't chdir to ", stderr);
+ perror(map);
+ exit(1);
+ }
+ map = sl + 1;
+ }
+
+ if (!printit) {
+ len = strlen(map);
+ mappag = (char *) malloc(len + 5);
+ mapdir = (char *) malloc(len + 5);
+ if (!mappag || !mapdir) {
+ perror("mk-amd-map: malloc");
+ exit(1);
+ }
+#ifdef HAVE_MKSTEMP
+ mapfd = mkstemp(maptmp);
+#else /* not HAVE_MKSTEMP */
+ map = mktemp(maptmp);
+ if (!maptmp) {
+ fprintf(stderr, "cannot create temporary file\n");
+ exit(1);
+ }
+ mapfd = open(map, O_RDONLY);
+#endif /* not HAVE_MKSTEMP */
+
+ /* open DBM files */
+ sprintf(maptpag, "%s.pag", maptmp);
+ sprintf(maptdir, "%s.dir", maptmp);
+ if (remove_file(maptpag) < 0 || remove_file(maptdir) < 0) {
+ fprintf(stderr, "Can't remove existing temporary files; %s and", maptpag);
+ perror(maptdir);
+ exit(1);
+ }
+ }
+ /* open and check if map file was opened OK */
+ mapf = fdopen(mapfd, "r");
+ if (mapf && !printit)
+ mapd = dbm_open(maptmp, O_RDWR|O_CREAT, 0444);
+ else
+ mapd = 0;
+
+#ifndef DEBUG
+ /* ignore ^C if debuggung is on (but why?) */
+ signal(SIGINT, SIG_IGN);
+#endif /* not DEBUG */
+
+ if (mapd || printit) {
+ int error = read_file(mapf, map, mapd);
+ (void) close(mapfd);
+ (void) fclose(mapf);
+ if (printit) {
+ if (error) {
+ fprintf(stderr, "Error creating ndbm map for %s\n", map);
+ rc = 1;
+ }
+ } else {
+
+ if (error) {
+ fprintf(stderr, "Error reading source file %s\n", map);
+ rc = 1;
+ } else {
+ sprintf(mappag, "%s.pag", map);
+ sprintf(mapdir, "%s.dir", map);
+ if (rename(maptpag, mappag) < 0) {
+ fprintf(stderr, "Couldn't rename %s to ", maptpag);
+ perror(mappag);
+ /* Throw away the temporary map */
+ unlink(maptpag);
+ unlink(maptdir);
+ rc = 1;
+
+ } else if (rename(maptdir, mapdir) < 0) {
+ fprintf(stderr, "Couldn't rename %s to ", maptdir);
+ perror(mapdir);
+ /* Put the .pag file back */
+ rename(mappag, maptpag);
+ /* Throw away remaining part of original map */
+ unlink(mapdir);
+ fprintf(stderr,
+ "WARNING: existing map \"%s.{dir,pag}\" destroyed\n",
+ map);
+ rc = 1;
+ }
+ }
+ }
+
+ } else {
+ fprintf(stderr, "Can't open \"%s.{dir,pag}\" for ", map);
+ perror("writing");
+ rc = 1;
+ }
+ exit(rc);
+}
+
+#else /* not HAVE_MAP_NDBM */
+
+main()
+{
+ fputs("mk-amd-map: This system does not support hashed database files\n", stderr);
+ exit(1);
+}
+
+#endif /* not HAVE_MAP_NDBM */
diff --git a/contrib/amd/scripts/Makefile.am b/contrib/amd/scripts/Makefile.am
new file mode 100644
index 000000000000..9ebd6914cb6c
--- /dev/null
+++ b/contrib/amd/scripts/Makefile.am
@@ -0,0 +1,49 @@
+## Process this file with automake to produce Makefile.in
+
+# Package: am-utils
+# Level: Makefile for scripts/ directory
+# Author: Erez Zadok
+
+sbin_SCRIPTS = \
+ am-eject \
+ amd2ldif \
+ amd2sun \
+ ctl-amd \
+ ctl-hlfsd \
+ fixrmtab \
+ fix-amd-map \
+ lostaltmail \
+ wait4amd \
+ wait4amd2die
+
+bin_SCRIPTS = \
+ expn
+
+sysconf_DATA = \
+ amd.conf-sample \
+ lostaltmail.conf-sample
+
+# man pages
+man_MANS = \
+ amd.conf.5 \
+ expn.1
+
+EXTRA_DIST = \
+ am-eject.in \
+ amd2ldif.in \
+ amd2sun.in \
+ ctl-amd.in \
+ ctl-hlfsd.in \
+ fixrmtab \
+ fix-amd-map.in \
+ lostaltmail.in \
+ wait4amd.in \
+ wait4amd2die.in \
+ \
+ expn.in \
+ \
+ amd.conf-sample \
+ lostaltmail.conf-sample \
+ $(man_MANS)
+
+CLEANFILES = $(sbin_SCRIPTS) $(bin_SCRIPTS)
diff --git a/contrib/amd/scripts/Makefile.in b/contrib/amd/scripts/Makefile.in
new file mode 100644
index 000000000000..1c69cec2aecf
--- /dev/null
+++ b/contrib/amd/scripts/Makefile.in
@@ -0,0 +1,379 @@
+# Makefile.in generated automatically by automake 1.3.1 from Makefile.am
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+# Package: am-utils
+# Level: Makefile for scripts/ directory
+# Author: Erez Zadok
+
+
+SHELL = /bin/sh
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+
+bindir = @bindir@
+sbindir = @sbindir@
+libexecdir = @libexecdir@
+datadir = @datadir@
+sysconfdir = @sysconfdir@
+sharedstatedir = @sharedstatedir@
+localstatedir = @localstatedir@
+libdir = @libdir@
+infodir = @infodir@
+mandir = @mandir@
+includedir = @includedir@
+oldincludedir = /usr/include
+
+DISTDIR =
+
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+
+top_builddir = ..
+
+ACLOCAL = @ACLOCAL@
+AUTOCONF = @AUTOCONF@
+AUTOMAKE = @AUTOMAKE@
+AUTOHEADER = @AUTOHEADER@
+
+INSTALL = @INSTALL@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+transform = @program_transform_name@
+
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+host_alias = @host_alias@
+host_triplet = @host@
+AR = @AR@
+CC = @CC@
+CPP = @CPP@
+LEX = @LEX@
+LIBTOOL = @LIBTOOL@
+LIBTOOL_LDFLAGS = @LIBTOOL_LDFLAGS@
+LTALLOCA = @LTALLOCA@
+LTLIBOBJS = @LTLIBOBJS@
+PACKAGE = @PACKAGE@
+PERL = @PERL@
+RANLIB = @RANLIB@
+VERSION = @VERSION@
+
+sbin_SCRIPTS = \
+ am-eject \
+ amd2ldif \
+ amd2sun \
+ ctl-amd \
+ ctl-hlfsd \
+ fixrmtab \
+ fix-amd-map \
+ lostaltmail \
+ wait4amd \
+ wait4amd2die
+
+bin_SCRIPTS = \
+ expn
+
+sysconf_DATA = \
+ amd.conf-sample \
+ lostaltmail.conf-sample
+
+# man pages
+man_MANS = \
+ amd.conf.5 \
+ expn.1
+
+EXTRA_DIST = \
+ am-eject.in \
+ amd2ldif.in \
+ amd2sun.in \
+ ctl-amd.in \
+ ctl-hlfsd.in \
+ fixrmtab \
+ fix-amd-map.in \
+ lostaltmail.in \
+ wait4amd.in \
+ wait4amd2die.in \
+ \
+ expn.in \
+ \
+ amd.conf-sample \
+ lostaltmail.conf-sample \
+ $(man_MANS)
+
+CLEANFILES = $(sbin_SCRIPTS) $(bin_SCRIPTS)
+mkinstalldirs = $(SHELL) $(top_srcdir)/aux/mkinstalldirs
+CONFIG_HEADER = ../config.h
+CONFIG_CLEAN_FILES = am-eject amd2ldif amd2sun ctl-amd ctl-hlfsd expn \
+fix-amd-map lostaltmail wait4amd wait4amd2die
+SCRIPTS = $(bin_SCRIPTS) $(sbin_SCRIPTS)
+
+man1dir = $(mandir)/man1
+man5dir = $(mandir)/man5
+MANS = $(man_MANS)
+
+NROFF = nroff
+DATA = $(sysconf_DATA)
+
+DIST_COMMON = Makefile.am Makefile.in am-eject.in amd2ldif.in \
+amd2sun.in ctl-amd.in ctl-hlfsd.in expn.in fix-amd-map.in \
+lostaltmail.in wait4amd.in wait4amd2die.in
+
+
+DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST)
+
+TAR = gtar
+GZIP = --best
+all: Makefile $(SCRIPTS) $(MANS) $(DATA)
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/./aux/configure.in $(ACLOCAL_M4)
+ cd $(top_srcdir) && $(AUTOMAKE) --localdir=./aux --gnu --include-deps scripts/Makefile
+
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ cd $(top_builddir) \
+ && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status
+
+am-eject: $(top_builddir)/config.status am-eject.in
+ cd $(top_builddir) && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= ./config.status
+amd2ldif: $(top_builddir)/config.status amd2ldif.in
+ cd $(top_builddir) && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= ./config.status
+amd2sun: $(top_builddir)/config.status amd2sun.in
+ cd $(top_builddir) && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= ./config.status
+ctl-amd: $(top_builddir)/config.status ctl-amd.in
+ cd $(top_builddir) && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= ./config.status
+ctl-hlfsd: $(top_builddir)/config.status ctl-hlfsd.in
+ cd $(top_builddir) && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= ./config.status
+expn: $(top_builddir)/config.status expn.in
+ cd $(top_builddir) && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= ./config.status
+fix-amd-map: $(top_builddir)/config.status fix-amd-map.in
+ cd $(top_builddir) && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= ./config.status
+lostaltmail: $(top_builddir)/config.status lostaltmail.in
+ cd $(top_builddir) && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= ./config.status
+wait4amd: $(top_builddir)/config.status wait4amd.in
+ cd $(top_builddir) && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= ./config.status
+wait4amd2die: $(top_builddir)/config.status wait4amd2die.in
+ cd $(top_builddir) && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= ./config.status
+
+install-binSCRIPTS: $(bin_SCRIPTS)
+ @$(NORMAL_INSTALL)
+ $(mkinstalldirs) $(DESTDIR)$(bindir)
+ @list='$(bin_SCRIPTS)'; for p in $$list; do \
+ if test -f $$p; then \
+ echo " $(INSTALL_SCRIPT) $$p $(DESTDIR)$(bindir)/`echo $$p|sed '$(transform)'`"; \
+ $(INSTALL_SCRIPT) $$p $(DESTDIR)$(bindir)/`echo $$p|sed '$(transform)'`; \
+ else if test -f $(srcdir)/$$p; then \
+ echo " $(INSTALL_SCRIPT) $(srcdir)/$$p $(DESTDIR)$(bindir)/`echo $$p|sed '$(transform)'`"; \
+ $(INSTALL_SCRIPT) $(srcdir)/$$p $(DESTDIR)$(bindir)/`echo $$p|sed '$(transform)'`; \
+ else :; fi; fi; \
+ done
+
+uninstall-binSCRIPTS:
+ @$(NORMAL_UNINSTALL)
+ list='$(bin_SCRIPTS)'; for p in $$list; do \
+ rm -f $(DESTDIR)$(bindir)/`echo $$p|sed '$(transform)'`; \
+ done
+
+install-sbinSCRIPTS: $(sbin_SCRIPTS)
+ @$(NORMAL_INSTALL)
+ $(mkinstalldirs) $(DESTDIR)$(sbindir)
+ @list='$(sbin_SCRIPTS)'; for p in $$list; do \
+ if test -f $$p; then \
+ echo " $(INSTALL_SCRIPT) $$p $(DESTDIR)$(sbindir)/`echo $$p|sed '$(transform)'`"; \
+ $(INSTALL_SCRIPT) $$p $(DESTDIR)$(sbindir)/`echo $$p|sed '$(transform)'`; \
+ else if test -f $(srcdir)/$$p; then \
+ echo " $(INSTALL_SCRIPT) $(srcdir)/$$p $(DESTDIR)$(sbindir)/`echo $$p|sed '$(transform)'`"; \
+ $(INSTALL_SCRIPT) $(srcdir)/$$p $(DESTDIR)$(sbindir)/`echo $$p|sed '$(transform)'`; \
+ else :; fi; fi; \
+ done
+
+uninstall-sbinSCRIPTS:
+ @$(NORMAL_UNINSTALL)
+ list='$(sbin_SCRIPTS)'; for p in $$list; do \
+ rm -f $(DESTDIR)$(sbindir)/`echo $$p|sed '$(transform)'`; \
+ done
+
+install-man1:
+ $(mkinstalldirs) $(DESTDIR)$(man1dir)
+ @list='$(man1_MANS)'; \
+ l2='$(man_MANS)'; for i in $$l2; do \
+ case "$$i" in \
+ *.1*) list="$$list $$i" ;; \
+ esac; \
+ done; \
+ for i in $$list; do \
+ if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \
+ else file=$$i; fi; \
+ ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+ inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+ echo " $(INSTALL_DATA) $$file $(DESTDIR)$(man1dir)/$$inst"; \
+ $(INSTALL_DATA) $$file $(DESTDIR)$(man1dir)/$$inst; \
+ done
+
+uninstall-man1:
+ @list='$(man1_MANS)'; \
+ l2='$(man_MANS)'; for i in $$l2; do \
+ case "$$i" in \
+ *.1*) list="$$list $$i" ;; \
+ esac; \
+ done; \
+ for i in $$list; do \
+ ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+ inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+ echo " rm -f $(DESTDIR)$(man1dir)/$$inst"; \
+ rm -f $(DESTDIR)$(man1dir)/$$inst; \
+ done
+
+install-man5:
+ $(mkinstalldirs) $(DESTDIR)$(man5dir)
+ @list='$(man5_MANS)'; \
+ l2='$(man_MANS)'; for i in $$l2; do \
+ case "$$i" in \
+ *.5*) list="$$list $$i" ;; \
+ esac; \
+ done; \
+ for i in $$list; do \
+ if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \
+ else file=$$i; fi; \
+ ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+ inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+ echo " $(INSTALL_DATA) $$file $(DESTDIR)$(man5dir)/$$inst"; \
+ $(INSTALL_DATA) $$file $(DESTDIR)$(man5dir)/$$inst; \
+ done
+
+uninstall-man5:
+ @list='$(man5_MANS)'; \
+ l2='$(man_MANS)'; for i in $$l2; do \
+ case "$$i" in \
+ *.5*) list="$$list $$i" ;; \
+ esac; \
+ done; \
+ for i in $$list; do \
+ ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+ inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+ echo " rm -f $(DESTDIR)$(man5dir)/$$inst"; \
+ rm -f $(DESTDIR)$(man5dir)/$$inst; \
+ done
+install-man: $(MANS)
+ @$(NORMAL_INSTALL)
+ $(MAKE) install-man1 install-man5
+uninstall-man:
+ @$(NORMAL_UNINSTALL)
+ $(MAKE) uninstall-man1 uninstall-man5
+
+install-sysconfDATA: $(sysconf_DATA)
+ @$(NORMAL_INSTALL)
+ $(mkinstalldirs) $(DESTDIR)$(sysconfdir)
+ @list='$(sysconf_DATA)'; for p in $$list; do \
+ if test -f $(srcdir)/$$p; then \
+ echo " $(INSTALL_DATA) $(srcdir)/$$p $(DESTDIR)$(sysconfdir)/$$p"; \
+ $(INSTALL_DATA) $(srcdir)/$$p $(DESTDIR)$(sysconfdir)/$$p; \
+ else if test -f $$p; then \
+ echo " $(INSTALL_DATA) $$p $(DESTDIR)$(sysconfdir)/$$p"; \
+ $(INSTALL_DATA) $$p $(DESTDIR)$(sysconfdir)/$$p; \
+ fi; fi; \
+ done
+
+uninstall-sysconfDATA:
+ @$(NORMAL_UNINSTALL)
+ list='$(sysconf_DATA)'; for p in $$list; do \
+ rm -f $(DESTDIR)$(sysconfdir)/$$p; \
+ done
+tags: TAGS
+TAGS:
+
+
+distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir)
+
+subdir = scripts
+
+distdir: $(DISTFILES)
+ @for file in $(DISTFILES); do \
+ d=$(srcdir); \
+ test -f $(distdir)/$$file \
+ || ln $$d/$$file $(distdir)/$$file 2> /dev/null \
+ || cp -p $$d/$$file $(distdir)/$$file; \
+ done
+info:
+dvi:
+check: all
+ $(MAKE)
+installcheck:
+install-exec: install-binSCRIPTS install-sbinSCRIPTS install-sysconfDATA
+ @$(NORMAL_INSTALL)
+
+install-data: install-man
+ @$(NORMAL_INSTALL)
+
+install: install-exec install-data all
+ @:
+
+uninstall: uninstall-binSCRIPTS uninstall-sbinSCRIPTS uninstall-man uninstall-sysconfDATA
+
+install-strip:
+ $(MAKE) INSTALL_PROGRAM='$(INSTALL_PROGRAM) -s' INSTALL_SCRIPT='$(INSTALL_PROGRAM)' install
+installdirs:
+ $(mkinstalldirs) $(DATADIR)$(bindir) $(DATADIR)$(sbindir) \
+ $(DESTDIR)$(mandir)/man1 $(DESTDIR)$(mandir)/man5 \
+ $(DATADIR)$(sysconfdir)
+
+
+mostlyclean-generic:
+ -test -z "$(MOSTLYCLEANFILES)" || rm -f $(MOSTLYCLEANFILES)
+
+clean-generic:
+ -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+ -rm -f Makefile $(DISTCLEANFILES)
+ -rm -f config.cache config.log stamp-h stamp-h[0-9]*
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+ -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES)
+ -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES)
+mostlyclean: mostlyclean-generic
+
+clean: clean-generic mostlyclean
+
+distclean: distclean-generic clean
+ -rm -f config.status
+ -rm -f libtool
+
+maintainer-clean: maintainer-clean-generic distclean
+ @echo "This command is intended for maintainers to use;"
+ @echo "it deletes files that may require special tools to rebuild."
+
+.PHONY: uninstall-binSCRIPTS install-binSCRIPTS uninstall-sbinSCRIPTS \
+install-sbinSCRIPTS install-man1 uninstall-man1 install-man5 \
+uninstall-man5 install-man uninstall-man uninstall-sysconfDATA \
+install-sysconfDATA tags distdir info dvi installcheck install-exec \
+install-data install uninstall all installdirs mostlyclean-generic \
+distclean-generic clean-generic maintainer-clean-generic clean \
+mostlyclean distclean maintainer-clean
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/contrib/amd/scripts/am-eject.in b/contrib/amd/scripts/am-eject.in
new file mode 100644
index 000000000000..ed7e2d0605d1
--- /dev/null
+++ b/contrib/amd/scripts/am-eject.in
@@ -0,0 +1,52 @@
+#!/bin/sh
+# auto-unmount floppy/cd directory before ejecting device
+# script taken from Debian Linux's amd
+#
+# Package: am-utils-6.0
+# (Additional) author: Erez Zadok <ezk@cs.columbia.edu>
+
+# set path
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+PATH=@sbindir@:@bindir@:/usr/ucb:/usr/bin:/bin:${PATH}
+export PATH
+
+if [ $# -ne 1 ]; then
+ echo "Usage: $0 cd|fd"
+ exit 2
+fi
+
+# determine toplevel mount point of amd
+fs=`amq | grep ' toplvl ' | cut -d' ' -f1`
+if [ "$fs" = "" ]; then
+ echo "Cannot determine amd toplevel directory"
+ exit 2
+fi
+
+# append name of medium
+case "$1" in
+ cd|fd) fs=$fs/$1;;
+ *) echo "Usage: $0 cd|fd"; exit 2;;
+esac
+
+# is the medium mounted?
+if amq | grep -q "^$fs" >/dev/null 2>&1; then
+ # if yes, try to unmount it
+ sync
+ amq -u $fs
+ sleep 2
+ if amq | grep -q "^$fs" >/dev/null 2>&1; then
+ # failed, bail out
+ echo -n "Cannot unmount $fs; in use by:"
+ fuser -uv -m $fs
+ echo ""
+ exit 1
+ fi
+else
+ echo "$fs not mounted"
+fi
+
+case $1 in
+ cd) eject;; # eject CD-ROM
+ fd) echo "Ok to remove disk";;
+esac
diff --git a/contrib/amd/scripts/amd.conf-sample b/contrib/amd/scripts/amd.conf-sample
new file mode 100644
index 000000000000..59ce1f81c752
--- /dev/null
+++ b/contrib/amd/scripts/amd.conf-sample
@@ -0,0 +1,94 @@
+# -*- text -*-
+# A SAMPLE CONFIGURATION FILE FOR AMD
+
+##############################################################################
+# GLOBAL OPTIONS SECTION (must be first in amd.conf file)
+[ global ]
+# (amd -n)
+normalize_hostnames = yes | no
+# (amd -p)
+print_pid = yes | no
+pid_file = /dev/stdout | /var/run/amd.pid
+# (amd -r)
+restart_mounts = yes | no
+unmount_on_exit = no | yes
+# (amd -a)
+auto_dir = /a
+# duration in seconds that a looked up name remain cached (amd -c)
+cache_duration = 300
+# (amd -d)
+local_domain = cs.columbia.edu
+# (amd -k)
+karch = sun4m
+arch = sun4
+# if you don't like autoconf picking up "sunos5" as the os-type, override it
+os = sos5
+# (amd -o)
+osver = 2.5.1
+# if you print_version after setting up "os", it will show it. (amd -v)
+print_version = yes | no
+
+# (amd -l)
+log_file = /var/log/amd | syslog | syslog:facility
+# NFS (RPC/UDP) retry interval, in tenths of secs (amd -t interval.counter)
+nfs_retry_interval = 8
+nfs_retransmit_counter = 110
+# (amd -w)
+dismount_interval = 120
+# (amd -y)
+nis_domain = nisDom-CS.columbia.edu
+# (amd -x)
+log_options = fatal,error,user,warn,info,map,stats,all
+# (amd -D)
+debug_options = all,amq,daemon,fork,full,info,mem,mtab,str,test,trace
+# (amd -S)
+plock = no | yes
+# selectors on /defaults are off by default
+selectors_on_default = yes | no
+# should browsable maps show number of entries to df/statfs (default=no)
+show_statfs_entries = yes | no
+# (hpux) cluster name (amd -C)
+cluster = ???
+# LDAP (Lightweight Directory Access Protocol) options
+ldap_base = ldap.your.domain:389
+ldap_hostports = "ou=Marketing, o=AMD Ltd, c=US"
+ldap_cache_seconds = 0 (default)
+ldap_cache_maxmem = 131072 (default)
+# default base name for hesiod maps
+hesiod_base = automount
+# these 5 options can be overridden by each map individually
+browsable_dirs = yes | no | full
+map_options = cache:=all
+map_type = file|hesiod|ndbm|nis|nisplus|passwd|union|ldap
+mount_type = nfs | autofs
+search_path = /etc/local:/etc/amdmaps:/misc/yp
+# alternate RPC program number to register with the port mapper
+portmap_program = 300019-300029
+# Use fully qualified host names
+fully_qualified_hosts = yes | no
+
+##############################################################################
+# DEFINE AN AMD MOUNT POINT
+[ /home ]
+# map name must be defined, all else are optional
+map_name = /etc/amd.home | amd.home
+map_options = cache:=all
+# if map type is not defined, will search all map types (default)
+map_type = file|hesiod|ndbm|nis|nisplus|passwd|union|ldap
+search_path = /etc/local:/etc/amdmaps:/misc/yp
+# an amd or autofs mount point
+mount_type = nfs | autofs
+browsable_dirs = yes | no
+# an optional tag to be used with amd -T tag. untagged entries are always
+# used. Tagged ones get used only if specified with "amd -T"
+tag = test
+
+##############################################################################
+# DEFINE ANOTHER AMD MOUNT POINT
+[ /src ]
+map_name = /usr/local/lib/amdmaps/amu.src
+# regular amd (nfs) mount point (default)
+# don't try the "autofs" type. It is not implemented yet.
+mount_type = nfs
+
+##############################################################################
diff --git a/contrib/amd/scripts/amd.conf.5 b/contrib/amd/scripts/amd.conf.5
new file mode 100644
index 000000000000..2022fad64390
--- /dev/null
+++ b/contrib/amd/scripts/amd.conf.5
@@ -0,0 +1,539 @@
+.\"
+.\" Copyright (c) 1997-1998 Erez Zadok
+.\" Copyright (c) 1990 Jan-Simon Pendry
+.\" Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+.\" Copyright (c) 1990 The Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Jan-Simon Pendry at Imperial College, London.
+.\"
+.\" 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.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgment:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+.\"
+.\" %W% (Berkeley) %G%
+.\"
+.\" $Id: amd.conf.5,v 5.2.2.1 1997/02/09 15:11:15 ezk beta $
+.\"
+.TH AMQ.CONF 8 "7 August 1997"
+.SH NAME
+amd.conf \- amd configuration file
+.SH SYNOPSIS
+.B amd.conf
+.SH DESCRIPTION
+The
+.B amd.conf
+file is the configuration file for amd, as part of the am-utils suite.
+.P
+.B amd.conf
+contains runtime configuration information for the
+.B amd
+automounter program.
+.\" **************************************************************************
+.SH FILE FORMAT
+.P
+The file consists of sections and parameters. A section begins with the
+name of the section in square brackets and continues until the next section
+begins or the end the file is reached. Sections contain parameters of the
+form 'name = value'.
+.P
+The file is line-based - that is, each newline-terminated line represents
+either a comment, a section name or a parameter. No line-continuation
+syntax is available.
+.P
+Section, parameter names and their values are case sensitive.
+.P
+Only the first equals sign in a parameter is significant. Whitespace before
+or after the first equals sign is discarded. Leading, trailing and
+internal whitespace in section and parameter names is irrelevant. Leading
+and trailing whitespace in a parameter value is discarded. Internal
+whitespace within a parameter value is not allowed, unless the whole
+parameter value is quoted with double quotes as in 'name = "some value"'.
+.P
+Any line beginning with a pound sign (#) is ignored, as are lines containing
+only whitespace.
+.P
+The values following the equals sign in parameters are all either a string
+(no quotes needed if string does not include spaces) or a boolean, which may
+be given as yes/no. Case is significant in all values. Some items such as
+cache timeouts are numeric.
+.\" **************************************************************************
+.SH SECTIONS
+.SS The [global] section
+Parameters in this section either apply to amd as a whole, or to all other
+regular map sections which follow. There should be only one global section
+defined in one configuration file.
+.P
+It is highly recommended that this section be specified first in the
+configuration file. If it is not, then regular map sections which precede
+it will not use global values defined later.
+
+.SS Regular [/map] sections
+Parameters in regular (non-global) sections apply to a single map entry.
+For example, if the map section
+.B [/homes]
+is defined, then all parameters following it will be applied to the
+.I /homes
+amd-managed mount point.
+.\" **************************************************************************
+.SH PARAMETERS
+.SS Parameters common to all sections
+These parameters can be specified either in the global or a map specific
+section. Entries specified in a map-specific section override the default
+value or one defined in the global section. If such a common parameter is
+specified only in the global section, it is applicable to all regular map
+sections that follow.
+.\" **************************************************************************
+.TP
+.BR browsable_dirs " (string, default=no)"
+If "yes", then amd's top-level mount points will be browsable to
+.BR readdir (3)
+calls. This means you could run for example
+.BR ls (3)
+and see what keys are available to mount in that directory. Not all entries
+are made visible to readdir(3): the "/default" entry, wildcard
+entries, and those with a "/" in them are not included. If you specify
+"full" to this option, all but "/default" will be visible.
+Note that if you run a command which will attempt to
+.BR stat (2)
+the entries, such as often done by "ls -l" or "ls -F", amd will attempt to
+mount
+.I every
+entry in that map. This is often called a ``mount storm''.
+
+.TP
+.BR map_options " (string, default no options)"
+This option is the same as specifying map options on the command line to
+amd, such as "cache:=all".
+
+.TP
+.BR map_type " (string, default search all map types)"
+If specified, amd will initialize the map only for the type given. This is
+useful to avoid the default map search type used by amd which takes longer
+and can have undesired side-effects such as initializing NIS even if not
+used. Possible values are
+
+.nf
+\fBfile\fR plain files
+\fBhesiod\fR Hesiod name service from MIT
+\fBldap\fR Lightweight Directory Access Protocol
+\fBndbm\fR (New) dbm style hash files
+\fBnis\fR Network Information Services (version 2)
+\fBnisplus\fR Network Information Services Plus (version 3)
+\fBpasswd\fR local password files
+\fBunion\fR union maps
+.fi
+
+.TP
+.BR mount_type " (string, default=nfs)"
+All amd mount types default to NFS. That is, amd is an NFS server on the
+map mount points, for the local host it is running on. If "autofs" is
+specified, amd will be an autofs server for those mount points.
+
+.TP
+.BR search_path " (string, default no search path)"
+This provides a (colon-delimited) search path for file maps. Using a search
+path, sites can allow for local map customizations and overrides, and can
+distributed maps in several locations as needed.
+
+.\" **************************************************************************
+.SS Parameters applicable to the global section only
+
+.TP
+.BR arch " (string, default to compiled in value)"
+Allows you to override the value of the
+.I arch
+amd variable.
+
+.TP
+.BR auto_dir " (string, default=/a)"
+Same as the
+.B \-a
+option to amd. This sets the private directory where amd will create
+sub-directories for its real mount points.
+
+.TP
+.BR cache_duration " (numeric, default=300)"
+Same as the
+.B \-c
+option to amd. Sets the duration in seconds that looked up map entries
+remain in the cache.
+
+.TP
+.BR cluster " (string, default no cluster)"
+Same as the
+.B \-C
+option to amd. Specifies the alternate HP-UX cluster to use.
+
+.TP
+.BR debug_options " (string, default no debug options)"
+Same as the
+.B \-D
+option to amd. Specify any debugging options for amd. Works only if
+am-utils was configured for debugging using the --enable-debug option. The
+"mem" option alone can be turned on via --enable-debug=mem. Otherwise
+debugging options are ignored. Options are comma delimited, and can be
+preceded by the string "no" to negate their meaning. You can get the list
+of supported debugging options by running amd \-v. Possible values are:
+
+.nf
+\fBall\fR all options
+\fBamq\fR register for amq
+\fBdaemon\fR enter daemon mode
+\fBfork\fR fork server
+\fBfull\fR program trace
+\fBinfo\fR info service specific debugging (hesiod, nis, etc.)
+\fBmem\fR trace memory allocations
+\fBmtab\fR use local "./mtab" file
+\fBstr\fR debug string munging
+\fBtest\fR full debug but no daemon
+\fBtrace\fR protocol trace
+.fi
+
+.TP
+.BR dismount_interval " (numeric, default=120)"
+Same as the
+.B \-w
+option to amd. Specify in seconds, the time between attempts to dismount
+file systems that have exceeded their cached times.
+
+.TP
+.BR fully_qualified_hosts " (string, default=no)"
+If "yes",
+.I Amd
+will perform RPC authentication using fully-qualified host names. This is
+necessary for some systems, and especially when performing cross-domain
+mounting. For this function to work, the
+.I Amd
+variable ${hostd} is used, requiring that ${domain} not be null.
+
+.TP
+.BR hesiod_base " (string, default=automount)"
+Specify the base name for hesiod maps.
+
+.TP
+.BR karch " (string, default to karch of the system)"
+Same as the
+.B \-k
+option to amd. Allows you to override the kernel-architecture of your
+system. Useful for example on Sun (Sparc) machines, where you can build one
+amd binary, and run it on multiple machines, yet you want each one to get
+the correct
+.I karch
+variable set (for example, sun4c, sun4m, sun4u, etc.) Note that if not
+specified, amd will use uname(2) to figure out the kernel architecture of
+the machine.
+
+.TP
+.BR ldap_base " (string, default not set)"
+Specify the base name for LDAP.
+
+.TP
+.BR ldap_cache_maxmem " (numeric, default=131072)"
+Specify the maximum memory amd should use to cache LDAP entries.
+
+.TP
+.BR ldap_cache_seconds " (numeric, default=0)"
+Specify the number of seconds to keep entries in the cache.
+
+.TP
+.BR ldap_hostports " (string, default not set)"
+Specify LDAP-specific values such as country and organization.
+
+.TP
+.BR local_domain " (string, default no sub-domain)"
+Same as the
+.B \-d
+option to amd. Specify the local domain name. If this option is not given
+the domain name is determined from the hostname, by removing the first
+component of the fully-qualified host name.
+
+.TP
+.BR log_file " (string, default=/dev/stderr)"
+Same as the
+.B \-l
+option to amd. Specify a file name to log amd events to.
+If the string
+.B /dev/stderr
+is specified, amd will send its events to the standard error file descriptor.
+If the string
+.B syslog
+is given, amd will record its events with the system logger
+.BR syslogd (8).
+The default syslog facility used is LOG_DAEMON. If you
+wish to change it, append its name to the log file name, delimited by a
+single colon. For example, if
+.I logfile
+is the string
+.B syslog:local7
+then amd will log messages via
+.IR syslog (3)
+using the LOG_LOCAL7 facility (if it exists on the system).
+
+.TP
+.BR log_options " (string, default no logging options)"
+Same as the
+.B \-x
+option to amd. Specify any logging options for amd. Options are comma
+delimited, and can be preceded by the string "no" to negate their meaning.
+The "debug" logging option is only available if am-utils was configured with
+--enable-debug. You can get the list of supported debugging options by
+running amd \-v. Possible values are:
+
+.nf
+\fBall\fR all messages
+\fBdebug\fR debug messages
+\fBerror\fR non-fatal system errors
+\fBfatal\fR fatal errors
+\fBinfo\fR information
+\fBmap\fR map errors
+\fBstats\fR additional statistical information
+\fBuser\fR non-fatal user errors
+\fBwarn\fR warnings
+\fBwarning\fR warnings
+.fi
+
+.TP
+.BR nfs_retransmit_counter " (numeric, default=110)"
+Same as the
+.I counter
+part of the
+.BI \-t " interval.counter"
+option to amd. Specifies the retransmit counter's value in
+.I tenths
+of seconds.
+
+.TP
+.BR nfs_retry_interval " (numeric, default=8)"
+Same as the
+.I interval
+part of the
+.BI \-t " interval.counter"
+option to amd. Specifies the interval in
+.I tenths
+of seconds, between NFS/RPC/UDP retries.
+
+.TP
+.BR nis_domain " (string, default to local NIS domain name)"
+Same as the
+.B \-y
+option to amd. Specify an alternative NIS domain from which to fetch the
+NIS maps. The default is the system domain name. This option is ignored if
+NIS support is not available.
+
+.TP
+.BR normalize_hostnames " (boolean, default=no)"
+Same as the
+.B \-n
+option to amd. If "yes", then the name refereed to by ${rhost} is
+normalized relative to the host database before being used. The effect is
+to translate aliases into ``official'' names.
+
+.TP
+.BR os " (string, default to compiled in value)"
+Same as the
+.B \-O
+option to amd. Allows you to override the compiled-in name of the operating
+system. Useful when the built-in name is not desired for backward
+compatibility reasons. For example, if the build in name is ``sunos5'', you
+can override it to ``sos5'', and use older maps which were written with the
+latter in mind.
+
+.TP
+.BR osver " (string, default to compiled in value)"
+Same as the
+.B \-o
+option to amd. Override the compiled-in version number of the operating
+system. Useful when the built in version is not desired for backward
+compatibility reasons. For example, if the build in version is ``2.5.1'',
+you can override it to ``5.5.1'', and use older maps that were written with
+the latter in mind.
+
+.TP
+.BR pid_file " (string, default=/dev/stdout)"
+Specify a file to store the process ID of the running daemon into. If not
+specified, amd will print its process id onto the standard output. Useful
+for killing amd after it had run. Note that the PID of a running amd can
+also be retrieved via
+.B amq
+.BR \-p .
+This file is used only if the print_pid option is on.
+
+.TP
+.BR plock " (boolean, default=yes)"
+Same as the
+.B \-S
+option to amd.
+If "yes", lock the running executable pages of amd into memory. To improve
+amd's performance, systems that support the
+.BR plock (3)
+call can lock the amd process into memory. This way there is less chance it
+the operating system will schedule, page out, and swap the amd process as
+needed. This improves amd's performance, at the cost of reserving the
+memory used by the amd process (making it unavailable for other processes).
+
+.TP
+.BR portmap_program " (numeric, default=300019)"
+Specify an alternate Port-mapper RPC program number, other than the official
+number. This is useful when running multiple amd processes. For example,
+you can run another amd in "test" mode, without affecting the primary amd
+process in any way. For safety reasons, the alternate program numbers that
+can be specified must be in the range 300019-300029, inclusive.
+.B amq
+has an option
+.B -P
+which can be used to specify an alternate program number of an amd to
+contact. In this way, amq can fully control any number of amd processes
+running on the same host.
+
+.TP
+.BR print_pid " (boolean, default=no)"
+Same as the
+.B \-p
+option to amd. If "yes", amd will print its process ID upon starting.
+
+.TP
+.BR print_version " (boolean, default=no)"
+Same as the
+.B \-v
+option to amd, but the version prints and amd continues to run. If "yes",
+amd will print its version information string, which includes some
+configuration and compilation values.
+
+.TP
+.BR restart_mounts " (boolean, default=no)"
+Same as the
+.B \-r
+option to amd. If "yes"
+.B amd
+will scan the mount table to determine which file systems are currently
+mounted. Whenever one of these would have been auto-mounted,
+.B amd
+inherits it.
+
+.TP
+.BR selectors_on_default " (boolean, default=no)"
+If "yes", then the /default entry of maps will be look for and process any
+selectors before setting defaults for all other keys in that map. Useful
+when you want to set different options for a complete map based on some
+parameters. For example, you may want to better the NFS performance over
+slow slip-based networks as follows:
+
+.nf
+/defaults \\
+ wire==slip-net;opts:=intr,rsize=1024,wsize=1024 \\
+ wire!=slip-net;opts:=intr,rsize=8192,wsize=8192
+.fi
+
+.TP
+.BR show_statfs_entries " (boolean), default=no)"
+If "yes", then all maps which are browsable will also show the number of
+entries (keys) they have when "df" runs. (This is accomplished by returning
+non-zero values to the statfs(2) system call).
+
+.TP
+.BR unmount_on_exist " (boolean), default=no)"
+If "yes", then amd will attempt to unmount all file systems which it knows
+about. Normally amd leaves all (esp. NFS) mounted file systems intact.
+Note that amd does not know about file systems mounted before it starts up,
+unless the restart_mounts option or
+.B \-r
+flag are used.
+
+.\" **************************************************************************
+.SS Parameters applicable to regular map sections
+
+.TP
+.BR map_name " (string, must be specified)"
+Name of the map where the keys are located.
+
+.TP
+.BR tag " (string, default no tag)"
+Each map entry in the configuration file can be tagged. If no tag is
+specified, that map section will always be processed by amd. If it is
+specified, then amd will process the map if the
+.B -T
+option was given to amd, and the value given to that command-line option
+matches that in the map section.
+
+.\" **************************************************************************
+.SH EXAMPLES
+Here is a real amd configuration file I use daily.
+.P
+.nf
+# GLOBAL OPTIONS SECTION
+[ global ]
+normalize_hostnames = no
+print_pid = no
+restart_mounts = yes
+auto_dir = /n
+log_file = /var/log/amd
+log_options = all
+#debug_options = all
+plock = no
+selectors_on_default = yes
+# config.guess picks up "sunos5" and I don't want to edit my maps yet
+os = sos5
+# if you print_version after setting up "os", it will show it.
+print_version = no
+map_type = file
+search_path = /etc/amdmaps:/usr/lib/amd:/usr/local/AMD/lib
+browsable_dirs = yes
+
+# DEFINE AN AMD MOUNT POINT
+[ /u ]
+map_name = amd.u
+
+[ /proj ]
+map_name = amd.proj
+
+[ /src ]
+map_name = amd.src
+
+[ /misc ]
+map_name = amd.misc
+
+[ /import ]
+map_name = amd.import
+
+[ /tftpboot/.amd ]
+tag = tftpboot
+map_name = amd.tftpboot
+.fi
+.\" **************************************************************************
+.SH "SEE ALSO"
+.BR amd (8),
+.BR amq (8),
+.BR ctl-amd (8).
+.SH AUTHORS
+Erez Zadok <ezk@cs.columbia.edu>, Department of Computer Science, Columbia
+University, New York, USA.
+.P
+Other authors and contributors to am-utils are listed in the
+.B AUTHORS
+file distributed with am-utils.
diff --git a/contrib/amd/scripts/amd2ldif.in b/contrib/amd/scripts/amd2ldif.in
new file mode 100755
index 000000000000..6d3c28a9b3bf
--- /dev/null
+++ b/contrib/amd/scripts/amd2ldif.in
@@ -0,0 +1,58 @@
+#!@PERL@
+
+$usage=<<EOU;
+Usage $0 mapname base < mapfile >mapfile.ldif
+
+mapname: name of the amd map beeing converted to ldif
+base : The LDAP search base. Do not forget the quotes!
+
+This script should/could be used in a Makefile together
+with ldif2ldbm(8C) to automagically update the ldap
+databases and restart slapd whenever a master copy of
+the maps have changed. Remember "cd /var/yp; make" ?
+EOU
+
+my $fmt = "%-12s: %s\n";
+my $tfmt = "%-15s: %s\n";
+my $mapname = $ARGV[0] or die $usage;
+my $base = $ARGV[1] or die $usage;
+$time = time();
+
+print "dn: cn=amdmap timestamp, $base\n";
+printf "$tfmt", "cn", "amdmap timestamp";
+printf "$tfmt", "objectClass", "amdmapTimestamp";
+printf "$tfmt", "amdmapTimestamp", $time;
+print "\n";
+
+my $line = "";
+my $done = 0;
+
+while (<STDIN>) {
+ chomp;
+ if (/\s*(.+)\\\s*/) {
+ if ($line) {
+ $line .= " ".$1;
+ } else {
+ $line = $1;
+ }
+ $done = 0;
+ } else {
+ s/^\s+//g;
+ $line .= $_;
+ $done = 1;
+ }
+ if ($done) {
+ my @vals = split(/\s+/,$line);
+ my $key = shift @vals;
+ my $entry;
+
+ print "dn: cn=amdmap $mapname\[$key\], $base\n";
+ printf "$fmt","cn","amdmap $mapname\[$key\]";
+ printf "$fmt","objectClass", "amdmap";
+ printf "$fmt","amdmapName", $mapname;
+ printf "$fmt","amdmapKey", $key;
+ printf "$fmt","amdmapValue", join(' ',@vals);
+ print "\n";
+ $line = ""; $done = 0;
+ }
+}
diff --git a/contrib/amd/scripts/amd2sun.in b/contrib/amd/scripts/amd2sun.in
new file mode 100755
index 000000000000..df69b09739cd
--- /dev/null
+++ b/contrib/amd/scripts/amd2sun.in
@@ -0,0 +1,51 @@
+#!@PERL@
+# convert amd maps to Sun automount maps
+# usage: amd2sun file
+#
+# Package: am-utils-6.0
+# Author: "Mark D. Baushke" <mdb@cisco.com>
+
+print "# file created by amd2sun
+#
+# DO NOT EDIT THIS FILE AT ALL
+# It is automatically generated from the amd mount map - edit that instead
+#
+";
+while (<>) {
+ print, next if /^#/;
+ chop;
+ $line = $_;
+ while ($line =~ /\\$/) {
+ chop $line;
+ $line2 = <>;
+ $line2 =~ s/^\s*//;
+ $line .= $line2;
+ chop $line;
+ }
+
+ next unless $line =~ /^([^\s]+)\s+(.*)$/;
+
+ $fs = $1; $rest=$2;
+
+ if ($fs =~ /^\/defaults/) {
+ ($defopts = $rest) =~ s/^.*[\s;]opts:=([^;\s]+)[;\s]*.*$/\1/;
+ next;
+ }
+
+ $opts=$defopts;
+
+ if ($rest =~ /opts:=([^;\s]+)[;\s]/) {
+ $opts = $1;
+ }
+
+ $opts =~ s/,ping=[-\d]+//g;
+
+ ($rhost = $rest) =~ s/^.*[\s;]rhost:=([^;\s]+)[;\s]*.*$/\1/;
+ ($rfs = $rest) =~ s/^.*[\s;]rfs:=([^;\s]+)[;\s]*.*$/\1/;
+
+ if ($rest =~ /sublink:=([^;\s]+)[;\s]/ ) {
+ $rfs .= "/$1";
+ }
+
+ print "$fs -$opts $rhost:$rfs\n";
+}
diff --git a/contrib/amd/scripts/ctl-amd.in b/contrib/amd/scripts/ctl-amd.in
new file mode 100755
index 000000000000..665a7dd89643
--- /dev/null
+++ b/contrib/amd/scripts/ctl-amd.in
@@ -0,0 +1,113 @@
+#!/bin/sh
+# control starting, stopping, or restarting amd.
+# usage: ctl-amd [start | stop | restart]
+#
+# Package: am-utils-6.0
+# Author: Erez Zadok <ezk@cs.columbia.edu>
+
+# set path
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+PATH=@sbindir@:@bindir@:/usr/ucb:/usr/bin:/bin:${PATH}
+export PATH
+
+# kill the named process(es)
+killproc()
+{
+# first try to get PID via an amq RPC
+pid=`amq -p 2>/dev/null`
+if test "$pid" != ""
+then
+ kill $pid
+ return 0
+fi
+
+# try bsd style ps
+pscmd="ps axc"
+pid=`${pscmd} 2>/dev/null | grep "$1" | sed -e 's/^ *//' -e 's/ .*//'`
+if test "$pid" != ""
+then
+ kill $pid
+ return 0
+fi
+
+# try bsd44 style ps
+pscmd="ps -x"
+pid=`${pscmd} 2>/dev/null | grep "$1" | sed -e 's/^ *//' -e 's/ .*//'`
+if test "$pid" != ""
+then
+ kill $pid
+ return 0
+fi
+
+# try svr4 style ps
+pscmd="ps -e"
+pid=`${pscmd} 2>/dev/null | grep "$1" | sed -e 's/^ *//' -e 's/ .*//'`
+if test "$pid" != ""
+then
+ kill $pid
+ return 0
+fi
+
+# failed
+return 1
+}
+
+# search for amd.conf file
+CF_FILE="${prefix}/etc/amd.conf"
+# any local copy of the conf file overrides the "global" one
+if [ -f /etc/amd.conf ]
+then
+ CF_FILE="/etc/amd.conf"
+fi
+if [ -f ${prefix}/etc/amd.conf ]
+then
+ CF_FILE="${prefix}/etc/amd.conf"
+fi
+if [ -f /etc/local/amd.conf ]
+then
+ CF_FILE="/etc/local/amd.conf"
+fi
+
+# if have the directory /tftpboot/.amd, then add a tag to include it
+CF_TAG=""
+if [ -d /tftpboot/.amd ]
+then
+ CF_TAG="-T tftpboot"
+fi
+
+case "$1" in
+'start')
+ #
+ # Start the amd automounter.
+ #
+ if [ -x @sbindir@/amd ]
+ then
+ # do not specify full path of amd so killproc() works
+ amd -F $CF_FILE $CF_TAG
+ fi
+ ;;
+
+'stop')
+ # prepend space to program name to ensure only amd process dies
+ killproc " amd"
+ ;;
+
+'restart')
+ # kill amd, wait for it to die, then restart
+ echo "killing amd..."
+ ctl-amd stop
+ wait4amd2die
+ if [ $? != 0 ]
+ then
+ echo "NOT restarting amd!"
+ else
+ echo "Restarting amd..."
+ ctl-amd start
+ fi
+ ;;
+
+*)
+ echo "Usage: @sbindir@/ctl-amd [ start | stop | restart ]"
+ ;;
+esac
diff --git a/contrib/amd/scripts/ctl-hlfsd.in b/contrib/amd/scripts/ctl-hlfsd.in
new file mode 100755
index 000000000000..a6e97f68edd9
--- /dev/null
+++ b/contrib/amd/scripts/ctl-hlfsd.in
@@ -0,0 +1,101 @@
+#!/bin/sh
+# control starting, stopping, or restarting hlfsd.
+# usage: ctl-hlfsd [start | stop | restart]
+#
+# Package: am-utils-6.0
+# Author: Erez Zadok <ezk@cs.columbia.edu>
+
+# set path
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+PATH=@sbindir@:@bindir@:/usr/ucb:/usr/bin:/bin:${PATH}
+export PATH
+
+# kill the named process(es)
+killproc()
+{
+# try bsd style ps
+pscmd="ps axc"
+pid=`${pscmd} 2>/dev/null | grep "$1" | sed -e 's/^ *//' -e 's/ .*//'`
+if test "$pid" != ""
+then
+ kill $pid
+ return 0
+fi
+
+# try bsd44 style ps
+pscmd="ps -x"
+pid=`${pscmd} 2>/dev/null | grep "$1" | sed -e 's/^ *//' -e 's/ .*//'`
+if test "$pid" != ""
+then
+ kill $pid
+ return 0
+fi
+
+# try svr4 style ps
+pscmd="ps -e"
+pid=`${pscmd} 2>/dev/null | grep "$1" | sed -e 's/^ *//' -e 's/ .*//'`
+if test "$pid" != ""
+then
+ kill $pid
+ return 0
+fi
+
+# failed
+return 1
+}
+
+# locate logs directory
+if [ -d /var/log ]; then
+ logdir="/var/log"
+else
+ logdir="/tmp"
+fi
+
+# locate the mail spool directory
+if [ -d /var/mail/. ]; then
+ maildir="/var/mail"
+ altmaildir="/var/alt_mail"
+else
+ maildir="/usr/spool/mail"
+ altmaildir="/usr/spool/alt_mail"
+fi
+
+# locate any optional password file
+if [ -f ${prefix}/etc/passwd ]; then
+ PASSWD_FILE="-P ${prefix}/etc/passwd"
+else
+ PASSWD_FILE=""
+fi
+
+case "$1" in
+'start')
+ #
+ # Start the hlfsd mail redirector service
+ #
+ if [ -x @sbindir@/hlfsd -a -h $maildir ]
+ then
+ echo @sbindir@/hlfsd ${PASSWD_FILE} -a $altmaildir -x all -D fork -l $logdir/hlfsd /mail/home .mailspool
+ @sbindir@/hlfsd ${PASSWD_FILE} -a $altmaildir -x all -D fork -l $logdir/hlfsd /mail/home .mailspool &
+ fi
+ ;;
+
+'stop')
+ # prepend space to program name to ensure only amd process dies
+ killproc " hlfsd"
+ ;;
+
+'restart')
+ # kill hlfsd, wait for it to die, then restart
+ echo "killing hlfsd..."
+ ctl-hlfsd stop
+ echo "Waiting for 10 seconds..."
+ sleep 10 # hope that's enough
+ echo "Restarting hlfsd..."
+ ctl-hlfsd start
+ ;;
+
+*)
+ echo "Usage: @sbindir@/ctl-hlfsd [ start | stop | restart ]"
+ ;;
+esac
diff --git a/contrib/amd/scripts/expn.1 b/contrib/amd/scripts/expn.1
new file mode 100644
index 000000000000..b2bd1b6f6ed3
--- /dev/null
+++ b/contrib/amd/scripts/expn.1
@@ -0,0 +1,1370 @@
+#!@PERL@
+'di ';
+'ds 00 \\"';
+'ig 00 ';
+#
+# THIS PROGRAM IS ITS OWN MANUAL PAGE. INSTALL IN man & bin.
+#
+
+# hardcoded constants, should work fine for BSD-based systems
+#require 'sys/socket.ph'; # perl 4
+use Socket; # perl 5
+$AF_INET = &AF_INET;
+$SOCK_STREAM = &SOCK_STREAM;
+$sockaddr = 'S n a4 x8';
+
+# system requirements:
+# must have 'nslookup' and 'hostname' programs.
+
+# $Header: /home/muir/bin/RCS/expn,v 3.9 1995/10/02 17:51:35 muir Exp muir $
+
+# TODO:
+# less magic should apply to command-line addresses
+# less magic should apply to local addresses
+# add magic to deal with cross-domain cnames
+
+# Checklist: (hard addresses)
+# 250 Kimmo Suominen <"|/usr/local/mh/lib/slocal -user kim"@grendel.tac.nyc.ny.us>
+# harry@hofmann.cs.Berkeley.EDU -> harry@tenet (.berkeley.edu) [dead]
+# bks@cs.berkeley.edu -> shiva.CS (.berkeley.edu) [dead]
+# dan@tc.cornell.edu -> brown@tiberius (.tc.cornell.edu)
+
+#############################################################################
+#
+# Copyright (c) 1993 David Muir Sharnoff
+# 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.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by the David Muir Sharnoff.
+# 4. The name of David Sharnoff may not be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE DAVID MUIR SHARNOFF ``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 DAVID MUIR SHARNOFF 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.
+#
+# This copyright notice derrived from material copyrighted by the Regents
+# of the University of California.
+#
+# Contributions accepted.
+#
+#############################################################################
+
+# overall structure:
+# in an effort to not trace each address individually, but rather
+# ask each server in turn a whole bunch of questions, addresses to
+# be expanded are queued up.
+#
+# This means that all accounting w.r.t. an address must be stored in
+# various arrays. Generally these arrays are indexed by the
+# string "$addr *** $server" where $addr is the address to be
+# expanded "foo" or maybe "foo@bar" and $server is the hostname
+# of the SMTP server to contact.
+#
+
+# important global variables:
+#
+# @hosts : list of servers still to be contacted
+# $server : name of the current we are currently looking at
+# @users = $users{@hosts[0]} : addresses to expand at this server
+# $u = $users[0] : the current address being expanded
+# $names{"$users[0] *** $server"} : the 'name' associated with the address
+# $mxbacktrace{"$users[0] *** $server"} : record of mx expansion
+# $mx_secondary{$server} : other mx relays at the same priority
+# $domainify_fallback{"$users[0] *** $server"} : alternative names to try
+# instead of $server if $server doesn't work
+# $temporary_redirect{"$users[0] *** $server"} : when trying alternates,
+# temporarily channel all tries along current path
+# $giveup{$server} : do not bother expanding addresses at $server
+# $verbose : -v
+# $watch : -w
+# $vw : -v or -w
+# $debug : -d
+# $valid : -a
+# $levels : -1
+# S : the socket connection to $server
+
+$have_nslookup = 1; # we have the nslookup program
+$port = 'smtp';
+$av0 = $0;
+$ENV{'PATH'} .= ":/usr/etc" unless $ENV{'PATH'} =~ m,/usr/etc,;
+$ENV{'PATH'} .= ":/usr/ucb" unless $ENV{'PATH'} =~ m,/usr/ucb,;
+select(STDERR);
+
+$0 = "$av0 - running hostname";
+chop($name = `hostname || uname -n`);
+
+$0 = "$av0 - lookup host FQDN and IP addr";
+($hostname,$aliases,$type,$len,$thisaddr) = gethostbyname($name);
+
+$0 = "$av0 - parsing args";
+$usage = "Usage: $av0 [-1avwd] user[\@host] [user2[host2] ...]";
+for $a (@ARGV) {
+ die $usage if $a eq "-";
+ while ($a =~ s/^(-.*)([1avwd])/$1/) {
+ eval '$'."flag_$2 += 1";
+ }
+ next if $a eq "-";
+ die $usage if $a =~ /^-/;
+ &expn(&parse($a,$hostname,undef,1));
+}
+$verbose = $flag_v;
+$watch = $flag_w;
+$vw = $flag_v + $flag_w;
+$debug = $flag_d;
+$valid = $flag_a;
+$levels = $flag_1;
+
+die $usage unless @hosts;
+if ($valid) {
+ if ($valid == 1) {
+ $validRequirement = 0.8;
+ } elsif ($valid == 2) {
+ $validRequirement = 1.0;
+ } elsif ($valid == 3) {
+ $validRequirement = 0.9;
+ } else {
+ $validRequirement = (1 - (1/($valid-3)));
+ print "validRequirement = $validRequirement\n" if $debug;
+ }
+}
+
+$0 = "$av0 - building local socket";
+($name,$aliases,$proto) = getprotobyname('tcp');
+($name,$aliases,$port) = getservbyname($port,'tcp')
+ unless $port =~ /^\d+/;
+$this = pack($sockaddr, &AF_INET, 0, $thisaddr);
+
+HOST:
+while (@hosts) {
+ $server = shift(@hosts);
+ @users = split(' ',$users{$server});
+ delete $users{$server};
+
+ # is this server already known to be bad?
+ $0 = "$av0 - looking up $server";
+ if ($giveup{$server}) {
+ &giveup('mx domainify',$giveup{$server});
+ next;
+ }
+
+ # do we already have an mx record for this host?
+ next HOST if &mxredirect($server,*users);
+
+ # look it up, or try for an mx.
+ $0 = "$av0 - gethostbyname($server)";
+
+ ($name,$aliases,$type,$len,$thataddr) = gethostbyname($server);
+ # if we can't get an A record, try for an MX record.
+ unless($thataddr) {
+ &mxlookup(1,$server,"$server: could not resolve name",*users);
+ next HOST;
+ }
+
+ # get a connection, or look for an mx
+ $0 = "$av0 - socket to $server";
+ $that = pack($sockaddr, &AF_INET, $port, $thataddr);
+ socket(S, &AF_INET, &SOCK_STREAM, $proto)
+ || die "socket: $!";
+ $0 = "$av0 - bind to $server";
+ bind(S, $this)
+ || die "bind $hostname,0: $!";
+ $0 = "$av0 - connect to $server";
+ print "debug = $debug server = $server\n" if $debug > 8;
+ if (! connect(S, $that) || ($debug == 10 && $server =~ /relay\d.UU.NET$/i)) {
+ $0 = "$av0 - $server: could not connect: $!\n";
+ $emsg = $!;
+ unless (&mxlookup(0,$server,"$server: could not connect: $!",*users)) {
+ &giveup('mx',"$server: Could not connect: $emsg");
+ }
+ next HOST;
+ }
+ select((select(S),$| = 1)[0]); # don't buffer output to S
+
+ # read the greeting
+ $0 = "$av0 - talking to $server";
+ &alarm("greeting with $server",'');
+ while(<S>) {
+ alarm(0);
+ print if $watch;
+ if (/^(\d+)([- ])/) {
+ if ($1 != 220) {
+ $0 = "$av0 - bad numeric response from $server";
+ &alarm("giving up after bad response from $server",'');
+ &read_response($2,$watch);
+ alarm(0);
+ print STDERR "$server: NOT 220 greeting: $_"
+ if ($debug || $vw);
+ if (&mxlookup(0,$server,"$server: did not respond with a 220 greeting",*users)) {
+ close(S);
+ next HOST;
+ }
+ }
+ last if ($2 eq " ");
+ } else {
+ $0 = "$av0 - bad response from $server";
+ print STDERR "$server: NOT 220 greeting: $_"
+ if ($debug || $vw);
+ unless (&mxlookup(0,$server,"$server: did not respond with SMTP codes",*users)) {
+ &giveup('',"$server: did not talk SMTP");
+ }
+ close(S);
+ next HOST;
+ }
+ &alarm("greeting with $server",'');
+ }
+ alarm(0);
+
+ # if this causes problems, remove it
+ $0 = "$av0 - sending helo to $server";
+ &alarm("sending helo to $server","");
+ &ps("helo $hostname");
+ while(<S>) {
+ print if $watch;
+ last if /^\d+ /;
+ }
+ alarm(0);
+
+ # try the users, one by one
+ USER:
+ while(@users) {
+ $u = shift(@users);
+ $0 = "$av0 - expanding $u [\@$server]";
+
+ # do we already have a name for this user?
+ $oldname = $names{"$u *** $server"};
+
+ print &compact($u,$server)." ->\n" if ($verbose && ! $valid);
+ if ($valid) {
+ #
+ # when running with -a, we delay taking any action
+ # on the results of our query until we have looked
+ # at the complete output. @toFinal stores expansions
+ # that will be final if we take them. @toExpn stores
+ # expnansions that are not final. @isValid keeps
+ # track of our ability to send mail to each of the
+ # expansions.
+ #
+ @isValid = ();
+ @toFinal = ();
+ @toExpn = ();
+ }
+
+# ($ecode,@expansion) = &expn_vrfy($u,$server);
+ (@foo) = &expn_vrfy($u,$server);
+ ($ecode,@expansion) = @foo;
+ if ($ecode) {
+ &giveup('',$ecode,$u);
+ last USER;
+ }
+
+ for $s (@expansion) {
+ $s =~ s/[\n\r]//g;
+ $0 = "$av0 - parsing $server: $s";
+
+ $skipwatch = $watch;
+
+ if ($s =~ /^[25]51([- ]).*<(.+)>/) {
+ print "$s" if $watch;
+ print "(pretending 250$1<$2>)" if ($debug && $watch);
+ print "\n" if $watch;
+ $s = "250$1<$2>";
+ $skipwatch = 0;
+ }
+
+ if ($s =~ /^250([- ])(.+)/) {
+ print "$s\n" if $skipwatch;
+ ($done,$addr) = ($1,$2);
+ ($newhost, $newaddr, $newname) = &parse($addr,$server,$oldname, $#expansion == 0);
+ print "($newhost, $newaddr, $newname) = &parse($addr, $server, $oldname)\n" if $debug;
+ if (! $newhost) {
+ # no expansion is possible w/o a new server to call
+ if ($valid) {
+ push(@isValid, &validAddr($newaddr));
+ push(@toFinal,$newaddr,$server,$newname);
+ } else {
+ &verbose(&final($newaddr,$server,$newname));
+ }
+ } else {
+ $newmxhost = &mx($newhost,$newaddr);
+ print "$newmxhost = &mx($newhost)\n"
+ if ($debug && $newhost ne $newmxhost);
+ $0 = "$av0 - parsing $newaddr [@$newmxhost]";
+ print "levels = $levels, level{$u *** $server} = ".$level{"$u *** $server"}."\n" if ($debug > 1);
+ # If the new server is the current one,
+ # it would have expanded things for us
+ # if it could have. Mx records must be
+ # followed to compare server names.
+ # We are also done if the recursion
+ # count has been exceeded.
+ if (&trhost($newmxhost) eq &trhost($server) || ($levels && $level{"$u *** $server"} >= $levels)) {
+ if ($valid) {
+ push(@isValid, &validAddr($newaddr));
+ push(@toFinal,$newaddr,$newmxhost,$newname);
+ } else {
+ &verbose(&final($newaddr,$newmxhost,$newname));
+ }
+ } else {
+ # more work to do...
+ if ($valid) {
+ push(@isValid, &validAddr($newaddr));
+ push(@toExpn,$newmxhost,$newaddr,$newname,$level{"$u *** $server"});
+ } else {
+ &verbose(&expn($newmxhost,$newaddr,$newname,$level{"$u *** $server"}));
+ }
+ }
+ }
+ last if ($done eq " ");
+ next;
+ }
+ # 550 is a known code... Should the be
+ # included in -a output? Might be a bug
+ # here. Does it matter? Can assume that
+ # there won't be UNKNOWN USER responses
+ # mixed with valid users?
+ if ($s =~ /^(550)([- ])/) {
+ if ($valid) {
+ print STDERR "\@$server:$u ($oldname) USER UNKNOWN\n";
+ } else {
+ &verbose(&final($u,$server,$oldname,"USER UNKNOWN"));
+ }
+ last if ($2 eq " ");
+ next;
+ }
+ # 553 is a known code...
+ if ($s =~ /^(553)([- ])/) {
+ if ($valid) {
+ print STDERR "\@$server:$u ($oldname) USER AMBIGUOUS\n";
+ } else {
+ &verbose(&final($u,$server,$oldname,"USER AMBIGUOUS"));
+ }
+ last if ($2 eq " ");
+ next;
+ }
+ # 252 is a known code...
+ if ($s =~ /^(252)([- ])/) {
+ if ($valid) {
+ print STDERR "\@$server:$u ($oldname) REFUSED TO VRFY\n";
+ } else {
+ &verbose(&final($u,$server,$oldname,"REFUSED TO VRFY"));
+ }
+ last if ($2 eq " ");
+ next;
+ }
+ &giveup('',"$server: did not grok '$s'",$u);
+ last USER;
+ }
+
+ if ($valid) {
+ #
+ # now we decide if we are going to take these
+ # expansions or roll them back.
+ #
+ $avgValid = &average(@isValid);
+ print "avgValid = $avgValid\n" if $debug;
+ if ($avgValid >= $validRequirement) {
+ print &compact($u,$server)." ->\n" if $verbose;
+ while (@toExpn) {
+ &verbose(&expn(splice(@toExpn,0,4)));
+ }
+ while (@toFinal) {
+ &verbose(&final(splice(@toFinal,0,3)));
+ }
+ } else {
+ print "Tossing some valid to avoid invalid ".&compact($u,$server)."\n" if ($avgValid > 0.0 && ($vw || $debug));
+ print &compact($u,$server)." ->\n" if $verbose;
+ &verbose(&final($u,$server,$newname));
+ }
+ }
+ }
+
+ &alarm("sending 'quit' to $server",'');
+ $0 = "$av0 - sending 'quit' to $server";
+ &ps("quit");
+ while(<S>) {
+ print if $watch;
+ last if /^\d+ /;
+ }
+ close(S);
+ alarm(0);
+}
+
+$0 = "$av0 - printing final results";
+print "----------\n" if $vw;
+select(STDOUT);
+for $f (sort @final) {
+ print "$f\n";
+}
+unlink("/tmp/expn$$");
+exit(0);
+
+
+# abandon all attempts deliver to $server
+# register the current addresses as the final ones
+sub giveup
+{
+ local($redirect_okay,$reason,$user) = @_;
+ local($us,@so,$nh,@remaining_users);
+ local($pk,$file,$line);
+ ($pk, $file, $line) = caller;
+
+ $0 = "$av0 - giving up on $server: $reason";
+ #
+ # add back a user if we gave up in the middle
+ #
+ push(@users,$user) if $user;
+ #
+ # don't bother with this system anymore
+ #
+ unless ($giveup{$server}) {
+ $giveup{$server} = $reason;
+ print STDERR "$reason\n";
+ }
+ print "Giveup at $file:$line!!! redirect okay = $redirect_okay; $reason\n" if $debug;
+ #
+ # Wait!
+ # Before giving up, see if there is a chance that
+ # there is another host to redirect to!
+ # (Kids, don't do this at home! Hacking is a dangerous
+ # crime and you could end up behind bars.)
+ #
+ for $u (@users) {
+ if ($redirect_okay =~ /\bmx\b/) {
+ next if &try_fallback('mx',$u,*server,
+ *mx_secondary,
+ *already_mx_fellback);
+ }
+ if ($redirect_okay =~ /\bdomainify\b/) {
+ next if &try_fallback('domainify',$u,*server,
+ *domainify_fallback,
+ *already_domainify_fellback);
+ }
+ push(@remaining_users,$u);
+ }
+ @users = @remaining_users;
+ for $u (@users) {
+ print &compact($u,$server)." ->\n" if ($verbose && $valid && $u);
+ &verbose(&final($u,$server,$names{"$u *** $server"},$reason));
+ }
+}
+#
+# This routine is used only within &giveup. It checks to
+# see if we really have to giveup or if there is a second
+# chance because we did something before that can be
+# backtracked.
+#
+# %fallback{"$user *** $host"} tracks what is able to fallback
+# %fellback{"$user *** $host"} tracks what has fallen back
+#
+# If there is a valid backtrack, then queue up the new possibility
+#
+sub try_fallback
+{
+ local($method,$user,*host,*fall_table,*fellback) = @_;
+ local($us,$fallhost,$oldhost,$ft,$i);
+
+ if ($debug > 8) {
+ print "Fallback table $method:\n";
+ for $i (sort keys %fall_table) {
+ print "\t'$i'\t\t'$fall_table{$i}'\n";
+ }
+ print "Fellback table $method:\n";
+ for $i (sort keys %fellback) {
+ print "\t'$i'\t\t'$fellback{$i}'\n";
+ }
+ print "U: $user H: $host\n";
+ }
+
+ $us = "$user *** $host";
+ if (defined $fellback{$us}) {
+ #
+ # Undo a previous fallback so that we can try again
+ # Nested fallbacks are avoided because they could
+ # lead to infinite loops
+ #
+ $fallhost = $fellback{$us};
+ print "Already $method fell back from $us -> \n" if $debug;
+ $us = "$user *** $fallhost";
+ $oldhost = $fallhost;
+ } elsif (($method eq 'mx') && (defined $mxbacktrace{$us}) && (defined $mx_secondary{$mxbacktrace{$us}})) {
+ print "Fallback an MX expansion $us -> \n" if $debug;
+ $oldhost = $mxbacktrace{$us};
+ } else {
+ print "Oldhost($host, $us) = " if $debug;
+ $oldhost = $host;
+ }
+ print "$oldhost\n" if $debug;
+ if (((defined $fall_table{$us}) && ($ft = $us)) || ((defined $fall_table{$oldhost}) && ($ft = $oldhost))) {
+ print "$method Fallback = ".$fall_table{$ft}."\n" if $debug;
+ local(@so,$newhost);
+ @so = split(' ',$fall_table{$ft});
+ $newhost = shift(@so);
+ print "Falling back ($method) $us -> $newhost (from $oldhost)\n" if $debug;
+ if ($method eq 'mx') {
+ if (! defined ($mxbacktrace{"$user *** $newhost"})) {
+ if (defined $mxbacktrace{"$user *** $oldhost"}) {
+ print "resetting oldhost $oldhost to the original: " if $debug;
+ $oldhost = $mxbacktrace{"$user *** $oldhost"};
+ print "$oldhost\n" if $debug;
+ }
+ $mxbacktrace{"$user *** $newhost"} = $oldhost;
+ print "mxbacktrace $user *** $newhost -> $oldhost\n" if $debug;
+ }
+ $mx{&trhost($oldhost)} = $newhost;
+ } else {
+ $temporary_redirect{$us} = $newhost;
+ }
+ if (@so) {
+ print "Can still $method $us: @so\n" if $debug;
+ $fall_table{$ft} = join(' ',@so);
+ } else {
+ print "No more fallbacks for $us\n" if $debug;
+ delete $fall_table{$ft};
+ }
+ if (defined $create_host_backtrack{$us}) {
+ $create_host_backtrack{"$user *** $newhost"}
+ = $create_host_backtrack{$us};
+ }
+ $fellback{"$user *** $newhost"} = $oldhost;
+ &expn($newhost,$user,$names{$us},$level{$us});
+ return 1;
+ }
+ delete $temporary_redirect{$us};
+ $host = $oldhost;
+ return 0;
+}
+# return 1 if you could send mail to the address as is.
+sub validAddr
+{
+ local($addr) = @_;
+ $res = &do_validAddr($addr);
+ print "validAddr($addr) = $res\n" if $debug;
+ $res;
+}
+sub do_validAddr
+{
+ local($addr) = @_;
+ local($urx) = "[-A-Za-z_.0-9+]+";
+
+ # \u
+ return 0 if ($addr =~ /^\\/);
+ # ?@h
+ return 1 if ($addr =~ /.\@$urx$/);
+ # @h:?
+ return 1 if ($addr =~ /^\@$urx\:./);
+ # h!u
+ return 1 if ($addr =~ /^$urx!./);
+ # u
+ return 1 if ($addr =~ /^$urx$/);
+ # ?
+ print "validAddr($addr) = ???\n" if $debug;
+ return 0;
+}
+# Some systems use expn and vrfy interchangeably. Some only
+# implement one or the other. Some check expn against mailing
+# lists and vrfy against users. It doesn't appear to be
+# consistent.
+#
+# So, what do we do? We try everything!
+#
+#
+# Ranking of result codes: good: 250, 251/551, 252, 550, anything else
+#
+# Ranking of inputs: best: user@host.domain, okay: user
+#
+# Return value: $error_string, @responses_from_server
+sub expn_vrfy
+{
+ local($u,$server) = @_;
+ local(@c) = ('expn', 'vrfy');
+ local(@try_u) = $u;
+ local(@ret,$code);
+
+ if (($u =~ /(.+)@(.+)/) && (&trhost($2) eq &trhost($server))) {
+ push(@try_u,$1);
+ }
+
+ TRY:
+ for $c (@c) {
+ for $try_u (@try_u) {
+ &alarm("${c}'ing $try_u on $server",'',$u);
+ &ps("$c $try_u");
+ alarm(0);
+ $s = <S>;
+ if ($s eq '') {
+ return "$server: lost connection";
+ }
+ if ($s !~ /^(\d+)([- ])/) {
+ return "$server: garbled reply to '$c $try_u'";
+ }
+ if ($1 == 250) {
+ $code = 250;
+ @ret = ("",$s);
+ push(@ret,&read_response($2,$debug));
+ return (@ret);
+ }
+ if ($1 == 551 || $1 == 251) {
+ $code = $1;
+ @ret = ("",$s);
+ push(@ret,&read_response($2,$debug));
+ next;
+ }
+ if ($1 == 252 && ($code == 0 || $code == 550)) {
+ $code = 252;
+ @ret = ("",$s);
+ push(@ret,&read_response($2,$watch));
+ next;
+ }
+ if ($1 == 550 && $code == 0) {
+ $code = 550;
+ @ret = ("",$s);
+ push(@ret,&read_response($2,$watch));
+ next;
+ }
+ &read_response($2,$watch);
+ }
+ }
+ return "$server: expn/vrfy not implemented" unless @ret;
+ return @ret;
+}
+# sometimes the old parse routine (now parse2) didn't
+# reject funky addresses.
+sub parse
+{
+ local($oldaddr,$server,$oldname,$one_to_one) = @_;
+ local($newhost, $newaddr, $newname, $um) = &parse2($oldaddr,$server,$oldname,$one_to_one);
+ if ($newaddr =~ m,^["/],) {
+ return (undef, $oldaddr, $newname) if $valid;
+ return (undef, $um, $newname);
+ }
+ return ($newhost, $newaddr, $newname);
+}
+
+# returns ($new_smtp_server,$new_address,$new_name)
+# given a response from a SMTP server ($newaddr), the
+# current host ($server), the old "name" and a flag that
+# indicates if it is being called during the initial
+# command line parsing ($parsing_args)
+sub parse2
+{
+ local($newaddr,$context_host,$old_name,$parsing_args) = @_;
+ local(@names) = $old_name;
+ local($urx) = "[-A-Za-z_.0-9+]+";
+ local($unmangle);
+
+ #
+ # first, separate out the address part.
+ #
+
+ #
+ # [NAME] <ADDR [(NAME)]>
+ # [NAME] <[(NAME)] ADDR
+ # ADDR [(NAME)]
+ # (NAME) ADDR
+ # [(NAME)] <ADDR>
+ #
+ if ($newaddr =~ /^\<(.*)\>$/) {
+ print "<A:$1>\n" if $debug;
+ ($newaddr) = &trim($1);
+ print "na = $newaddr\n" if $debug;
+ }
+ if ($newaddr =~ /^([^\<\>]*)\<([^\<\>]*)\>([^\<\>]*)$/) {
+ # address has a < > pair in it.
+ print "N:$1 <A:$2> N:$3\n" if $debug;
+ ($newaddr) = &trim($2);
+ unshift(@names, &trim($3,$1));
+ print "na = $newaddr\n" if $debug;
+ }
+ if ($newaddr =~ /^([^\(\)]*)\(([^\(\)]*)\)([^\(\)]*)$/) {
+ # address has a ( ) pair in it.
+ print "A:$1 (N:$2) A:$3\n" if $debug;
+ unshift(@names,&trim($2));
+ local($f,$l) = (&trim($1),&trim($3));
+ if (($f && $l) || !($f || $l)) {
+ # address looks like:
+ # foo (bar) baz or (bar)
+ # not allowed!
+ print STDERR "Could not parse $newaddr\n" if $vw;
+ return(undef,$newaddr,&firstname(@names));
+ }
+ $newaddr = $f if $f;
+ $newaddr = $l if $l;
+ print "newaddr now = $newaddr\n" if $debug;
+ }
+ #
+ # @foo:bar
+ # j%k@l
+ # a@b
+ # b!a
+ # a
+ #
+ $unmangle = $newaddr;
+ if ($newaddr =~ /^\@($urx)\:(.+)$/) {
+ print "(\@:)" if $debug;
+ # this is a bit of a cheat, but it seems necessary
+ return (&domainify($1,$context_host,$2),$2,&firstname(@names),$unmangle);
+ }
+ if ($newaddr =~ /^(.+)\@($urx)$/) {
+ print "(\@)" if $debug;
+ return (&domainify($2,$context_host,$newaddr),$newaddr,&firstname(@names),$unmangle);
+ }
+ if ($parsing_args) {
+ if ($newaddr =~ /^($urx)\!(.+)$/) {
+ return (&domainify($1,$context_host,$newaddr),$newaddr,&firstname(@names),$unmangle);
+ }
+ if ($newaddr =~ /^($urx)$/) {
+ return ($context_host,$newaddr,&firstname(@names),$unmangle);
+ }
+ print STDERR "Could not parse $newaddr\n";
+ }
+ print "(?)" if $debug;
+ return(undef,$newaddr,&firstname(@names),$unmangle);
+}
+# return $u (@$server) unless $u includes reference to $server
+sub compact
+{
+ local($u, $server) = @_;
+ local($se) = $server;
+ local($sp);
+ $se =~ s/(\W)/\\$1/g;
+ $sp = " (\@$server)";
+ if ($u !~ /$se/i) {
+ return "$u$sp";
+ }
+ return $u;
+}
+# remove empty (spaces don't count) members from an array
+sub trim
+{
+ local(@v) = @_;
+ local($v,@r);
+ for $v (@v) {
+ $v =~ s/^\s+//;
+ $v =~ s/\s+$//;
+ push(@r,$v) if ($v =~ /\S/);
+ }
+ return(@r);
+}
+# using the host part of an address, and the server name, add the
+# servers' domain to the address if it doesn't already have a
+# domain. Since this sometimes fails, save a back reference so
+# it can be unrolled.
+sub domainify
+{
+ local($host,$domain_host,$u) = @_;
+ local($domain,$newhost);
+
+ # cut of trailing dots
+ $host =~ s/\.$//;
+ $domain_host =~ s/\.$//;
+
+ if ($domain_host !~ /\./) {
+ #
+ # domain host isn't, keep $host whatever it is
+ #
+ print "domainify($host,$domain_host) = $host\n" if $debug;
+ return $host;
+ }
+
+ #
+ # There are several weird situtations that need to be
+ # accounted for. They have to do with domain relay hosts.
+ #
+ # Examples:
+ # host server "right answer"
+ #
+ # shiva.cs cs.berkeley.edu shiva.cs.berkeley.edu
+ # shiva cs.berkeley.edu shiva.cs.berekley.edu
+ # cumulus reed.edu @reed.edu:cumulus.uucp
+ # tiberius tc.cornell.edu tiberius.tc.cornell.edu
+ #
+ # The first try must always be to cut the domain part out of
+ # the server and tack it onto the host.
+ #
+ # A reasonable second try is to tack the whole server part onto
+ # the host and for each possible repeated element, eliminate
+ # just that part.
+ #
+ # These extra "guesses" get put into the %domainify_fallback
+ # array. They will be used to give addresses a second chance
+ # in the &giveup routine
+ #
+
+ local(%fallback);
+
+ local($long);
+ $long = "$host $domain_host";
+ $long =~ tr/A-Z/a-z/;
+ print "long = $long\n" if $debug;
+ if ($long =~ s/^([^ ]+\.)([^ ]+) \2(\.[^ ]+\.[^ ]+)/$1$2$3/) {
+ # matches shiva.cs cs.berkeley.edu and returns shiva.cs.berkeley.edu
+ print "condensed fallback $host $domain_host -> $long\n" if $debug;
+ $fallback{$long} = 9;
+ }
+
+ local($fh);
+ $fh = $domain_host;
+ while ($fh =~ /\./) {
+ print "FALLBACK $host.$fh = 1\n" if $debug > 7;
+ $fallback{"$host.$fh"} = 1;
+ $fh =~ s/^[^\.]+\.//;
+ }
+
+ $fallback{"$host.$domain_host"} = 2;
+
+ ($domain = $domain_host) =~ s/^[^\.]+//;
+ $fallback{"$host$domain"} = 6
+ if ($domain =~ /\./);
+
+ if ($host =~ /\./) {
+ #
+ # Host is already okay, but let's look for multiple
+ # interpretations
+ #
+ print "domainify($host,$domain_host) = $host\n" if $debug;
+ delete $fallback{$host};
+ $domainify_fallback{"$u *** $host"} = join(' ',sort {$fallback{$b} <=> $fallback{$a};} keys %fallback) if %fallback;
+ return $host;
+ }
+
+ $domain = ".$domain_host"
+ if ($domain !~ /\..*\./);
+ $newhost = "$host$domain";
+
+ $create_host_backtrack{"$u *** $newhost"} = $domain_host;
+ print "domainify($host,$domain_host) = $newhost\n" if $debug;
+ delete $fallback{$newhost};
+ $domainify_fallback{"$u *** $newhost"} = join(' ',sort {$fallback{$b} <=> $fallback{$a};} keys %fallback) if %fallback;
+ if ($debug) {
+ print "fallback = ";
+ print $domainify_fallback{"$u *** $newhost"}
+ if defined($domainify_fallback{"$u *** $newhost"});
+ print "\n";
+ }
+ return $newhost;
+}
+# return the first non-empty element of an array
+sub firstname
+{
+ local(@names) = @_;
+ local($n);
+ while(@names) {
+ $n = shift(@names);
+ return $n if $n =~ /\S/;
+ }
+ return undef;
+}
+# queue up more addresses to expand
+sub expn
+{
+ local($host,$addr,$name,$level) = @_;
+ if ($host) {
+ $host = &trhost($host);
+
+ if (($debug > 3) || (defined $giveup{$host})) {
+ unshift(@hosts,$host) unless $users{$host};
+ } else {
+ push(@hosts,$host) unless $users{$host};
+ }
+ $users{$host} .= " $addr";
+ $names{"$addr *** $host"} = $name;
+ $level{"$addr *** $host"} = $level + 1;
+ print "expn($host,$addr,$name)\n" if $debug;
+ return "\t$addr\n";
+ } else {
+ return &final($addr,'NONE',$name);
+ }
+}
+# compute the numerical average value of an array
+sub average
+{
+ local(@e) = @_;
+ return 0 unless @e;
+ local($e,$sum);
+ for $e (@e) {
+ $sum += $e;
+ }
+ $sum / @e;
+}
+# print to the server (also to stdout, if -w)
+sub ps
+{
+ local($p) = @_;
+ print ">>> $p\n" if $watch;
+ print S "$p\n";
+}
+# return case-adjusted name for a host (for comparison purposes)
+sub trhost
+{
+ # treat foo.bar as an alias for Foo.BAR
+ local($host) = @_;
+ local($trhost) = $host;
+ $trhost =~ tr/A-Z/a-z/;
+ if ($trhost{$trhost}) {
+ $host = $trhost{$trhost};
+ } else {
+ $trhost{$trhost} = $host;
+ }
+ $trhost{$trhost};
+}
+# re-queue users if an mx record dictates a redirect
+# don't allow a user to be redirected more than once
+sub mxredirect
+{
+ local($server,*users) = @_;
+ local($u,$nserver,@still_there);
+
+ $nserver = &mx($server);
+
+ if (&trhost($nserver) ne &trhost($server)) {
+ $0 = "$av0 - mx redirect $server -> $nserver\n";
+ for $u (@users) {
+ if (defined $mxbacktrace{"$u *** $nserver"}) {
+ push(@still_there,$u);
+ } else {
+ $mxbacktrace{"$u *** $nserver"} = $server;
+ print "mxbacktrace{$u *** $nserver} = $server\n"
+ if ($debug > 1);
+ &expn($nserver,$u,$names{"$u *** $server"});
+ }
+ }
+ @users = @still_there;
+ if (! @users) {
+ return $nserver;
+ } else {
+ return undef;
+ }
+ }
+ return undef;
+}
+# follow mx records, return a hostname
+# also follow temporary redirections comming from &domainify and
+# &mxlookup
+sub mx
+{
+ local($h,$u) = @_;
+
+ for (;;) {
+ if (defined $mx{&trhost($h)} && $h ne $mx{&trhost($h)}) {
+ $0 = "$av0 - mx expand $h";
+ $h = $mx{&trhost($h)};
+ return $h;
+ }
+ if ($u) {
+ if (defined $temporary_redirect{"$u *** $h"}) {
+ $0 = "$av0 - internal redirect $h";
+ print "Temporary redirect taken $u *** $h -> " if $debug;
+ $h = $temporary_redirect{"$u *** $h"};
+ print "$h\n" if $debug;
+ next;
+ }
+ $htr = &trhost($h);
+ if (defined $temporary_redirect{"$u *** $htr"}) {
+ $0 = "$av0 - internal redirect $h";
+ print "temporary redirect taken $u *** $h -> " if $debug;
+ $h = $temporary_redirect{"$u *** $htr"};
+ print "$h\n" if $debug;
+ next;
+ }
+ }
+ return $h;
+ }
+}
+# look up mx records with the name server.
+# re-queue expansion requests if possible
+# optionally give up on this host.
+sub mxlookup
+{
+ local($lastchance,$server,$giveup,*users) = @_;
+ local(*T);
+ local(*NSLOOKUP);
+ local($nh, $pref,$cpref);
+ local($o0) = $0;
+ local($nserver);
+ local($name,$aliases,$type,$len,$thataddr);
+ local(%fallback);
+
+ return 1 if &mxredirect($server,*users);
+
+ if ((defined $mx{$server}) || (! $have_nslookup)) {
+ return 0 unless $lastchance;
+ &giveup('mx domainify',$giveup);
+ return 0;
+ }
+
+ $0 = "$av0 - nslookup of $server";
+ open(T,">/tmp/expn$$") || die "open > /tmp/expn$$: $!\n";
+ print T "set querytype=MX\n";
+ print T "$server\n";
+ close(T);
+ $cpref = 1.0E12;
+ undef $nserver;
+ open(NSLOOKUP,"nslookup < /tmp/expn$$ 2>&1 |") || die "open nslookup: $!";
+ while(<NSLOOKUP>) {
+ print if ($debug > 2);
+ if (/mail exchanger = ([-A-Za-z_.0-9+]+)/) {
+ $nh = $1;
+ if (/preference = (\d+)/) {
+ $pref = $1;
+ if ($pref < $cpref) {
+ $nserver = $nh;
+ $cpref = $pref;
+ } elsif ($pref) {
+ $fallback{$pref} .= " $nh";
+ }
+ }
+ }
+ if (/Non-existent domain/) {
+ #
+ # These addresss are hosed. Kaput! Dead!
+ # However, if we created the address in the
+ # first place then there is a chance of
+ # salvation.
+ #
+ 1 while(<NSLOOKUP>);
+ close(NSLOOKUP);
+ return 0 unless $lastchance;
+ &giveup('domainify',"$server: Non-existent domain",undef,1);
+ return 0;
+ }
+
+ }
+ close(NSLOOKUP);
+ unlink("/tmp/expn$$");
+ unless ($nserver) {
+ $0 = "$o0 - finished mxlookup";
+ return 0 unless $lastchance;
+ &giveup('mx domainify',"$server: Could not resolve address");
+ return 0;
+ }
+
+ # provide fallbacks in case $nserver doesn't work out
+ if (defined $fallback{$cpref}) {
+ $mx_secondary{$server} = $fallback{$cpref};
+ }
+
+ $0 = "$av0 - gethostbyname($nserver)";
+ ($name,$aliases,$type,$len,$thataddr) = gethostbyname($nserver);
+
+ unless ($thataddr) {
+ $0 = $o0;
+ return 0 unless $lastchance;
+ &giveup('mx domainify',"$nserver: could not resolve address");
+ return 0;
+ }
+ print "MX($server) = $nserver\n" if $debug;
+ print "$server -> $nserver\n" if $vw && !$debug;
+ $mx{&trhost($server)} = $nserver;
+ # redeploy the users
+ unless (&mxredirect($server,*users)) {
+ return 0 unless $lastchance;
+ &giveup('mx domainify',"$nserver: only one level of mx redirect allowed");
+ return 0;
+ }
+ $0 = "$o0 - finished mxlookup";
+ return 1;
+}
+# if mx expansion did not help to resolve an address
+# (ie: foo@bar became @baz:foo@bar, then undo the
+# expansion).
+# this is only used by &final
+sub mxunroll
+{
+ local(*host,*addr) = @_;
+ local($r) = 0;
+ print "looking for mxbacktrace{$addr *** $host}\n"
+ if ($debug > 1);
+ while (defined $mxbacktrace{"$addr *** $host"}) {
+ print "Unrolling MX expnasion: \@$host:$addr -> "
+ if ($debug || $verbose);
+ $host = $mxbacktrace{"$addr *** $host"};
+ print "\@$host:$addr\n"
+ if ($debug || $verbose);
+ $r = 1;
+ }
+ return 1 if $r;
+ $addr = "\@$host:$addr"
+ if ($host =~ /\./);
+ return 0;
+}
+# register a completed expnasion. Make the final address as
+# simple as possible.
+sub final
+{
+ local($addr,$host,$name,$error) = @_;
+ local($he);
+ local($hb,$hr);
+ local($au,$ah);
+
+ if ($error =~ /Non-existent domain/) {
+ #
+ # If we created the domain, then let's undo the
+ # damage...
+ #
+ if (defined $create_host_backtrack{"$addr *** $host"}) {
+ while (defined $create_host_backtrack{"$addr *** $host"}) {
+ print "Un&domainifying($host) = " if $debug;
+ $host = $create_host_backtrack{"$addr *** $host"};
+ print "$host\n" if $debug;
+ }
+ $error = "$host: could not locate";
+ } else {
+ #
+ # If we only want valid addresses, toss out
+ # bad host names.
+ #
+ if ($valid) {
+ print STDERR "\@$host:$addr ($name) Non-existent domain\n";
+ return "";
+ }
+ }
+ }
+
+ MXUNWIND: {
+ $0 = "$av0 - final parsing of \@$host:$addr";
+ ($he = $host) =~ s/(\W)/\\$1/g;
+ if ($addr !~ /@/) {
+ # addr does not contain any host
+ $addr = "$addr@$host";
+ } elsif ($addr !~ /$he/i) {
+ # if host part really something else, use the something
+ # else.
+ if ($addr =~ m/(.*)\@([^\@]+)$/) {
+ ($au,$ah) = ($1,$2);
+ print "au = $au ah = $ah\n" if $debug;
+ if (defined $temporary_redirect{"$addr *** $ah"}) {
+ $addr = "$au\@".$temporary_redirect{"$addr *** $ah"};
+ print "Rewrite! to $addr\n" if $debug;
+ next MXUNWIND;
+ }
+ }
+ # addr does not contain full host
+ if ($valid) {
+ if ($host =~ /^([^\.]+)(\..+)$/) {
+ # host part has a . in it - foo.bar
+ ($hb, $hr) = ($1, $2);
+ if ($addr =~ /\@([^\.\@]+)$/ && ($1 eq $hb)) {
+ # addr part has not .
+ # and matches beginning of
+ # host part -- tack on a
+ # domain name.
+ $addr .= $hr;
+ } else {
+ &mxunroll(*host,*addr)
+ && redo MXUNWIND;
+ }
+ } else {
+ &mxunroll(*host,*addr)
+ && redo MXUNWIND;
+ }
+ } else {
+ $addr = "${addr}[\@$host]"
+ if ($host =~ /\./);
+ }
+ }
+ }
+ $name = "$name " if $name;
+ $error = " $error" if $error;
+ if ($valid) {
+ push(@final,"$name<$addr>");
+ } else {
+ push(@final,"$name<$addr>$error");
+ }
+ "\t$name<$addr>$error\n";
+}
+
+sub alarm
+{
+ local($alarm_action,$alarm_redirect,$alarm_user) = @_;
+ alarm(3600);
+ $SIG{ALRM} = 'handle_alarm';
+}
+# this involves one great big ugly hack.
+# the "next HOST" unwinds the stack!
+sub handle_alarm
+{
+ &giveup($alarm_redirect,"Timed out during $alarm_action",$alarm_user);
+ next HOST;
+}
+
+# read the rest of the current smtp daemon's response (and toss it away)
+sub read_response
+{
+ local($done,$watch) = @_;
+ local(@resp);
+ print $s if $watch;
+ while(($done eq "-") && ($s = <S>) && ($s =~ /^\d+([- ])/)) {
+ print $s if $watch;
+ $done = $1;
+ push(@resp,$s);
+ }
+ return @resp;
+}
+# print args if verbose. Return them in any case
+sub verbose
+{
+ local(@tp) = @_;
+ print "@tp" if $verbose;
+}
+# to pass perl -w:
+@tp;
+$flag_a;
+$flag_d;
+$flag_1;
+%already_domainify_fellback;
+%already_mx_fellback;
+&handle_alarm;
+################### BEGIN PERL/TROFF TRANSITION
+.00 ;
+
+'di
+.nr nl 0-1
+.nr % 0
+.\\"'; __END__
+.\" ############## END PERL/TROFF TRANSITION
+.TH EXPN 1 "March 11, 1993"
+.AT 3
+.SH NAME
+expn \- recursively expand mail aliases
+.SH SYNOPSIS
+.B expn
+.RI [ -a ]
+.RI [ -v ]
+.RI [ -w ]
+.RI [ -d ]
+.RI [ -1 ]
+.IR user [@ hostname ]
+.RI [ user [@ hostname ]]...
+.SH DESCRIPTION
+.B expn
+will use the SMTP
+.B expn
+and
+.B vrfy
+commands to expand mail aliases.
+It will first look up the addresses you provide on the command line.
+If those expand into addresses on other systems, it will
+connect to the other systems and expand again. It will keep
+doing this until no further expansion is possible.
+.SH OPTIONS
+The default output of
+.B expn
+can contain many lines which are not valid
+email addresses. With the
+.I -aa
+flag, only expansions that result in legal addresses
+are used. Since many mailing lists have an illegal
+address or two, the single
+.IR -a ,
+address, flag specifies that a few illegal addresses can
+be mixed into the results. More
+.I -a
+flags vary the ratio. Read the source to track down
+the formula. With the
+.I -a
+option, you should be able to construct a new mailing
+list out of an existing one.
+.LP
+If you wish to limit the number of levels deep that
+.B expn
+will recurse as it traces addresses, use the
+.I -1
+option. For each
+.I -1
+another level will be traversed. So,
+.I -111
+will traverse no more than three levels deep.
+.LP
+The normal mode of operation for
+.B expn
+is to do all of its work silently.
+The following options make it more verbose.
+It is not necessary to make it verbose to see what it is
+doing because as it works, it changes its
+.BR argv [0]
+variable to reflect its current activity.
+To see how it is expanding things, the
+.IR -v ,
+verbose, flag will cause
+.B expn
+to show each address before
+and after translation as it works.
+The
+.IR -w ,
+watch, flag will cause
+.B expn
+to show you its conversations with the mail daemons.
+Finally, the
+.IR -d ,
+debug, flag will expose many of the inner workings so that
+it is possible to eliminate bugs.
+.SH ENVIRONMENT
+No enviroment variables are used.
+.SH FILES
+.PD 0
+.B /tmp/expn$$
+.B temporary file used as input to
+.BR nslookup .
+.SH SEE ALSO
+.BR aliases (5),
+.BR sendmail (8),
+.BR nslookup (8),
+RFC 823, and RFC 1123.
+.SH BUGS
+Not all mail daemons will implement
+.B expn
+or
+.BR vrfy .
+It is not possible to verify addresses that are served
+by such daemons.
+.LP
+When attempting to connect to a system to verify an address,
+.B expn
+only tries one IP address. Most mail daemons
+will try harder.
+.LP
+It is assumed that you are running domain names and that
+the
+.BR nslookup (8)
+program is available. If not,
+.B expn
+will not be able to verify many addresses. It will also pause
+for a long time unless you change the code where it says
+.I $have_nslookup = 1
+to read
+.I $have_nslookup =
+.IR 0 .
+.LP
+Lastly,
+.B expn
+does not handle every valid address. If you have an example,
+please submit a bug report.
+.SH CREDITS
+In 1986 or so, Jon Broome wrote a program of the same name
+that did about the same thing. It has since suffered bit rot
+and Jon Broome has dropped off the face of the earth!
+(Jon, if you are out there, drop me a line)
+.SH AVAILABILITY
+The latest version of
+.B expn
+is available through anonymous ftp at
+.IR ftp://ftp.idiom.com/pub/muir-programs/expn .
+.SH AUTHOR
+.I David Muir Sharnoff\ \ \ \ <muir@idiom.com>
diff --git a/contrib/amd/scripts/expn.in b/contrib/amd/scripts/expn.in
new file mode 100755
index 000000000000..b2bd1b6f6ed3
--- /dev/null
+++ b/contrib/amd/scripts/expn.in
@@ -0,0 +1,1370 @@
+#!@PERL@
+'di ';
+'ds 00 \\"';
+'ig 00 ';
+#
+# THIS PROGRAM IS ITS OWN MANUAL PAGE. INSTALL IN man & bin.
+#
+
+# hardcoded constants, should work fine for BSD-based systems
+#require 'sys/socket.ph'; # perl 4
+use Socket; # perl 5
+$AF_INET = &AF_INET;
+$SOCK_STREAM = &SOCK_STREAM;
+$sockaddr = 'S n a4 x8';
+
+# system requirements:
+# must have 'nslookup' and 'hostname' programs.
+
+# $Header: /home/muir/bin/RCS/expn,v 3.9 1995/10/02 17:51:35 muir Exp muir $
+
+# TODO:
+# less magic should apply to command-line addresses
+# less magic should apply to local addresses
+# add magic to deal with cross-domain cnames
+
+# Checklist: (hard addresses)
+# 250 Kimmo Suominen <"|/usr/local/mh/lib/slocal -user kim"@grendel.tac.nyc.ny.us>
+# harry@hofmann.cs.Berkeley.EDU -> harry@tenet (.berkeley.edu) [dead]
+# bks@cs.berkeley.edu -> shiva.CS (.berkeley.edu) [dead]
+# dan@tc.cornell.edu -> brown@tiberius (.tc.cornell.edu)
+
+#############################################################################
+#
+# Copyright (c) 1993 David Muir Sharnoff
+# 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.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by the David Muir Sharnoff.
+# 4. The name of David Sharnoff may not be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE DAVID MUIR SHARNOFF ``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 DAVID MUIR SHARNOFF 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.
+#
+# This copyright notice derrived from material copyrighted by the Regents
+# of the University of California.
+#
+# Contributions accepted.
+#
+#############################################################################
+
+# overall structure:
+# in an effort to not trace each address individually, but rather
+# ask each server in turn a whole bunch of questions, addresses to
+# be expanded are queued up.
+#
+# This means that all accounting w.r.t. an address must be stored in
+# various arrays. Generally these arrays are indexed by the
+# string "$addr *** $server" where $addr is the address to be
+# expanded "foo" or maybe "foo@bar" and $server is the hostname
+# of the SMTP server to contact.
+#
+
+# important global variables:
+#
+# @hosts : list of servers still to be contacted
+# $server : name of the current we are currently looking at
+# @users = $users{@hosts[0]} : addresses to expand at this server
+# $u = $users[0] : the current address being expanded
+# $names{"$users[0] *** $server"} : the 'name' associated with the address
+# $mxbacktrace{"$users[0] *** $server"} : record of mx expansion
+# $mx_secondary{$server} : other mx relays at the same priority
+# $domainify_fallback{"$users[0] *** $server"} : alternative names to try
+# instead of $server if $server doesn't work
+# $temporary_redirect{"$users[0] *** $server"} : when trying alternates,
+# temporarily channel all tries along current path
+# $giveup{$server} : do not bother expanding addresses at $server
+# $verbose : -v
+# $watch : -w
+# $vw : -v or -w
+# $debug : -d
+# $valid : -a
+# $levels : -1
+# S : the socket connection to $server
+
+$have_nslookup = 1; # we have the nslookup program
+$port = 'smtp';
+$av0 = $0;
+$ENV{'PATH'} .= ":/usr/etc" unless $ENV{'PATH'} =~ m,/usr/etc,;
+$ENV{'PATH'} .= ":/usr/ucb" unless $ENV{'PATH'} =~ m,/usr/ucb,;
+select(STDERR);
+
+$0 = "$av0 - running hostname";
+chop($name = `hostname || uname -n`);
+
+$0 = "$av0 - lookup host FQDN and IP addr";
+($hostname,$aliases,$type,$len,$thisaddr) = gethostbyname($name);
+
+$0 = "$av0 - parsing args";
+$usage = "Usage: $av0 [-1avwd] user[\@host] [user2[host2] ...]";
+for $a (@ARGV) {
+ die $usage if $a eq "-";
+ while ($a =~ s/^(-.*)([1avwd])/$1/) {
+ eval '$'."flag_$2 += 1";
+ }
+ next if $a eq "-";
+ die $usage if $a =~ /^-/;
+ &expn(&parse($a,$hostname,undef,1));
+}
+$verbose = $flag_v;
+$watch = $flag_w;
+$vw = $flag_v + $flag_w;
+$debug = $flag_d;
+$valid = $flag_a;
+$levels = $flag_1;
+
+die $usage unless @hosts;
+if ($valid) {
+ if ($valid == 1) {
+ $validRequirement = 0.8;
+ } elsif ($valid == 2) {
+ $validRequirement = 1.0;
+ } elsif ($valid == 3) {
+ $validRequirement = 0.9;
+ } else {
+ $validRequirement = (1 - (1/($valid-3)));
+ print "validRequirement = $validRequirement\n" if $debug;
+ }
+}
+
+$0 = "$av0 - building local socket";
+($name,$aliases,$proto) = getprotobyname('tcp');
+($name,$aliases,$port) = getservbyname($port,'tcp')
+ unless $port =~ /^\d+/;
+$this = pack($sockaddr, &AF_INET, 0, $thisaddr);
+
+HOST:
+while (@hosts) {
+ $server = shift(@hosts);
+ @users = split(' ',$users{$server});
+ delete $users{$server};
+
+ # is this server already known to be bad?
+ $0 = "$av0 - looking up $server";
+ if ($giveup{$server}) {
+ &giveup('mx domainify',$giveup{$server});
+ next;
+ }
+
+ # do we already have an mx record for this host?
+ next HOST if &mxredirect($server,*users);
+
+ # look it up, or try for an mx.
+ $0 = "$av0 - gethostbyname($server)";
+
+ ($name,$aliases,$type,$len,$thataddr) = gethostbyname($server);
+ # if we can't get an A record, try for an MX record.
+ unless($thataddr) {
+ &mxlookup(1,$server,"$server: could not resolve name",*users);
+ next HOST;
+ }
+
+ # get a connection, or look for an mx
+ $0 = "$av0 - socket to $server";
+ $that = pack($sockaddr, &AF_INET, $port, $thataddr);
+ socket(S, &AF_INET, &SOCK_STREAM, $proto)
+ || die "socket: $!";
+ $0 = "$av0 - bind to $server";
+ bind(S, $this)
+ || die "bind $hostname,0: $!";
+ $0 = "$av0 - connect to $server";
+ print "debug = $debug server = $server\n" if $debug > 8;
+ if (! connect(S, $that) || ($debug == 10 && $server =~ /relay\d.UU.NET$/i)) {
+ $0 = "$av0 - $server: could not connect: $!\n";
+ $emsg = $!;
+ unless (&mxlookup(0,$server,"$server: could not connect: $!",*users)) {
+ &giveup('mx',"$server: Could not connect: $emsg");
+ }
+ next HOST;
+ }
+ select((select(S),$| = 1)[0]); # don't buffer output to S
+
+ # read the greeting
+ $0 = "$av0 - talking to $server";
+ &alarm("greeting with $server",'');
+ while(<S>) {
+ alarm(0);
+ print if $watch;
+ if (/^(\d+)([- ])/) {
+ if ($1 != 220) {
+ $0 = "$av0 - bad numeric response from $server";
+ &alarm("giving up after bad response from $server",'');
+ &read_response($2,$watch);
+ alarm(0);
+ print STDERR "$server: NOT 220 greeting: $_"
+ if ($debug || $vw);
+ if (&mxlookup(0,$server,"$server: did not respond with a 220 greeting",*users)) {
+ close(S);
+ next HOST;
+ }
+ }
+ last if ($2 eq " ");
+ } else {
+ $0 = "$av0 - bad response from $server";
+ print STDERR "$server: NOT 220 greeting: $_"
+ if ($debug || $vw);
+ unless (&mxlookup(0,$server,"$server: did not respond with SMTP codes",*users)) {
+ &giveup('',"$server: did not talk SMTP");
+ }
+ close(S);
+ next HOST;
+ }
+ &alarm("greeting with $server",'');
+ }
+ alarm(0);
+
+ # if this causes problems, remove it
+ $0 = "$av0 - sending helo to $server";
+ &alarm("sending helo to $server","");
+ &ps("helo $hostname");
+ while(<S>) {
+ print if $watch;
+ last if /^\d+ /;
+ }
+ alarm(0);
+
+ # try the users, one by one
+ USER:
+ while(@users) {
+ $u = shift(@users);
+ $0 = "$av0 - expanding $u [\@$server]";
+
+ # do we already have a name for this user?
+ $oldname = $names{"$u *** $server"};
+
+ print &compact($u,$server)." ->\n" if ($verbose && ! $valid);
+ if ($valid) {
+ #
+ # when running with -a, we delay taking any action
+ # on the results of our query until we have looked
+ # at the complete output. @toFinal stores expansions
+ # that will be final if we take them. @toExpn stores
+ # expnansions that are not final. @isValid keeps
+ # track of our ability to send mail to each of the
+ # expansions.
+ #
+ @isValid = ();
+ @toFinal = ();
+ @toExpn = ();
+ }
+
+# ($ecode,@expansion) = &expn_vrfy($u,$server);
+ (@foo) = &expn_vrfy($u,$server);
+ ($ecode,@expansion) = @foo;
+ if ($ecode) {
+ &giveup('',$ecode,$u);
+ last USER;
+ }
+
+ for $s (@expansion) {
+ $s =~ s/[\n\r]//g;
+ $0 = "$av0 - parsing $server: $s";
+
+ $skipwatch = $watch;
+
+ if ($s =~ /^[25]51([- ]).*<(.+)>/) {
+ print "$s" if $watch;
+ print "(pretending 250$1<$2>)" if ($debug && $watch);
+ print "\n" if $watch;
+ $s = "250$1<$2>";
+ $skipwatch = 0;
+ }
+
+ if ($s =~ /^250([- ])(.+)/) {
+ print "$s\n" if $skipwatch;
+ ($done,$addr) = ($1,$2);
+ ($newhost, $newaddr, $newname) = &parse($addr,$server,$oldname, $#expansion == 0);
+ print "($newhost, $newaddr, $newname) = &parse($addr, $server, $oldname)\n" if $debug;
+ if (! $newhost) {
+ # no expansion is possible w/o a new server to call
+ if ($valid) {
+ push(@isValid, &validAddr($newaddr));
+ push(@toFinal,$newaddr,$server,$newname);
+ } else {
+ &verbose(&final($newaddr,$server,$newname));
+ }
+ } else {
+ $newmxhost = &mx($newhost,$newaddr);
+ print "$newmxhost = &mx($newhost)\n"
+ if ($debug && $newhost ne $newmxhost);
+ $0 = "$av0 - parsing $newaddr [@$newmxhost]";
+ print "levels = $levels, level{$u *** $server} = ".$level{"$u *** $server"}."\n" if ($debug > 1);
+ # If the new server is the current one,
+ # it would have expanded things for us
+ # if it could have. Mx records must be
+ # followed to compare server names.
+ # We are also done if the recursion
+ # count has been exceeded.
+ if (&trhost($newmxhost) eq &trhost($server) || ($levels && $level{"$u *** $server"} >= $levels)) {
+ if ($valid) {
+ push(@isValid, &validAddr($newaddr));
+ push(@toFinal,$newaddr,$newmxhost,$newname);
+ } else {
+ &verbose(&final($newaddr,$newmxhost,$newname));
+ }
+ } else {
+ # more work to do...
+ if ($valid) {
+ push(@isValid, &validAddr($newaddr));
+ push(@toExpn,$newmxhost,$newaddr,$newname,$level{"$u *** $server"});
+ } else {
+ &verbose(&expn($newmxhost,$newaddr,$newname,$level{"$u *** $server"}));
+ }
+ }
+ }
+ last if ($done eq " ");
+ next;
+ }
+ # 550 is a known code... Should the be
+ # included in -a output? Might be a bug
+ # here. Does it matter? Can assume that
+ # there won't be UNKNOWN USER responses
+ # mixed with valid users?
+ if ($s =~ /^(550)([- ])/) {
+ if ($valid) {
+ print STDERR "\@$server:$u ($oldname) USER UNKNOWN\n";
+ } else {
+ &verbose(&final($u,$server,$oldname,"USER UNKNOWN"));
+ }
+ last if ($2 eq " ");
+ next;
+ }
+ # 553 is a known code...
+ if ($s =~ /^(553)([- ])/) {
+ if ($valid) {
+ print STDERR "\@$server:$u ($oldname) USER AMBIGUOUS\n";
+ } else {
+ &verbose(&final($u,$server,$oldname,"USER AMBIGUOUS"));
+ }
+ last if ($2 eq " ");
+ next;
+ }
+ # 252 is a known code...
+ if ($s =~ /^(252)([- ])/) {
+ if ($valid) {
+ print STDERR "\@$server:$u ($oldname) REFUSED TO VRFY\n";
+ } else {
+ &verbose(&final($u,$server,$oldname,"REFUSED TO VRFY"));
+ }
+ last if ($2 eq " ");
+ next;
+ }
+ &giveup('',"$server: did not grok '$s'",$u);
+ last USER;
+ }
+
+ if ($valid) {
+ #
+ # now we decide if we are going to take these
+ # expansions or roll them back.
+ #
+ $avgValid = &average(@isValid);
+ print "avgValid = $avgValid\n" if $debug;
+ if ($avgValid >= $validRequirement) {
+ print &compact($u,$server)." ->\n" if $verbose;
+ while (@toExpn) {
+ &verbose(&expn(splice(@toExpn,0,4)));
+ }
+ while (@toFinal) {
+ &verbose(&final(splice(@toFinal,0,3)));
+ }
+ } else {
+ print "Tossing some valid to avoid invalid ".&compact($u,$server)."\n" if ($avgValid > 0.0 && ($vw || $debug));
+ print &compact($u,$server)." ->\n" if $verbose;
+ &verbose(&final($u,$server,$newname));
+ }
+ }
+ }
+
+ &alarm("sending 'quit' to $server",'');
+ $0 = "$av0 - sending 'quit' to $server";
+ &ps("quit");
+ while(<S>) {
+ print if $watch;
+ last if /^\d+ /;
+ }
+ close(S);
+ alarm(0);
+}
+
+$0 = "$av0 - printing final results";
+print "----------\n" if $vw;
+select(STDOUT);
+for $f (sort @final) {
+ print "$f\n";
+}
+unlink("/tmp/expn$$");
+exit(0);
+
+
+# abandon all attempts deliver to $server
+# register the current addresses as the final ones
+sub giveup
+{
+ local($redirect_okay,$reason,$user) = @_;
+ local($us,@so,$nh,@remaining_users);
+ local($pk,$file,$line);
+ ($pk, $file, $line) = caller;
+
+ $0 = "$av0 - giving up on $server: $reason";
+ #
+ # add back a user if we gave up in the middle
+ #
+ push(@users,$user) if $user;
+ #
+ # don't bother with this system anymore
+ #
+ unless ($giveup{$server}) {
+ $giveup{$server} = $reason;
+ print STDERR "$reason\n";
+ }
+ print "Giveup at $file:$line!!! redirect okay = $redirect_okay; $reason\n" if $debug;
+ #
+ # Wait!
+ # Before giving up, see if there is a chance that
+ # there is another host to redirect to!
+ # (Kids, don't do this at home! Hacking is a dangerous
+ # crime and you could end up behind bars.)
+ #
+ for $u (@users) {
+ if ($redirect_okay =~ /\bmx\b/) {
+ next if &try_fallback('mx',$u,*server,
+ *mx_secondary,
+ *already_mx_fellback);
+ }
+ if ($redirect_okay =~ /\bdomainify\b/) {
+ next if &try_fallback('domainify',$u,*server,
+ *domainify_fallback,
+ *already_domainify_fellback);
+ }
+ push(@remaining_users,$u);
+ }
+ @users = @remaining_users;
+ for $u (@users) {
+ print &compact($u,$server)." ->\n" if ($verbose && $valid && $u);
+ &verbose(&final($u,$server,$names{"$u *** $server"},$reason));
+ }
+}
+#
+# This routine is used only within &giveup. It checks to
+# see if we really have to giveup or if there is a second
+# chance because we did something before that can be
+# backtracked.
+#
+# %fallback{"$user *** $host"} tracks what is able to fallback
+# %fellback{"$user *** $host"} tracks what has fallen back
+#
+# If there is a valid backtrack, then queue up the new possibility
+#
+sub try_fallback
+{
+ local($method,$user,*host,*fall_table,*fellback) = @_;
+ local($us,$fallhost,$oldhost,$ft,$i);
+
+ if ($debug > 8) {
+ print "Fallback table $method:\n";
+ for $i (sort keys %fall_table) {
+ print "\t'$i'\t\t'$fall_table{$i}'\n";
+ }
+ print "Fellback table $method:\n";
+ for $i (sort keys %fellback) {
+ print "\t'$i'\t\t'$fellback{$i}'\n";
+ }
+ print "U: $user H: $host\n";
+ }
+
+ $us = "$user *** $host";
+ if (defined $fellback{$us}) {
+ #
+ # Undo a previous fallback so that we can try again
+ # Nested fallbacks are avoided because they could
+ # lead to infinite loops
+ #
+ $fallhost = $fellback{$us};
+ print "Already $method fell back from $us -> \n" if $debug;
+ $us = "$user *** $fallhost";
+ $oldhost = $fallhost;
+ } elsif (($method eq 'mx') && (defined $mxbacktrace{$us}) && (defined $mx_secondary{$mxbacktrace{$us}})) {
+ print "Fallback an MX expansion $us -> \n" if $debug;
+ $oldhost = $mxbacktrace{$us};
+ } else {
+ print "Oldhost($host, $us) = " if $debug;
+ $oldhost = $host;
+ }
+ print "$oldhost\n" if $debug;
+ if (((defined $fall_table{$us}) && ($ft = $us)) || ((defined $fall_table{$oldhost}) && ($ft = $oldhost))) {
+ print "$method Fallback = ".$fall_table{$ft}."\n" if $debug;
+ local(@so,$newhost);
+ @so = split(' ',$fall_table{$ft});
+ $newhost = shift(@so);
+ print "Falling back ($method) $us -> $newhost (from $oldhost)\n" if $debug;
+ if ($method eq 'mx') {
+ if (! defined ($mxbacktrace{"$user *** $newhost"})) {
+ if (defined $mxbacktrace{"$user *** $oldhost"}) {
+ print "resetting oldhost $oldhost to the original: " if $debug;
+ $oldhost = $mxbacktrace{"$user *** $oldhost"};
+ print "$oldhost\n" if $debug;
+ }
+ $mxbacktrace{"$user *** $newhost"} = $oldhost;
+ print "mxbacktrace $user *** $newhost -> $oldhost\n" if $debug;
+ }
+ $mx{&trhost($oldhost)} = $newhost;
+ } else {
+ $temporary_redirect{$us} = $newhost;
+ }
+ if (@so) {
+ print "Can still $method $us: @so\n" if $debug;
+ $fall_table{$ft} = join(' ',@so);
+ } else {
+ print "No more fallbacks for $us\n" if $debug;
+ delete $fall_table{$ft};
+ }
+ if (defined $create_host_backtrack{$us}) {
+ $create_host_backtrack{"$user *** $newhost"}
+ = $create_host_backtrack{$us};
+ }
+ $fellback{"$user *** $newhost"} = $oldhost;
+ &expn($newhost,$user,$names{$us},$level{$us});
+ return 1;
+ }
+ delete $temporary_redirect{$us};
+ $host = $oldhost;
+ return 0;
+}
+# return 1 if you could send mail to the address as is.
+sub validAddr
+{
+ local($addr) = @_;
+ $res = &do_validAddr($addr);
+ print "validAddr($addr) = $res\n" if $debug;
+ $res;
+}
+sub do_validAddr
+{
+ local($addr) = @_;
+ local($urx) = "[-A-Za-z_.0-9+]+";
+
+ # \u
+ return 0 if ($addr =~ /^\\/);
+ # ?@h
+ return 1 if ($addr =~ /.\@$urx$/);
+ # @h:?
+ return 1 if ($addr =~ /^\@$urx\:./);
+ # h!u
+ return 1 if ($addr =~ /^$urx!./);
+ # u
+ return 1 if ($addr =~ /^$urx$/);
+ # ?
+ print "validAddr($addr) = ???\n" if $debug;
+ return 0;
+}
+# Some systems use expn and vrfy interchangeably. Some only
+# implement one or the other. Some check expn against mailing
+# lists and vrfy against users. It doesn't appear to be
+# consistent.
+#
+# So, what do we do? We try everything!
+#
+#
+# Ranking of result codes: good: 250, 251/551, 252, 550, anything else
+#
+# Ranking of inputs: best: user@host.domain, okay: user
+#
+# Return value: $error_string, @responses_from_server
+sub expn_vrfy
+{
+ local($u,$server) = @_;
+ local(@c) = ('expn', 'vrfy');
+ local(@try_u) = $u;
+ local(@ret,$code);
+
+ if (($u =~ /(.+)@(.+)/) && (&trhost($2) eq &trhost($server))) {
+ push(@try_u,$1);
+ }
+
+ TRY:
+ for $c (@c) {
+ for $try_u (@try_u) {
+ &alarm("${c}'ing $try_u on $server",'',$u);
+ &ps("$c $try_u");
+ alarm(0);
+ $s = <S>;
+ if ($s eq '') {
+ return "$server: lost connection";
+ }
+ if ($s !~ /^(\d+)([- ])/) {
+ return "$server: garbled reply to '$c $try_u'";
+ }
+ if ($1 == 250) {
+ $code = 250;
+ @ret = ("",$s);
+ push(@ret,&read_response($2,$debug));
+ return (@ret);
+ }
+ if ($1 == 551 || $1 == 251) {
+ $code = $1;
+ @ret = ("",$s);
+ push(@ret,&read_response($2,$debug));
+ next;
+ }
+ if ($1 == 252 && ($code == 0 || $code == 550)) {
+ $code = 252;
+ @ret = ("",$s);
+ push(@ret,&read_response($2,$watch));
+ next;
+ }
+ if ($1 == 550 && $code == 0) {
+ $code = 550;
+ @ret = ("",$s);
+ push(@ret,&read_response($2,$watch));
+ next;
+ }
+ &read_response($2,$watch);
+ }
+ }
+ return "$server: expn/vrfy not implemented" unless @ret;
+ return @ret;
+}
+# sometimes the old parse routine (now parse2) didn't
+# reject funky addresses.
+sub parse
+{
+ local($oldaddr,$server,$oldname,$one_to_one) = @_;
+ local($newhost, $newaddr, $newname, $um) = &parse2($oldaddr,$server,$oldname,$one_to_one);
+ if ($newaddr =~ m,^["/],) {
+ return (undef, $oldaddr, $newname) if $valid;
+ return (undef, $um, $newname);
+ }
+ return ($newhost, $newaddr, $newname);
+}
+
+# returns ($new_smtp_server,$new_address,$new_name)
+# given a response from a SMTP server ($newaddr), the
+# current host ($server), the old "name" and a flag that
+# indicates if it is being called during the initial
+# command line parsing ($parsing_args)
+sub parse2
+{
+ local($newaddr,$context_host,$old_name,$parsing_args) = @_;
+ local(@names) = $old_name;
+ local($urx) = "[-A-Za-z_.0-9+]+";
+ local($unmangle);
+
+ #
+ # first, separate out the address part.
+ #
+
+ #
+ # [NAME] <ADDR [(NAME)]>
+ # [NAME] <[(NAME)] ADDR
+ # ADDR [(NAME)]
+ # (NAME) ADDR
+ # [(NAME)] <ADDR>
+ #
+ if ($newaddr =~ /^\<(.*)\>$/) {
+ print "<A:$1>\n" if $debug;
+ ($newaddr) = &trim($1);
+ print "na = $newaddr\n" if $debug;
+ }
+ if ($newaddr =~ /^([^\<\>]*)\<([^\<\>]*)\>([^\<\>]*)$/) {
+ # address has a < > pair in it.
+ print "N:$1 <A:$2> N:$3\n" if $debug;
+ ($newaddr) = &trim($2);
+ unshift(@names, &trim($3,$1));
+ print "na = $newaddr\n" if $debug;
+ }
+ if ($newaddr =~ /^([^\(\)]*)\(([^\(\)]*)\)([^\(\)]*)$/) {
+ # address has a ( ) pair in it.
+ print "A:$1 (N:$2) A:$3\n" if $debug;
+ unshift(@names,&trim($2));
+ local($f,$l) = (&trim($1),&trim($3));
+ if (($f && $l) || !($f || $l)) {
+ # address looks like:
+ # foo (bar) baz or (bar)
+ # not allowed!
+ print STDERR "Could not parse $newaddr\n" if $vw;
+ return(undef,$newaddr,&firstname(@names));
+ }
+ $newaddr = $f if $f;
+ $newaddr = $l if $l;
+ print "newaddr now = $newaddr\n" if $debug;
+ }
+ #
+ # @foo:bar
+ # j%k@l
+ # a@b
+ # b!a
+ # a
+ #
+ $unmangle = $newaddr;
+ if ($newaddr =~ /^\@($urx)\:(.+)$/) {
+ print "(\@:)" if $debug;
+ # this is a bit of a cheat, but it seems necessary
+ return (&domainify($1,$context_host,$2),$2,&firstname(@names),$unmangle);
+ }
+ if ($newaddr =~ /^(.+)\@($urx)$/) {
+ print "(\@)" if $debug;
+ return (&domainify($2,$context_host,$newaddr),$newaddr,&firstname(@names),$unmangle);
+ }
+ if ($parsing_args) {
+ if ($newaddr =~ /^($urx)\!(.+)$/) {
+ return (&domainify($1,$context_host,$newaddr),$newaddr,&firstname(@names),$unmangle);
+ }
+ if ($newaddr =~ /^($urx)$/) {
+ return ($context_host,$newaddr,&firstname(@names),$unmangle);
+ }
+ print STDERR "Could not parse $newaddr\n";
+ }
+ print "(?)" if $debug;
+ return(undef,$newaddr,&firstname(@names),$unmangle);
+}
+# return $u (@$server) unless $u includes reference to $server
+sub compact
+{
+ local($u, $server) = @_;
+ local($se) = $server;
+ local($sp);
+ $se =~ s/(\W)/\\$1/g;
+ $sp = " (\@$server)";
+ if ($u !~ /$se/i) {
+ return "$u$sp";
+ }
+ return $u;
+}
+# remove empty (spaces don't count) members from an array
+sub trim
+{
+ local(@v) = @_;
+ local($v,@r);
+ for $v (@v) {
+ $v =~ s/^\s+//;
+ $v =~ s/\s+$//;
+ push(@r,$v) if ($v =~ /\S/);
+ }
+ return(@r);
+}
+# using the host part of an address, and the server name, add the
+# servers' domain to the address if it doesn't already have a
+# domain. Since this sometimes fails, save a back reference so
+# it can be unrolled.
+sub domainify
+{
+ local($host,$domain_host,$u) = @_;
+ local($domain,$newhost);
+
+ # cut of trailing dots
+ $host =~ s/\.$//;
+ $domain_host =~ s/\.$//;
+
+ if ($domain_host !~ /\./) {
+ #
+ # domain host isn't, keep $host whatever it is
+ #
+ print "domainify($host,$domain_host) = $host\n" if $debug;
+ return $host;
+ }
+
+ #
+ # There are several weird situtations that need to be
+ # accounted for. They have to do with domain relay hosts.
+ #
+ # Examples:
+ # host server "right answer"
+ #
+ # shiva.cs cs.berkeley.edu shiva.cs.berkeley.edu
+ # shiva cs.berkeley.edu shiva.cs.berekley.edu
+ # cumulus reed.edu @reed.edu:cumulus.uucp
+ # tiberius tc.cornell.edu tiberius.tc.cornell.edu
+ #
+ # The first try must always be to cut the domain part out of
+ # the server and tack it onto the host.
+ #
+ # A reasonable second try is to tack the whole server part onto
+ # the host and for each possible repeated element, eliminate
+ # just that part.
+ #
+ # These extra "guesses" get put into the %domainify_fallback
+ # array. They will be used to give addresses a second chance
+ # in the &giveup routine
+ #
+
+ local(%fallback);
+
+ local($long);
+ $long = "$host $domain_host";
+ $long =~ tr/A-Z/a-z/;
+ print "long = $long\n" if $debug;
+ if ($long =~ s/^([^ ]+\.)([^ ]+) \2(\.[^ ]+\.[^ ]+)/$1$2$3/) {
+ # matches shiva.cs cs.berkeley.edu and returns shiva.cs.berkeley.edu
+ print "condensed fallback $host $domain_host -> $long\n" if $debug;
+ $fallback{$long} = 9;
+ }
+
+ local($fh);
+ $fh = $domain_host;
+ while ($fh =~ /\./) {
+ print "FALLBACK $host.$fh = 1\n" if $debug > 7;
+ $fallback{"$host.$fh"} = 1;
+ $fh =~ s/^[^\.]+\.//;
+ }
+
+ $fallback{"$host.$domain_host"} = 2;
+
+ ($domain = $domain_host) =~ s/^[^\.]+//;
+ $fallback{"$host$domain"} = 6
+ if ($domain =~ /\./);
+
+ if ($host =~ /\./) {
+ #
+ # Host is already okay, but let's look for multiple
+ # interpretations
+ #
+ print "domainify($host,$domain_host) = $host\n" if $debug;
+ delete $fallback{$host};
+ $domainify_fallback{"$u *** $host"} = join(' ',sort {$fallback{$b} <=> $fallback{$a};} keys %fallback) if %fallback;
+ return $host;
+ }
+
+ $domain = ".$domain_host"
+ if ($domain !~ /\..*\./);
+ $newhost = "$host$domain";
+
+ $create_host_backtrack{"$u *** $newhost"} = $domain_host;
+ print "domainify($host,$domain_host) = $newhost\n" if $debug;
+ delete $fallback{$newhost};
+ $domainify_fallback{"$u *** $newhost"} = join(' ',sort {$fallback{$b} <=> $fallback{$a};} keys %fallback) if %fallback;
+ if ($debug) {
+ print "fallback = ";
+ print $domainify_fallback{"$u *** $newhost"}
+ if defined($domainify_fallback{"$u *** $newhost"});
+ print "\n";
+ }
+ return $newhost;
+}
+# return the first non-empty element of an array
+sub firstname
+{
+ local(@names) = @_;
+ local($n);
+ while(@names) {
+ $n = shift(@names);
+ return $n if $n =~ /\S/;
+ }
+ return undef;
+}
+# queue up more addresses to expand
+sub expn
+{
+ local($host,$addr,$name,$level) = @_;
+ if ($host) {
+ $host = &trhost($host);
+
+ if (($debug > 3) || (defined $giveup{$host})) {
+ unshift(@hosts,$host) unless $users{$host};
+ } else {
+ push(@hosts,$host) unless $users{$host};
+ }
+ $users{$host} .= " $addr";
+ $names{"$addr *** $host"} = $name;
+ $level{"$addr *** $host"} = $level + 1;
+ print "expn($host,$addr,$name)\n" if $debug;
+ return "\t$addr\n";
+ } else {
+ return &final($addr,'NONE',$name);
+ }
+}
+# compute the numerical average value of an array
+sub average
+{
+ local(@e) = @_;
+ return 0 unless @e;
+ local($e,$sum);
+ for $e (@e) {
+ $sum += $e;
+ }
+ $sum / @e;
+}
+# print to the server (also to stdout, if -w)
+sub ps
+{
+ local($p) = @_;
+ print ">>> $p\n" if $watch;
+ print S "$p\n";
+}
+# return case-adjusted name for a host (for comparison purposes)
+sub trhost
+{
+ # treat foo.bar as an alias for Foo.BAR
+ local($host) = @_;
+ local($trhost) = $host;
+ $trhost =~ tr/A-Z/a-z/;
+ if ($trhost{$trhost}) {
+ $host = $trhost{$trhost};
+ } else {
+ $trhost{$trhost} = $host;
+ }
+ $trhost{$trhost};
+}
+# re-queue users if an mx record dictates a redirect
+# don't allow a user to be redirected more than once
+sub mxredirect
+{
+ local($server,*users) = @_;
+ local($u,$nserver,@still_there);
+
+ $nserver = &mx($server);
+
+ if (&trhost($nserver) ne &trhost($server)) {
+ $0 = "$av0 - mx redirect $server -> $nserver\n";
+ for $u (@users) {
+ if (defined $mxbacktrace{"$u *** $nserver"}) {
+ push(@still_there,$u);
+ } else {
+ $mxbacktrace{"$u *** $nserver"} = $server;
+ print "mxbacktrace{$u *** $nserver} = $server\n"
+ if ($debug > 1);
+ &expn($nserver,$u,$names{"$u *** $server"});
+ }
+ }
+ @users = @still_there;
+ if (! @users) {
+ return $nserver;
+ } else {
+ return undef;
+ }
+ }
+ return undef;
+}
+# follow mx records, return a hostname
+# also follow temporary redirections comming from &domainify and
+# &mxlookup
+sub mx
+{
+ local($h,$u) = @_;
+
+ for (;;) {
+ if (defined $mx{&trhost($h)} && $h ne $mx{&trhost($h)}) {
+ $0 = "$av0 - mx expand $h";
+ $h = $mx{&trhost($h)};
+ return $h;
+ }
+ if ($u) {
+ if (defined $temporary_redirect{"$u *** $h"}) {
+ $0 = "$av0 - internal redirect $h";
+ print "Temporary redirect taken $u *** $h -> " if $debug;
+ $h = $temporary_redirect{"$u *** $h"};
+ print "$h\n" if $debug;
+ next;
+ }
+ $htr = &trhost($h);
+ if (defined $temporary_redirect{"$u *** $htr"}) {
+ $0 = "$av0 - internal redirect $h";
+ print "temporary redirect taken $u *** $h -> " if $debug;
+ $h = $temporary_redirect{"$u *** $htr"};
+ print "$h\n" if $debug;
+ next;
+ }
+ }
+ return $h;
+ }
+}
+# look up mx records with the name server.
+# re-queue expansion requests if possible
+# optionally give up on this host.
+sub mxlookup
+{
+ local($lastchance,$server,$giveup,*users) = @_;
+ local(*T);
+ local(*NSLOOKUP);
+ local($nh, $pref,$cpref);
+ local($o0) = $0;
+ local($nserver);
+ local($name,$aliases,$type,$len,$thataddr);
+ local(%fallback);
+
+ return 1 if &mxredirect($server,*users);
+
+ if ((defined $mx{$server}) || (! $have_nslookup)) {
+ return 0 unless $lastchance;
+ &giveup('mx domainify',$giveup);
+ return 0;
+ }
+
+ $0 = "$av0 - nslookup of $server";
+ open(T,">/tmp/expn$$") || die "open > /tmp/expn$$: $!\n";
+ print T "set querytype=MX\n";
+ print T "$server\n";
+ close(T);
+ $cpref = 1.0E12;
+ undef $nserver;
+ open(NSLOOKUP,"nslookup < /tmp/expn$$ 2>&1 |") || die "open nslookup: $!";
+ while(<NSLOOKUP>) {
+ print if ($debug > 2);
+ if (/mail exchanger = ([-A-Za-z_.0-9+]+)/) {
+ $nh = $1;
+ if (/preference = (\d+)/) {
+ $pref = $1;
+ if ($pref < $cpref) {
+ $nserver = $nh;
+ $cpref = $pref;
+ } elsif ($pref) {
+ $fallback{$pref} .= " $nh";
+ }
+ }
+ }
+ if (/Non-existent domain/) {
+ #
+ # These addresss are hosed. Kaput! Dead!
+ # However, if we created the address in the
+ # first place then there is a chance of
+ # salvation.
+ #
+ 1 while(<NSLOOKUP>);
+ close(NSLOOKUP);
+ return 0 unless $lastchance;
+ &giveup('domainify',"$server: Non-existent domain",undef,1);
+ return 0;
+ }
+
+ }
+ close(NSLOOKUP);
+ unlink("/tmp/expn$$");
+ unless ($nserver) {
+ $0 = "$o0 - finished mxlookup";
+ return 0 unless $lastchance;
+ &giveup('mx domainify',"$server: Could not resolve address");
+ return 0;
+ }
+
+ # provide fallbacks in case $nserver doesn't work out
+ if (defined $fallback{$cpref}) {
+ $mx_secondary{$server} = $fallback{$cpref};
+ }
+
+ $0 = "$av0 - gethostbyname($nserver)";
+ ($name,$aliases,$type,$len,$thataddr) = gethostbyname($nserver);
+
+ unless ($thataddr) {
+ $0 = $o0;
+ return 0 unless $lastchance;
+ &giveup('mx domainify',"$nserver: could not resolve address");
+ return 0;
+ }
+ print "MX($server) = $nserver\n" if $debug;
+ print "$server -> $nserver\n" if $vw && !$debug;
+ $mx{&trhost($server)} = $nserver;
+ # redeploy the users
+ unless (&mxredirect($server,*users)) {
+ return 0 unless $lastchance;
+ &giveup('mx domainify',"$nserver: only one level of mx redirect allowed");
+ return 0;
+ }
+ $0 = "$o0 - finished mxlookup";
+ return 1;
+}
+# if mx expansion did not help to resolve an address
+# (ie: foo@bar became @baz:foo@bar, then undo the
+# expansion).
+# this is only used by &final
+sub mxunroll
+{
+ local(*host,*addr) = @_;
+ local($r) = 0;
+ print "looking for mxbacktrace{$addr *** $host}\n"
+ if ($debug > 1);
+ while (defined $mxbacktrace{"$addr *** $host"}) {
+ print "Unrolling MX expnasion: \@$host:$addr -> "
+ if ($debug || $verbose);
+ $host = $mxbacktrace{"$addr *** $host"};
+ print "\@$host:$addr\n"
+ if ($debug || $verbose);
+ $r = 1;
+ }
+ return 1 if $r;
+ $addr = "\@$host:$addr"
+ if ($host =~ /\./);
+ return 0;
+}
+# register a completed expnasion. Make the final address as
+# simple as possible.
+sub final
+{
+ local($addr,$host,$name,$error) = @_;
+ local($he);
+ local($hb,$hr);
+ local($au,$ah);
+
+ if ($error =~ /Non-existent domain/) {
+ #
+ # If we created the domain, then let's undo the
+ # damage...
+ #
+ if (defined $create_host_backtrack{"$addr *** $host"}) {
+ while (defined $create_host_backtrack{"$addr *** $host"}) {
+ print "Un&domainifying($host) = " if $debug;
+ $host = $create_host_backtrack{"$addr *** $host"};
+ print "$host\n" if $debug;
+ }
+ $error = "$host: could not locate";
+ } else {
+ #
+ # If we only want valid addresses, toss out
+ # bad host names.
+ #
+ if ($valid) {
+ print STDERR "\@$host:$addr ($name) Non-existent domain\n";
+ return "";
+ }
+ }
+ }
+
+ MXUNWIND: {
+ $0 = "$av0 - final parsing of \@$host:$addr";
+ ($he = $host) =~ s/(\W)/\\$1/g;
+ if ($addr !~ /@/) {
+ # addr does not contain any host
+ $addr = "$addr@$host";
+ } elsif ($addr !~ /$he/i) {
+ # if host part really something else, use the something
+ # else.
+ if ($addr =~ m/(.*)\@([^\@]+)$/) {
+ ($au,$ah) = ($1,$2);
+ print "au = $au ah = $ah\n" if $debug;
+ if (defined $temporary_redirect{"$addr *** $ah"}) {
+ $addr = "$au\@".$temporary_redirect{"$addr *** $ah"};
+ print "Rewrite! to $addr\n" if $debug;
+ next MXUNWIND;
+ }
+ }
+ # addr does not contain full host
+ if ($valid) {
+ if ($host =~ /^([^\.]+)(\..+)$/) {
+ # host part has a . in it - foo.bar
+ ($hb, $hr) = ($1, $2);
+ if ($addr =~ /\@([^\.\@]+)$/ && ($1 eq $hb)) {
+ # addr part has not .
+ # and matches beginning of
+ # host part -- tack on a
+ # domain name.
+ $addr .= $hr;
+ } else {
+ &mxunroll(*host,*addr)
+ && redo MXUNWIND;
+ }
+ } else {
+ &mxunroll(*host,*addr)
+ && redo MXUNWIND;
+ }
+ } else {
+ $addr = "${addr}[\@$host]"
+ if ($host =~ /\./);
+ }
+ }
+ }
+ $name = "$name " if $name;
+ $error = " $error" if $error;
+ if ($valid) {
+ push(@final,"$name<$addr>");
+ } else {
+ push(@final,"$name<$addr>$error");
+ }
+ "\t$name<$addr>$error\n";
+}
+
+sub alarm
+{
+ local($alarm_action,$alarm_redirect,$alarm_user) = @_;
+ alarm(3600);
+ $SIG{ALRM} = 'handle_alarm';
+}
+# this involves one great big ugly hack.
+# the "next HOST" unwinds the stack!
+sub handle_alarm
+{
+ &giveup($alarm_redirect,"Timed out during $alarm_action",$alarm_user);
+ next HOST;
+}
+
+# read the rest of the current smtp daemon's response (and toss it away)
+sub read_response
+{
+ local($done,$watch) = @_;
+ local(@resp);
+ print $s if $watch;
+ while(($done eq "-") && ($s = <S>) && ($s =~ /^\d+([- ])/)) {
+ print $s if $watch;
+ $done = $1;
+ push(@resp,$s);
+ }
+ return @resp;
+}
+# print args if verbose. Return them in any case
+sub verbose
+{
+ local(@tp) = @_;
+ print "@tp" if $verbose;
+}
+# to pass perl -w:
+@tp;
+$flag_a;
+$flag_d;
+$flag_1;
+%already_domainify_fellback;
+%already_mx_fellback;
+&handle_alarm;
+################### BEGIN PERL/TROFF TRANSITION
+.00 ;
+
+'di
+.nr nl 0-1
+.nr % 0
+.\\"'; __END__
+.\" ############## END PERL/TROFF TRANSITION
+.TH EXPN 1 "March 11, 1993"
+.AT 3
+.SH NAME
+expn \- recursively expand mail aliases
+.SH SYNOPSIS
+.B expn
+.RI [ -a ]
+.RI [ -v ]
+.RI [ -w ]
+.RI [ -d ]
+.RI [ -1 ]
+.IR user [@ hostname ]
+.RI [ user [@ hostname ]]...
+.SH DESCRIPTION
+.B expn
+will use the SMTP
+.B expn
+and
+.B vrfy
+commands to expand mail aliases.
+It will first look up the addresses you provide on the command line.
+If those expand into addresses on other systems, it will
+connect to the other systems and expand again. It will keep
+doing this until no further expansion is possible.
+.SH OPTIONS
+The default output of
+.B expn
+can contain many lines which are not valid
+email addresses. With the
+.I -aa
+flag, only expansions that result in legal addresses
+are used. Since many mailing lists have an illegal
+address or two, the single
+.IR -a ,
+address, flag specifies that a few illegal addresses can
+be mixed into the results. More
+.I -a
+flags vary the ratio. Read the source to track down
+the formula. With the
+.I -a
+option, you should be able to construct a new mailing
+list out of an existing one.
+.LP
+If you wish to limit the number of levels deep that
+.B expn
+will recurse as it traces addresses, use the
+.I -1
+option. For each
+.I -1
+another level will be traversed. So,
+.I -111
+will traverse no more than three levels deep.
+.LP
+The normal mode of operation for
+.B expn
+is to do all of its work silently.
+The following options make it more verbose.
+It is not necessary to make it verbose to see what it is
+doing because as it works, it changes its
+.BR argv [0]
+variable to reflect its current activity.
+To see how it is expanding things, the
+.IR -v ,
+verbose, flag will cause
+.B expn
+to show each address before
+and after translation as it works.
+The
+.IR -w ,
+watch, flag will cause
+.B expn
+to show you its conversations with the mail daemons.
+Finally, the
+.IR -d ,
+debug, flag will expose many of the inner workings so that
+it is possible to eliminate bugs.
+.SH ENVIRONMENT
+No enviroment variables are used.
+.SH FILES
+.PD 0
+.B /tmp/expn$$
+.B temporary file used as input to
+.BR nslookup .
+.SH SEE ALSO
+.BR aliases (5),
+.BR sendmail (8),
+.BR nslookup (8),
+RFC 823, and RFC 1123.
+.SH BUGS
+Not all mail daemons will implement
+.B expn
+or
+.BR vrfy .
+It is not possible to verify addresses that are served
+by such daemons.
+.LP
+When attempting to connect to a system to verify an address,
+.B expn
+only tries one IP address. Most mail daemons
+will try harder.
+.LP
+It is assumed that you are running domain names and that
+the
+.BR nslookup (8)
+program is available. If not,
+.B expn
+will not be able to verify many addresses. It will also pause
+for a long time unless you change the code where it says
+.I $have_nslookup = 1
+to read
+.I $have_nslookup =
+.IR 0 .
+.LP
+Lastly,
+.B expn
+does not handle every valid address. If you have an example,
+please submit a bug report.
+.SH CREDITS
+In 1986 or so, Jon Broome wrote a program of the same name
+that did about the same thing. It has since suffered bit rot
+and Jon Broome has dropped off the face of the earth!
+(Jon, if you are out there, drop me a line)
+.SH AVAILABILITY
+The latest version of
+.B expn
+is available through anonymous ftp at
+.IR ftp://ftp.idiom.com/pub/muir-programs/expn .
+.SH AUTHOR
+.I David Muir Sharnoff\ \ \ \ <muir@idiom.com>
diff --git a/contrib/amd/scripts/fix-amd-map.in b/contrib/amd/scripts/fix-amd-map.in
new file mode 100755
index 000000000000..6746462daa77
--- /dev/null
+++ b/contrib/amd/scripts/fix-amd-map.in
@@ -0,0 +1,52 @@
+#!@PERL@
+#
+# fix an old-syntax amd map to new one
+#
+# takes any number of files on the command line, and produces
+# a fixed map on stdout.
+#
+# Package: am-utils-6.0
+# Author: Erez Zadok <ezk@cs.columbia.edu>
+#
+
+##############################################################################
+### MAINTAINER EDITABLE SECTION
+
+# Mappings of old names to new ones:
+# Update when needed, do not forget commas but not on the last entry!
+# For your convenience, this is the complete list of all OSs that were
+# supported by amd-upl102, in their old names:
+#
+# 386bsd acis43 aix3 aoi aux bsd43 bsd44 bsdi11
+# concentrix dgux fpx4 freebsd hcx hlh42 hpux irix3 irix4 irix5 isc3
+# linux mach2 mach3 netbsd news4 next osf1 pyrOSx riscix riscos
+# rtu6 sos3 sos4 sos5 stellix svr4 u2_2 u3_0 u4_0 u4_2 u4_3 u4_4
+# umax43 utek utx32 xinu43
+#
+%mappings = (
+ "sos4", "sunos4",
+ "sos5", "sunos5",
+ "freebsd", "freebsd2"
+);
+
+##############################################################################
+### DO NOT EDIT ANYTHING BELOW
+
+# This is a trivial parser and works as follows:
+# (1) read each line
+# (2) search of regexps that start with '=', continue with a word to replace
+# and end with a non-value name (whitespace, ';', or newline
+while (<>) {
+ # skip trivial lines
+ if ($_ =~ /^$/ || $_ =~ /^#/) {
+ print;
+ next;
+ }
+ # modify the line if needed
+ foreach $m (keys %mappings) {
+ $val = $mappings{$m};
+ $_ =~ s/=$m([^a-zA-Z0-9_])/=$val$1/g;
+ }
+ # print the (possibly) modified line
+ print;
+}
diff --git a/contrib/amd/scripts/fixrmtab b/contrib/amd/scripts/fixrmtab
new file mode 100755
index 000000000000..33b7bcfdb6ce
--- /dev/null
+++ b/contrib/amd/scripts/fixrmtab
@@ -0,0 +1,24 @@
+#!/bin/sh
+#
+# Invalidate /etc/rmtab entries for hosts named.
+# Restart mountd for changes to take effect.
+#
+# usage: fixrmtab host1 host2 ...
+#
+# Package: am-utils-6.0
+# Author: Andreas Stolcke <stolcke@icsi.berkeley.edu>
+
+#set -x
+
+RMTAB=/etc/rmtab
+TMP=/tmp/rmtab.$$
+
+if [ ! -f /etc/rmtab ]; then
+ exit 0
+fi
+
+for host in $*
+do
+ sed -e '/^'$host':/s/^./#/' $RMTAB > $TMP && cp $TMP $RMTAB
+done
+rm -f $TMP
diff --git a/contrib/amd/scripts/lostaltmail.conf-sample b/contrib/amd/scripts/lostaltmail.conf-sample
new file mode 100644
index 000000000000..a20158c14f83
--- /dev/null
+++ b/contrib/amd/scripts/lostaltmail.conf-sample
@@ -0,0 +1,84 @@
+# -*- perl -*-
+##############################################################################
+# #
+# CONFIGURABLE VALUES #
+# #
+##############################################################################
+
+$MAILGRUNT="postmaster"; # To whom to send log mail if mail is prefered.
+
+$TMPDIR="/tmp/"; # Place lostmail can do its dirty work.
+
+$LOCAL_LOCK_EXT=".lock"; # Name of file local mailer uses to lock
+ # spool file. This the correct setting for
+ # /bin/mail
+
+$SYSTEM_FROM_ADDRESS="Mailer-Daemon";
+
+$MAILDIR="/var/alt_mail"; # What directory should I run out of.
+$MAILER='/usr/lib/sendmail -t'; # Which mailer should I use.
+
+$LOCALMAILJUNK='.*~|\#.*|core'; # Files name patterns that might appear in
+ # alt_mail and should be ignored. This REGEXP
+ # gets or'ed with $MAILJUNK below.
+
+$SMTPHOST='localhost'; # The name of a local host which speaks SMTP
+ # and knows *all* your aliases. You probably
+ # don't want to change this. If the machine
+ # running lost_alt mail doesn't run an SMTP,
+ # daemon then something is either wrong or you
+ # should be setting `noverify' to prevent
+ # SMTP verification.
+
+$HOSTNAME='localhost'; # Hostname to use for SMTP HELO
+
+# Subject of lost log mail message. Must define $MAILGRUNT.
+# I overwrite this variable in the subroutine Clean_up. Please make sure I
+# haven't noodle-headdly forgotten to remove that hack in the distribution!
+# No newline here please. The script will insert it for you.
+$LOG_SUBJECT="Log of lostmail resends";
+
+##############################################################################
+# #
+# DEFAULTED CONFIGURATIONS #
+# #
+##############################################################################
+
+$LOGFILE="$TMPDIR" . "lostlog";
+
+
+# MAILJUNK is a pattern of ignorable alt_mail files which are either common
+# to most platforms or actually produced by this script. You should customize
+# this REGEXP by hacking at $LOCALMAILJUNK above.
+$MAILJUNK='[a-z]\.[0-9]*|\.\.?|lost\+found';
+
+$LOCKEXT=".lostlock"; # our lock file extension. Should not need to
+ # modify
+
+$MESSAGE_DELIM="^From[^:]"; # /bin/mail message delimiter. Your milage
+ # may differ
+
+$HEADER_BODY_DELIM="\n"; # RFC 822 header-body delimiter.
+
+$RESENT_TO="Resent-To: "; #
+$RESENT_FROM="Resent-From: "; # Resent headers (RFC 822).
+$RESENT_DATE="Resent-Date: "; # You probably don't want to muck with these.
+$RESENT_INFO="X-Resent-Info: "; # (special one to alert folks about mail).
+
+
+##############################################################################
+# #
+# LOSTMAIL DEFINITIONS (DON'T TOUCH) #
+# #
+##############################################################################
+
+$FALSE=0;
+$TRUE=(! $FALSE);
+
+$OK=$TRUE;
+$ABORT_RESEND=2;
+$LOCK_RETRIES=10; # The number of seconds/retries lost mail
+ # should wait before requeing or aborting a
+ # resend.
+
+TRUE; # Ansures true return from include file.
diff --git a/contrib/amd/scripts/lostaltmail.in b/contrib/amd/scripts/lostaltmail.in
new file mode 100755
index 000000000000..5ba454c12449
--- /dev/null
+++ b/contrib/amd/scripts/lostaltmail.in
@@ -0,0 +1,648 @@
+#!@PERL@ -sw
+#
+# Package: am-utils-6.0
+# Author: James Tanis <jtt@cs.columbia.edu>
+#
+
+############################################################################
+#
+# lostaltmail -- remail files files found alt_mail (or -a argument to hlfsd) to
+# whomever should receive it. This version is for SMTP varient which
+# support VRFY as a non-expanding verifier!!! (sendmail V8 is a an
+# example).
+#
+# Usage: lostaltmail [-debug] [-nomail] [-noverify]
+#
+# GLOBAL VARIABLES (as if you care :-) )
+# Probably a very incomplete list.
+#
+# Everything in the config file for this program *and* ...
+#
+# $debug: set it from the command line with -debug. Does the obvious
+# $nomail: set it from the command line with -nomail. *Not* implied by
+# $debug
+# $currentTO: The addresss we are currently checking on. Actually this is
+# left over from an earlier version of lostaltmail and will hopefully
+# go away.
+# $noverify: set it from the address line. Avoid verification of $currentTO.
+# This should be relatively safe as long as your are willing to
+# endure bounces from mail that cannot be redelivered as opposed to
+# just getting a warning. UNTESTED (but should work).
+#
+# $logopen: state variable indicating weather the log file (should there be
+# one) is in fact open.
+#
+# @allentries: Array of all the directory entries in $MAILDIR
+# @allnames: Array of all *likely* recipients. It is created from @allentries
+# sans junk files (see $MAILJUNK and $LOCALMAILJUNK)
+# @wanderers: Array of all the files associated with a *single* address
+# which might need remailing. Should lostaltmail die unexpectedly,
+# it might leave a temporary file containing messages it was
+# currently trying to deliver. These will get picked and resent
+# later.
+#
+# VRFY: Handle onto SMTP verification channel. Not to be confused with mail
+# delivery; only verification occurs accross this handle.
+#
+############################################################################
+
+##############################################################################
+# #
+# SMTP_SEND #
+# #
+##############################################################################
+#
+# Send a message to the smtp channel. Inserts the necessary NEWLINE if it
+# does not exist;
+# I stole this from myself. It shouldn nott be printing errors to STDERR, but
+# this is a quick hack.
+#
+sub smtp_send {
+ local ($msg) = @_;
+ local ($length);
+
+ $length=length($msg);
+
+ if ( $msg !~ /^.*\n$/ ) {
+ $msg = $msg . "\n";
+ $length++;
+ }
+
+
+ if ( ! syswrite (VRFY, $msg, $length)) {
+ print STDERR "Failing SMTP write: $msg";
+ return 0;
+ }
+
+ return 1;
+}
+
+##############################################################################
+# #
+# SMTP_RECV #
+# #
+##############################################################################
+#
+# Read in lines from SMTP connection and return the final
+# Really hideous -- please excuse.
+#
+sub smtp_recv {
+ local ($line,$rin, $win, $ein, $readbuf, $ret);
+ $readbuf = "";
+
+ $rin = $win = $ein = ''; # Null fd sets,
+ vec ($rin, fileno(VRFY), 1) = 1; # Stolen straight from the example;
+ $ein = $rin | $win; # This is probably useless
+
+
+LINE_OF_INPUT:
+ while (1) { # Read in all the input
+
+ if ((select ( $rin, $win, $ein, 600.0))[0] == 0 ) {
+ print "select returned -1" if ($debug);
+ return -1; # timeout
+ }
+ sysread (VRFY, $readbuf, 1024);
+ chop ($readbuf);
+
+ foreach $line ( split('\n', $readbuf)) {
+
+ # This loop is actually needed since V8 has a multi-line greet.
+
+ ( $line =~ /^(\d\d\d).*/ && ($SMTP_retval=$1)) ||
+ warn "Badly formed reply from SMTP peer: $line\n";
+
+ # Space after return code indicates EOT
+
+ if ($line =~ /^\d\d\d /) {
+ $ret = $line; # Oddly $line is in a different context here;
+ # and thus we need to export it out of the
+ # while loop via $ret.
+ last LINE_OF_INPUT;
+ }
+ } # End of read.
+ } # End of input.
+
+ return $ret;
+}
+
+
+
+
+##############################################################################
+# #
+# LOG_INFO #
+# #
+##############################################################################
+#
+#
+# Opens appropriate logging file -- STDOUT (cron) or temp file (mail).
+#
+sub Log_info {
+ local($message) = @_;
+
+ if ( !$logopened ) {
+ if ( $MAILGRUNT eq "" || $debug) {
+ open (LOGFILE, ">-") || die "Unable to open stdout";
+ }
+ else {
+ # Snarf the log into a tmp file for final mailing to MAILGRUNT
+ $logfile = $LOGFILE . ".$$";
+ open (LOGFILE, (">". "$logfile")) || die "Unable to create log file";
+ }
+ }
+
+ $logopened=1; # Note that the log is now open
+
+ # Heart of the function.
+ print LOGFILE "$message";
+
+ print LOGFILE "\n" if ( index($message,"\n") == -1 );
+}
+
+##############################################################################
+# #
+# LOCK_FILE #
+# #
+##############################################################################
+
+#
+# Tries to grab a lock on the supplied file name.
+# Spins for a bit if it can't on the assumption that the lock will be released
+# quickly. If it times out and it's allowed to requeue, it will defer
+# until later, other wise write a message to loginfo.
+
+# If a recurring error or really unexpected situation arrises, return
+# ABORT_RESEND
+#
+# PARAMETERS
+# mailfile: path to the file to resend.
+# should_requeue: BOOLEAN - TRUE if the mailfile should be put on the
+# queue for a later retry if we can not finish
+# now.
+
+sub Lock_file {
+
+ local($mailfile,$should_requeue,$i,$new_lost_file) = @_;
+
+# We need to rename the current mailbox so that mail can loop back into it if
+# the resent mail just gets looped right back to us.
+ $new_lost_file = $mailfile . ".$$";
+
+# make a tmpfile name based on mailfile;
+ $lostlockfile = "$mailfile" . "$LOCKEXT";
+
+ if ( ! open(LOCKFILE, (">" . $lostlockfile)) ) {
+ printf(STDERR "Could not create lostlockfile for %s: %s\n", $mailfile,$!);
+ return $ABORT_RESEND;
+ }
+ close(LOCKFILE);
+
+ $maillockfile = "$mailfile" . "$LOCAL_LOCK_EXT";
+
+ for ($i=0; $i < $LOCK_RETRIES && ! link ($lostlockfile, $maillockfile);
+ $i++) {
+ sleep(1);
+ }
+
+ unlink($lostlockfile); # No matter what eliminate our cruft
+
+ if ( $i == $LOCK_RETRIES ) {
+ &Log_info("Could not grab lock on: " . "$mailfile" . " :timed out");
+ if ( $should_requeue ) {
+ &Log_info("Requeing " . "$mailfile" . " for later retry");
+ $retry_list .= " $mailfile";
+ }
+ else {
+ &Log_info("Giving up on: " . "$mailfile");
+ }
+
+ return $ABORT_RESEND;
+ }
+
+ # We created the link and therefore have the lock
+
+ if (rename ($mailfile, $new_lost_file) == 0 ){
+ # Failed to rename file -- this is serious.
+ unlink($maillockfile);
+ return $ABORT_RESEND;
+ }
+
+ unlink($maillockfile);
+ return $new_lost_file;
+
+}
+
+##############################################################################
+# #
+# PARSE NEXT MAIL MESSAGE #
+# #
+##############################################################################
+#
+# Parameters:
+# mailfile: handle of mailfile to use.
+#
+# Parses the next message in the mail file and inserts it in $current_msg
+#
+sub Get_next_msg {
+ local($mailfile,$found_body_delimiter) = @_;
+
+ # If this is the first message in the spool file, read the first line
+ # otherwise use the MESSAGE_DELIM line from the previous message (which we
+ # were forced to overread).
+
+ $done=$FALSE;
+ $found_body_delimiter=$FALSE;
+
+ # This if eats the very first "From " line and should never fire again.
+ if ( ! defined $current_msg ) {<$mailfile>};
+ undef ($current_msg); # Erase the old message.
+
+
+ # Read the mailfile and pass through all the lines up until the next
+ # message delimiter. Kill any previous resend headers.
+ while ( <$mailfile> ) {
+ last if (/$MESSAGE_DELIM/);
+ next if ( !$found_body_delimiter && /[Rr][Ee][Ss][Ee][Nn][Tt]-.+:/);
+ if ( !$found_body_delimiter && /^$HEADER_BODY_DELIM/) {
+ &Splice_in_resent_headers();
+ $found_body_delimiter=$TRUE;
+ }
+ if (defined($current_msg)) {
+ $current_msg .= $_;
+ } else {
+ $current_msg = $_;
+ }
+ }
+
+ # Return TRUE when we have hit the end of the file.
+ if (!defined($_) || $_ eq "" ) {
+ return $TRUE;
+ } else {
+ return $FALSE;
+ }
+}
+
+##############################################################################
+# #
+# SPLICE IN RESENT_HEADERS #
+# #
+##############################################################################
+#
+# Insert the Resent- headers at the *current location* of the message stream
+# (In Engish, print out a few Resent-X: lines and return :-) )
+# In addition splice in the X-resent-info: header.
+
+#
+# Paremters: None.
+# Return: None
+#
+sub Splice_in_resent_headers {
+ local($date,$utctime,$weekday,$time,$month,$hostname);
+
+ $current_msg .= "$RESENT_TO" . "$currentTO" . "\n";
+ $current_msg .= "$RESENT_FROM" . "$SYSTEM_FROM_ADDRESS" . "\n";
+
+ # Calculate date and time. It is a bit of a shame to do this each time
+ # the time needs to be acurate.
+
+ @utctime=gmtime(time);
+
+ $weekday=(Sun,Mon,Tue,Wed,Thu,Fri,Sat)[$utctime[6]];
+
+
+ # If the minutes or second do not take two columns each, patch em up.
+ if ( $utctime[1] < 10 ) {
+ if ( $utctime[0] < 10 ) {
+ $time=sprintf("%d:0%d:0%d",$utctime[2],$utctime[1],$utctime[0]);
+ }
+ else {
+ $time=sprintf("%d:0%d:%d",$utctime[2],$utctime[1],$utctime[0]);
+ }
+ }
+ else {
+ if ( $utctime[0] < 10 ) {
+ $time=sprintf("%d:%d:0%d",$utctime[2],$utctime[1],$utctime[0]);
+ }
+ else {
+ $time=sprintf("%d:%2d:%2d",$utctime[2],$utctime[1],$utctime[0]);
+ }
+ }
+
+ $month=(Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec)[$utctime[4]];
+
+ $date=sprintf("%s, %d %s %d %s UTC", $weekday, $utctime[3], $month, (($utctime[5] < 93 ? 20 : 19).$utctime[5]), $time);
+
+ $current_msg .= "$RESENT_DATE" . $date . "\n";
+
+ if ( defined $RESENT_INFO && $RESENT_INFO ne "") {
+ $hostname=`uname -n`;
+ $current_msg .= "$RESENT_INFO" . "Lost mail resent from ". $hostname;
+ }
+
+ return;
+}
+
+##############################################################################
+# #
+# DO_REMAIL #
+# #
+##############################################################################
+#
+# Actually resends the mail. Talks to the process configured as $MAILER
+# We need better handling.
+#
+sub Do_remail {
+ open (MAILER, "| $MAILER $currentTO") || return $ABORT_RESEND;
+ print MAILER $current_msg;
+ close (MAILER);
+}
+
+##############################################################################
+# #
+# CLEAN_UP #
+# #
+##############################################################################
+#
+# Clean up my messes.
+#
+sub Clean_up {
+ local ($hostname);
+
+ # Ugly local hack that you should never have seen, but I forgot to
+ # remove. Hopefully it did not kill you (I tried as you see), but you
+ # should eiter remove or update it for yourself. I find the message
+ # subject needs to have the hostname to be useful.
+ #
+ chop ($hostname=`uname -n`);
+ $LOG_SUBJECT="$LOG_SUBJECT from $hostname" if ( $hostname =~ /.*\.cs\.columbia\.edu/ );
+ #
+ # End of ugly local hack
+
+ # Mail any log info to MAILGRUNT.
+ if (defined($logfile) && $logfile ne "" ) {
+ close (LOGFILE); # Flush logfile output.
+ if ( -s $logfile ) {
+ open (MAILER, "| $MAILER $MAILGRUNT");
+
+ print MAILER "To: $MAILGRUNT\n";
+ print MAILER "Subject: $LOG_SUBJECT\n";
+ print MAILER "$HEADER_BODY_DELIM";
+
+ open (LOGFILE, "< $logfile");
+
+ while (<LOGFILE>) {
+ print MAILER $_;
+ }
+ close (MAILER);
+ close (LOGFILE);
+ }
+
+ unlink($logfile);
+ }
+ exit(0);
+}
+
+
+##############################################################################
+# #
+# COLLECT_WANDERERS #
+# #
+##############################################################################
+
+#
+# Collects other files that appear to be mail file for the $currentTO
+# but were not remailed successfully.
+#
+# Parameters: none (but uses $currentTO)
+# Return: True if a old mail directory is found. False otherwise.
+# Side effects: $wanderers set.
+#
+sub Collect_wanderers {
+
+ undef (@wanderers);
+
+ # Slurp in the directory and close.
+
+ return ($found);
+}
+
+#############################################################################
+# #
+# REMAIL ALL #
+# #
+#############################################################################
+
+#
+# Takes an array of files that all seem to share a common repcipient and
+# remails them if possible.
+#
+# Parameters: None (uses @wanderers).
+#
+sub Remail_all {
+ local($file,$i);
+
+ $i=0;
+ foreach $file (@wanderers) {
+ if ( !open (LOSTFILE, "< $file")) {
+ &Log_info("Could not open " . "$file" . " for remailing");
+ next;
+ }
+
+ do { # Power loop!
+ $done = &Get_next_msg(LOSTFILE); # Retrieve the next message...
+ &Do_remail; # and remail it.
+ } until $done;
+ undef ($current_msg); # Erase the final remailed message.
+
+ close(LOSTFILE); # Tidy up.
+
+ unlink ($file); # Remove the remailed file
+ $i++;
+ }
+
+}
+
+#############################################################################
+# #
+# CHECK_USER #
+# #
+#############################################################################
+
+#
+# Checks the password tables for the uid of $currentTO. If the user is
+# uid 0 (ie *supposed* to get mail in altmail) or unknown the resend is
+# aborted.
+#
+#
+sub Check_user {
+ local (@passwdinfo);
+ undef (@passwdinfo);
+
+ if ( !&vrfy_user($currentTO) ) {
+ &Log_info("Possible non user mail file: $currentTO");
+ return $ABORT_RESEND;
+ }
+
+ @passwdinfo = getpwnam($currentTO);
+
+ print "Non user mailable mail: Name: $currentTO\n"
+ if ( $debug && ! defined @passwdinfo );
+
+ return !$ABORT_RESEND if ( ! defined @passwdinfo ); # A non user but evidently mailable
+
+ print "Check User(): Name: $currentTO -- UID: $passwdinfo[2]\n" if ($debug);
+
+ return $ABORT_RESEND if ( $passwdinfo[2] == 0 );
+
+
+ return !$ABORT_RESEND;
+}
+
+#############################################################################
+# #
+# VRFY USER #
+# #
+#############################################################################
+#
+# Use SMTP VRFY to insure that argument is in fact a legal mail id.
+# Boolean: TRUE if mailable account, FALSE if not.
+
+sub vrfy_user {
+
+ local ($mailname,$repl) = @_;
+
+ if ( !&smtp_send("vrfy $mailname") ) {
+ &Log_info("Failed sending to vrfy smtp command for: $mailname");
+ return 0;
+ }
+
+ $repl = &smtp_recv;
+
+ print "VRFY REPLY: $repl\n" if ($debug);
+
+ return ( $repl =~ /^2\d\d/ );
+
+
+}
+
+
+#############################################################################
+# #
+# MAIN PROC #
+# #
+#############################################################################
+
+# dummy code to shut up perl -w
+$debug = 0 if !defined($debug);
+print $nomail if $debug > 1;
+print $RESENT_FROM if $debug > 1;
+print $logopen if $debug > 1;
+print $LOCAL_LOCK_EXT if $debug > 1;
+print $RESENT_TO if $debug > 1;
+print $LOCKEXT if $debug > 1;
+print $RESENT_DATE if $debug > 1;
+print $MESSAGE_DELIM if $debug > 1;
+print $SMTP_retval if $debug > 1;
+print $found if $debug > 1;
+print $retry_list if $debug > 1;
+print $MAILJUNK if $debug > 1;
+print $noverify if $debug > 1;
+print $SYSTEM_FROM_ADDRESS if $debug > 1;
+
+# BEGIN: stuff
+$prefix="@prefix@";
+$CONFIGDIR="@sysconfdir@"; # Directory where global config lives
+require "$CONFIGDIR/lostaltmail.conf" if (-f "$CONFIGDIR/lostaltmail.conf");
+require "/etc/global/lostaltmail.conf" if (-f "/etc/global/lostaltmail.conf");
+require "/etc/os/lostaltmail.conf" if (-f "/etc/os/lostaltmail.conf");
+require "/etc/local/lostaltmail.conf" if (-f "/etc/local/lostaltmail.conf");
+
+
+require "ctime.pl";
+use Socket;
+#require "sys/socket.ph";
+
+# SET some initial state variales
+$logopen = 0;
+
+#
+# Change to alt_dir
+#
+# Important!! This directory should be local. Folks will be responsible
+# for finding this out for themselves.
+#
+chdir ( $MAILDIR ) || die "Cannot change to $MAILDIR (`x' bit not set?)";
+
+#
+# slurp in directory
+#
+opendir (MAIL, ".") || die "Cannot open $MAILDIR (`r' bit not set?)";
+@allentries= readdir (MAIL);
+closedir (MAIL);
+@allnames = grep (!/$LOCALMAILJUNK|$MAILJUNK/, @allentries);
+
+# Open chanel to SMTP for verification -- unless this option is
+# configured off.
+
+if ( ! $noverify ) {
+ local($addr, $port,$sockaddr);
+
+ socket (VRFY, &AF_INET, &SOCK_STREAM, 0) ||
+ die "Could not create TCP socket (SMTP channel)";
+
+ $addr = (gethostbyname($SMTPHOST))[4]; # Just use the first addr
+
+ die "Could not obtain STMP host ($SMTPHOST) address"
+ if ( $addr eq "" );
+
+ $port = (getservbyname('smtp','tcp'))[2]; # Get smtp port.
+ die "Could not obtain SMTP port number" if (!defined($port));
+
+ printf("SMTP: address: %s port: $port\n",
+ join ('.',unpack('C4',$addr))) if ($debug);
+
+ $sockaddr = pack('n2C4x8',2, $port ,unpack('C4',$addr));
+
+ printf("Sockaddr: %s\n", join (' ',unpack('C14',$sockaddr))) if ($debug);
+
+ connect (VRFY, $sockaddr) ||
+ die "Could not connect to SMTP daemon on $SMTPHOST";
+
+ print "Establshed SMTP channel\n" if ($debug);
+
+ &smtp_recv; # Greet wait
+ &smtp_send("helo $SMTPHOST"); # Helo message for picky SMTPs
+ &smtp_recv; # Helo reply
+
+ # Connection is up and ready to VRFY
+}
+
+# main stuff starts here
+foreach $currentTO (@allnames) {
+ next if ( &Check_user == $ABORT_RESEND);
+
+ undef (@wanderers); # Just reset this at each pass.
+ @wanderers=grep (/$currentTO\.\d+/, @allentries);
+
+ $remail_file = &Lock_file($currentTO,$FALSE); # Need to lock the spool.
+
+ next if ( $remail_file eq $ABORT_RESEND); # Could not get that lock
+
+ push (@wanderers, $remail_file); # Try to resend "old" files.
+ print "List to remail: @wanderers\n" if ($debug);
+ # check if there is something to remail
+ &Remail_all if ( defined @wanderers && !$nomail);
+}
+
+# this stuff should run at the end
+foreach $file (grep (/$LOCALMAILJUNK/,@allentries)) {
+
+ if ($debug) {
+ print "Would unlink $file\n" if ($debug);
+ } else {
+ unlink $file if (-f $file);
+ }
+
+}
+&Clean_up; # Do a clean exit.
diff --git a/contrib/amd/scripts/wait4amd.in b/contrib/amd/scripts/wait4amd.in
new file mode 100755
index 000000000000..5fd503075672
--- /dev/null
+++ b/contrib/amd/scripts/wait4amd.in
@@ -0,0 +1,45 @@
+#!/bin/sh
+# wait for amd to start up and then execute program
+# usage: wait4amd <hostname> [<command> [args ...]]
+# If only hostname is supplied, command defaults to rsh $hostname
+#
+# Package: am-utils-6.0
+# Author: Erez Zadok <ezk@cs.columbia.edu>
+
+#set -x
+
+if [ "X$1" = "X" ]; then
+ echo "Usage: wait4amd <hostname> [<command> [args ...]]"
+ exit 1
+else
+ hostname=$1
+ shift
+fi
+
+# set path
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+PATH=@sbindir@:@bindir@:${PATH}
+export PATH
+
+while true
+do
+ amq -h $hostname > /dev/null 2>&1
+ if [ $? != 0 ]
+ then
+ # failed
+ echo "Amd not up. Sleeping..."
+ sleep 5;
+ else
+ echo "Amd is active on host $hostname!"
+ cmd=$*
+ if [ -z "${cmd}" ]
+ then
+ cmd="rlogin $hostname"
+ fi
+ echo "Running: $cmd"
+ $cmd
+ echo "Sleep 1 second"
+ sleep 1
+ fi
+done
diff --git a/contrib/amd/scripts/wait4amd2die.in b/contrib/amd/scripts/wait4amd2die.in
new file mode 100755
index 000000000000..d3541e78672c
--- /dev/null
+++ b/contrib/amd/scripts/wait4amd2die.in
@@ -0,0 +1,49 @@
+#!/bin/sh
+# wait for amd to die on local host before returning from program.
+# Usage: wait4amd2die [delay [count]]
+# If not specified, delay=5 seconds and count=6 (total 30 seconds)
+# If at end of total delay amd is till up, return 1; else return 0.
+#
+# Package: am-utils-6.0
+# Author: Erez Zadok <ezk@cs.columbia.edu>
+
+#set -x
+
+# set path
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+PATH=@sbindir@:@bindir@:/usr/bin:/bin:${PATH}
+export PATH
+
+# how long to wait?
+if test -n "$1"
+then
+ delay=$1
+else
+ delay=5
+fi
+# how many times to delay
+if test -n "$2"
+then
+ count=$2
+else
+ count=6
+fi
+
+i=1
+maxcount=`expr $count + 1`
+while [ $i != $maxcount ]; do
+ # run amq
+ @sbindir@/amq > /dev/null 2>&1
+ if [ $? != 0 ]
+ then
+ # amq failed to run (because amd is dead)
+ echo "wait4amd2die: amd is down!"
+ exit 0
+ fi
+ echo "wait4amd2die: delay $delay sec ($i of $count)..."
+ sleep $delay
+ i=`expr $i + 1`
+done
+echo "wait4amd2die: amd is still up..."
+exit 1
diff --git a/contrib/amd/tasks b/contrib/amd/tasks
new file mode 100644
index 000000000000..a29b554cae86
--- /dev/null
+++ b/contrib/amd/tasks
@@ -0,0 +1,64 @@
+# -*- text -*-
+
+ AM-UTILS-6.0 TASKS TODO
+
+Please volunteer to do any of the following:
+
+- complete testing of untested platforms in INSTALL file
+ ncr2
+ sunos 3.5
+
+- autofs support: see README.autofs for details.
+ needs to be re-ported to solaris 2.6 (headers changed)
+
+- deal with everything that has XXX on it in the sources
+- documentation update ("XXX: FILL IN" sections).
+
+- hlfsd should be able to not use /var/alt_mail, but send code 75 back to
+sendmail (telling it to re-queue the mail and retry later).
+
+- compatibility with Sun's automount maps?
+ should be easier via the amd.conf file to specify type of map
+ perhaps done at the same time autofs support is done.
+
+- a selector ala if_exists() for networkmask(1.2.3.4/5.6.7.8), will match
+against all known IP addresses of this host.
+
+- convert to using my own rpcgen .x files for amq/amq/nfs (v2 and v3)
+
+- support multiple "fail-over" NFS mounts in Solaris 2.6.
+
+- add LSM file
+
+- use packaging info for various OSs (such as RPM, Redhat Package Format)
+
+- $mindelay and $maxdelay in milliseconds
+
+- multiple nfsl should be matched if one matched and nfs mount failed. fall
+through.
+- fall through syntax? opts:=nofail,fallthrough?
+
+- random nfs rhost:={srv1, srv2, srv3}
+
+- after cutting next release dist, check that all files are there
+
+- nfslx, same as nfsl, but using linkx
+
+- find out why this sometimes works and sometimes not:
+ mcl -rhost:=minetta host!=${rhost};type:=nfs host==${rhost}
+
+- loadable info_*, amfs_*, and ops_* modules (lazy evaluation).
+
+- hlfsd for ~root/.mailspool (getpwnam("root"))
+- hlfsd should daemonize even if -DDEBUG, then use -D nofork
+
+- fixmount should use generic code from transp/transp_{tli,sockets}.c
+
+- report netbsd/openbsd bugs in BUGS file
+
+- contribute patches to automake 1.3
+
+- reverse notion of -F and other cmd-line options, so they override the
+amd.conf file (right now amd.conf overrides cmd-line options).
+
+- y2k compliance.
diff --git a/contrib/amd/wire-test/wire-test.8 b/contrib/amd/wire-test/wire-test.8
new file mode 100644
index 000000000000..874ca921c214
--- /dev/null
+++ b/contrib/amd/wire-test/wire-test.8
@@ -0,0 +1,70 @@
+.\"
+.\" Copyright (c) 1997-1998 Erez Zadok
+.\" Copyright (c) 1990 Jan-Simon Pendry
+.\" Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+.\" Copyright (c) 1990 The Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Jan-Simon Pendry at Imperial College, London.
+.\"
+.\" 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.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgment:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+.\"
+.\" %W% (Berkeley) %G%
+.\"
+.\" $Id: fixmount.8,v 5.2.2.1 1992/02/09 15:11:15 jsp beta $
+.\"
+.TH WIRE-TEST 8L "26 Feb 1993"
+.SH NAME
+wire-test \- test your network interfaces and local IP address
+.SH SYNOPSIS
+.B wire-test
+[
+.I host
+]
+.SH DESCRIPTION
+.LP
+.B wire-test
+is used to find out what amd thinks are the first two network
+interfaces and network names/numbers used, as well as the IP address
+used for amd to NFS-mount itself.
+
+If
+.I host
+is specified, then
+.B wire-test
+will test for the working combinations of NFS protocol and version from
+the current client to the NFS server
+.I host.
+If not specified,
+.I host
+defaults to "localhost".
+
+.SH "SEE ALSO"
+.BR amd (8).
diff --git a/contrib/amd/wire-test/wire-test.c b/contrib/amd/wire-test/wire-test.c
new file mode 100644
index 000000000000..c53094b35d7e
--- /dev/null
+++ b/contrib/amd/wire-test/wire-test.c
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 1997-1998 Erez Zadok
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * %W% (Berkeley) %G%
+ *
+ * $Id: wire-test.c,v 5.2.2.2 1992/06/07 18:06:46 jsp Exp jsp $
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+
+#define STRMAX 100
+
+/* dummy variables */
+char *progname, hostname[MAXHOSTNAMELEN];
+int orig_umask, foreground, debug_flags;
+pid_t mypid;
+serv_state amd_state;
+
+int
+main(int argc, char **argv)
+{
+ char *networkName1, *networkNumber1;
+ struct in_addr myipaddr; /* (An) IP address of this host */
+ char *testhost, *proto, *tmp_buf;
+ int nv, ret;
+ struct sockaddr_in *ip;
+ struct hostent *hp = 0;
+
+ progname = argv[0];
+ mypid = getpid();
+ orig_umask = umask(0);
+
+ if (gethostname(hostname, MAXHOSTNAMELEN) < 0) {
+ perror(argv[0]);
+ exit(1);
+ }
+
+ /* get list of networks */
+ getwire(&networkName1, &networkNumber1);
+ tmp_buf = print_wires();
+ if (tmp_buf) {
+ fprintf(stderr, "%s", tmp_buf);
+ XFREE(tmp_buf);
+ }
+
+ /* also print my IP address */
+ amu_get_myaddress(&myipaddr);
+ fprintf(stderr, "My IP address is 0x%x.\n", (unsigned int) htonl(myipaddr.s_addr));
+
+ /*
+ * NFS VERSION/PROTOCOL TESTS:
+ * If argv[1] is specified perform nfs tests to that host, else use
+ * localhost.
+ */
+ if (argc > 1)
+ testhost = argv[1];
+ else
+ testhost = "localhost";
+ hp = gethostbyname(testhost);
+ if (!hp) {
+ fprintf(stderr, "NFS vers/proto failed: no such hostname \"%s\"\n", testhost);
+ exit(1);
+ }
+ ip = (struct sockaddr_in *) xmalloc(sizeof(struct sockaddr_in));
+ memset((voidp) ip, 0, sizeof(*ip));
+ ip->sin_family = AF_INET;
+ memmove((voidp) &ip->sin_addr, (voidp) hp->h_addr, sizeof(ip->sin_addr));
+ ip->sin_port = htons(NFS_PORT);
+
+ xlog_level = 0; /* turn off debugging */
+ fprintf(stderr, "NFS Version and protocol tests to host \"%s\"...\n", testhost);
+ proto = "udp";
+ for (nv=2; nv<=3; ++nv) {
+ fprintf(stderr, "\ttesting vers=%d, proto=\"%s\" -> ", nv, proto);
+ ret = get_nfs_version(testhost, ip, nv, proto);
+ if (ret == 0)
+ fprintf(stderr, "failed!\n");
+ else
+ fprintf(stderr, "found version %d.\n", ret);
+ }
+
+ proto = "tcp";
+ for (nv=2; nv<=3; ++nv) {
+ fprintf(stderr, "\ttesting vers=%d, proto=\"%s\" -> ", nv, proto);
+ ret = get_nfs_version(testhost, ip, nv, proto);
+ if (ret == 0)
+ fprintf(stderr, "failed!\n");
+ else
+ fprintf(stderr, "found version %d.\n", ret);
+ }
+
+ exit(0);
+ return 0; /* should never reach here */
+}