]> git.decadent.org.uk Git - nfs-utils.git/commitdiff
Initial revision
authorhjl <hjl>
Mon, 18 Oct 1999 23:21:12 +0000 (23:21 +0000)
committerhjl <hjl>
Mon, 18 Oct 1999 23:21:12 +0000 (23:21 +0000)
169 files changed:
ChangeLog [new file with mode: 0644]
Makefile [new file with mode: 0644]
README [new file with mode: 0644]
aclocal.m4 [new file with mode: 0644]
config.mk.in [new file with mode: 0644]
configure [new file with mode: 0755]
configure.in [new file with mode: 0644]
etc/redhat/nfs.init [new file with mode: 0755]
etc/redhat/nfslock.init [new file with mode: 0755]
install-sh [new file with mode: 0644]
linux-nfs/ChangeLog [new file with mode: 0644]
linux-nfs/INSTALL [new file with mode: 0644]
linux-nfs/KNOWNBUGS [new file with mode: 0644]
linux-nfs/NEW [new file with mode: 0644]
linux-nfs/README [new file with mode: 0644]
linux-nfs/THANKS [new file with mode: 0644]
linux-nfs/TODO [new file with mode: 0644]
nfs-utils.spec [new file with mode: 0644]
rules.mk [new file with mode: 0644]
support/Makefile [new file with mode: 0644]
support/export/Makefile [new file with mode: 0644]
support/export/client.c [new file with mode: 0644]
support/export/export.c [new file with mode: 0644]
support/export/hostname.c [new file with mode: 0644]
support/export/keys.c [new file with mode: 0644]
support/export/mount.x [new file with mode: 0644]
support/export/nfsctl.c [new file with mode: 0644]
support/export/rmtab.c [new file with mode: 0644]
support/export/xtab.c [new file with mode: 0644]
support/include/Makefile [new file with mode: 0644]
support/include/config.h.in [new file with mode: 0644]
support/include/exportfs.h [new file with mode: 0644]
support/include/misc.h [new file with mode: 0644]
support/include/nfs/debug.h [new file with mode: 0644]
support/include/nfs/export.h [new file with mode: 0644]
support/include/nfs/nfs.h [new file with mode: 0644]
support/include/nfslib.h [new file with mode: 0644]
support/include/rpcdispatch.h [new file with mode: 0644]
support/include/rpcmisc.h [new file with mode: 0644]
support/include/rpcsec.h [new file with mode: 0644]
support/include/rpcsvc/nfs_prot.h [new file with mode: 0644]
support/include/sys/fs/ext2fs.h [new file with mode: 0644]
support/include/version.h [new file with mode: 0644]
support/include/xio.h [new file with mode: 0644]
support/include/xlog.h [new file with mode: 0644]
support/include/xmalloc.h [new file with mode: 0644]
support/include/ypupdate.h [new file with mode: 0644]
support/lib/Makefile [new file with mode: 0644]
support/nfs/Makefile [new file with mode: 0644]
support/nfs/clients.c [new file with mode: 0644]
support/nfs/exports.c [new file with mode: 0644]
support/nfs/getfh.c [new file with mode: 0644]
support/nfs/keytab.c [new file with mode: 0644]
support/nfs/lockdsvc.c [new file with mode: 0644]
support/nfs/nfsclient.c [new file with mode: 0644]
support/nfs/nfsctl.c [new file with mode: 0644]
support/nfs/nfsexport.c [new file with mode: 0644]
support/nfs/nfssvc.c [new file with mode: 0644]
support/nfs/rmtab.c [new file with mode: 0644]
support/nfs/rpcdispatch.c [new file with mode: 0644]
support/nfs/rpcmisc.c [new file with mode: 0644]
support/nfs/wildmat.c [new file with mode: 0644]
support/nfs/xio.c [new file with mode: 0644]
support/nfs/xlog.c [new file with mode: 0644]
support/nfs/xmalloc.c [new file with mode: 0644]
support/nfs/ypupdate_xdr.c [new file with mode: 0644]
tools/Makefile [new file with mode: 0644]
tools/getiversion/Makefile [new file with mode: 0644]
tools/getiversion/getiversion.c [new file with mode: 0644]
tools/getkversion/Makefile [new file with mode: 0644]
tools/getkversion/getkversion.c [new file with mode: 0644]
tools/locktest/Makefile [new file with mode: 0644]
tools/locktest/testlk.c [new file with mode: 0644]
tools/nlmtest/Makefile [new file with mode: 0644]
tools/nlmtest/README [new file with mode: 0644]
tools/nlmtest/host.h [new file with mode: 0644]
tools/nlmtest/nlm_prot.x [new file with mode: 0644]
tools/nlmtest/nlmtest.c [new file with mode: 0644]
tools/rpcdebug/Makefile [new file with mode: 0644]
tools/rpcdebug/neat_idea.c [new file with mode: 0644]
tools/rpcdebug/rpcdebug.c [new file with mode: 0644]
tools/rpcgen/Makefile [new file with mode: 0644]
tools/rpcgen/README [new file with mode: 0644]
tools/rpcgen/rpc_clntout.c [new file with mode: 0644]
tools/rpcgen/rpc_cout.c [new file with mode: 0644]
tools/rpcgen/rpc_hout.c [new file with mode: 0644]
tools/rpcgen/rpc_main.c [new file with mode: 0644]
tools/rpcgen/rpc_output.h [new file with mode: 0644]
tools/rpcgen/rpc_parse.c [new file with mode: 0644]
tools/rpcgen/rpc_parse.h [new file with mode: 0644]
tools/rpcgen/rpc_sample.c [new file with mode: 0644]
tools/rpcgen/rpc_scan.c [new file with mode: 0644]
tools/rpcgen/rpc_scan.h [new file with mode: 0644]
tools/rpcgen/rpc_svcout.c [new file with mode: 0644]
tools/rpcgen/rpc_tblout.c [new file with mode: 0644]
tools/rpcgen/rpc_util.c [new file with mode: 0644]
tools/rpcgen/rpc_util.h [new file with mode: 0644]
tools/rpcgen/rpcgen.new.1 [new file with mode: 0644]
utils/Makefile [new file with mode: 0644]
utils/exportfs/Makefile [new file with mode: 0644]
utils/exportfs/exportfs.c [new file with mode: 0644]
utils/exportfs/exportfs.man [new file with mode: 0644]
utils/exportfs/exports.man [new file with mode: 0644]
utils/lockd/Makefile [new file with mode: 0644]
utils/lockd/lockd.c [new file with mode: 0644]
utils/mountd/Makefile [new file with mode: 0644]
utils/mountd/auth.c [new file with mode: 0644]
utils/mountd/mount_dispatch.c [new file with mode: 0644]
utils/mountd/mount_xdr.c [new file with mode: 0644]
utils/mountd/mountd.c [new file with mode: 0644]
utils/mountd/mountd.h [new file with mode: 0644]
utils/mountd/mountd.man [new file with mode: 0644]
utils/mountd/rmtab.c [new file with mode: 0644]
utils/nfsd/Makefile [new file with mode: 0644]
utils/nfsd/nfsd.c [new file with mode: 0644]
utils/nfsd/nfsd.man [new file with mode: 0644]
utils/nfsstat/Makefile [new file with mode: 0644]
utils/nfsstat/nfsstat.c [new file with mode: 0644]
utils/nfsstat/nfsstat.man [new file with mode: 0644]
utils/nhfsstone/DISCLAIMER [new file with mode: 0644]
utils/nhfsstone/Makefile [new file with mode: 0644]
utils/nhfsstone/README [new file with mode: 0644]
utils/nhfsstone/README.linux [new file with mode: 0644]
utils/nhfsstone/nhfsgraph [new file with mode: 0755]
utils/nhfsstone/nhfsnums [new file with mode: 0755]
utils/nhfsstone/nhfsrun [new file with mode: 0755]
utils/nhfsstone/nhfsstone.1 [new file with mode: 0644]
utils/nhfsstone/nhfsstone.c [new file with mode: 0644]
utils/rquotad/Makefile [new file with mode: 0644]
utils/rquotad/NEW [new file with mode: 0644]
utils/rquotad/README.okir [new file with mode: 0644]
utils/rquotad/hasquota.c [new file with mode: 0644]
utils/rquotad/mntent.h [new file with mode: 0644]
utils/rquotad/pathnames.h [new file with mode: 0644]
utils/rquotad/quotactl.c [new file with mode: 0644]
utils/rquotad/rquota.h [new file with mode: 0644]
utils/rquotad/rquota.x [new file with mode: 0644]
utils/rquotad/rquota_server.c [new file with mode: 0644]
utils/rquotad/rquota_svc.c [new file with mode: 0644]
utils/rquotad/rquota_xdr.c [new file with mode: 0644]
utils/rquotad/rquotad.man [new file with mode: 0644]
utils/showmount/Makefile [new file with mode: 0644]
utils/showmount/showmount.c [new file with mode: 0644]
utils/showmount/showmount.man [new file with mode: 0644]
utils/statd/COPYING [new file with mode: 0644]
utils/statd/COPYRIGHT [new file with mode: 0644]
utils/statd/Makefile [new file with mode: 0644]
utils/statd/TODO [new file with mode: 0644]
utils/statd/callback.c [new file with mode: 0644]
utils/statd/log.c [new file with mode: 0644]
utils/statd/log.h [new file with mode: 0644]
utils/statd/misc.c [new file with mode: 0644]
utils/statd/monitor.c [new file with mode: 0644]
utils/statd/notify.c [new file with mode: 0644]
utils/statd/notlist.c [new file with mode: 0644]
utils/statd/notlist.h [new file with mode: 0644]
utils/statd/rmtcall.c [new file with mode: 0644]
utils/statd/sim_sm_inter.x [new file with mode: 0644]
utils/statd/simu.c [new file with mode: 0644]
utils/statd/simulate.c [new file with mode: 0644]
utils/statd/sm_inter.x [new file with mode: 0644]
utils/statd/stat.c [new file with mode: 0644]
utils/statd/statd.c [new file with mode: 0644]
utils/statd/statd.h [new file with mode: 0644]
utils/statd/statd.man [new file with mode: 0644]
utils/statd/state.c [new file with mode: 0644]
utils/statd/svc_run.c [new file with mode: 0644]
utils/statd/system.h [new file with mode: 0644]
utils/statd/version.h [new file with mode: 0644]

diff --git a/ChangeLog b/ChangeLog
new file mode 100644 (file)
index 0000000..f8c4d9e
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,178 @@
+Mon Oct 18 14:56:22 1999  H.J. Lu <hjl@lucon.org>
+
+       * Initial version 0.1 released.
+
+       * configure.in (VERSION): Set to "nfs-utils 0.1".
+       * configure: Regenerated.
+
+Mon Oct 18 14:54:57 1999  H.J. Lu <hjl@lucon.org>
+
+       * utils/mountd/mountd.c (get_exportlist): Cleanup.
+
+       * utils/exportfs/exportfs.c (unexport_all): Unexport from
+       kernel only if the entry is exported to kernel.
+       (unexportfs): Likewise.
+
+Wed Sep 08 16:49:32 1999  Neil Brown <neilb@cse.unsw.edu.au>
+
+1/ utils/mountd/rmtab.c::mountlist_list
+
+     This routine stats the rmtab file to see if it has changed.  It
+     if has, it cleans up it's old copy of the data. But it still
+     always re-read the file, thus returning multiple copies of the
+     data on consecutive calls without intervening changes.
+     "Showmount -a" didn't show this as it appears to sort/unique the
+     data, but 'strace showmount -a' showed that the size of the
+     datagram that it received grew.
+
+     I moved the getrmtabent loop inside the mtime test.
+
+2/ utils/exportfs/exportfs.c
+   
+     Many routines used the m_path field of m_export instead of
+     e_path.
+     According to the comment in nfslib.h, m_path should only
+     be used when processing a mount request (i.e. in mountd)
+     where the mountpoint may be a subdirectory of the export point.
+
+     I changed all occurances of m_path to e_path
+
+
+3/ utils/exportfs/exportfs.c:main
+
+      extra arguments are not meaningful with -a or -r, but
+      exportfs accepted them and then ignored the -a/-r, expect that
+      -r would still unexport everything first.
+
+      I generate an error if there are extra args and f_all
+
+4/ utils/exportfs/exportfs.c:main
+     extract dump out as a special case.
+
+5/ utils/exportfs/exportfs.c
+     made f_reexport a local variable.
+
+
+6/ utils/exportfs/exportfs.c:main,exportall
+
+    support/export/rmtab.c
+       only  mayexport on newly created entries, don't set xtabent at all
+
+7/ support/include/nfslib.h
+
+      add #define _PATH_PROC_EXPORTS to be /proc/fs/nds/exports
+
+8/ support/export/xtab.c
+
+      xtab_mount_read loads data from _PATH_PROC_EXPORTS if it exists,
+      else from xtab
+
+
+9/ support/export/xtab.c
+
+      xtab_mount_read now sets m_exported, and NOT
+       xtabent and mayexport
+
+      removed the append arguement from xtab_write as it was
+       never used.
+
+      added is_export flag to xtab_write similar to xtab_read
+       if is_export, only write entries with m_xtabent or m_addxtab
+       if !is_export, only write entries with m_exported
+     
+10/ support/export/export.c::export_allowed_internal
+
+      added test for exp->m_mayexport, as the export tree
+       may have entries that are no longer allowed to be exported,
+       and so shouldn't caused deduced exported by rmtab_read
+
+11/ utils/exportfs/exportfs.c::main
+       error checking of flags.
+
+12/ utils/exportfs/exportfs.c
+
+       total rewrite of export and unexport logic.
+       We now:
+       -  build an exportslist of valid exports, based on
+          current etab file  and arguments,
+        -  read rmtab to instantiate relevant wild card entries
+       -  read etab to find out what is currently exported
+       -  synchronise intention with reality
+       -  write out etab and xtab
+
+13/  various
+       discard the m_addxtab flag
+       add m_changed flag so we know what to report in exportfs
+
+14/ utils/mountd/auth.c:auth_authenticate
+
+       the value returned by gethostbyaddr was trusted.
+
+       It now follows this with a call to gethostbyname
+       and checks that the address is in the list.
+
+15/ support/export/nfsctl.c::cltsetup,expsetup
+
+       force client names to lowercase as kernel is
+       sensitive to case
+
+16/ quietened a few compiler warnings
+
+17/ support/export/client:client_lookup
+
+     look for pre-existing client with same name before creating
+       a new one.
+
+18/ support/include/exportfs.h
+
+     The ordering of the MCL_* enum was:
+       ANONYMOUS, FQDN, SUBNETWORK, WILDCARD, NETGROUP
+
+     I moved ANONYMOUS to the end.
+
+     The ordering is significant when an export entry is being searched for to 
+       match a given address.  There are two problems with ANONYMOUS being first.
+
+       1/ if a directory is exported rw to a couple of hosts and ro to everyone else,
+          then the ro case will always be found first and the privileged hosts won't get
+          their privilege
+       2/ When mountd gets a request to mount an ANONYMOUSly exported tree, it creates a FQDN
+          export entry for the specific host, and writes it to xtab.
+          When another request comes from the same host, the ANONYMOUS entry is found again, 
+          before the new FQDN entry, so it creates another FQDN entry and writes it to xtab
+          again.  If causes bloat in xtab.
+
+      Putting ANONYMOUS at the end reflects it's nature as a catch-all
+
+19/ utils/exportfs/exportfs.man
+       many updates to the man page to reflect changes to the code
+
+-----------------------
+
+
+
+TODO:
+
+- allow exportfs to modify rmtab file
+- make sure kernel never gets two clients with same IP address
+    - possible kernel should reject
+    - needs to be some way to lookup client in kernel by IP address
+- maybe get kernel to do case-insensitive comparisons on client names
+- remove unused clients from kernel
+
+- change etab to xtab and xtab to xtab.active
+
+- timestamp and/or statd-stamp in rmtab for removing old entries.
+
+Mon Oct 18 11:48:07 1999  H.J. Lu <hjl@lucon.org>
+
+       * linux-nfs: New directory.
+       * linux-nfs/ChangeLog: Moved from ..
+       * linux-nfs/INSTALL: Likewise.
+       * linux-nfs/KNOWNBUGS: Likewise.
+       * linux-nfs/NEW: Likewise.
+       * linux-nfs/README: Likewise.
+       * linux-nfs/THANKS: Likewise.
+       * linux-nfs/TODO: Likewise.
+
+       * Starting from knfsd 1.4.7.
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..2789f00
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,22 @@
+#
+# linux-nfs/Makefile
+#
+
+SUBDIRS        = tools support utils
+TOP    = 
+
+include $(TOP)rules.mk
+
+distclean clean::
+       rm -f postscript/*.ps
+       rm -f LOG make.log
+
+distclean::
+       rm -fr bin
+       rm -f config.cache config.log config.mk config.status
+
+install:: installman
+       if [ ! -d $(STATEDIR) ]; then mkdir -p $(STATEDIR); fi
+       touch $(STATEDIR)/xtab; chmod 644 $(STATEDIR)/xtab
+       touch $(STATEDIR)/etab; chmod 644 $(STATEDIR)/etab
+       touch $(STATEDIR)/rmtab; chmod 644 $(STATEDIR)/rmtab
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..3fb4bb0
--- /dev/null
+++ b/README
@@ -0,0 +1,92 @@
+This is the Linux NFS utility package version 0.1. It is based on knfsd
+1.4.7.
+
+WARNING: The NFS servers in Linux 2.2 to 2.2.12 are not compatible with
+other NFS client implemenations. If you plan to use Linux 2.2.x as an
+NFS server for non-Linux NFS clients, you should get the Linux NFS
+kernel from the Linux NFS CVS server:
+
+1. Set the environment variable, CVS_RSH, to ssh.
+2. Login to the Linux NFS CVS server:
+
+# cvs -z 9 -d:pserver:anonymous@cvs.linuxnfs.sourceforge.org:/cvsroot/nfs login
+
+without password if it is your first time.
+
+3. Check out the current Linux 2.2 NFS kernel:
+
+# cvs -z 9 -d:pserver:anonymous@cvs.linuxnfs.sourceforge.org:/cvsroot/nfs co -r linux-2-2-nfsv2 linux-2.2
+
+4. If you don't want to use the current NFS kernel, you can find out
+for which kernels the NFS patch is available:
+
+# cd linux-2.2
+# cvs -z 9 -d:pserver:anonymous@cvs.linuxnfs.sourceforge.org:/cvsroot/nfs status -v Makefile
+
+Then generate the kernel patch:
+
+# cvs -z 9 -d:pserver:anonymous@cvs.linuxnfs.sourceforge.org:/cvsroot/nfs rdiff -ko -u -r linux-2-2-xx -r linux-2-2-xx-nfsv2-xxxxx linux-2.2
+
+If there is no NFS patch for the kernel you are interested in, you have
+to make a patch closest to your kernel version and apply it by hand.
+
+There is a Linux NFS kernel source tree for Linux 2.3, linux-2.3, on
+the Linux NFS CVS server. However, it is not maintained. We will need
+all the help we can get. To contribute to the Linux NFS project, please
+go to
+
+http://www.linuxnfs.sourceforge.org
+
+and login as "beta" with password "beta4u". You register yourself.
+please send an email to nfs-admin@linuxnfs.sourceforge.org with
+
+1. Your user id on www.linuxnfs.sourceforge.org.
+2. The area in NFS you'd like to work on.
+
+You will be notified when it is done.
+
+There is a Linux NFS mailing list at
+
+http://lists.varesearch.com/lists/listinfo/nfs/
+
+You can subscribe it and search the mailing list archive via a web
+browser.
+
+The nfs-utils package is avaible from the CVS server:
+
+# cvs -z 9 -d:pserver:anonymous@cvs.linuxnfs.sourceforge.org:/cvsroot/nfs co nfs-utils
+
+will get the latest version.
+
+The tar file is at
+
+ftp://ftp.valinux.com/pub/support/hjl/nfs/nfs-utils-0.1.tar.gz
+
+To compile, just do
+
+# ./configure
+# make
+
+# make install
+
+will install the nfs-utils binaries. You have to install the NFS
+service scripts. There are 2 in etc/redhat provided for RedHat 6.x.
+They are tested on RedHat 6.1.
+
+On RedHat 6.1, you can use
+
+# rpm -ta nfs-utils-0.1.tar.gz
+
+to build the source and binary RPMs.
+
+If your mount from util-linux is too old, you will need 2 patches:
+
+ftp://ftp.valinux.com/pub/support/hjl/nfs/util-linux-2.9o-mount-nfsv3.patch
+ftp://ftp.valinux.com/pub/support/hjl/nfs/util-linux-2.9w-mount-nfsv3try.patch
+
+Thanks.
+
+
+H.J.
+hjl@lucon.org
+10/18/99
diff --git a/aclocal.m4 b/aclocal.m4
new file mode 100644 (file)
index 0000000..baa54d1
--- /dev/null
@@ -0,0 +1,120 @@
+dnl aclocal.m4 -- custom autoconf macros for various purposes
+dnl Updated for Autoconf v2
+dnl
+dnl ******** save/restore stuff **********
+define(AC_KNFSD_SAVE,
+  [AC_LANG_SAVE
+   save_LDFLAGS=$LDFLAGS
+   save_CFLAGS=$CFLAGS
+   save_CXXFLAGS=$CXXFLAGS
+   save_LIBS=$LIBS
+])dnl
+define(AC_KNFSD_RESTORE,
+  [LDFLAGS=$save_LDFLAGS
+   CFLAGS=$save_CFLAGS
+   CXXFLAGS=$save_CXXFLAGS
+   LIBS=$save_LIBS
+   AC_LANG_RESTORE
+])dnl
+dnl *********** GNU libc 2 ***************
+define(AC_GNULIBC,
+  [AC_MSG_CHECKING(for GNU libc2)
+  AC_CACHE_VAL(knfsd_cv_glibc2,
+  [AC_TRY_CPP([
+      #include <features.h>
+      #if !defined(__GLIBC__)
+      # error Nope
+      #endif], knfsd_cv_glibc2=yes, knfsd_cv_glibc2=no)])
+  AC_MSG_RESULT($knfsd_cv_glibc2)
+  if test $knfsd_cv_glibc2 = yes; then
+    CFLAGS="$CFLAGS -D_GNU_SOURCE"
+    CXXFLAGS="$CXXFLAGS -D_GNU_SOURCE"
+  fi
+]) dnl
+dnl
+dnl ************* egcs *******************
+define(AC_PROG_EGCS,
+  [AC_MSG_CHECKING(for egcs)
+  AC_CACHE_VAL(knfsd_cv_prog_EGCS,
+  [case `$CC --version 2>/dev/null` in
+   egcs*)
+       knfsd_cv_prog_EGCS=yes;;
+   *)
+       knfsd_cv_prog_EGCS=no;;
+   esac
+  ])
+  AC_MSG_RESULT($knfsd_cv_prog_EGCS)
+  test $knfsd_cv_prog_EGCS = yes && AC_DEFINE(HAVE_EGCS)
+]) dnl
+dnl *********** sizeof(dev_t) **************
+dnl ** We have to kludge this rather than use AC_CHECK_SIZEOF because
+dnl ** we have to include sys/types.h. Ugh.
+define(AC_DEV_T_SIZE,
+  [AC_MSG_CHECKING(size of dev_t)
+   AC_CACHE_VAL(ac_cv_sizeof_dev_t,
+   [AC_TRY_RUN(
+    [#include <stdio.h>
+     #include <sys/types.h>
+     main()
+     {
+      FILE *f=fopen("conftestval", "w");
+      if (!f) exit(1);
+      fprintf(f, "%d\n", sizeof(dev_t));
+      exit(0);
+    }], ac_cv_sizeof_dev_t=`cat conftestval`, ac_cv_sizeof_dev_t=0)])
+    AC_MSG_RESULT($ac_cv_sizeof_dev_t)
+    AC_DEFINE(SIZEOF_DEV_T,$ac_cv_sizeof_dev_t)
+  ])
+dnl *********** sizeof(xxx_t) **************
+dnl ** Overwrite the AC_CHECK_SIZEOF macro as we must include sys/types.h
+define([AC_CHECK_SIZEOF],
+  [changequote(<<, >>)dnl
+   define(<<AC_TYPE_NAME>>,translit(sizeof_$1, [a-z *], [A-Z_P]))dnl
+   define(<<AC_CV_NAME>>, translit(ac_cv_sizeof_$1, [ *], [_p]))dnl
+   changequote([, ])dnl
+   AC_MSG_CHECKING(size of $1)
+   AC_CACHE_VAL(AC_CV_NAME,
+   [AC_TRY_RUN(
+    [#include <stdio.h>
+     #include <sys/types.h>
+     main()
+     {
+      FILE *f=fopen("conftestval", "w");
+      if (!f) exit(1);
+      fprintf(f, "%d\n", sizeof($1));
+      exit(0);
+    }], AC_CV_NAME=`cat conftestval`, AC_CV_NAME=0)])
+    AC_MSG_RESULT($AC_CV_NAME)
+    AC_DEFINE_UNQUOTED(AC_TYPE_NAME,$AC_CV_NAME)
+    undefine([AC_TYPE_NAME])dnl
+    undefine([AC_CV_NAME])dnl
+  ])
+dnl *********** BSD vs. POSIX signal handling **************
+define([AC_BSD_SIGNALS],
+  [AC_MSG_CHECKING(for BSD signal semantics)
+  AC_CACHE_VAL(knfsd_cv_bsd_signals,
+    [AC_TRY_RUN([
+       #include <signal.h>
+       #include <unistd.h>
+       #include <sys/wait.h>
+
+       static int counter = 0;
+       static RETSIGTYPE handler(int num) { counter++; }
+
+       int main()
+       {
+               int     s;
+               if ((s = fork()) < 0) return 1;
+               if (s != 0) {
+                       if (wait(&s) < 0) return 1;
+                       return WIFSIGNALED(s)? 1 : 0;
+               }
+
+               signal(SIGHUP, handler);
+               kill(getpid(), SIGHUP); kill(getpid(), SIGHUP);
+               return (counter == 2)? 0 : 1;
+       }
+    ], knfsd_cv_bsd_signals=yes, knfsd_cv_bsd_signals=no)]) dnl
+    AC_MSG_RESULT($knfsd_cv_bsd_signals)
+    test $knfsd_cv_bsd_signals = yes && AC_DEFINE(HAVE_BSD_SIGNALS)
+])dnl
diff --git a/config.mk.in b/config.mk.in
new file mode 100644 (file)
index 0000000..57d16be
--- /dev/null
@@ -0,0 +1,79 @@
+#
+# Configuration stuff for nfs-utils
+#
+
+VERSION                = @VERSION@
+
+prefix         = $(install_prefix)@prefix@
+exec_prefix    = $(install_prefix)@prefix@
+BINDIR         = @bindir@
+SBINDIR                = @sbindir@
+MANDIR         = @mandir@
+STATEDIR       = $(install_prefix)@statedir@
+
+##################################################################
+# This is the prefix that will be used for nfsd and mountd. Leave this
+# empty, or set to `k'.
+KPREFIX                = @kprefix@
+
+# This define will turn NFSv3 support on or off one day. Not functional yet.
+NFSV3          = @enable_nfsv3@
+
+# Where and how to install manpages
+MAN1EXT                = 1
+MAN5EXT                = 5
+MAN8EXT                = 8
+MAN9EXT                = 9
+MANOWNER       = root
+MANGROUP       = root
+
+# Various libs
+LIBBSD         = @LIBBSD@
+
+################# END OF USER SERVICEABLE PARTS ##################
+ALLTARGETS     = all clean distclean install installman \
+                 depend dep postscript indent
+
+ifndef ARCHFLAGS
+  ARCH         = $(shell uname -m)
+  FLAGS_alpha  = -mno-fp-regs -ffixed-8
+  ARCHFLAGS    = $(FLAGS_$(ARCH))
+.EXPORT:       ARCHFLAGS
+endif
+
+CC             = gcc
+AR             = ar
+LD             = ld
+RM             = rm -f
+MKDIR          = mkdir -p
+LN_S           = ln -sf
+RANLIB         = ranlib
+INDENT         = indent
+RPCGEN         = $(TOP)bin/rpcgen
+GETKVER                = $(TOP)tools/getkversion
+INSTALL                = install
+MAN2PS         = groff -Tps -man
+
+AFLAGS         = -I$(TOP)support/include \
+                 -Wall $(ARCHFLAGS) -pipe
+ifdef KERNEL_INCDIR
+AFLAGS        += -I$(KERNEL_INCDIR)
+endif
+
+CFLAGS         = @CFLAGS@ $(AFLAGS) $(CCOPTS) -DVERSION="\"$(VERSION)\""
+LDFLAGS                = @LDFLAGS@ $(LDOPTS) -L$(TOP)support/lib
+
+ifdef NFSV3
+ CFLAGS        += -DNFS3_SUPPORTED
+endif
+
+k              = $(KPREFIX)
+
+INSTALLBIN     = $(INSTALL) -m 755 -s
+INSTALLSUID    = $(INSTALL) -m 4755
+INSTALLMOD     = $(INSTALL) -m 600
+INSTALLMAN     = $(INSTALL) -m 644
+MAN1DIR                = $(MANDIR)/man$(MAN1EXT)
+MAN5DIR                = $(MANDIR)/man$(MAN5EXT)
+MAN8DIR                = $(MANDIR)/man$(MAN8EXT)
+MAN9DIR                = $(MANDIR)/man$(MAN9EXT)
diff --git a/configure b/configure
new file mode 100755 (executable)
index 0000000..947822a
--- /dev/null
+++ b/configure
@@ -0,0 +1,1861 @@
+#! /bin/sh
+
+# Guess values for system-dependent variables and create Makefiles.
+# Generated automatically using autoconf version 2.13 
+# Copyright (C) 1992, 93, 94, 95, 96 Free Software Foundation, Inc.
+#
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+
+# Defaults:
+ac_help=
+ac_default_prefix=/usr/local
+# Any additions from configure.in:
+ac_default_prefix=/usr
+ac_help="$ac_help
+  --with-statedir=/foo    use state dir /foo [/var/lib/nfs]"
+ac_help="$ac_help
+  --enable-nfsv3          enable support for NFSv3"
+ac_help="$ac_help
+  --enable-kprefix       install progs as rpc.knfsd etc"
+ac_help="$ac_help
+  --enable-secure-statd  Only lockd can use statd (security)"
+
+# Initialize some variables set by options.
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+build=NONE
+cache_file=./config.cache
+exec_prefix=NONE
+host=NONE
+no_create=
+nonopt=NONE
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+sitefile=
+srcdir=
+target=NONE
+verbose=
+x_includes=NONE
+x_libraries=NONE
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datadir='${prefix}/share'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+libdir='${exec_prefix}/lib'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+infodir='${prefix}/info'
+mandir='${prefix}/man'
+
+# Initialize some other variables.
+subdirs=
+MFLAGS= MAKEFLAGS=
+SHELL=${CONFIG_SHELL-/bin/sh}
+# Maximum number of lines to put in a shell here document.
+ac_max_here_lines=12
+
+ac_prev=
+for ac_option
+do
+
+  # If the previous option needs an argument, assign it.
+  if test -n "$ac_prev"; then
+    eval "$ac_prev=\$ac_option"
+    ac_prev=
+    continue
+  fi
+
+  case "$ac_option" in
+  -*=*) ac_optarg=`echo "$ac_option" | sed 's/[-_a-zA-Z0-9]*=//'` ;;
+  *) ac_optarg= ;;
+  esac
+
+  # Accept the important Cygnus configure options, so we can diagnose typos.
+
+  case "$ac_option" in
+
+  -bindir | --bindir | --bindi | --bind | --bin | --bi)
+    ac_prev=bindir ;;
+  -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+    bindir="$ac_optarg" ;;
+
+  -build | --build | --buil | --bui | --bu)
+    ac_prev=build ;;
+  -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+    build="$ac_optarg" ;;
+
+  -cache-file | --cache-file | --cache-fil | --cache-fi \
+  | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+    ac_prev=cache_file ;;
+  -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+  | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+    cache_file="$ac_optarg" ;;
+
+  -datadir | --datadir | --datadi | --datad | --data | --dat | --da)
+    ac_prev=datadir ;;
+  -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \
+  | --da=*)
+    datadir="$ac_optarg" ;;
+
+  -disable-* | --disable-*)
+    ac_feature=`echo $ac_option|sed -e 's/-*disable-//'`
+    # Reject names that are not valid shell variable names.
+    if test -n "`echo $ac_feature| sed 's/[-a-zA-Z0-9_]//g'`"; then
+      { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; }
+    fi
+    ac_feature=`echo $ac_feature| sed 's/-/_/g'`
+    eval "enable_${ac_feature}=no" ;;
+
+  -enable-* | --enable-*)
+    ac_feature=`echo $ac_option|sed -e 's/-*enable-//' -e 's/=.*//'`
+    # Reject names that are not valid shell variable names.
+    if test -n "`echo $ac_feature| sed 's/[-_a-zA-Z0-9]//g'`"; then
+      { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; }
+    fi
+    ac_feature=`echo $ac_feature| sed 's/-/_/g'`
+    case "$ac_option" in
+      *=*) ;;
+      *) ac_optarg=yes ;;
+    esac
+    eval "enable_${ac_feature}='$ac_optarg'" ;;
+
+  -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+  | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+  | --exec | --exe | --ex)
+    ac_prev=exec_prefix ;;
+  -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+  | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+  | --exec=* | --exe=* | --ex=*)
+    exec_prefix="$ac_optarg" ;;
+
+  -gas | --gas | --ga | --g)
+    # Obsolete; use --with-gas.
+    with_gas=yes ;;
+
+  -help | --help | --hel | --he)
+    # Omit some internal or obsolete options to make the list less imposing.
+    # This message is too long to be a string in the A/UX 3.1 sh.
+    cat << EOF
+Usage: configure [options] [host]
+Options: [defaults in brackets after descriptions]
+Configuration:
+  --cache-file=FILE       cache test results in FILE
+  --help                  print this message
+  --no-create             do not create output files
+  --quiet, --silent       do not print \`checking...' messages
+  --site-file=FILE        use FILE as the site file
+  --version               print the version of autoconf that created configure
+Directory and file names:
+  --prefix=PREFIX         install architecture-independent files in PREFIX
+                          [$ac_default_prefix]
+  --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
+                          [same as prefix]
+  --bindir=DIR            user executables in DIR [EPREFIX/bin]
+  --sbindir=DIR           system admin executables in DIR [EPREFIX/sbin]
+  --libexecdir=DIR        program executables in DIR [EPREFIX/libexec]
+  --datadir=DIR           read-only architecture-independent data in DIR
+                          [PREFIX/share]
+  --sysconfdir=DIR        read-only single-machine data in DIR [PREFIX/etc]
+  --sharedstatedir=DIR    modifiable architecture-independent data in DIR
+                          [PREFIX/com]
+  --localstatedir=DIR     modifiable single-machine data in DIR [PREFIX/var]
+  --libdir=DIR            object code libraries in DIR [EPREFIX/lib]
+  --includedir=DIR        C header files in DIR [PREFIX/include]
+  --oldincludedir=DIR     C header files for non-gcc in DIR [/usr/include]
+  --infodir=DIR           info documentation in DIR [PREFIX/info]
+  --mandir=DIR            man documentation in DIR [PREFIX/man]
+  --srcdir=DIR            find the sources in DIR [configure dir or ..]
+  --program-prefix=PREFIX prepend PREFIX to installed program names
+  --program-suffix=SUFFIX append SUFFIX to installed program names
+  --program-transform-name=PROGRAM
+                          run sed PROGRAM on installed program names
+EOF
+    cat << EOF
+Host type:
+  --build=BUILD           configure for building on BUILD [BUILD=HOST]
+  --host=HOST             configure for HOST [guessed]
+  --target=TARGET         configure for TARGET [TARGET=HOST]
+Features and packages:
+  --disable-FEATURE       do not include FEATURE (same as --enable-FEATURE=no)
+  --enable-FEATURE[=ARG]  include FEATURE [ARG=yes]
+  --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
+  --without-PACKAGE       do not use PACKAGE (same as --with-PACKAGE=no)
+  --x-includes=DIR        X include files are in DIR
+  --x-libraries=DIR       X library files are in DIR
+EOF
+    if test -n "$ac_help"; then
+      echo "--enable and --with options recognized:$ac_help"
+    fi
+    exit 0 ;;
+
+  -host | --host | --hos | --ho)
+    ac_prev=host ;;
+  -host=* | --host=* | --hos=* | --ho=*)
+    host="$ac_optarg" ;;
+
+  -includedir | --includedir | --includedi | --included | --include \
+  | --includ | --inclu | --incl | --inc)
+    ac_prev=includedir ;;
+  -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+  | --includ=* | --inclu=* | --incl=* | --inc=*)
+    includedir="$ac_optarg" ;;
+
+  -infodir | --infodir | --infodi | --infod | --info | --inf)
+    ac_prev=infodir ;;
+  -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+    infodir="$ac_optarg" ;;
+
+  -libdir | --libdir | --libdi | --libd)
+    ac_prev=libdir ;;
+  -libdir=* | --libdir=* | --libdi=* | --libd=*)
+    libdir="$ac_optarg" ;;
+
+  -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+  | --libexe | --libex | --libe)
+    ac_prev=libexecdir ;;
+  -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+  | --libexe=* | --libex=* | --libe=*)
+    libexecdir="$ac_optarg" ;;
+
+  -localstatedir | --localstatedir | --localstatedi | --localstated \
+  | --localstate | --localstat | --localsta | --localst \
+  | --locals | --local | --loca | --loc | --lo)
+    ac_prev=localstatedir ;;
+  -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+  | --localstate=* | --localstat=* | --localsta=* | --localst=* \
+  | --locals=* | --local=* | --loca=* | --loc=* | --lo=*)
+    localstatedir="$ac_optarg" ;;
+
+  -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+    ac_prev=mandir ;;
+  -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+    mandir="$ac_optarg" ;;
+
+  -nfp | --nfp | --nf)
+    # Obsolete; use --without-fp.
+    with_fp=no ;;
+
+  -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+  | --no-cr | --no-c)
+    no_create=yes ;;
+
+  -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+  | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+    no_recursion=yes ;;
+
+  -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+  | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+  | --oldin | --oldi | --old | --ol | --o)
+    ac_prev=oldincludedir ;;
+  -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+  | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+  | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+    oldincludedir="$ac_optarg" ;;
+
+  -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+    ac_prev=prefix ;;
+  -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+    prefix="$ac_optarg" ;;
+
+  -program-prefix | --program-prefix | --program-prefi | --program-pref \
+  | --program-pre | --program-pr | --program-p)
+    ac_prev=program_prefix ;;
+  -program-prefix=* | --program-prefix=* | --program-prefi=* \
+  | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+    program_prefix="$ac_optarg" ;;
+
+  -program-suffix | --program-suffix | --program-suffi | --program-suff \
+  | --program-suf | --program-su | --program-s)
+    ac_prev=program_suffix ;;
+  -program-suffix=* | --program-suffix=* | --program-suffi=* \
+  | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+    program_suffix="$ac_optarg" ;;
+
+  -program-transform-name | --program-transform-name \
+  | --program-transform-nam | --program-transform-na \
+  | --program-transform-n | --program-transform- \
+  | --program-transform | --program-transfor \
+  | --program-transfo | --program-transf \
+  | --program-trans | --program-tran \
+  | --progr-tra | --program-tr | --program-t)
+    ac_prev=program_transform_name ;;
+  -program-transform-name=* | --program-transform-name=* \
+  | --program-transform-nam=* | --program-transform-na=* \
+  | --program-transform-n=* | --program-transform-=* \
+  | --program-transform=* | --program-transfor=* \
+  | --program-transfo=* | --program-transf=* \
+  | --program-trans=* | --program-tran=* \
+  | --progr-tra=* | --program-tr=* | --program-t=*)
+    program_transform_name="$ac_optarg" ;;
+
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil)
+    silent=yes ;;
+
+  -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+    ac_prev=sbindir ;;
+  -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+  | --sbi=* | --sb=*)
+    sbindir="$ac_optarg" ;;
+
+  -sharedstatedir | --sharedstatedir | --sharedstatedi \
+  | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+  | --sharedst | --shareds | --shared | --share | --shar \
+  | --sha | --sh)
+    ac_prev=sharedstatedir ;;
+  -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+  | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+  | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+  | --sha=* | --sh=*)
+    sharedstatedir="$ac_optarg" ;;
+
+  -site | --site | --sit)
+    ac_prev=site ;;
+  -site=* | --site=* | --sit=*)
+    site="$ac_optarg" ;;
+
+  -site-file | --site-file | --site-fil | --site-fi | --site-f)
+    ac_prev=sitefile ;;
+  -site-file=* | --site-file=* | --site-fil=* | --site-fi=* | --site-f=*)
+    sitefile="$ac_optarg" ;;
+
+  -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+    ac_prev=srcdir ;;
+  -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+    srcdir="$ac_optarg" ;;
+
+  -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+  | --syscon | --sysco | --sysc | --sys | --sy)
+    ac_prev=sysconfdir ;;
+  -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+  | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+    sysconfdir="$ac_optarg" ;;
+
+  -target | --target | --targe | --targ | --tar | --ta | --t)
+    ac_prev=target ;;
+  -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+    target="$ac_optarg" ;;
+
+  -v | -verbose | --verbose | --verbos | --verbo | --verb)
+    verbose=yes ;;
+
+  -version | --version | --versio | --versi | --vers)
+    echo "configure generated by autoconf version 2.13"
+    exit 0 ;;
+
+  -with-* | --with-*)
+    ac_package=`echo $ac_option|sed -e 's/-*with-//' -e 's/=.*//'`
+    # Reject names that are not valid shell variable names.
+    if test -n "`echo $ac_package| sed 's/[-_a-zA-Z0-9]//g'`"; then
+      { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; }
+    fi
+    ac_package=`echo $ac_package| sed 's/-/_/g'`
+    case "$ac_option" in
+      *=*) ;;
+      *) ac_optarg=yes ;;
+    esac
+    eval "with_${ac_package}='$ac_optarg'" ;;
+
+  -without-* | --without-*)
+    ac_package=`echo $ac_option|sed -e 's/-*without-//'`
+    # Reject names that are not valid shell variable names.
+    if test -n "`echo $ac_package| sed 's/[-a-zA-Z0-9_]//g'`"; then
+      { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; }
+    fi
+    ac_package=`echo $ac_package| sed 's/-/_/g'`
+    eval "with_${ac_package}=no" ;;
+
+  --x)
+    # Obsolete; use --with-x.
+    with_x=yes ;;
+
+  -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+  | --x-incl | --x-inc | --x-in | --x-i)
+    ac_prev=x_includes ;;
+  -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+  | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+    x_includes="$ac_optarg" ;;
+
+  -x-libraries | --x-libraries | --x-librarie | --x-librari \
+  | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+    ac_prev=x_libraries ;;
+  -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+  | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+    x_libraries="$ac_optarg" ;;
+
+  -*) { echo "configure: error: $ac_option: invalid option; use --help to show usage" 1>&2; exit 1; }
+    ;;
+
+  *)
+    if test -n "`echo $ac_option| sed 's/[-a-z0-9.]//g'`"; then
+      echo "configure: warning: $ac_option: invalid host type" 1>&2
+    fi
+    if test "x$nonopt" != xNONE; then
+      { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; }
+    fi
+    nonopt="$ac_option"
+    ;;
+
+  esac
+done
+
+if test -n "$ac_prev"; then
+  { echo "configure: error: missing argument to --`echo $ac_prev | sed 's/_/-/g'`" 1>&2; exit 1; }
+fi
+
+trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15
+
+# File descriptor usage:
+# 0 standard input
+# 1 file creation
+# 2 errors and warnings
+# 3 some systems may open it to /dev/tty
+# 4 used on the Kubota Titan
+# 6 checking for... messages and results
+# 5 compiler messages saved in config.log
+if test "$silent" = yes; then
+  exec 6>/dev/null
+else
+  exec 6>&1
+fi
+exec 5>./config.log
+
+echo "\
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+" 1>&5
+
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Also quote any args containing shell metacharacters.
+ac_configure_args=
+for ac_arg
+do
+  case "$ac_arg" in
+  -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+  | --no-cr | --no-c) ;;
+  -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+  | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) ;;
+  *" "*|*"     "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?]*)
+  ac_configure_args="$ac_configure_args '$ac_arg'" ;;
+  *) ac_configure_args="$ac_configure_args $ac_arg" ;;
+  esac
+done
+
+# NLS nuisances.
+# Only set these to C if already set.  These must not be set unconditionally
+# because not all systems understand e.g. LANG=C (notably SCO).
+# Fixing LC_MESSAGES prevents Solaris sh from translating var values in `set'!
+# Non-C LC_CTYPE values break the ctype check.
+if test "${LANG+set}"   = set; then LANG=C;   export LANG;   fi
+if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi
+if test "${LC_MESSAGES+set}" = set; then LC_MESSAGES=C; export LC_MESSAGES; fi
+if test "${LC_CTYPE+set}"    = set; then LC_CTYPE=C;    export LC_CTYPE;    fi
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -rf conftest* confdefs.h
+# AIX cpp loses on an empty file, so make sure it contains at least a newline.
+echo > confdefs.h
+
+# A filename unique to this package, relative to the directory that
+# configure is in, which we can look for to find out if srcdir is correct.
+ac_unique_file=rules.mk
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+  ac_srcdir_defaulted=yes
+  # Try the directory containing this script, then its parent.
+  ac_prog=$0
+  ac_confdir=`echo $ac_prog|sed 's%/[^/][^/]*$%%'`
+  test "x$ac_confdir" = "x$ac_prog" && ac_confdir=.
+  srcdir=$ac_confdir
+  if test ! -r $srcdir/$ac_unique_file; then
+    srcdir=..
+  fi
+else
+  ac_srcdir_defaulted=no
+fi
+if test ! -r $srcdir/$ac_unique_file; then
+  if test "$ac_srcdir_defaulted" = yes; then
+    { echo "configure: error: can not find sources in $ac_confdir or .." 1>&2; exit 1; }
+  else
+    { echo "configure: error: can not find sources in $srcdir" 1>&2; exit 1; }
+  fi
+fi
+srcdir=`echo "${srcdir}" | sed 's%\([^/]\)/*$%\1%'`
+
+# Prefer explicitly selected file to automatically selected ones.
+if test -z "$sitefile"; then
+  if test -z "$CONFIG_SITE"; then
+    if test "x$prefix" != xNONE; then
+      CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site"
+    else
+      CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site"
+    fi
+  fi
+else
+  CONFIG_SITE="$sitefile"
+fi
+for ac_site_file in $CONFIG_SITE; do
+  if test -r "$ac_site_file"; then
+    echo "loading site script $ac_site_file"
+    . "$ac_site_file"
+  fi
+done
+
+if test -r "$cache_file"; then
+  echo "loading cache $cache_file"
+  . $cache_file
+else
+  echo "creating cache $cache_file"
+  > $cache_file
+fi
+
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+ac_exeext=
+ac_objext=o
+if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then
+  # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu.
+  if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then
+    ac_n= ac_c='
+' ac_t='       '
+  else
+    ac_n=-n ac_c= ac_t=
+  fi
+else
+  ac_n= ac_c='\c' ac_t=
+fi
+
+
+
+
+# The nfs-utils version
+VERSION="nfs-utils 0.1"
+
+
+# Check whether --with-statedir or --without-statedir was given.
+if test "${with_statedir+set}" = set; then
+  withval="$with_statedir"
+  statedir=$withval
+else
+  statedir=/var/lib/nfs
+fi
+
+       
+# Check whether --enable-nfsv3 or --disable-nfsv3 was given.
+if test "${enable_nfsv3+set}" = set; then
+  enableval="$enable_nfsv3"
+  enable_nfsv3=$enableval
+else
+  enable_nfsv3=no
+fi
+
+       if test "$enable_nfsv3" = yes; then
+               cat >> confdefs.h <<\EOF
+#define NFS3_SUPPORTED 1
+EOF
+
+       else
+               enable_nfsv3=
+       fi
+       
+# Check whether --enable-kprefix or --disable-kprefix was given.
+if test "${enable_kprefix+set}" = set; then
+  enableval="$enable_kprefix"
+  test "$enableval" = "yes" && kprefix=k
+else
+  kprefix=
+fi
+
+       
+# Check whether --enable-secure-statd or --disable-secure-statd was given.
+if test "${enable_secure_statd+set}" = set; then
+  enableval="$enable_secure_statd"
+  test "$enableval" = "yes" && secure_statd=yes
+else
+  secure_statd=no
+fi
+
+       if test "$secure_statd" = yes; then
+               cat >> confdefs.h <<\EOF
+#define RESTRICTED_STATD 1
+EOF
+
+       fi
+       
+
+
+# Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:606: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  IFS="${IFS=  }"; ac_save_ifs="$IFS"; IFS=":"
+  ac_dummy="$PATH"
+  for ac_dir in $ac_dummy; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_prog_CC="gcc"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+  echo "$ac_t""$CC" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+if test -z "$CC"; then
+  # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:636: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  IFS="${IFS=  }"; ac_save_ifs="$IFS"; IFS=":"
+  ac_prog_rejected=no
+  ac_dummy="$PATH"
+  for ac_dir in $ac_dummy; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      if test "$ac_dir/$ac_word" = "/usr/ucb/cc"; then
+        ac_prog_rejected=yes
+       continue
+      fi
+      ac_cv_prog_CC="cc"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+if test $ac_prog_rejected = yes; then
+  # We found a bogon in the path, so make sure we never use it.
+  set dummy $ac_cv_prog_CC
+  shift
+  if test $# -gt 0; then
+    # We chose a different compiler from the bogus one.
+    # However, it has the same basename, so the bogon will be chosen
+    # first if we set CC to just the basename; use the full file name.
+    shift
+    set dummy "$ac_dir/$ac_word" "$@"
+    shift
+    ac_cv_prog_CC="$@"
+  fi
+fi
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+  echo "$ac_t""$CC" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+  if test -z "$CC"; then
+    case "`uname -s`" in
+    *win32* | *WIN32*)
+      # Extract the first word of "cl", so it can be a program name with args.
+set dummy cl; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:687: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  IFS="${IFS=  }"; ac_save_ifs="$IFS"; IFS=":"
+  ac_dummy="$PATH"
+  for ac_dir in $ac_dummy; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_prog_CC="cl"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+  echo "$ac_t""$CC" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+ ;;
+    esac
+  fi
+  test -z "$CC" && { echo "configure: error: no acceptable cc found in \$PATH" 1>&2; exit 1; }
+fi
+
+echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works""... $ac_c" 1>&6
+echo "configure:719: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5
+
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+cat > conftest.$ac_ext << EOF
+
+#line 730 "configure"
+#include "confdefs.h"
+
+main(){return(0);}
+EOF
+if { (eval echo configure:735: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  ac_cv_prog_cc_works=yes
+  # If we can't run a trivial program, we are probably using a cross compiler.
+  if (./conftest; exit) 2>/dev/null; then
+    ac_cv_prog_cc_cross=no
+  else
+    ac_cv_prog_cc_cross=yes
+  fi
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  ac_cv_prog_cc_works=no
+fi
+rm -fr conftest*
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+echo "$ac_t""$ac_cv_prog_cc_works" 1>&6
+if test $ac_cv_prog_cc_works = no; then
+  { echo "configure: error: installation or configuration problem: C compiler cannot create executables." 1>&2; exit 1; }
+fi
+echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6
+echo "configure:761: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5
+echo "$ac_t""$ac_cv_prog_cc_cross" 1>&6
+cross_compiling=$ac_cv_prog_cc_cross
+
+echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6
+echo "configure:766: checking whether we are using GNU C" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.c <<EOF
+#ifdef __GNUC__
+  yes;
+#endif
+EOF
+if { ac_try='${CC-cc} -E conftest.c'; { (eval echo configure:775: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then
+  ac_cv_prog_gcc=yes
+else
+  ac_cv_prog_gcc=no
+fi
+fi
+
+echo "$ac_t""$ac_cv_prog_gcc" 1>&6
+
+if test $ac_cv_prog_gcc = yes; then
+  GCC=yes
+else
+  GCC=
+fi
+
+ac_test_CFLAGS="${CFLAGS+set}"
+ac_save_CFLAGS="$CFLAGS"
+CFLAGS=
+echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6
+echo "configure:794: checking whether ${CC-cc} accepts -g" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_cc_g'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  echo 'void f(){}' > conftest.c
+if test -z "`${CC-cc} -g -c conftest.c 2>&1`"; then
+  ac_cv_prog_cc_g=yes
+else
+  ac_cv_prog_cc_g=no
+fi
+rm -f conftest*
+
+fi
+
+echo "$ac_t""$ac_cv_prog_cc_g" 1>&6
+if test "$ac_test_CFLAGS" = set; then
+  CFLAGS="$ac_save_CFLAGS"
+elif test $ac_cv_prog_cc_g = yes; then
+  if test "$GCC" = yes; then
+    CFLAGS="-g -O2"
+  else
+    CFLAGS="-g"
+  fi
+else
+  if test "$GCC" = yes; then
+    CFLAGS="-O2"
+  else
+    CFLAGS=
+  fi
+fi
+
+for ac_prog in $CCC c++ g++ gcc CC cxx cc++ cl
+do
+# Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:830: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CXX'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test -n "$CXX"; then
+  ac_cv_prog_CXX="$CXX" # Let the user override the test.
+else
+  IFS="${IFS=  }"; ac_save_ifs="$IFS"; IFS=":"
+  ac_dummy="$PATH"
+  for ac_dir in $ac_dummy; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_prog_CXX="$ac_prog"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+fi
+fi
+CXX="$ac_cv_prog_CXX"
+if test -n "$CXX"; then
+  echo "$ac_t""$CXX" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+test -n "$CXX" && break
+done
+test -n "$CXX" || CXX="gcc"
+
+
+echo $ac_n "checking whether the C++ compiler ($CXX $CXXFLAGS $LDFLAGS) works""... $ac_c" 1>&6
+echo "configure:862: checking whether the C++ compiler ($CXX $CXXFLAGS $LDFLAGS) works" >&5
+
+ac_ext=C
+# CXXFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='${CXX-g++} -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CXX-g++} -o conftest${ac_exeext} $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cxx_cross
+
+cat > conftest.$ac_ext << EOF
+
+#line 873 "configure"
+#include "confdefs.h"
+
+int main(){return(0);}
+EOF
+if { (eval echo configure:878: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  ac_cv_prog_cxx_works=yes
+  # If we can't run a trivial program, we are probably using a cross compiler.
+  if (./conftest; exit) 2>/dev/null; then
+    ac_cv_prog_cxx_cross=no
+  else
+    ac_cv_prog_cxx_cross=yes
+  fi
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  ac_cv_prog_cxx_works=no
+fi
+rm -fr conftest*
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+echo "$ac_t""$ac_cv_prog_cxx_works" 1>&6
+if test $ac_cv_prog_cxx_works = no; then
+  { echo "configure: error: installation or configuration problem: C++ compiler cannot create executables." 1>&2; exit 1; }
+fi
+echo $ac_n "checking whether the C++ compiler ($CXX $CXXFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6
+echo "configure:904: checking whether the C++ compiler ($CXX $CXXFLAGS $LDFLAGS) is a cross-compiler" >&5
+echo "$ac_t""$ac_cv_prog_cxx_cross" 1>&6
+cross_compiling=$ac_cv_prog_cxx_cross
+
+echo $ac_n "checking whether we are using GNU C++""... $ac_c" 1>&6
+echo "configure:909: checking whether we are using GNU C++" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_gxx'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.C <<EOF
+#ifdef __GNUC__
+  yes;
+#endif
+EOF
+if { ac_try='${CXX-g++} -E conftest.C'; { (eval echo configure:918: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then
+  ac_cv_prog_gxx=yes
+else
+  ac_cv_prog_gxx=no
+fi
+fi
+
+echo "$ac_t""$ac_cv_prog_gxx" 1>&6
+
+if test $ac_cv_prog_gxx = yes; then
+  GXX=yes
+else
+  GXX=
+fi
+
+ac_test_CXXFLAGS="${CXXFLAGS+set}"
+ac_save_CXXFLAGS="$CXXFLAGS"
+CXXFLAGS=
+echo $ac_n "checking whether ${CXX-g++} accepts -g""... $ac_c" 1>&6
+echo "configure:937: checking whether ${CXX-g++} accepts -g" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_cxx_g'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  echo 'void f(){}' > conftest.cc
+if test -z "`${CXX-g++} -g -c conftest.cc 2>&1`"; then
+  ac_cv_prog_cxx_g=yes
+else
+  ac_cv_prog_cxx_g=no
+fi
+rm -f conftest*
+
+fi
+
+echo "$ac_t""$ac_cv_prog_cxx_g" 1>&6
+if test "$ac_test_CXXFLAGS" = set; then
+  CXXFLAGS="$ac_save_CXXFLAGS"
+elif test $ac_cv_prog_cxx_g = yes; then
+  if test "$GXX" = yes; then
+    CXXFLAGS="-g -O2"
+  else
+    CXXFLAGS="-g"
+  fi
+else
+  if test "$GXX" = yes; then
+    CXXFLAGS="-O2"
+  else
+    CXXFLAGS=
+  fi
+fi
+
+echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6
+echo "configure:969: checking how to run the C preprocessor" >&5
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+  CPP=
+fi
+if test -z "$CPP"; then
+if eval "test \"`echo '$''{'ac_cv_prog_CPP'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+    # This must be in double quotes, not single quotes, because CPP may get
+  # substituted into the Makefile and "${CC-cc}" will confuse make.
+  CPP="${CC-cc} -E"
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp.
+  cat > conftest.$ac_ext <<EOF
+#line 984 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:990: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+  :
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  CPP="${CC-cc} -E -traditional-cpp"
+  cat > conftest.$ac_ext <<EOF
+#line 1001 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1007: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+  :
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  CPP="${CC-cc} -nologo -E"
+  cat > conftest.$ac_ext <<EOF
+#line 1018 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1024: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+  :
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  CPP=/lib/cpp
+fi
+rm -f conftest*
+fi
+rm -f conftest*
+fi
+rm -f conftest*
+  ac_cv_prog_CPP="$CPP"
+fi
+  CPP="$ac_cv_prog_CPP"
+else
+  ac_cv_prog_CPP="$CPP"
+fi
+echo "$ac_t""$CPP" 1>&6
+
+ac_aux_dir=
+for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do
+  if test -f $ac_dir/install-sh; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/install-sh -c"
+    break
+  elif test -f $ac_dir/install.sh; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/install.sh -c"
+    break
+  fi
+done
+if test -z "$ac_aux_dir"; then
+  { echo "configure: error: can not find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." 1>&2; exit 1; }
+fi
+ac_config_guess=$ac_aux_dir/config.guess
+ac_config_sub=$ac_aux_dir/config.sub
+ac_configure=$ac_aux_dir/configure # This should be Cygnus configure.
+
+# Find a good install program.  We prefer a C program (faster),
+# so one script is as good as another.  But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# ./install, which can be erroneously created by make from ./install.sh.
+echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6
+echo "configure:1079: checking for a BSD compatible install" >&5
+if test -z "$INSTALL"; then
+if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+    IFS="${IFS=        }"; ac_save_IFS="$IFS"; IFS=":"
+  for ac_dir in $PATH; do
+    # Account for people who put trailing slashes in PATH elements.
+    case "$ac_dir/" in
+    /|./|.//|/etc/*|/usr/sbin/*|/usr/etc/*|/sbin/*|/usr/afsws/bin/*|/usr/ucb/*) ;;
+    *)
+      # OSF1 and SCO ODT 3.0 have their own names for install.
+      # Don't use installbsd from OSF since it installs stuff as root
+      # by default.
+      for ac_prog in ginstall scoinst install; do
+        if test -f $ac_dir/$ac_prog; then
+         if test $ac_prog = install &&
+            grep dspmsg $ac_dir/$ac_prog >/dev/null 2>&1; then
+           # AIX install.  It has an incompatible calling convention.
+           :
+         else
+           ac_cv_path_install="$ac_dir/$ac_prog -c"
+           break 2
+         fi
+       fi
+      done
+      ;;
+    esac
+  done
+  IFS="$ac_save_IFS"
+
+fi
+  if test "${ac_cv_path_install+set}" = set; then
+    INSTALL="$ac_cv_path_install"
+  else
+    # As a last resort, use the slow shell script.  We don't cache a
+    # path for INSTALL within a source directory, because that will
+    # break other packages using the cache if that directory is
+    # removed, or if the path is relative.
+    INSTALL="$ac_install_sh"
+  fi
+fi
+echo "$ac_t""$INSTALL" 1>&6
+
+# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+# It thinks the first close brace ends the variable substitution.
+test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
+
+test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL_PROGRAM}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+
+echo $ac_n "checking for ANSI C header files""... $ac_c" 1>&6
+echo "configure:1132: checking for ANSI C header files" >&5
+if eval "test \"`echo '$''{'ac_cv_header_stdc'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1137 "configure"
+#include "confdefs.h"
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1145: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+  rm -rf conftest*
+  ac_cv_header_stdc=yes
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+if test $ac_cv_header_stdc = yes; then
+  # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+cat > conftest.$ac_ext <<EOF
+#line 1162 "configure"
+#include "confdefs.h"
+#include <string.h>
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "memchr" >/dev/null 2>&1; then
+  :
+else
+  rm -rf conftest*
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+cat > conftest.$ac_ext <<EOF
+#line 1180 "configure"
+#include "confdefs.h"
+#include <stdlib.h>
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "free" >/dev/null 2>&1; then
+  :
+else
+  rm -rf conftest*
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+if test "$cross_compiling" = yes; then
+  :
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1201 "configure"
+#include "confdefs.h"
+#include <ctype.h>
+#define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+#define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int main () { int i; for (i = 0; i < 256; i++)
+if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) exit(2);
+exit (0); }
+
+EOF
+if { (eval echo configure:1212: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+then
+  :
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -fr conftest*
+  ac_cv_header_stdc=no
+fi
+rm -fr conftest*
+fi
+
+fi
+fi
+
+echo "$ac_t""$ac_cv_header_stdc" 1>&6
+if test $ac_cv_header_stdc = yes; then
+  cat >> confdefs.h <<\EOF
+#define STDC_HEADERS 1
+EOF
+
+fi
+
+echo $ac_n "checking for GNU libc2""... $ac_c" 1>&6
+echo "configure:1236: checking for GNU libc2" >&5
+  if eval "test \"`echo '$''{'knfsd_cv_glibc2'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1241 "configure"
+#include "confdefs.h"
+
+      #include <features.h>
+      #if !defined(__GLIBC__)
+      # error Nope
+      #endif
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1250: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+  rm -rf conftest*
+  knfsd_cv_glibc2=yes
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  knfsd_cv_glibc2=no
+fi
+rm -f conftest*
+fi
+
+  echo "$ac_t""$knfsd_cv_glibc2" 1>&6
+  if test $knfsd_cv_glibc2 = yes; then
+    CFLAGS="$CFLAGS -D_GNU_SOURCE"
+    CXXFLAGS="$CXXFLAGS -D_GNU_SOURCE"
+  fi
+
+
+echo $ac_n "checking for main in -lsocket""... $ac_c" 1>&6
+echo "configure:1273: checking for main in -lsocket" >&5
+ac_lib_var=`echo socket'_'main | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  ac_save_LIBS="$LIBS"
+LIBS="-lsocket  $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1281 "configure"
+#include "confdefs.h"
+
+int main() {
+main()
+; return 0; }
+EOF
+if { (eval echo configure:1288: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  LIBSOCKET="-lnsl"
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+echo $ac_n "checking for main in -lnsl""... $ac_c" 1>&6
+echo "configure:1309: checking for main in -lnsl" >&5
+ac_lib_var=`echo nsl'_'main | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  ac_save_LIBS="$LIBS"
+LIBS="-lnsl  $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1317 "configure"
+#include "confdefs.h"
+
+int main() {
+main()
+; return 0; }
+EOF
+if { (eval echo configure:1324: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  LIBNSL="-lnsl"
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+echo $ac_n "checking for crypt in -lcrypt""... $ac_c" 1>&6
+echo "configure:1345: checking for crypt in -lcrypt" >&5
+ac_lib_var=`echo crypt'_'crypt | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  ac_save_LIBS="$LIBS"
+LIBS="-lcrypt  $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1353 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char crypt();
+
+int main() {
+crypt()
+; return 0; }
+EOF
+if { (eval echo configure:1364: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  LIBCRYPT="-lcrypt"
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+if test "$knfsd_cv_glibc2" = no; then
+    echo $ac_n "checking for daemon in -lbsd""... $ac_c" 1>&6
+echo "configure:1386: checking for daemon in -lbsd" >&5
+ac_lib_var=`echo bsd'_'daemon | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  ac_save_LIBS="$LIBS"
+LIBS="-lbsd  $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1394 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char daemon();
+
+int main() {
+daemon()
+; return 0; }
+EOF
+if { (eval echo configure:1405: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  LIBBSD="-lbsd"
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+fi
+
+
+
+
+
+
+for ac_func in innetgr
+do
+echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
+echo "configure:1435: checking for $ac_func" >&5
+if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1440 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $ac_func(); below.  */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char $ac_func();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+$ac_func();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:1463: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_func_$ac_func=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_func_$ac_func=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_func 1
+EOF
+else
+  echo "$ac_t""no" 1>&6
+fi
+done
+
+
+cat >> confdefs.h <<EOF
+#define NFS_STATEDIR "$statedir"
+EOF
+
+
+
+
+
+trap '' 1 2 15
+cat > confcache <<\EOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs.  It is not useful on other systems.
+# If it contains results you don't want to keep, you may remove or edit it.
+#
+# By default, configure uses ./config.cache as the cache file,
+# creating it if it does not exist already.  You can give configure
+# the --cache-file=FILE option to use a different cache file; that is
+# what configure does when it calls configure scripts in
+# subdirectories, so they share the cache.
+# Giving --cache-file=/dev/null disables caching, for debugging configure.
+# config.status only pays attention to the cache file if you give it the
+# --recheck option to rerun configure.
+#
+EOF
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, don't put newlines in cache variables' values.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(set) 2>&1 |
+  case `(ac_space=' '; set | grep ac_space) 2>&1` in
+  *ac_space=\ *)
+    # `set' does not quote correctly, so add quotes (double-quote substitution
+    # turns \\\\ into \\, and sed turns \\ into \).
+    sed -n \
+      -e "s/'/'\\\\''/g" \
+      -e "s/^\\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\\)=\\(.*\\)/\\1=\${\\1='\\2'}/p"
+    ;;
+  *)
+    # `set' quotes correctly as required by POSIX, so do not add quotes.
+    sed -n -e 's/^\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\)=\(.*\)/\1=${\1=\2}/p'
+    ;;
+  esac >> confcache
+if cmp -s $cache_file confcache; then
+  :
+else
+  if test -w $cache_file; then
+    echo "updating cache $cache_file"
+    cat confcache > $cache_file
+  else
+    echo "not updating unwritable cache $cache_file"
+  fi
+fi
+rm -f confcache
+
+trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+# Any assignment to VPATH causes Sun make to only execute
+# the first set of double-colon rules, so remove it if not needed.
+# If there is a colon in the path, we need to keep it.
+if test "x$srcdir" = x.; then
+  ac_vpsub='/^[        ]*VPATH[        ]*=[^:]*$/d'
+fi
+
+trap 'rm -f $CONFIG_STATUS conftest*; exit 1' 1 2 15
+
+DEFS=-DHAVE_CONFIG_H
+
+# Without the "./", some shells look in PATH for config.status.
+: ${CONFIG_STATUS=./config.status}
+
+echo creating $CONFIG_STATUS
+rm -f $CONFIG_STATUS
+cat > $CONFIG_STATUS <<EOF
+#! /bin/sh
+# Generated automatically by configure.
+# Run this file to recreate the current configuration.
+# This directory was configured as follows,
+# on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
+#
+# $0 $ac_configure_args
+#
+# Compiler output produced by configure, useful for debugging
+# configure, is in ./config.log if it exists.
+
+ac_cs_usage="Usage: $CONFIG_STATUS [--recheck] [--version] [--help]"
+for ac_option
+do
+  case "\$ac_option" in
+  -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+    echo "running \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion"
+    exec \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion ;;
+  -version | --version | --versio | --versi | --vers | --ver | --ve | --v)
+    echo "$CONFIG_STATUS generated by autoconf version 2.13"
+    exit 0 ;;
+  -help | --help | --hel | --he | --h)
+    echo "\$ac_cs_usage"; exit 0 ;;
+  *) echo "\$ac_cs_usage"; exit 1 ;;
+  esac
+done
+
+ac_given_srcdir=$srcdir
+ac_given_INSTALL="$INSTALL"
+
+trap 'rm -fr `echo "config.mk support/include/config.h" | sed "s/:[^ ]*//g"` conftest*; exit 1' 1 2 15
+EOF
+cat >> $CONFIG_STATUS <<EOF
+
+# Protect against being on the right side of a sed subst in config.status.
+sed 's/%@/@@/; s/@%/@@/; s/%g\$/@g/; /@g\$/s/[\\\\&%]/\\\\&/g;
+ s/@@/%@/; s/@@/@%/; s/@g\$/%g/' > conftest.subs <<\\CEOF
+$ac_vpsub
+$extrasub
+s%@SHELL@%$SHELL%g
+s%@CFLAGS@%$CFLAGS%g
+s%@CPPFLAGS@%$CPPFLAGS%g
+s%@CXXFLAGS@%$CXXFLAGS%g
+s%@FFLAGS@%$FFLAGS%g
+s%@DEFS@%$DEFS%g
+s%@LDFLAGS@%$LDFLAGS%g
+s%@LIBS@%$LIBS%g
+s%@exec_prefix@%$exec_prefix%g
+s%@prefix@%$prefix%g
+s%@program_transform_name@%$program_transform_name%g
+s%@bindir@%$bindir%g
+s%@sbindir@%$sbindir%g
+s%@libexecdir@%$libexecdir%g
+s%@datadir@%$datadir%g
+s%@sysconfdir@%$sysconfdir%g
+s%@sharedstatedir@%$sharedstatedir%g
+s%@localstatedir@%$localstatedir%g
+s%@libdir@%$libdir%g
+s%@includedir@%$includedir%g
+s%@oldincludedir@%$oldincludedir%g
+s%@infodir@%$infodir%g
+s%@mandir@%$mandir%g
+s%@VERSION@%$VERSION%g
+s%@statedir@%$statedir%g
+s%@enable_nfsv3@%$enable_nfsv3%g
+s%@kprefix@%$kprefix%g
+s%@secure_statd@%$secure_statd%g
+s%@CC@%$CC%g
+s%@CXX@%$CXX%g
+s%@CPP@%$CPP%g
+s%@INSTALL_PROGRAM@%$INSTALL_PROGRAM%g
+s%@INSTALL_SCRIPT@%$INSTALL_SCRIPT%g
+s%@INSTALL_DATA@%$INSTALL_DATA%g
+s%@LIBSOCKET@%$LIBSOCKET%g
+s%@LIBNSL@%$LIBNSL%g
+s%@LIBCRYPT@%$LIBCRYPT%g
+s%@LIBBSD@%$LIBBSD%g
+
+CEOF
+EOF
+
+cat >> $CONFIG_STATUS <<\EOF
+
+# Split the substitutions into bite-sized pieces for seds with
+# small command number limits, like on Digital OSF/1 and HP-UX.
+ac_max_sed_cmds=90 # Maximum number of lines to put in a sed script.
+ac_file=1 # Number of current file.
+ac_beg=1 # First line for current file.
+ac_end=$ac_max_sed_cmds # Line after last line for current file.
+ac_more_lines=:
+ac_sed_cmds=""
+while $ac_more_lines; do
+  if test $ac_beg -gt 1; then
+    sed "1,${ac_beg}d; ${ac_end}q" conftest.subs > conftest.s$ac_file
+  else
+    sed "${ac_end}q" conftest.subs > conftest.s$ac_file
+  fi
+  if test ! -s conftest.s$ac_file; then
+    ac_more_lines=false
+    rm -f conftest.s$ac_file
+  else
+    if test -z "$ac_sed_cmds"; then
+      ac_sed_cmds="sed -f conftest.s$ac_file"
+    else
+      ac_sed_cmds="$ac_sed_cmds | sed -f conftest.s$ac_file"
+    fi
+    ac_file=`expr $ac_file + 1`
+    ac_beg=$ac_end
+    ac_end=`expr $ac_end + $ac_max_sed_cmds`
+  fi
+done
+if test -z "$ac_sed_cmds"; then
+  ac_sed_cmds=cat
+fi
+EOF
+
+cat >> $CONFIG_STATUS <<EOF
+
+CONFIG_FILES=\${CONFIG_FILES-"config.mk"}
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then
+  # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+  case "$ac_file" in
+  *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'`
+       ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
+  *) ac_file_in="${ac_file}.in" ;;
+  esac
+
+  # Adjust a relative srcdir, top_srcdir, and INSTALL for subdirectories.
+
+  # Remove last slash and all that follows it.  Not all systems have dirname.
+  ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'`
+  if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then
+    # The file is in a subdirectory.
+    test ! -d "$ac_dir" && mkdir "$ac_dir"
+    ac_dir_suffix="/`echo $ac_dir|sed 's%^\./%%'`"
+    # A "../" for each directory in $ac_dir_suffix.
+    ac_dots=`echo $ac_dir_suffix|sed 's%/[^/]*%../%g'`
+  else
+    ac_dir_suffix= ac_dots=
+  fi
+
+  case "$ac_given_srcdir" in
+  .)  srcdir=.
+      if test -z "$ac_dots"; then top_srcdir=.
+      else top_srcdir=`echo $ac_dots|sed 's%/$%%'`; fi ;;
+  /*) srcdir="$ac_given_srcdir$ac_dir_suffix"; top_srcdir="$ac_given_srcdir" ;;
+  *) # Relative path.
+    srcdir="$ac_dots$ac_given_srcdir$ac_dir_suffix"
+    top_srcdir="$ac_dots$ac_given_srcdir" ;;
+  esac
+
+  case "$ac_given_INSTALL" in
+  [/$]*) INSTALL="$ac_given_INSTALL" ;;
+  *) INSTALL="$ac_dots$ac_given_INSTALL" ;;
+  esac
+
+  echo creating "$ac_file"
+  rm -f "$ac_file"
+  configure_input="Generated automatically from `echo $ac_file_in|sed 's%.*/%%'` by configure."
+  case "$ac_file" in
+  *Makefile*) ac_comsub="1i\\
+# $configure_input" ;;
+  *) ac_comsub= ;;
+  esac
+
+  ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"`
+  sed -e "$ac_comsub
+s%@configure_input@%$configure_input%g
+s%@srcdir@%$srcdir%g
+s%@top_srcdir@%$top_srcdir%g
+s%@INSTALL@%$INSTALL%g
+" $ac_file_inputs | (eval "$ac_sed_cmds") > $ac_file
+fi; done
+rm -f conftest.s*
+
+# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where
+# NAME is the cpp macro being defined and VALUE is the value it is being given.
+#
+# ac_d sets the value in "#define NAME VALUE" lines.
+ac_dA='s%^\([  ]*\)#\([        ]*define[       ][      ]*\)'
+ac_dB='\([     ][      ]*\)[^  ]*%\1#\2'
+ac_dC='\3'
+ac_dD='%g'
+# ac_u turns "#undef NAME" with trailing blanks into "#define NAME VALUE".
+ac_uA='s%^\([  ]*\)#\([        ]*\)undef\([    ][      ]*\)'
+ac_uB='\([     ]\)%\1#\2define\3'
+ac_uC=' '
+ac_uD='\4%g'
+# ac_e turns "#undef NAME" without trailing blanks into "#define NAME VALUE".
+ac_eA='s%^\([  ]*\)#\([        ]*\)undef\([    ][      ]*\)'
+ac_eB='$%\1#\2define\3'
+ac_eC=' '
+ac_eD='%g'
+
+if test "${CONFIG_HEADERS+set}" != set; then
+EOF
+cat >> $CONFIG_STATUS <<EOF
+  CONFIG_HEADERS="support/include/config.h"
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+fi
+for ac_file in .. $CONFIG_HEADERS; do if test "x$ac_file" != x..; then
+  # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+  case "$ac_file" in
+  *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'`
+       ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
+  *) ac_file_in="${ac_file}.in" ;;
+  esac
+
+  echo creating $ac_file
+
+  rm -f conftest.frag conftest.in conftest.out
+  ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"`
+  cat $ac_file_inputs > conftest.in
+
+EOF
+
+# Transform confdefs.h into a sed script conftest.vals that substitutes
+# the proper values into config.h.in to produce config.h.  And first:
+# Protect against being on the right side of a sed subst in config.status.
+# Protect against being in an unquoted here document in config.status.
+rm -f conftest.vals
+cat > conftest.hdr <<\EOF
+s/[\\&%]/\\&/g
+s%[\\$`]%\\&%g
+s%#define \([A-Za-z_][A-Za-z0-9_]*\) *\(.*\)%${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD}%gp
+s%ac_d%ac_u%gp
+s%ac_u%ac_e%gp
+EOF
+sed -n -f conftest.hdr confdefs.h > conftest.vals
+rm -f conftest.hdr
+
+# This sed command replaces #undef with comments.  This is necessary, for
+# example, in the case of _POSIX_SOURCE, which is predefined and required
+# on some systems where configure will not decide to define it.
+cat >> conftest.vals <<\EOF
+s%^[   ]*#[    ]*undef[        ][      ]*[a-zA-Z_][a-zA-Z_0-9]*%/* & */%
+EOF
+
+# Break up conftest.vals because some shells have a limit on
+# the size of here documents, and old seds have small limits too.
+
+rm -f conftest.tail
+while :
+do
+  ac_lines=`grep -c . conftest.vals`
+  # grep -c gives empty output for an empty file on some AIX systems.
+  if test -z "$ac_lines" || test "$ac_lines" -eq 0; then break; fi
+  # Write a limited-size here document to conftest.frag.
+  echo '  cat > conftest.frag <<CEOF' >> $CONFIG_STATUS
+  sed ${ac_max_here_lines}q conftest.vals >> $CONFIG_STATUS
+  echo 'CEOF
+  sed -f conftest.frag conftest.in > conftest.out
+  rm -f conftest.in
+  mv conftest.out conftest.in
+' >> $CONFIG_STATUS
+  sed 1,${ac_max_here_lines}d conftest.vals > conftest.tail
+  rm -f conftest.vals
+  mv conftest.tail conftest.vals
+done
+rm -f conftest.vals
+
+cat >> $CONFIG_STATUS <<\EOF
+  rm -f conftest.frag conftest.h
+  echo "/* $ac_file.  Generated automatically by configure.  */" > conftest.h
+  cat conftest.in >> conftest.h
+  rm -f conftest.in
+  if cmp -s $ac_file conftest.h 2>/dev/null; then
+    echo "$ac_file is unchanged"
+    rm -f conftest.h
+  else
+    # Remove last slash and all that follows it.  Not all systems have dirname.
+      ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'`
+      if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then
+      # The file is in a subdirectory.
+      test ! -d "$ac_dir" && mkdir "$ac_dir"
+    fi
+    rm -f $ac_file
+    mv conftest.h $ac_file
+  fi
+fi; done
+
+EOF
+cat >> $CONFIG_STATUS <<EOF
+
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+
+exit 0
+EOF
+chmod +x $CONFIG_STATUS
+rm -fr confdefs* $ac_clean_files
+test "$no_create" = yes || ${CONFIG_SHELL-/bin/sh} $CONFIG_STATUS || exit 1
+
diff --git a/configure.in b/configure.in
new file mode 100644 (file)
index 0000000..1d91f5b
--- /dev/null
@@ -0,0 +1,85 @@
+dnl Process this file with autoconf to produce a configure script.
+dnl
+AC_INIT(rules.mk)
+AC_PREFIX_DEFAULT(/usr)
+
+# The nfs-utils version
+VERSION="nfs-utils 0.1"
+AC_SUBST(VERSION)
+
+dnl *************************************************************
+dnl * Define the set of applicable options
+dnl *************************************************************
+AC_ARG_WITH(statedir,
+       [  --with-statedir=/foo    use state dir /foo [/var/lib/nfs]],
+       statedir=$withval,
+       statedir=/var/lib/nfs)
+       AC_SUBST(statedir)
+AC_ARG_ENABLE(nfsv3,
+       [  --enable-nfsv3          enable support for NFSv3],
+       enable_nfsv3=$enableval,
+       enable_nfsv3=no)
+       if test "$enable_nfsv3" = yes; then
+               AC_DEFINE(NFS3_SUPPORTED)
+       else
+               enable_nfsv3=
+       fi
+       AC_SUBST(enable_nfsv3)
+AC_ARG_ENABLE(kprefix,
+       [  --enable-kprefix       install progs as rpc.knfsd etc],
+       test "$enableval" = "yes" && kprefix=k,
+       kprefix=)
+       AC_SUBST(kprefix)
+AC_ARG_ENABLE(secure-statd,
+       [  --enable-secure-statd  Only lockd can use statd (security)],
+       test "$enableval" = "yes" && secure_statd=yes,
+       secure_statd=no)
+       if test "$secure_statd" = yes; then
+               AC_DEFINE(RESTRICTED_STATD)
+       fi
+       AC_SUBST(secure_statd)
+dnl AC_ARG_ENABLE(frob,        enable frobnicator,,                enable_frob=test)
+AC_CONFIG_HEADER(support/include/config.h)
+
+AC_PROG_CC
+AC_PROG_CXX
+AC_PROG_CPP
+AC_PROG_INSTALL
+AC_STDC_HEADERS
+AC_GNULIBC
+dnl AC_LN_SF
+dnl AC_BSD_SIGNALS
+
+dnl *************************************************************
+dnl * Check for required librarues
+dnl *************************************************************
+AC_CHECK_LIB(socket, main, [LIBSOCKET="-lnsl"])
+AC_CHECK_LIB(nsl, main,        [LIBNSL="-lnsl"])
+AC_CHECK_LIB(crypt, crypt, [LIBCRYPT="-lcrypt"])
+if test "$knfsd_cv_glibc2" = no; then
+    AC_CHECK_LIB(bsd, daemon, [LIBBSD="-lbsd"])
+fi
+AC_SUBST(LIBSOCKET)
+AC_SUBST(LIBNSL)
+AC_SUBST(LIBCRYPT)
+AC_SUBST(LIBBSD)
+
+dnl *************************************************************
+dnl Check for headers
+dnl *************************************************************
+dnl AC_HAVE_HEADERS(string.h)
+
+dnl *************************************************************
+dnl Check for functions
+dnl *************************************************************
+AC_HAVE_FUNCS(innetgr)
+
+dnl *************************************************************
+dnl Export some path names to config.h
+dnl *************************************************************
+AC_DEFINE_UNQUOTED(NFS_STATEDIR, "$statedir")
+
+AC_SUBST(LDFLAGS)
+AC_SUBST(CXXFLAGS)
+AC_SUBST(CFLAGS)
+AC_OUTPUT(config.mk)
diff --git a/etc/redhat/nfs.init b/etc/redhat/nfs.init
new file mode 100755 (executable)
index 0000000..6da8e0c
--- /dev/null
@@ -0,0 +1,102 @@
+#!/bin/sh
+#
+# nfs           This shell script takes care of starting and stopping
+#               the NFS services.
+#
+# chkconfig: - 60 20
+# description: NFS is a popular protocol for file sharing across TCP/IP \
+#              networks. This service provides NFS server functionality, \
+#              which is configured via the /etc/exports file.
+# probe: true
+
+# Source function library.
+. /etc/rc.d/init.d/functions
+
+# Source networking configuration.
+if [ ! -f /etc/sysconfig/network ]; then
+    exit 0
+fi
+
+. /etc/sysconfig/network
+
+# Check that networking is up.
+[ ${NETWORKING} = "no" ] && exit 0
+
+[ -x /usr/sbin/rpc.nfsd ] || exit 0
+[ -x /usr/sbin/rpc.mountd ] || exit 0
+[ -x /usr/sbin/exportfs ] || exit 0
+[ -s /etc/exports ] || exit 0
+
+# Number of servers to be started uo by default
+RPCNFSDCOUNT=8
+# No NFS V3.
+RPCMOUNTDOPTS="--no-nfs-version 3"
+
+# See how we were called.
+case "$1" in
+  start)
+       # Start daemons.
+       action "Starting NFS services: " /usr/sbin/exportfs -r
+       echo -n "Starting NFS quotas: "
+       daemon rpc.rquotad
+       echo
+       echo -n "Starting NFS mountd: "
+       daemon rpc.mountd $RPCMOUNTDOPTS
+       echo
+       echo -n "Starting NFS daemon: "
+       daemon rpc.nfsd $RPCNFSDCOUNT
+       echo
+       touch /var/lock/subsys/nfs
+       ;;
+  stop)
+       # Stop daemons.
+       action "Shutting down NFS services: " /usr/sbin/exportfs -au
+       echo -n "Shutting down NFS mountd: "
+       killproc rpc.mountd
+       echo
+       echo -n "Shutting down NFS daemon: "
+       killproc nfsd
+       echo
+       echo -n "Shutting down NFS quotas: "
+       killproc rpc.rquotad
+       echo
+       rm -f /var/lock/subsys/nfs
+       ;;
+  status)
+       status rpc.mountd
+       status nfsd
+       status rpc.rquotad
+       ;;
+  restart)
+       echo -n "Restarting NFS services: "
+       echo -n "rpc.mountd "
+       killproc rpc.mountd
+       daemon rpc.mountd $RPCMOUNTDOPTS
+       /usr/sbin/exportfs -r
+       touch /var/lock/subsys/nfs
+       echo "done."
+       ;;
+  reload)
+       /usr/sbin/exportfs -r
+       touch /var/lock/subsys/nfs
+       ;;
+  probe)
+       if [ ! -f /var/lock/subsys/nfs ] ; then
+         echo start; exit 0
+       fi
+       /sbin/pidof rpc.mountd >/dev/null 2>&1; MOUNTD="$?"
+       /sbin/pidof nfsd >/dev/null 2>&1; NFSD="$?"
+       if [ $MOUNTD = 1 -o $NFSD = 1 ] ; then
+         echo restart; exit 0
+       fi
+       if [ /etc/exports -nt /var/lock/subsys/nfs ] ; then
+         echo reload; exit 0
+       fi
+       ;;
+  *)
+       echo "Usage: nfs {start|stop|status|restart|reload}"
+       exit 1
+esac
+
+exit 0
+
diff --git a/etc/redhat/nfslock.init b/etc/redhat/nfslock.init
new file mode 100755 (executable)
index 0000000..1442637
--- /dev/null
@@ -0,0 +1,82 @@
+#!/bin/sh
+#
+# nfslock       This shell script takes care of starting and stopping
+#               the NFS file locking service.
+#
+# chkconfig: 345 60 20
+# description: NFS is a popular protocol for file sharing across \
+#             TCP/IP networks. This service provides NFS file \
+#             locking functionality.
+# probe: true
+
+# Source function library.
+. /etc/rc.d/init.d/functions
+
+# Source networking configuration.
+if [ ! -f /etc/sysconfig/network ]; then
+    exit 0
+fi
+
+. /etc/sysconfig/network
+
+# Check that networking is up.
+[ ${NETWORKING} = "no" ] && exit 0
+
+[ -x /usr/sbin/rpc.lockd ] || exit 0
+[ -x /usr/sbin/rpc.statd ] || exit 0
+
+# See how we were called.
+case "$1" in
+  start)
+       # Start daemons.
+       echo "Starting NFS file locking services: "
+       echo -n "Starting NFS lockd: "
+       daemon rpc.lockd
+       echo
+       echo -n "Starting NFS statd: "
+       daemon rpc.statd
+       echo
+       touch /var/lock/subsys/nfslock
+       ;;
+  stop)
+       # Stop daemons.
+       echo "Shutting down NFS file locking services: "
+       echo -n "Shutting down NFS lockd: "
+       killproc lockd
+       echo
+       echo -n "Shutting down NFS statd: "
+       killproc rpc.statd
+       echo
+       rm -f /var/lock/subsys/nfslock
+       ;;
+  status)
+       status lockd
+       status rpc.statd
+       ;;
+  restart)
+       echo -n "Restarting NFS file locking services: "
+       echo -n "rpc.lockd "
+       killproc lockd
+       daemon rpc.lockd
+       echo -n "rpc.statd "
+       killproc rpc.statd
+       daemon rpc.statd
+       touch /var/lock/subsys/nfslock
+       echo "done."
+       ;;
+  probe)
+       if [ ! -f /var/lock/subsys/nfslock ] ; then
+         echo start; exit 0
+       fi
+       /sbin/pidof rpc.statd >/dev/null 2>&1; STATD="$?"
+       /sbin/pidof lockd >/dev/null 2>&1; LOCKD="$?"
+       if [ $STATD = 1 -o $LOCKD = 1 ] ; then
+         echo restart; exit 0
+       fi
+       ;;
+  *)
+       echo "Usage: nfslock {start|stop|status|restart}"
+       exit 1
+esac
+
+exit 0
diff --git a/install-sh b/install-sh
new file mode 100644 (file)
index 0000000..89fc9b0
--- /dev/null
@@ -0,0 +1,238 @@
+#! /bin/sh
+#
+# install - install a program, script, or datafile
+# This comes from X11R5.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch.
+#
+
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+mkdirprog="${MKDIRPROG-mkdir}"
+
+tranformbasename=""
+transform_arg=""
+instcmd="$mvprog"
+chmodcmd="$chmodprog 0755"
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=""
+dst=""
+dir_arg=""
+
+while [ x"$1" != x ]; do
+    case $1 in
+       -c) instcmd="$cpprog"
+           shift
+           continue;;
+
+       -d) dir_arg=true
+           shift
+           continue;;
+
+       -m) chmodcmd="$chmodprog $2"
+           shift
+           shift
+           continue;;
+
+       -o) chowncmd="$chownprog $2"
+           shift
+           shift
+           continue;;
+
+       -g) chgrpcmd="$chgrpprog $2"
+           shift
+           shift
+           continue;;
+
+       -s) stripcmd="$stripprog"
+           shift
+           continue;;
+
+       -t=*) transformarg=`echo $1 | sed 's/-t=//'`
+           shift
+           continue;;
+
+       -b=*) transformbasename=`echo $1 | sed 's/-b=//'`
+           shift
+           continue;;
+
+       *)  if [ x"$src" = x ]
+           then
+               src=$1
+           else
+               # this colon is to work around a 386BSD /bin/sh bug
+               :
+               dst=$1
+           fi
+           shift
+           continue;;
+    esac
+done
+
+if [ x"$src" = x ]
+then
+       echo "install:  no input file specified"
+       exit 1
+else
+       true
+fi
+
+if [ x"$dir_arg" != x ]; then
+       dst=$src
+       src=""
+       
+       if [ -d $dst ]; then
+               instcmd=:
+       else
+               instcmd=mkdir
+       fi
+else
+
+# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
+# might cause directories to be created, which would be especially bad 
+# if $src (and thus $dsttmp) contains '*'.
+
+       if [ -f $src -o -d $src ]
+       then
+               true
+       else
+               echo "install:  $src does not exist"
+               exit 1
+       fi
+       
+       if [ x"$dst" = x ]
+       then
+               echo "install:  no destination specified"
+               exit 1
+       else
+               true
+       fi
+
+# If destination is a directory, append the input filename; if your system
+# does not like double slashes in filenames, you may need to add some logic
+
+       if [ -d $dst ]
+       then
+               dst="$dst"/`basename $src`
+       else
+               true
+       fi
+fi
+
+## this sed command emulates the dirname command
+dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
+
+# Make sure that the destination directory exists.
+#  this part is taken from Noah Friedman's mkinstalldirs script
+
+# Skip lots of stat calls in the usual case.
+if [ ! -d "$dstdir" ]; then
+defaultIFS='   
+'
+IFS="${IFS-${defaultIFS}}"
+
+oIFS="${IFS}"
+# Some sh's can't handle IFS=/ for some reason.
+IFS='%'
+set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
+IFS="${oIFS}"
+
+pathcomp=''
+
+while [ $# -ne 0 ] ; do
+       pathcomp="${pathcomp}${1}"
+       shift
+
+       if [ ! -d "${pathcomp}" ] ;
+        then
+               $mkdirprog "${pathcomp}"
+       else
+               true
+       fi
+
+       pathcomp="${pathcomp}/"
+done
+fi
+
+if [ x"$dir_arg" != x ]
+then
+       $doit $instcmd $dst &&
+
+       if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
+       if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
+       if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
+       if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
+else
+
+# If we're going to rename the final executable, determine the name now.
+
+       if [ x"$transformarg" = x ] 
+       then
+               dstfile=`basename $dst`
+       else
+               dstfile=`basename $dst $transformbasename | 
+                       sed $transformarg`$transformbasename
+       fi
+
+# don't allow the sed command to completely eliminate the filename
+
+       if [ x"$dstfile" = x ] 
+       then
+               dstfile=`basename $dst`
+       else
+               true
+       fi
+
+# Make a temp file name in the proper directory.
+
+       dsttmp=$dstdir/#inst.$$#
+
+# Move or copy the file name to the temp name
+
+       $doit $instcmd $src $dsttmp &&
+
+       trap "rm -f ${dsttmp}" 0 &&
+
+# and set any options; do chmod last to preserve setuid bits
+
+# If any of these fail, we abort the whole thing.  If we want to
+# ignore errors from any of these, just make sure not to ignore
+# errors from the above "$doit $instcmd $src $dsttmp" command.
+
+       if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
+       if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
+       if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
+       if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
+
+# Now rename the file to the real destination.
+
+       $doit $rmcmd -f $dstdir/$dstfile &&
+       $doit $mvcmd $dsttmp $dstdir/$dstfile 
+
+fi &&
+
+
+exit 0
diff --git a/linux-nfs/ChangeLog b/linux-nfs/ChangeLog
new file mode 100644 (file)
index 0000000..11ffe15
--- /dev/null
@@ -0,0 +1,78 @@
+
+Release 0.4.11
+
+ *     Added async writes.
+ *     Fixed bug where two rpciod's would be started when insmod'ing
+       both nfs.o and nfsd.o.
+
+Release 0.4.12
+
+ *     Fixed compile problem after renaming some debug macros.
+ *     Improved readdir cache, which can now hold up to 16 (configurable)
+       readdir replies.
+ *     Fixed async write bug(s)
+ *     client file locking now does at least lock/unlock without
+       crashing the machine
+ *     Started to work on NFS swapping
+ *     nfs_get_super no longer requires the file handle passed by
+       mount but does a straight xprt_create_proto().
+ *     TCP reconnect should work now (not yet tested for long disconnect
+       periods, but it does work if you kill and restart nfsd).
+
+Release 0.4.13
+
+ *     More writeback bugs removed.
+ *     Added a modified (and ansified) tirpc rpcgen to get rid of all
+       the warnings in files generated from *.x descriptions. That old Sun
+       code is a real mess.
+ *     Cleaned up nfsd export handling a bit. All syscalls now
+       take dev/ino rather than the pathname.
+ *     Added sysctl interface to set/get debug flags (see tools/rpcdebug).
+ *     Cleaned up Makefiles.
+ *     (experimental) Gathered writes for nfsd (use the wdelay option in
+       /etc/exports).
+ *     Fixed silly bug in nfs_readdir (the in-place decoding of readdir
+       replies requires a temporary buffer).
+ *     Fixed readdir bug in nfsd (long directories were truncated).
+
+Release 0.4.14
+
+ *     Upgraded to kernel 2.0.23
+ *     Fixed bug in rpcdebug
+ *     readdir still didn't work right in nfsd. Argh!
+ *     nfsd would refuse to create symlinks with slashes in them:-)
+ *     nfsd's RPC reply cache should now work again.
+ *     Heavily modified rpc.statd for more robust callback/notify handling
+
+Release 0.4.17
+
+ *     Upgraded to kernel 2.1.14
+ *     Got lockd working with HPUX in most areas.
+
+Release 0.4.19
+
+ *     RPC server UDP sockets now receive the sk_buff directly rather
+       than going through sock->ops->recvmsg.
+       Also got rid of all those cli/sti's and replaced them with
+       disable_bh/enable_bh calls.
+ *     Fixed a bug in nfsd's handling of rename and friends.
+
+Release 0.4.20
+
+ *     Some bugfixes, esp in the writeback code
+ *     Avoid some unnecessary cli/sti pairs
+ *     Added nhfsstone
+
+Release 0.4.21
+
+ *     Minor bugfixes
+ *     Moved to post-2.1.16 module handling code
+
+Release 0.4.22
+
+ *     Fixed a bug that made rpcinfo -u host nlockmgr provoke a kernel
+       oops.
+ *     Upgraded to mount-2.6b
+ *     Added NFSv3 support to mountd and nfsd
+ *     Made sure it compiles with glibc2.
+
diff --git a/linux-nfs/INSTALL b/linux-nfs/INSTALL
new file mode 100644 (file)
index 0000000..351f733
--- /dev/null
@@ -0,0 +1,11 @@
+
+Even though the Makefiles offer a `make install' instruction,
+I would suggest against using it yet. I have run nfsd and the nfs
+client without kernel oopses for a while, but the picture may change
+if you start playing with lockd. Automatic installation may not even
+work for the kernel makefiles yet.
+
+I'd therefore advise that you use a separate Linux box for testing
+if you have one. Use the etc/copy script to copy all modules and
+support programs, and run the ins script to start the show. rmm
+will clean up afterwards (provided you didn't trigger an oops).
diff --git a/linux-nfs/KNOWNBUGS b/linux-nfs/KNOWNBUGS
new file mode 100644 (file)
index 0000000..b0ecd5c
--- /dev/null
@@ -0,0 +1,37 @@
+
+nfsd:
+
+ *     We currently keep the inode in the exports struct. This is
+       a bad idea with directories that are intended to be used as
+       a mount point. Must store the file name instead and do a
+       lookup when getfh is called. Yuck!
+
+       Even yuckier: what do we do about exports matching when we
+       can't keep the inode number?
+
+ *     stating a file on remote cdrom returns st_blocks == 0 for some
+       apps.
+
+ *     Should allow multiple exports per dev if one of the directories
+       isn't a subdir of the other.
+
+nfsclnt:
+
+ *     On some occasions, an EAGAIN reported by the transport layer
+       will be propagated to the VFS.
+ *     Some operations do not seem to release the inode properly, so
+       unmounting the device fails.
+
+lockd:
+
+ *     Handle portmap registration in a separate thread. portmap may
+       not be running when we try to mount the first NFS volume (esp.
+       when mounting /usr).
+
+ *     Does not inform rpc.statd when hosts no longer require
+       monitoring; hosts are incorrectly monitored until next system
+       reboot.
+
+exportfs/mountd:
+
+ *     Export handling is reported to do odd things at times.
diff --git a/linux-nfs/NEW b/linux-nfs/NEW
new file mode 100644 (file)
index 0000000..43f5c69
--- /dev/null
@@ -0,0 +1,319 @@
+This is the Linux kernel NFS daemon 1.4.7. It is based on linux-nfs
+0.4.22. It is tested on Linux/alpha and Linux/x86 running glibc 2.1.1.
+
+WARNING: The NFS servers in Linux 2.2 to 2.2.11 are not compatible with
+other NFS client implemenations. If you plan to use Linux 2.2.x as an
+NFS server for non-Linux NFS clients, you should apply the patches
+enlosed here.
+
+linux-2.2.7-sunrpc.patch, nfsd-2.2.7-2.lockd.patch, nfsd-2.2.7-3.patch
+and nfsd-2.2.7-nfsfh.patch are required for Linux 2.2.7 to 2.2.11.
+For other kernel versions, they have to be applied by hand if they are
+still needed.
+
+For Linux 2.2.7 to 2.2.10, nfsd-2.2.7-1.lock.patch is also required.
+
+I made my knfsd package available only because I use it and noone else
+seems to maintain it. But I don't have much time to really work on it.
+I will only fix bugs in the NFS utilities and serious kernel NFS bugs
+which I can duplcate easily. If you have any kernel NFS server problem,
+please report it to the Linux kernel mailing list. If it can be
+reproduced with Linux NFS server and client in less than 5 minutes, you
+can also send me a copy in addition to sending it to the Linux kernel
+mailing list. I may take a look when I have time. However I will collect
+kernel NFS related patches. Contributions are more than welcome.
+
+The NFS lock only works with lockd. Please make sure the portmapper,
+portmap, is started before mounting NFS.
+
+Changes from knfsd 1.4.6:
+
+1. Fix a typo in knfslock.init.
+2. A new kernel patch, nfsd-2.2.7-1.lock.patch, to fix some NFS lock
+   bugs.
+
+Changes from knfsd 1.4.5:
+
+1. Rename /var/lib/nfs/xtab.export to /var/lib/nfs/etab.
+
+Changes from knfsd 1.4.4:
+
+1. Try to fix mountd performance problem by introducing
+   /var/lib/nfs/xtab.export. That is
+   
+  a. "exportfs" reads from /var/lib/nfs/xtab and writes to
+     /var/lib/nfs/xtab.export.
+  b. "mountd" reads from /var/lib/nfs/xtab.export and writes to
+     /var/lib/nfs/xtab.
+
+  The idea is "mountd" doesn't have to read /var/lib/nfs/xtab, which
+  is very expensive.
+
+Changes from knfsd 1.4.3:
+
+1. nfsd-2.2.7-nfsfh.patch, a new kernel patch for filehandle.
+2. nfsd-2.2.7-2.lockd.patch, a new patch for lockd.
+3. Misc bug fixes.
+
+Changes from knfsd 1.4.2:
+
+1. A mountd patch so that the syslog reports unknown requests, and also
+   reports *what* is being (un)mounted, from Piete Brooks
+   <Piete.Brooks@cl.cam.ac.uk>.
+2. Fix knfsd.init for restart.
+3. Add knfslock.init.
+4. knfsd-compat.spec is removed.
+5. nfsd-2.2.7-lockd.patch, a patch to start lockd independent of
+   nfs and nfsd.
+
+Changes from knfsd 1.4.1:
+
+1. Resolve symlink for umount from Piete.Brooks@cl.cam.ac.uk (Piete
+   Brooks)
+2. Fix knfsd.init for statd.
+
+Changes from knfsd 1.4:
+
+1. nfsd-2.2.7-3.patch. This is the only patch you need for Linux 2.2.7
+   to 2.2.10.
+2. Remove
+       nfsd-2.2.5-1.patch
+       nfsd-2.2.5-3.patch
+       nfsd-2.2.8-1.patch
+       nfsd-2.2.7-iget.diff
+       nfsd-2.2.5-nfsfh.diff
+       nfsd-2.2.5-file.patch
+       nfsd-2.2.7-quota.patch
+       nfsd-2.2.7-mknod.patch
+3. Statd update by Jeff Uphoff <juphoff@transmeta.com>.
+4. netgroups patch from Peter Breitenlohner <peb@mppmu.mpg.de>.
+5. Add option checking to exportfs.
+
+Changes from knfsd 1.3.3b:
+
+1. Add linux-2.2.7-sunrpc.patch for a SMP bug in sunrpc.
+2. Add --port/-P to nfsd/mountd, by Jeff Johnson <jbj@redhat.com>.
+3. Add nfsd.8, mountd.8 and statd.man, by Olaf Kirch
+   <okir@monad.swb.de>.
+4. Update nfsstat.man by Olaf Kirch <okir@monad.swb.de>.
+5. Statd fix by Jeff Uphoff <juphoff@transmeta.com>.
+6. Remove knfsd-nok.patch.
+
+Changes from knfsd 1.3.3a:
+
+1. Fix stdin/stdout/stdout handling in mountd.
+2. nfsd-2.2.7-mknod.patch. A patch for mknod.
+3. nfsd-2.2.7-quota.patch. A patch for quota.
+
+Changes from knfsd 1.3.3:
+
+1. Fix hostname matching for wildcard, subnet and netgroup.
+
+Changes from knfsd 1.3.2:
+
+1. Modified mountd to allow clients without IP address to hostname map.
+
+Changes from knfsd 1.3.1a:
+
+1. nfsd-2.2.5-3.patch. This is the only patch you need for Linux 2.2.5.
+
+Changes from knfsd 1.3.1:
+
+1. A patch for knfsd.spec from Markus Linnala <maage@cs.tut.fi>.
+
+Changes from knfsd 1.3a:
+
+1. nfsd-2.2.8-1.patch. This is the only patch you need for Linux 2.2.8.
+2. nfsd-2.2.7-2.patch. This is the only patch you need for Linux 2.2.7.
+
+Changes from knfsd 1.3:
+
+1. Adding "--no-nfs-version 3" to mountd in knfsd.init from RedHat 6.0.
+
+Changes from knfsd 1.2.2a:
+
+1. Updated knfsd.init from RedHat 6.0.
+2. nfsd-2.2.7-1.patch. This is the only patch you need for Linux 2.2.7.
+3. Misc updates from RedHat 6.0.
+
+Changes from knfsd 1.2.2:
+
+1. Make the default NFS server kernel thread to 8 in the rc script.
+
+Changes from knfsd 1.2:
+
+1. Moved knfsd.spec to knfsd-compat.spec.
+2. Update knfsd.spec from knfsd-981204-3.src.rpm.
+3. Fix the squash_[ug]id parsing in /etc/exports from Anders
+   Hammarquist <iko@cd.chalmers.se>.
+4. nfsd-2.2.5-file.patch to clear the bogus bit for MKDIR and SYMLINK.
+5. nfsd-2.2.5-1.patch. A NFS patch based on nfsd-2.2.3-1.patch for
+   Linux 2.2.5.
+
+Changes from knfsd 1.1:
+
+1. Remove
+       cache-2.1.131-1.patch
+       linux-2.1.1xx.diff
+       lock-2.1.131.diff
+       lock-2.1.1xx.diff
+       nfsd-2.1.127-5.patch
+       nullproc-2.1.1xx.diff
+       procfs-2.1.127.patch
+       quota-2.1.1xx.diff
+       root-2.1.1xx.diff
+       socket-2.1.1xx.diff
+       sunrpc-2.1.123-1.patch
+2. locks-2.2.3.diff. A patch for file lock.
+3. nfsd-2.2.3-1.patch. A NFS patch by "G. Allen Morris III"
+(gam3@acm.org).
+
+Changes from knfsd 1.0:
+
+1. Handle broken /var/lib/nfs/rmtab.
+2. Handle lower/upper cases in wildcard hostnames in /etc/exports.
+
+Changes from knfsd-981204:
+
+1. Modify etc/rc.nfsd to check /var/lib/nfs/rmtab during startup.
+2. Add knfsd.spec for RedHat 5. Need nfs-server-2.2beta37-1.1.src.rpm
+   and initscripts-3.78.1-2.src.rpm.
+3. Add support for "make install prefix=...".
+
+Changes from knfsd-981122:
+
+1. Modify etc/rc.nfsd and etc/rc.nfsfs to handle statd during shutdown.
+2. Remove maximum knfsd count checking.
+3. Clean up mountd.
+4. cache-2.1.131-1.patch from G. Allen Morris III (gam3@acm.org).
+5. lock-2.1.131.diff. A nfsd lock patch for Linux 2.1.131.
+
+Changes from knfsd-981113:
+
+1. procfs-2.1.127.patch from G. Allen Morris III (gam3@acm.org).
+2. Modify etc/rc.nfsd and etc/rc.nfsfs to better handle statd.
+3. Fix the sub-mounted directories.
+
+Changes from knfsd-981022:
+
+1. Fix buffer overruns from Peter Benie <pjb1008@cus.cam.ac.uk>.
+2. Fix hostname matching.
+3. Correctly handle dupilcations in /etc/exports.
+4. Add -F flag to statd.
+5. nfsd-2.1.127-5.patch from G. Allen Morris III (gam3@acm.org).
+
+Changes from knfsd-981014:
+
+1. lock-2.1.1xx.diff. A nfsd lock patch.
+2. nullproc-2.1.1xx.diff. Allow any clients to call the nfsd NULL proc.
+3. Add etc/rc.nfsfs to handle statd.
+3. Update etc/rc.nfsd to handle statd.
+4. nfsd-2.1.125-2.patch from G. Allen Morris III (gam3@acm.org).
+5. Fix inet_ntoa usage in statd.
+
+Changes from knfsd-981010:
+
+1. Check client aliases when matching for wildcard client hostnames.
+2. Fix memory leak in mountd.
+3. Fix filename in nfsd-2.1.125-1.patch.
+
+Changes from knfsd-980930:
+
+1. nfsd-2.1.125-1.patch from G. Allen Morris III (gam3@acm.org) and me.
+2. Fix the hostent bugs in mountd and statd.
+3. Remove "kexportfs -au" for "rc.nfsd stop".
+
+Changes from knfsd-980925:
+
+1. socket-2.1.1xx.diff for creating socket on NFS client.
+2. There is a knsfd root_squash patch for Linux 2.1.1xx,
+   root-2.1.1xx.diff. It is only tested on linux 2.1.123.
+   It also fixes the server side 0711 mode bug.
+3. sunrpc-2.1.123-1.patch from Bill Hawes <whawes@transmeta.com>.
+4. nfsd-2.1.122-3.patch from G. Allen Morris III (gam3@acm.org).
+5. Various buffer overrun changes.
+6. Fix mountd to check the duplicated entry in rmtab.
+7. Change exportfs to ignore warnings for "-r".
+8. Fix showmount -e.
+
+Changes from knfsd-980922:
+
+1. nfsd-2.1.121-4.patch from G. Allen Morris III (gam3@acm.org).
+2. Make async as default for export. It matches the user space NFS
+   server.
+
+Changes from knfsd-980920:
+
+1. Add NFS mount version flags to mountd. Change rc.nfsd to disable
+   NFS V3 for mountd.
+2. Fix client hostname.
+3. rc.nfsd runs kexportfs with -r instead of -a for restart and reload.
+
+Changes from knfsd-980915:
+
+1. There is a knsfd quota patch for Linux 2.1.1xx, quota-2.1.1xx.diff.
+   It is only tested on linux 2.1.122.
+2. The submount pathname is removed from the xtab file.
+3. rc.nfsd runs kexportfs with -r instead of -a for start.
+4. Fix kshowmount -e.
+5. Fix hostname matching.
+6. Fix compiling on libc 5.
+
+Changes from knfsd-980910:
+
+1. nfsd-2.1.121-3.patch from G. Allen Morris III (gam3@acm.org).
+2. A new flag, -r, for exportfs.
+3. Don't put an entry in xtab if kernel rejects it.
+4. Use the official hostname when checking if 2 hostnames are the same.
+5. Allow submounts.
+
+It is available at
+
+ftp://ftp.varesearch.com/pub/support/hjl/knfsd/knfsd-1.4.7.tar.gz
+ftp://ftp.varesearch.com/pub/support/hjl/knfsd/knfsd-1.4.6-1.4.7.diff.gz
+ftp://ftp.kernel.org/pub/linux/devel/gcc/knfsd-1.4.7.tar.gz
+ftp://ftp.kernel.org/pub/linux/devel/gcc/knfsd-1.4.6-1.4.7.diff.gz
+
+You have to apply the patch, locks-2.2.3.diff, to the Linux kernel
+first. It fixes quite some file lock bugs. That patch is against Linux
+2.2.3. If your kernel is different, you have to apply it by hand.
+
+nfsd-2.2.3-1.patch is a new knfsd patch against linux 2.2.3 from
+"G. Allen Morris III" (gam3@acm.org). It works for me on x86 and alpha.
+It is needed for the none-Linux NFS clients. You can get Allen's
+current patch from
+
+http://www.CSUA.Berkeley.EDU/~gam3/knfsd/
+
+nfsd-2.2.7-3.patch is based on nfsd-2.2.3-1.patch for Linux 2.2.7. If
+you use Linux 2.2.7, you should apply nfsd-2.2.7-3.patch instead of
+nfsd-2.2.3-1.patch. Please don't use any other patches included here
+for Linux 2.2.7. For other kernel versions, you may have to apply it
+by hand.
+
+nfsd-2.2.7-2.lockd.patch is also necessary to start lockd independent
+of nfs and nfsd.
+
+To compile, just do
+
+# ./configure
+# make
+
+Makefile will try to determine which C library you are using and compile
+this package accordingly.
+
+# make install
+
+will install the knfsd binaries. You have to install a knfs start up
+script by hand. There is a new rc.nfsd in etc. I use it for both the
+user-space nfsd and the kernel nfsd.
+
+There is one RPM spec file, knfs.spec, which is for a Linux system
+based on Linux 2.2 without support for Linux 2.0. You also need
+knfsd-1.4.6.tar.gz to create the knfsd RPMs.
+
+Thanks.
+
+H.J.
+hjl@lucon.org
+08/14/99
diff --git a/linux-nfs/README b/linux-nfs/README
new file mode 100644 (file)
index 0000000..b210c61
--- /dev/null
@@ -0,0 +1,56 @@
+
+This package contains a greatly revised NFS implementation for Linux
+along with the necessary daemons and utilities. There are still several
+features missing that I'd want to include, and there are some recent
+improvements to the Linux NFS kernel client not reflected here (notably
+the attrtimeo fix).
+
+This thing has become much too large for me to handle all alone anymore.
+Originally, I had planned to have most of the NFS implementation running
+stably by August, so I could start to concentrate more on other jobs that
+are currently in the queue (like updating the NAG). As it turned out, it
+was much more work than I anticipated, and I fell short of my time goal.
+I'm therefore looking for volunteers who would like to work with me on
+finishing this package. Otherwise, this project could end up rusting in
+the corner of some FTP site...
+
+Ideally, I would want to hand over parts of the source tree to other
+hackers to maintain/enhance/etc. But that's not a requirement; if you
+feel you don't have that much time, you can also contribute by picking
+up one of the loose ends and finish what needs to be done (take a look
+at the TODO file...) And then, you can also be plainly a tester.
+
+There's currently a mailing list for lockd development at NRAO
+(lockd-statd@linux.nrao.edu --- mail majordomo@linux.nrao.edu to
+subscribe). If Jeff agress, we could turn this into a general linux-nfs
+mailing list.
+
+
+Hope this covers about what I wanted to say,
+Olaf
+
+------------------------------------------------------------------
+
+
+                       SOURCE TREE OVERVIEW
+
+
+
+support/       Support libraries for user-space programs
+
+support/nfs    Generic library for nfsd utilities
+support/export Manipulation of /etc/exports and /var/lib/nfs/{xtab,rmtab}
+
+utils/         Code for various user-space programs.
+utils/exportfs Management of nfsd export table.
+utils/mount    Modified mount command to support NFS over TCP.
+utils/mountd   New rpc.mountd for kernel nfsd.
+utils/nfsd     New nfsd (just starts kernel nfsd).
+utils/nfsstat  Pretty-print NFS stats from /proc/net/rpc/nfs*
+utils/rquotad  Marco van Wieringen's rquotad
+utils/showmount        Rick Sladkey's showmount client
+utils/statd    Jeff Uphoff's rpc.statd.
+
+tools/         Support tools for developers/debuggers/testers
+tools/rpcdebug This one sets/gets the debug flags for each of the kernel
+               modules.
diff --git a/linux-nfs/THANKS b/linux-nfs/THANKS
new file mode 100644 (file)
index 0000000..22a80dc
--- /dev/null
@@ -0,0 +1,10 @@
+
+       This piece of software owes a lot to all the people who
+       hacked on Linux NFS before me, most notably Rick Sladkey
+       and Donald Becker.
+
+       I also wish to thank Holger Grothe for loaning me a hard
+       disk and a monitor to get my old 486 flying again so I have
+       a decentish test platform.
+
+       Olaf
diff --git a/linux-nfs/TODO b/linux-nfs/TODO
new file mode 100644 (file)
index 0000000..3a439d4
--- /dev/null
@@ -0,0 +1,121 @@
+
+Todo/Status List for Linux-NFS
+
+ *     denotes to be done;
+ o     denotes draft implementation, possibly commented out
+ -     denotes done,
+ +     denotes done and tested
+------------------------------------------------------------------
+
+RPC:
+
+ *     Server-side AUTH_DES authentication
+
+NFS:
+
+ *     stat() calls don't check whether the cached attrs are stil valid
+       (this is a problem in the VFS).
+ -     NFS_ROOT stuff needs fixing.
+ o     Swapping over NFS.
+
+       Issues of swapout:
+        *      Avoid recursion in low memory situations where
+               kmalloc may call try_to_swap_out etc ad inf.
+        *      Don't do async I/O on swap files.
+
+       For special-casing related to NFS swap I/O, flag swap file 
+       semantics in inode->i_flags. In swapfile.c, change functions
+       to call readpage/writepage if available, otherwise proceed
+       as usual.
+
+ -     Write-back support.
+ *     Disable page cache invalidation/flushing for locked file
+       regions.
+ -     Directory caching (we now have page-sized dircache entries
+       which could easily be organized into a linked list). These
+       dircache pages come along as a linked list that can be copied
+       almost 1-to-1 into a dirent struct. If this is put into the
+       VFS, other remote fs's will also benefit.
+
+       [Note: I just increased the readdir cache to hold more than
+       one directory. With this, the exclusive lock on readdir goes
+       away, too. With a larger cache, it may also be worth to think
+       about directory readahead...]
+ *     Better lookup caching?
+ *     When a read lock is present, don't time out attr cache or
+       page cache for that region. Likewise, if a write lock is present,
+       be lazy on write-back.
+ *     Implement CTO.
+ -     BUG: Invalidate readdir cache after remove/rename/unlink
+ *     Automatic `mounting' when the server crosses mount points
+       transparently (some IRIX machines seem to do this when
+       using -nohide).
+ *     NFSv3 support. This requires careful design to maximize
+       code sharing between NFSv2 and NFSv3.
+ *     More robust rename handling (see comment before nfs_rename).
+ *     Add Miquel's O_EXCL hack for file creation.
+ *     Performance improvement: When a complete reply is received, and
+       the (async) task is woken up, don't put it on rpciod's scheduling
+       queue, but add it to a `fast scheduler queue.' The fast scheduler
+       could be a special handler that's registered on the tq_scheduler task
+       queue. This queue is fired by the kernel scheduler as soon as
+       the other bottom halves have been run.
+
+       Note that implementing this for sync tasks is even trickier than
+       for async tasks, because you have to make sure you do the right
+       thing in rpc_sleep_on().
+ *     writeback of writable mmaps. Dirty pages are not subject to
+       writeback scheduling. Also, msync should make sure pages are
+       written with O_SYNC on.
+
+
+nfsd:
+
+ *     uid/gid mapping, and rpc.ugidd support
+ -     Don't read/write a file that might have mandatory locks.
+ *     Implement secure/kerberos export options (take care of lockd
+       fopen() calls--most clients seem to use NULL creds for lockd).
+ -     there's a bug in readdir wrt large directories. Try mounting
+       the linux source tree and do an ls on include/linux...
+ *     Support for UNIX socket creation.
+ *     Someone should look over the error return codes. I tend to
+       mix up EPERM and EACCES.
+ *     NFSv3 support.
+ -     Refuse to look up inodes in procfs (security issues).
+ o     Delayed writes (delay syncing of file data when nfsd handles
+       several write requests for the same file concurrently).
+       (Draft - see nfsd_write in fs/nfsd/write.c. Needs benchmarking).
+ *     Faster read operations (single copy): mmap the file region
+       to be read into VM, and pass the VMA to the xdr routines
+       which pass the region's VM address into sock->ops->writemsg.
+       This copies the file data directly from the page cache into
+       the network buffer.
+       Release the vma region after encoding.
+ *     Faster write operations (single copy, with IPv6 net layout):
+       Get the unfragmented UDP datagram, pull the header and
+       do normal processing. Then mmap the file, copy the write data,
+       and release VMA.
+ -     Clear setuid/setgid bit after write().
+ *     Quota support.
+
+lockd:
+
+ *     Server should run on privileged port.
+ *     Testing reclaim support.
+ *     HP lockd accepts our GRANT_MSG callback and passes on the grant
+       to the blocking process, but doesn't reply with a GRANT_RES. 
+       It's not clear to me why it would do this.
+ *     Unregister hosts (SM_UNMON) with rpc.statd when appropriate.
+
+mountd
+
+ *     Unregister service from portmapper upon exit/SIGTERM
+
+mount
+
+ *     If available, use version 3 of the mount protocol and
+       obtain pathconf data (fill in data->bsize).
+
+documentation:
+
+ -     Manpages need to be written
diff --git a/nfs-utils.spec b/nfs-utils.spec
new file mode 100644 (file)
index 0000000..245c933
--- /dev/null
@@ -0,0 +1,80 @@
+Summary: The utilities for Linux NFS client and server.
+Name: nfs-utils
+Version: 0.1
+Release: 1
+Source0: ftp://ftp.valinux.com/pub/support/hjl/nfs/%{name}-%{version}.tar.gz
+Group: System Environment/Daemons
+Obsoletes: nfs-server nfs-server-clients knfsd knfsd-client knfsd-lock
+Provides: nfs-server nfs-server-clients knfsd knfsd-client knfsd-lock
+Copyright: GPL
+ExcludeArch: armv4l
+Buildroot: /var/tmp/%{name}-root
+Serial: 1
+Requires: kernel >= 2.2.5, portmap >= 4.0
+
+%description
+The nfs-utils package provides the utilities for Linux NFS client and
+server.
+
+%prep
+%setup -q
+
+%build
+./configure
+make all
+
+%install
+rm -rf $RPM_BUILD_ROOT
+mkdir -p $RPM_BUILD_ROOT{/sbin,/usr/{sbin,man/man5,man/man8}}
+mkdir -p $RPM_BUILD_ROOT/etc/rc.d/init.d
+mkdir -p $RPM_BUILD_ROOT/dev
+
+make install install_prefix=$RPM_BUILD_ROOT
+install -s -m 755 tools/rpcdebug/rpcdebug $RPM_BUILD_ROOT/sbin
+install -m 755 etc/redhat/nfsd.init $RPM_BUILD_ROOT/etc/rc.d/init.d/nfs
+install -m 755 etc/redhat/nfslock.init $RPM_BUILD_ROOT/etc/rc.d/init.d/nfslock
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%post
+/sbin/chkconfig --add nfs
+/sbin/chkconfig --add nfslock
+
+%preun
+if [ "$1" = "0" ]; then
+    /sbin/chkconfig --del nfs
+    /sbin/chkconfig --del nfslock
+fi
+
+%files
+%defattr(-,root,root)
+/sbin/rpcdebug
+/usr/sbin/exportfs
+/usr/sbin/nfsstat
+/usr/sbin/nhfsstone
+/usr/sbin/rpc.lockd
+/usr/sbin/rpc.mountd
+/usr/sbin/rpc.nfsd
+/usr/sbin/rpc.rquotad
+/usr/sbin/rpc.statd
+/usr/sbin/showmount
+/usr/man/man5/exports.5
+/usr/man/man8/exportfs.8
+/usr/man/man8/mountd.8
+/usr/man/man8/nfsd.8
+/usr/man/man8/nfsstat.8
+/usr/man/man8/rpc.mountd.8
+/usr/man/man8/rpc.nfsd.8
+/usr/man/man8/rpc.statd.8
+/usr/man/man8/rpc.rquotad.8
+/usr/man/man8/rquotad.8
+/usr/man/man8/showmount.8
+/usr/man/man8/statd.8
+%config /etc/rc.d/init.d/nfs
+%config /etc/rc.d/init.d/nfslock
+%dir /var/lib/nfs
+%config(noreplace) /var/lib/nfs/xtab
+%config(noreplace) /var/lib/nfs/etab
+%config(noreplace) /var/lib/nfs/rmtab
+%doc README
diff --git a/rules.mk b/rules.mk
new file mode 100644 (file)
index 0000000..c773460
--- /dev/null
+++ b/rules.mk
@@ -0,0 +1,126 @@
+#
+# General make rules
+#
+.DEFAULT:      all
+.PHONY:                $(ALLTARGETS)
+
+include $(TOP)config.mk
+
+##################################################################
+# Subdirectory handling
+##################################################################
+ifneq ($(SUBDIRS),)
+$(ALLTARGETS)::
+       @set -e; for d in $(SUBDIRS); do \
+               echo "Making $@ in $$d"; \
+               $(MAKE) --no-print-directory TOP=../$(TOP) -C $$d $@; \
+       done
+endif
+
+##################################################################
+# Building an RPC daemon
+##################################################################
+ifneq ($(PROGRAM),)
+TARGET = $(PROGRAM)
+
+$(PROGRAM): $(OBJS) $(LIBDEPS)
+       $(CC) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)
+
+install:: $(PROGRAM)
+       -$(MKDIR) $(SBINDIR)
+       $(INSTALLBIN) $(PROGRAM) $(SBINDIR)/$(PREFIX)$k$(PROGRAM)
+endif
+
+##################################################################
+# Building a tool
+##################################################################
+ifneq ($(TOOL),)
+TARGET = $(TOOL)
+
+$(TOOL): $(OBJS) $(LIBDEPS)
+       $(CC) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)
+endif
+
+##################################################################
+# Building a library
+##################################################################
+ifneq ($(LIBNAME),)
+TARGET = $(LIBNAME)
+
+$(LIBNAME): $(OBJS)
+       $(AR) cr $@ $^
+       $(RANLIB) $@
+endif
+
+##################################################################
+# Generic target rules
+##################################################################
+ifneq ($(TARGET),)
+all:: $(TARGET)
+       @echo "Building $(TARGET) done."
+
+install:: $(TARGET)
+
+distclean::
+       rm -f $(TARGET)
+endif
+
+##################################################################
+# Cleaning rules
+##################################################################
+clean distclean::
+       rm -f *.o *~ \#* a.out core
+
+distclean::
+       rm -f LOG X Y Z x y z .depend
+
+##################################################################
+# Manpage installation
+# Isn't GNU make a wonderful thing?
+##################################################################
+ifneq ($(MAN1)$(MAN5)$(MAN8)$(MAN9),)
+MANINIT        = ext=$(MAN$sEXT); dir=$(MAN$sDIR); pgs="$(MAN$s)";
+MANLOOP = $(MANINIT) for man in $$pgs; do eval $$cmd; done
+MDCMD  = $(MKDIR) \$$dir
+MICMD  = $(RM) \$$dir/\$$man.\$$ext; \
+         echo $(INSTALLMAN) \$$man.man \$$dir/\$$man.\$$ext; \
+         $(INSTALLMAN) \$$man.man \$$dir/\$$man.\$$ext
+LNCMD  = $(RM) \$$dir/$(PREFIX)\$$man.\$$ext; \
+         echo $(LN_S) \$$man.\$$ext \$$dir/$(PREFIX)\$$man.\$$ext; \
+         $(LN_S) \$$man.\$$ext \$$dir/$(PREFIX)\$$man.\$$ext
+PSCMD  = echo \"$(MAN2PS) \$$man.man > $(TOP)postscript/\$$man.ps\"; \
+         $(MAN2PS) \$$man.man > $(TOP)postscript/\$$man.ps
+
+installman::
+       @$(foreach s, 1 5 8 9, cmd="$(MDCMD)" $(MANLOOP);)
+       @$(foreach s, 1 5 8 9, cmd="$(MICMD)" $(MANLOOP);)
+ifneq ($(PREFIX),)
+       @$(foreach s, 1 5 8 9, cmd="$(LNCMD)" $(MANLOOP);)
+endif
+
+postscript::
+       @$(foreach s, 1 5 8 9, cmd="$(PSCMD)" $(MANLOOP);)
+else
+postscript installman::
+       @: No manpages...
+endif
+
+##################################################################
+# Indenting
+##################################################################
+ifneq ($(SRCS),)
+indent:
+       $(INDENT) $(SRCS)
+endif
+       
+##################################################################
+# Handling of dependencies
+##################################################################
+ifneq ($(OBJS),)
+depend dep::
+       $(CC) $(CFLAGS) -M $(OBJS:.o=.c) > .depend
+endif
+
+ifeq (.depend,$(wildcard .depend))
+include .depend
+endif
diff --git a/support/Makefile b/support/Makefile
new file mode 100644 (file)
index 0000000..6b8598b
--- /dev/null
@@ -0,0 +1,9 @@
+#
+# Makefile for linux-nfs/support
+#
+
+SUBDIRS        = include nfs export lib
+.DEFAULT: all
+
+include $(TOP)rules.mk
+
diff --git a/support/export/Makefile b/support/export/Makefile
new file mode 100644 (file)
index 0000000..1243305
--- /dev/null
@@ -0,0 +1,27 @@
+#
+# libexport.a
+# Miscellaneous utility functions related to exporting and mounting
+# of NFS volumes.
+#
+
+LIBNAME        = libexport.a
+SRCS   = $(RPCSRCS) client.c export.c hostname.c nfsctl.c rmtab.c \
+         xtab.c
+OBJS   = $(SRCS:.c=.o)
+
+RPCSRCS        = mount_clnt.c mount_xdr.c
+RPCHDRS        = mount.h
+
+include $(TOP)rules.mk
+
+$(RPCHDRS) $(RPCSRCS): mount.x
+       $(RM) $(RPCHDRS) $(RPCSRCS)
+       $(RPCGEN) -h -o mount.h $<
+       $(RPCGEN) -l -o mount_clnt.c $<
+       $(RPCGEN) -c -o mount_xdr.c $<
+
+clean distclean::
+       $(RM) $(RPCHDRS) $(RPCSRCS)
+
+install::
+       @:
diff --git a/support/export/client.c b/support/export/client.c
new file mode 100644 (file)
index 0000000..8c5200a
--- /dev/null
@@ -0,0 +1,298 @@
+/*
+ * support/export/client.c
+ *
+ * Maintain list of nfsd clients.
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netdb.h>
+#include "xmalloc.h"
+#include "misc.h"
+#include "nfslib.h"
+#include "exportfs.h"
+
+/* netgroup stuff never seems to be defined in any header file. Linux is
+ * not alone in this.
+ */
+#if !defined(__GLIBC__) || __GLIBC__ < 2
+extern int     innetgr(char *netgr, char *host, char *, char *);
+#endif
+static void    client_init(nfs_client *clp, const char *hname,
+                                       struct hostent *hp);
+static int     client_checkaddr(nfs_client *clp, struct in_addr addr);
+
+nfs_client     *clientlist[MCL_MAXTYPES] = { NULL, };
+
+
+nfs_client *
+client_lookup(char *hname)
+{
+       nfs_client      *clp = NULL;
+       int             htype;
+       struct hostent  *hp = NULL;
+
+       htype = client_gettype(hname);
+
+       if (htype == MCL_FQDN) {
+               hp = gethostbyname(hname);
+               if (hp == NULL || hp->h_addrtype != AF_INET) {
+                       xlog(L_ERROR, "%s has non-inet addr", hname);
+                       return NULL;
+               }
+               hp = hostent_dup (hp);
+               hname = (char *) hp->h_name;
+
+               for (clp = clientlist[htype]; clp; clp = clp->m_next) {
+                       if (client_check(clp, hp))
+                               break;
+               }
+       } else {
+               for (clp = clientlist[htype]; clp; clp = clp->m_next) {
+                       if (strcmp(hname, clp->m_hostname)==0)
+                               break;
+               }
+       }
+
+       if (!clp) {
+               clp = (nfs_client *) xmalloc(sizeof(*clp));
+               memset(clp, 0, sizeof(*clp));
+               clp->m_type = htype;
+               client_init(clp, hname, NULL);
+               client_add(clp);
+       }
+
+       if (htype == MCL_FQDN && clp->m_naddr == 0 && hp != NULL) {
+               char    **ap = hp->h_addr_list;
+               int     i;
+
+               for (i = 0; *ap && i < NFSCLNT_ADDRMAX; i++, ap++)
+                       clp->m_addrlist[i] = *(struct in_addr *)*ap;
+               clp->m_naddr = i;
+       }
+
+       if (hp)
+               free (hp);
+
+       return clp;
+}
+
+nfs_client *
+client_dup(nfs_client *clp, struct hostent *hp)
+{
+       nfs_client              *new;
+
+       new = (nfs_client *) xmalloc(sizeof(*new));
+       memcpy(new, clp, sizeof(*new));
+       new->m_type = MCL_FQDN;
+
+       client_init(new, (char *) hp->h_name, hp);
+       client_add(new);
+       return new;
+}
+
+static void
+client_init(nfs_client *clp, const char *hname, struct hostent *hp)
+{
+       if (hp) {
+               strncpy(clp->m_hostname, hp->h_name,
+                       sizeof (clp->m_hostname) -  1);
+       } else {
+               strncpy(clp->m_hostname, hname,
+                       sizeof (clp->m_hostname) - 1);
+       }
+       clp->m_hostname[sizeof (clp->m_hostname) - 1] = '\0';
+
+       clp->m_exported = 0;
+       clp->m_count = 0;
+
+       if (clp->m_type == MCL_SUBNETWORK) {
+               char    *cp = strchr(clp->m_hostname, '/');
+
+               *cp = '\0';
+               clp->m_addrlist[0].s_addr = inet_addr(clp->m_hostname);
+               clp->m_addrlist[1].s_addr = inet_addr(cp+1);
+               *cp = '/';
+               clp->m_naddr = 0;
+       } else if (!hp) {
+               clp->m_naddr = 0;
+       } else {
+               char    **ap = hp->h_addr_list;
+               int     i;
+
+               for (i = 0; *ap && i < NFSCLNT_ADDRMAX; i++, ap++) {
+                       clp->m_addrlist[i] = *(struct in_addr *)*ap;
+               }
+               clp->m_naddr = i;
+       }
+}
+
+void
+client_add(nfs_client *clp)
+{
+       nfs_client      **cpp;
+
+       if (clp->m_type < 0 || clp->m_type >= MCL_MAXTYPES)
+               xlog(L_FATAL, "unknown client type in client_add");
+       cpp = clientlist + clp->m_type;
+       while (*cpp)
+               cpp = &((*cpp)->m_next);
+       clp->m_next = NULL;
+       *cpp = clp;
+}
+
+void
+client_release(nfs_client *clp)
+{
+       if (clp->m_count <= 0)
+               xlog(L_FATAL, "client_free: m_count <= 0!");
+       clp->m_count--;
+}
+
+void
+client_freeall(void)
+{
+       nfs_client      *clp, **head;
+       int             i;
+
+       for (i = 0; i < MCL_MAXTYPES; i++) {
+               head = clientlist + i;
+               while (*head) {
+                       *head = (clp = *head)->m_next;
+                       xfree(clp);
+               }
+       }
+}
+
+nfs_client *
+client_find(struct hostent *hp)
+{
+       nfs_client      *clp;
+       int             i;
+
+       for (i = 0; i < MCL_MAXTYPES; i++) {
+               for (clp = clientlist[i]; clp; clp = clp->m_next) {
+                       if (!client_check(clp, hp))
+                               continue;
+#ifdef notdef
+                       if (clp->m_type == MCL_FQDN)
+                               return clp;
+                       return client_dup(clp, hp);
+#else
+                       return clp;
+#endif
+               }
+       }
+       return NULL;
+}
+
+/*
+ * Match a host (given its hostent record) to a client record. This
+ * is usually called from mountd.
+ */
+int
+client_check(nfs_client *clp, struct hostent *hp)
+{
+       char    *hname = (char *) hp->h_name;
+       char    *cname = clp->m_hostname;
+       char    **ap;
+
+       switch (clp->m_type) {
+       case MCL_FQDN:
+       case MCL_SUBNETWORK:
+               for (ap = hp->h_addr_list; *ap; ap++) {
+                       if (client_checkaddr(clp, *(struct in_addr *) *ap))
+                               return 1;
+               }
+               return 0;
+       case MCL_WILDCARD:
+               if (wildmat(hname, cname))
+                       return 1;
+               else {
+                       for (ap = hp->h_aliases; *ap; ap++)
+                               if (wildmat(*ap, cname))
+                                       return 1;
+               }
+               return 0;
+       case MCL_NETGROUP:
+#ifdef HAVE_INNETGR
+               {
+                       char    *dot;
+                       int     match;
+
+                       /* First, try to match the hostname without
+                        * splitting off the domain */
+                       if (innetgr(cname+1, hname, NULL, NULL))
+                               return 1;
+
+                       /* Okay, strip off the domain (if we have one) */
+                       if ((dot = strchr(hname, '.')) == NULL)
+                               return 0;
+
+                       *dot = '\0';
+                       match = innetgr(cname+1, hname, NULL, dot + 1);
+                       *dot = '.';
+
+                       return match;
+               }
+#else
+               return 0;
+#endif
+       case MCL_ANONYMOUS:
+               return 1;
+       default:
+               xlog(L_FATAL, "internal: bad client type %d", clp->m_type);
+       }
+
+       return 0;
+}
+
+static int
+client_checkaddr(nfs_client *clp, struct in_addr addr)
+{
+       int     i;
+
+       switch (clp->m_type) {
+       case MCL_FQDN:
+               for (i = 0; i < clp->m_naddr; i++) {
+                       if (clp->m_addrlist[i].s_addr == addr.s_addr)
+                               return 1;
+               }
+               return 0;
+       case MCL_SUBNETWORK:
+               return !((clp->m_addrlist[0].s_addr ^ addr.s_addr)
+                       & clp->m_addrlist[1].s_addr);
+       }
+       return 0;
+}
+
+int
+client_gettype(char *ident)
+{
+       char    *sp;
+
+       if (ident[0] == '\0')
+               return MCL_ANONYMOUS;
+       if (ident[0] == '@') {
+#ifndef HAVE_INNETGR
+               xlog(L_WARNING, "netgroup support not compiled in");
+#endif
+               return MCL_NETGROUP;
+       }
+       for (sp = ident; *sp; sp++) {
+               if (*sp == '*' || *sp == '?' || *sp == '[')
+                       return MCL_WILDCARD;
+               if (*sp == '/')
+                       return MCL_SUBNETWORK;
+               if (*sp == '\\' && sp[1])
+                       sp++;
+       }
+       return MCL_FQDN;
+}
diff --git a/support/export/export.c b/support/export/export.c
new file mode 100644 (file)
index 0000000..09efaa8
--- /dev/null
@@ -0,0 +1,259 @@
+/*
+ * support/export/export.c
+ *
+ * Maintain list of exported file systems.
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <netinet/in.h>
+#include <stdlib.h>
+#include "xmalloc.h"
+#include "nfslib.h"
+#include "exportfs.h"
+
+nfs_export     *exportlist[MCL_MAXTYPES] = { NULL, };
+
+static void    export_init(nfs_export *exp, nfs_client *clp,
+                                       struct exportent *nep);
+static int     export_check(nfs_export *, struct hostent *, char *);
+static nfs_export *
+               export_allowed_internal(struct hostent *hp, char *path);
+
+int
+export_read(char *fname)
+{
+       struct exportent        *eep;
+       nfs_export              *exp;
+
+       setexportent(fname, "r");
+       while ((eep = getexportent()) != NULL) {
+         exp = export_lookup(eep->e_hostname, eep->e_path);
+         if (!exp)
+           export_create(eep);
+         else {
+           if (exp->m_export.e_flags != eep->e_flags) {
+             xlog(L_ERROR, "incompatible dupilcated export entries:");
+             xlog(L_ERROR, "\t%s:%s (0x%x) [IGNORED]", eep->e_hostname,
+                  eep->e_path, eep->e_flags);
+             xlog(L_ERROR, "\t%s:%s (0x%x)", exp->m_export.e_hostname,
+                  exp->m_export.e_path, exp->m_export.e_flags);
+           }
+           else {
+             xlog(L_ERROR, "dupilcated export entries:");
+             xlog(L_ERROR, "\t%s:%s", eep->e_hostname, eep->e_path);
+             xlog(L_ERROR, "\t%s:%s", exp->m_export.e_hostname,
+                  exp->m_export.e_path);
+           }
+         }
+       }
+       endexportent();
+
+       return 0;
+}
+
+/*
+ * Create an in-core export struct from an export entry.
+ */
+nfs_export *
+export_create(struct exportent *xep)
+{
+       nfs_client      *clp;
+       nfs_export      *exp;
+
+       if (!(clp = client_lookup(xep->e_hostname))) {
+               /* bad export entry; complaint already logged */
+               return NULL;
+       }
+       exp = (nfs_export *) xmalloc(sizeof(*exp));
+       export_init(exp, clp, xep);
+       export_add(exp);
+
+       return exp;
+}
+
+static void
+export_init(nfs_export *exp, nfs_client *clp, struct exportent *nep)
+{
+       struct exportent        *e = &exp->m_export;
+
+       dupexportent(e, nep);
+
+       exp->m_exported = 0;
+       exp->m_xtabent = 0;
+       exp->m_mayexport = 0;
+       exp->m_changed = 0;
+       exp->m_client = clp;
+       clp->m_count++;
+}
+
+/*
+ * Duplicate exports data. The in-core export struct retains the
+ * original hostname from /etc/exports, while the in-core client struct
+ * gets the newly found FQDN.
+ */
+nfs_export *
+export_dup(nfs_export *exp, struct hostent *hp)
+{
+       nfs_export              *new;
+       nfs_client              *clp;
+
+       new = (nfs_export *) xmalloc(sizeof(*new));
+       memcpy(new, exp, sizeof(*new));
+       dupexportent(&new->m_export, &exp->m_export);
+       clp = client_dup(exp->m_client, hp);
+       clp->m_count++;
+       new->m_client = clp;
+       new->m_mayexport = exp->m_mayexport;
+       new->m_exported = 0;
+       new->m_xtabent = 0;
+       new->m_changed = 0;
+       export_add(new);
+
+       return new;
+}
+
+void
+export_add(nfs_export *exp)
+{
+       nfs_export      **epp;
+       int             type = exp->m_client->m_type;
+       int             slen = strlen(exp->m_export.e_path);
+
+       if (type < 0 || type >= MCL_MAXTYPES)
+               xlog(L_FATAL, "unknown client type in export_add");
+
+       epp = exportlist + type;
+       while (*epp && slen < strlen((*epp)->m_export.e_path))
+               epp = &((*epp)->m_next);
+       exp->m_next = *epp;
+       *epp = exp;
+}
+
+nfs_export *
+export_find(struct hostent *hp, char *path)
+{
+       nfs_export      *exp;
+       int             i;
+
+       for (i = 0; i < MCL_MAXTYPES; i++) {
+               for (exp = exportlist[i]; exp; exp = exp->m_next) {
+                       if (!export_check(exp, hp, path))
+                               continue;
+                       if (exp->m_client->m_type == MCL_FQDN)
+                               return exp;
+                       return export_dup(exp, hp);
+               }
+       }
+
+       return NULL;
+}
+
+static nfs_export *
+export_allowed_internal (struct hostent *hp, char *path)
+{
+       nfs_export      *exp;
+       int             i;
+
+       for (i = 0; i < MCL_MAXTYPES; i++) {
+               for (exp = exportlist[i]; exp; exp = exp->m_next) {
+                       if (!exp->m_mayexport ||
+                           !export_check(exp, hp, path))
+                               continue;
+                       return exp;
+               }
+       }
+
+       return NULL;
+}
+
+struct exportent *
+export_allowed(struct hostent *hp, char *path)
+{
+       static struct exportent ee;
+       nfs_export              *exp;
+       char                    epath[MAXPATHLEN+1];
+       char                    *p = NULL;
+
+       if (path [0] != '/') return NULL;
+
+       strncpy(epath, path, sizeof (epath) - 1);
+       epath[sizeof (epath) - 1] = '\0';
+
+       /* Try the longest matching exported pathname. */
+       while (1) {
+               exp = export_allowed_internal (hp, epath);
+               if (exp) {
+                       dupexportent(&ee, &exp->m_export);
+                       return &ee;
+               }
+               /* We have to treat the root, "/", specially. */
+               if (p == &epath[1]) break;
+               p = strrchr(epath, '/');
+               if (p == epath) p++;
+               *p = '\0';
+       }
+
+       return NULL;
+}
+
+nfs_export *
+export_lookup(char *hname, char *path)
+{
+       nfs_client      *clp;
+       nfs_export      *exp;
+
+       if (!(clp = client_lookup(hname)))
+               return NULL;
+       for (exp = exportlist[clp->m_type]; exp; exp = exp->m_next)
+               if (exp->m_client == clp && !strcmp(exp->m_export.e_path, path))
+                       return exp;
+       return NULL;
+}
+
+static int
+export_check(nfs_export *exp, struct hostent *hp, char *path)
+{
+       if (strcmp(path, exp->m_export.e_path))
+               return 0;
+
+       return client_check(exp->m_client, hp);
+}
+
+void
+export_freeall(void)
+{
+       nfs_export      *exp, *nxt;
+       int             i;
+
+       for (i = 0; i < MCL_MAXTYPES; i++) {
+               for (exp = exportlist[i]; exp; exp = nxt) {
+                       nxt = exp->m_next;
+                       client_release(exp->m_client);
+                       if (exp->m_export.e_squids)
+                               xfree(exp->m_export.e_squids);
+                       if (exp->m_export.e_sqgids)
+                               xfree(exp->m_export.e_sqgids);
+                       xfree(exp);
+               }
+               exportlist[i] = NULL;
+       }
+       client_freeall();
+}
+
+void
+export_reset(nfs_export *exp)
+{
+       if (!exp)
+               return;
+
+       /* Restore m_path. */
+       strncpy(exp->m_export.m_path, exp->m_export.e_path,
+               sizeof (exp->m_export.m_path) - 1);
+       exp->m_export.m_path[sizeof (exp->m_export.m_path) - 1] = '\0';
+}
diff --git a/support/export/hostname.c b/support/export/hostname.c
new file mode 100644 (file)
index 0000000..a37d4de
--- /dev/null
@@ -0,0 +1,262 @@
+/*
+ * support/export/hostname.c
+ *
+ * Functions for hostname.
+ *
+ */
+
+#include "config.h"
+
+/*
+#define TEST
+*/
+
+#include <string.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <stdlib.h>
+#ifdef TEST
+#define xmalloc malloc
+#else
+#include "xmalloc.h"
+#include "misc.h"
+#endif
+
+#define ALIGNMENT      sizeof (char *)
+
+static int
+align (int len, int al)
+{
+  int i;
+  i = len % al;
+  if (i)
+    len += al - i;
+  return len;
+}
+
+struct hostent *
+get_hostent (const char *addr, int len, int type)
+{
+  struct hostent *cp;
+  int len_ent;
+  const char *name;
+  int len_name;
+  int num_aliases = 1;
+  int len_aliases = sizeof (char *);
+  int num_addr_list = 1;
+  int len_addr_list = sizeof (char *);
+  int pos;
+  struct in_addr *ipv4;
+
+  switch (type)
+    {
+    case AF_INET:
+      ipv4 = (struct in_addr *) addr;
+      name = inet_ntoa (*ipv4);
+      break;
+
+    default:
+      return NULL;
+    }
+
+  len_ent = align (sizeof (*cp), ALIGNMENT);
+  len_name = align (strlen (name) + 1, ALIGNMENT);
+
+  num_addr_list++;
+  len_addr_list += align (len, ALIGNMENT) + sizeof (char *);
+
+  cp = (struct hostent *) xmalloc (len_ent + len_name + len_aliases
+                                  + len_addr_list);
+
+  cp->h_addrtype = type;
+  cp->h_length = len;
+  pos = len_ent;
+  cp->h_name = (char *) &(((char *) cp) [pos]);
+  strcpy (cp->h_name, name);
+
+  pos += len_name;
+  cp->h_aliases = (char **) &(((char *) cp) [pos]);
+  pos += num_aliases * sizeof (char *);
+  cp->h_aliases [0] = NULL;
+
+  pos = len_ent + len_name + len_aliases;
+  cp->h_addr_list = (char **) &(((char *) cp) [pos]);
+  pos += num_addr_list * sizeof (char *);
+  cp->h_addr_list [0] = (char *) &(((char *) cp) [pos]);
+  memcpy (cp->h_addr_list [0], addr, cp->h_length);
+  pos += align (cp->h_length, ALIGNMENT);
+  cp->h_addr_list [1] = NULL;
+
+  return cp;
+}
+
+struct hostent *
+hostent_dup (struct hostent *hp)
+{
+  int len_ent = align (sizeof (*hp), ALIGNMENT);
+  int len_name = align (strlen (hp->h_name) + 1, ALIGNMENT);
+  int num_aliases = 1;
+  int len_aliases = sizeof (char *);
+  int num_addr_list = 1;
+  int len_addr_list = sizeof (char *);
+  int pos, i;
+  char **sp;
+  struct hostent *cp;
+
+  for (sp = hp->h_aliases; *sp; sp++)
+    {
+      num_aliases++;
+      len_aliases += align (strlen (*sp) + 1, ALIGNMENT)
+                    + sizeof (char *);
+    }
+
+  for (sp = hp->h_addr_list; *sp; sp++)
+    {
+      num_addr_list++;
+      len_addr_list += align (hp->h_length, ALIGNMENT)
+                      + sizeof (char *);
+    }
+  
+  cp = (struct hostent *) xmalloc (len_ent + len_name + len_aliases
+                                  + len_addr_list);
+
+  *cp = *hp;
+  pos = len_ent;
+  cp->h_name = (char *) &(((char *) cp) [pos]);
+  strcpy (cp->h_name, hp->h_name);
+
+  pos += len_name;
+  cp->h_aliases = (char **) &(((char *) cp) [pos]);
+  pos += num_aliases * sizeof (char *);
+  for (sp = hp->h_aliases, i = 0; i < num_aliases; i++, sp++)
+    if (*sp)
+      {
+       cp->h_aliases [i] = (char *) &(((char *) cp) [pos]);
+       strcpy (cp->h_aliases [i], *sp);
+       pos += align (strlen (*sp) + 1, ALIGNMENT);
+      }
+    else
+      cp->h_aliases [i] = *sp;
+
+  pos = len_ent + len_name + len_aliases;
+  cp->h_addr_list = (char **) &(((char *) cp) [pos]);
+  pos += num_addr_list * sizeof (char *);
+  for (sp = hp->h_addr_list, i = 0; i < num_addr_list; i++, sp++)
+    if (*sp)
+      {
+       cp->h_addr_list [i] = (char *) &(((char *) cp) [pos]);
+       memcpy (cp->h_addr_list [i], *sp, hp->h_length);
+       pos += align (hp->h_length, ALIGNMENT);
+      }
+    else
+      cp->h_addr_list [i] = *sp;
+
+  return cp;
+}
+
+static int
+is_hostname(const char *sp)
+{
+  if (*sp == '\0' || *sp == '@')
+    return 0;
+
+  for (; *sp; sp++)
+    {
+      if (*sp == '*' || *sp == '?' || *sp == '[' || *sp == '/')
+       return 0;
+      if (*sp == '\\' && sp[1])
+       sp++;
+    }
+
+  return 1;
+}
+
+int
+matchhostname (const char *h1, const char *h2)
+{
+  struct hostent *hp1, *hp2;
+  int status;
+
+  if (strcasecmp (h1, h2) == 0)
+    return 1;
+
+  if (!is_hostname (h1) || !is_hostname (h2))
+    return 0;
+
+  hp1 = gethostbyname (h1);
+  if (hp1 == NULL)
+    return 0;
+
+  hp1 = hostent_dup (hp1);
+
+  hp2 = gethostbyname (h2);
+  if (hp2)
+    {
+      if (strcasecmp (hp1->h_name, hp2->h_name) == 0)
+       status = 1;
+      else
+       {
+         char **ap1, **ap2;
+
+         status = 0;
+         for (ap1 = hp1->h_addr_list; *ap1 && status == 0; ap1++)
+           for (ap2 = hp2->h_addr_list; *ap2; ap2++)
+             if (memcmp (*ap1, *ap2, sizeof (struct in_addr)) == 0)
+               {
+                 status = 1;
+                 break;
+               }
+       }
+    }
+  else
+    status = 0;
+
+  free (hp1);
+  return status;
+}
+
+#ifdef TEST
+void
+print_host (struct hostent *hp)
+{
+  char **sp;
+
+  if (hp)
+    {
+      printf ("official hostname: %s\n", hp->h_name); 
+      printf ("aliases:\n");
+      for (sp = hp->h_aliases; *sp; sp++)
+       printf ("  %s\n", *sp);
+      printf ("IP addresses:\n");
+      for (sp = hp->h_addr_list; *sp; sp++)
+       printf ("  %s\n", inet_ntoa (*(struct in_addr *) *sp));
+    }
+  else
+    printf ("Not host information\n");
+}
+
+int
+main (int argc, char **argv)
+{
+  struct hostent *hp = gethostbyname (argv [1]);
+  struct hostent *cp;
+  struct in_addr addr;
+
+  print_host (hp);
+
+  if (hp)
+    {
+      cp = hostent_dup (hp);
+      print_host (cp);
+      free (cp);
+    }
+  printf ("127.0.0.1 == %s: %d\n", argv [1],
+         matchhostname ("127.0.0.1", argv [1]));
+  addr.s_addr = inet_addr(argv [2]);
+  printf ("%s\n", inet_ntoa (addr));
+  cp = get_hostent ((const char *)&addr, sizeof(addr), AF_INET);
+  print_host (cp);
+  return 0;
+}
+#endif
diff --git a/support/export/keys.c b/support/export/keys.c
new file mode 100644 (file)
index 0000000..4814808
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * keys.c              Key management for nfsd. Currently, keys
+ *                     are kept in a single file only, but eventually,
+ *                     support for a key server should be added.
+ *
+ * Copyright (C) 1995 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include "config.h"
+
+#include <sys/stat.h>
+#include "nfslib.h"
+#include "exportfs.h"
+#include "xmalloc.h"
+
+struct keycache {
+       struct keycache *       k_next;
+       struct nfskeyent        k_data;
+};
+
+static struct keycache *       keycache = NULL;
+static time_t                  lastmod = 0;
+
+static void    key_reload(void);
+
+
+struct nfskey *
+key_lookup(char *hname)
+{
+       struct keycache *kc;
+
+       key_reload();
+
+       for (kc = keycache; kc; kc = kc->k_next) {
+#if 0
+               if (matchhostname(kc->k_data.k_hostname, hname))
+#else
+               if (!strcmp(kc->k_data.k_hostname, hname))
+#endif
+                       return &kc->k_data.k_key;
+       }
+
+       return NULL;
+}
+
+static void
+key_reload(void)
+{
+       struct stat     stb;
+       struct keycache *cp;
+       struct nfskeyent *kp;
+
+       if (stat(_PATH_NFSKEYS, &stb) >= 0 && stb.st_mtime == lastmod)
+               return;
+
+       while (keycache) {
+               cp = keycache->k_next;
+               xfree(keycache);
+               keycache = cp;
+       }
+
+       setnfskeyent(_PATH_NFSKEYS);
+       while ((kp = getnfskeyent()) != NULL) {
+               cp = (struct keycache *) xmalloc(sizeof(*cp));
+               cp->k_data = *kp;
+               cp->k_next = keycache;
+               keycache = cp;
+       }
+       endnfskeyent();
+
+       lastmod = stb.st_mtime;
+}
diff --git a/support/export/mount.x b/support/export/mount.x
new file mode 100644 (file)
index 0000000..f504e7c
--- /dev/null
@@ -0,0 +1,345 @@
+%/*
+% * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+% * unrestricted use provided that this legend is included on all tape
+% * media and as a part of the software program in whole or part.  Users
+% * may copy or modify Sun RPC without charge, but are not authorized
+% * to license or distribute it to anyone else except as part of a product or
+% * program developed by the user or with the express written consent of
+% * Sun Microsystems, Inc.
+% *
+% * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+% * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+% * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+% *
+% * Sun RPC is provided with no support and without any obligation on the
+% * part of Sun Microsystems, Inc. to assist in its use, correction,
+% * modification or enhancement.
+% *
+% * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+% * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+% * OR ANY PART THEREOF.
+% *
+% * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+% * or profits or other special, indirect and consequential damages, even if
+% * Sun has been advised of the possibility of such damages.
+% *
+% * Sun Microsystems, Inc.
+% * 2550 Garcia Avenue
+% * Mountain View, California  94043
+% */
+
+%/*
+% * Copyright (c) 1985, 1990 by Sun Microsystems, Inc.
+% */
+%
+%/* from @(#)mount.x   1.3 91/03/11 TIRPC 1.0 */
+
+/*
+ * Protocol description for the mount program
+ */
+
+#ifdef RPC_HDR
+%#ifndef _rpcsvc_mount_h
+%#define _rpcsvc_mount_h
+%#include <memory.h>
+#endif
+
+const MNTPATHLEN = 1024;       /* maximum bytes in a pathname argument */
+const MNTNAMLEN = 255;         /* maximum bytes in a name argument */
+const FHSIZE = 32;             /* size in bytes of a file handle */
+
+/*
+ * The fhandle is the file handle that the server passes to the client.
+ * All file operations are done using the file handles to refer to a file
+ * or a directory. The file handle can contain whatever information the
+ * server needs to distinguish an individual file.
+ */
+typedef opaque fhandle[FHSIZE];        
+
+/*
+ * If a status of zero is returned, the call completed successfully, and 
+ * a file handle for the directory follows. A non-zero status indicates
+ * some sort of error. The status corresponds with UNIX error numbers.
+ */
+union fhstatus switch (unsigned fhs_status) {
+case 0:
+       fhandle fhs_fhandle;
+default:
+       void;
+};
+
+/*
+ * The type dirpath is the pathname of a directory
+ */
+typedef string dirpath<MNTPATHLEN>;
+
+/*
+ * The type name is used for arbitrary names (hostnames, groupnames)
+ */
+typedef string name<MNTNAMLEN>;
+
+/*
+ * A list of who has what mounted
+ */
+typedef struct mountbody *mountlist;
+struct mountbody {
+       name ml_hostname;
+       dirpath ml_directory;
+       mountlist ml_next;
+};
+
+/*
+ * A list of netgroups
+ */
+typedef struct groupnode *groups;
+struct groupnode {
+       name gr_name;
+       groups gr_next;
+};
+
+/*
+ * A list of what is exported and to whom
+ */
+typedef struct exportnode *exports;
+struct exportnode {
+       dirpath ex_dir;
+       groups ex_groups;
+       exports ex_next;
+};
+
+/*
+ * POSIX pathconf information
+ */
+struct ppathcnf {
+       int     pc_link_max;    /* max links allowed */
+       short   pc_max_canon;   /* max line len for a tty */
+       short   pc_max_input;   /* input a tty can eat all at once */
+       short   pc_name_max;    /* max file name length (dir entry) */
+       short   pc_path_max;    /* max path name length (/x/y/x/.. ) */
+       short   pc_pipe_buf;    /* size of a pipe (bytes) */
+       u_char  pc_vdisable;    /* safe char to turn off c_cc[i] */
+       char    pc_xxx;         /* alignment padding; cc_t == char */
+       short   pc_mask[2];     /* validity and boolean bits */
+};
+
+/*
+ * NFSv3 file handle
+ */
+const FHSIZE3 =        64;             /* max size of NFSv3 file handle in bytes */
+typedef opaque         fhandle3<FHSIZE3>;
+
+/*
+ * NFSv3 mount status
+ */
+enum mountstat3 {
+       MNT_OK                  = 0,    /* no error */
+       MNT3ERR_PERM            = 1,    /* not owner */
+       MNT3ERR_NOENT           = 2,    /* no such file or directory */
+       MNT3ERR_IO              = 5,    /* I/O error */
+       MNT3ERR_ACCES           = 13,   /* Permission denied */
+       MNT3ERR_NOTDIR          = 20,   /* Not a directory */
+       MNT3ERR_INVAL           = 22,   /* Invalid argument */
+       MNT3ERR_NAMETOOLONG     = 63,   /* File name too long */
+       MNT3ERR_NOTSUPP         = 10004,/* Operation not supported */
+       MNT3ERR_SERVERFAULT     = 10006 /* A failure on the server */
+};
+
+/*
+ * NFSv3 mount result
+ */
+struct mountres3_ok {
+       fhandle3        fhandle;
+       int             auth_flavors<>;
+};
+
+union mountres3 switch (mountstat3 fhs_status) {
+case MNT_OK:
+       mountres3_ok    mountinfo; /* File handle and supported flavors */
+default:
+       void;
+};
+
+program MOUNTPROG {
+       /*
+        * Version one of the mount protocol communicates with version two
+        * of the NFS protocol. The only connecting point is the fhandle 
+        * structure, which is the same for both protocols.
+        */
+       version MOUNTVERS {
+               /*
+                * Does no work. It is made available in all RPC services
+                * to allow server reponse testing and timing
+                */
+               void
+               MOUNTPROC_NULL(void) = 0;
+
+               /*      
+                * If fhs_status is 0, then fhs_fhandle contains the
+                * file handle for the directory. This file handle may
+                * be used in the NFS protocol. This procedure also adds
+                * a new entry to the mount list for this client mounting
+                * the directory.
+                * Unix authentication required.
+                */
+               fhstatus 
+               MOUNTPROC_MNT(dirpath) = 1;
+
+               /*
+                * Returns the list of remotely mounted filesystems. The 
+                * mountlist contains one entry for each hostname and 
+                * directory pair.
+                */
+               mountlist
+               MOUNTPROC_DUMP(void) = 2;
+
+               /*
+                * Removes the mount list entry for the directory
+                * Unix authentication required.
+                */
+               void
+               MOUNTPROC_UMNT(dirpath) = 3;
+
+               /*
+                * Removes all of the mount list entries for this client
+                * Unix authentication required.
+                */
+               void
+               MOUNTPROC_UMNTALL(void) = 4;
+
+               /*
+                * Returns a list of all the exported filesystems, and which
+                * machines are allowed to import it.
+                */
+               exports
+               MOUNTPROC_EXPORT(void)  = 5;
+
+               /*
+                * Identical to MOUNTPROC_EXPORT above
+                */
+               exports
+               MOUNTPROC_EXPORTALL(void) = 6;
+       } = 1;
+
+       /*
+        * Version two of the mount protocol communicates with version two
+        * of the NFS protocol.
+        * The only difference from version one is the addition of a POSIX
+        * pathconf call.
+        */
+       version MOUNTVERS_POSIX {
+               /*
+                * Does no work. It is made available in all RPC services
+                * to allow server reponse testing and timing
+                */
+               void
+               MOUNTPROC_NULL(void) = 0;
+
+               /*      
+                * If fhs_status is 0, then fhs_fhandle contains the
+                * file handle for the directory. This file handle may
+                * be used in the NFS protocol. This procedure also adds
+                * a new entry to the mount list for this client mounting
+                * the directory.
+                * Unix authentication required.
+                */
+               fhstatus 
+               MOUNTPROC_MNT(dirpath) = 1;
+
+               /*
+                * Returns the list of remotely mounted filesystems. The 
+                * mountlist contains one entry for each hostname and 
+                * directory pair.
+                */
+               mountlist
+               MOUNTPROC_DUMP(void) = 2;
+
+               /*
+                * Removes the mount list entry for the directory
+                * Unix authentication required.
+                */
+               void
+               MOUNTPROC_UMNT(dirpath) = 3;
+
+               /*
+                * Removes all of the mount list entries for this client
+                * Unix authentication required.
+                */
+               void
+               MOUNTPROC_UMNTALL(void) = 4;
+
+               /*
+                * Returns a list of all the exported filesystems, and which
+                * machines are allowed to import it.
+                */
+               exports
+               MOUNTPROC_EXPORT(void)  = 5;
+
+               /*
+                * Identical to MOUNTPROC_EXPORT above
+                */
+               exports
+               MOUNTPROC_EXPORTALL(void) = 6;
+
+               /*
+                * POSIX pathconf info (Sun hack)
+                */
+               ppathcnf
+               MOUNTPROC_PATHCONF(dirpath) = 7;
+       } = 2;
+
+       /*
+        * Version 3 of the protocol is for NFSv3
+        */
+       version MOUNTVERS_NFSV3 {
+               /*
+                * Does no work. It is made available in all RPC services
+                * to allow server reponse testing and timing
+                */
+               void
+               MOUNTPROC3_NULL(void) = 0;
+
+               /*      
+                * If fhs_status is 0, then fhs_fhandle contains the
+                * file handle for the directory. This file handle may
+                * be used in the NFS protocol. This procedure also adds
+                * a new entry to the mount list for this client mounting
+                * the directory.
+                * Unix authentication required.
+                */
+               mountres3 
+               MOUNTPROC3_MNT(dirpath) = 1;
+
+               /*
+                * Returns the list of remotely mounted filesystems. The 
+                * mountlist contains one entry for each hostname and 
+                * directory pair.
+                */
+               mountlist
+               MOUNTPROC3_DUMP(void) = 2;
+
+               /*
+                * Removes the mount list entry for the directory
+                * Unix authentication required.
+                */
+               void
+               MOUNTPROC3_UMNT(dirpath) = 3;
+
+               /*
+                * Removes all of the mount list entries for this client
+                * Unix authentication required.
+                */
+               void
+               MOUNTPROC3_UMNTALL(void) = 4;
+
+               /*
+                * Returns a list of all the exported filesystems, and which
+                * machines are allowed to import it.
+                */
+               exports
+               MOUNTPROC3_EXPORT(void)  = 5;
+       } = 3;
+} = 100005;
+
+#ifdef RPC_HDR
+%#endif /*!_rpcsvc_mount_h*/
+#endif
diff --git a/support/export/nfsctl.c b/support/export/nfsctl.c
new file mode 100644 (file)
index 0000000..6612a76
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * support/export/nfsctl.c
+ *
+ * Communicate export information to knfsd.
+ *
+ * Copyright (C) 1995 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include "config.h"
+
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include "nfslib.h"
+#include "exportfs.h"
+#include "xio.h"
+
+static int     expsetup(struct nfsctl_export *exparg, nfs_export *exp);
+static int     cltsetup(struct nfsctl_client *cltarg, nfs_client *clp);
+
+int
+export_export(nfs_export *exp)
+{
+       nfs_client *    clp = exp->m_client;
+       struct nfsctl_export    exparg;
+       struct nfsctl_client    cltarg;
+
+       if (!clp->m_exported) {
+               if (!cltsetup(&cltarg, clp))
+                       return 0;
+               if (nfsaddclient(&cltarg) < 0)
+                       return 0;
+               clp->m_exported = 1;
+       }
+       if (!expsetup(&exparg, exp))
+               return 0;
+       if (nfsexport(&exparg) < 0)
+               return 0;
+       exp->m_exported = 1;
+       return 1;
+}
+
+int
+export_unexport(nfs_export *exp)
+{
+       struct nfsctl_export    exparg;
+
+       if (!expsetup(&exparg, exp) || nfsunexport(&exparg) < 0)
+               return 0;
+       exp->m_exported = 0;
+       return 1;
+}
+
+static void
+str_tolower(char *s)
+{
+       for ( ; *s; s++)
+               if (isupper(*s))
+                       *s = tolower(*s);
+}
+
+static int
+cltsetup(struct nfsctl_client *cltarg, nfs_client *clp)
+{
+       int     i;
+
+       if (clp->m_type != MCL_FQDN) {
+               xlog(L_ERROR, "internal: can't export non-FQDN host");
+               return 0;
+       }
+       memset(cltarg, 0, sizeof(*cltarg));
+       strncpy(cltarg->cl_ident, clp->m_hostname,
+               sizeof (cltarg->cl_ident) - 1);
+       str_tolower(cltarg->cl_ident);
+       cltarg->cl_naddr = clp->m_naddr;
+       for (i = 0; i < cltarg->cl_naddr && i < NFSCLNT_ADDRMAX; i++)
+               cltarg->cl_addrlist[i] = clp->m_addrlist[i];
+
+       return 1;
+}
+
+static int
+expsetup(struct nfsctl_export *exparg, nfs_export *exp)
+{
+       nfs_client              *clp = exp->m_client;
+       struct stat             stb;
+
+       if (stat(exp->m_export.m_path, &stb) < 0)
+               return 0;
+
+       memset(exparg, 0, sizeof(*exparg));
+       strncpy(exparg->ex_path, exp->m_export.m_path,
+               sizeof (exparg->ex_path) - 1);
+       strncpy(exparg->ex_client, clp->m_hostname,
+               sizeof (exparg->ex_client) - 1);
+       str_tolower(exparg->ex_client);
+       exparg->ex_flags    = exp->m_export.e_flags;
+       exparg->ex_dev      = stb.st_dev;
+       exparg->ex_ino      = stb.st_ino;
+       exparg->ex_anon_uid = exp->m_export.e_anonuid;
+       exparg->ex_anon_gid = exp->m_export.e_anongid;
+
+       return 1;
+}
diff --git a/support/export/rmtab.c b/support/export/rmtab.c
new file mode 100644 (file)
index 0000000..44a0edc
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * support/export/rmntab.c
+ *
+ * Interface to the rmnt file.
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include "xmalloc.h"
+#include "misc.h"
+#include "nfslib.h"
+#include "exportfs.h"
+#include "xio.h"
+#include "xlog.h"
+
+int
+rmtab_read(void)
+{
+       struct rmtabent         *rep;
+       nfs_export              *exp;
+
+       setrmtabent("r");
+       while ((rep = getrmtabent(1)) != NULL) {
+               exp = export_lookup(rep->r_client, rep->r_path);
+               if (!exp) {
+                       struct exportent        *xp;
+                       struct hostent          *hp;
+                       int                     htype;
+
+                       htype = client_gettype(rep->r_client);
+                       if (htype == MCL_FQDN
+                           && (hp = gethostbyname (rep->r_client), hp)
+                           && (hp = hostent_dup (hp),
+                                  xp = export_allowed (hp, rep->r_path))) {
+                               strncpy (xp->e_hostname, rep->r_client,
+                                        sizeof (xp->e_hostname) - 1);
+                               xp->e_hostname[sizeof (xp->e_hostname) -1] = '\0';
+                               exp = export_create(xp);
+                               free (hp);
+                       }
+
+                       if (!exp)
+                               continue;
+                       exp->m_mayexport = 1;
+               }
+       }
+       if (errno == EINVAL) {
+               /* Something goes wrong. We need to fix the rmtab
+                  file. */
+               int     lockid;
+               FILE    *fp;
+               if ((lockid = xflock(_PATH_RMTAB, "w")) < 0)
+                       return -1;
+               rewindrmtabent();
+               if (!(fp = fsetrmtabent(_PATH_RMTABTMP, "w"))) {
+                       endrmtabent ();
+                       xfunlock(lockid);
+                       return -1;
+               }
+               while ((rep = getrmtabent(0)) != NULL) {
+                       fputrmtabent(fp, rep);
+               }
+               if (rename(_PATH_RMTABTMP, _PATH_RMTAB) < 0) {
+                       xlog(L_ERROR, "couldn't rename %s to %s",
+                            _PATH_RMTABTMP, _PATH_RMTAB);
+               }
+               endrmtabent();
+               fendrmtabent(fp);
+               xfunlock(lockid);
+       }
+       else {
+               endrmtabent();
+       }
+       return 0;
+}
diff --git a/support/export/xtab.c b/support/export/xtab.c
new file mode 100644 (file)
index 0000000..4289d7c
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * support/export/xtab.c
+ *
+ * Interface to the xtab file.
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include "config.h"
+
+#include <sys/fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include "xmalloc.h"
+#include "nfslib.h"
+#include "exportfs.h"
+#include "xio.h"
+#include "xlog.h"
+
+static int
+xtab_read(char *xtab, int is_export)
+{
+       struct exportent        *xp;
+       nfs_export              *exp;
+       int                     lockid;
+
+       if ((lockid = xflock(xtab, "r")) < 0)
+               return 0;
+       setexportent(xtab, "r");
+       while ((xp = getexportent()) != NULL) {
+               if (!(exp = export_lookup(xp->e_hostname, xp->e_path)) &&
+                   !(exp = export_create(xp))) {
+                       continue;
+               }
+               if (is_export) {
+                       exp->m_xtabent = 1;
+                       exp->m_mayexport = 1;
+               } else
+                       exp->m_exported = 1;
+       }
+       endexportent();
+       xfunlock(lockid);
+
+       return 0;
+}
+
+int
+xtab_mount_read(void)
+{
+       int fd;
+       if ((fd=open(_PATH_PROC_EXPORTS, O_RDONLY))>=0) {
+               close(fd);
+               return xtab_read(_PATH_PROC_EXPORTS, 0);
+       } else
+               return xtab_read(_PATH_XTAB, 0);
+}
+
+int
+xtab_export_read(void)
+{
+       return xtab_read(_PATH_ETAB, 1);
+}
+
+static int
+xtab_write(char *xtab, char *xtabtmp, int is_export)
+{
+       struct exportent        xe;
+       nfs_export              *exp;
+       int                     lockid, i;
+
+       if ((lockid = xflock(xtab, "w")) < 0) {
+               xlog(L_ERROR, "can't lock %s for writing", xtab);
+               return 0;
+       }
+       setexportent(xtabtmp, "w");
+
+       for (i = 0; i < MCL_MAXTYPES; i++) {
+               for (exp = exportlist[i]; exp; exp = exp->m_next) {
+                       if (is_export && !exp->m_xtabent)
+                               continue;
+                       if (!is_export && ! exp->m_exported)
+                               continue;
+
+                       /* write out the export entry using the FQDN */
+                       xe = exp->m_export;
+                       strncpy(xe.e_hostname,
+                               exp->m_client->m_hostname,
+                               sizeof (xe.e_hostname) - 1);
+                       xe.e_hostname[sizeof (xe.e_hostname) - 1] = '\0';
+                       putexportent(&xe);
+               }
+       }
+       endexportent();
+
+       rename(xtabtmp, xtab);
+
+       xfunlock(lockid);
+
+       return 1;
+}
+
+int
+xtab_export_write()
+{
+       return xtab_write(_PATH_ETAB, _PATH_ETABTMP, 1);
+}
+
+int
+xtab_mount_write()
+{
+       return xtab_write(_PATH_XTAB, _PATH_XTABTMP, 0);
+}
+
+void
+xtab_append(nfs_export *exp)
+{
+       struct exportent xe;
+       int             lockid;
+
+       if ((lockid = xflock(_PATH_XTAB, "w")) < 0)
+               return;
+       setexportent(_PATH_XTAB, "a");
+       xe = exp->m_export;
+       strncpy(xe.e_hostname, exp->m_client->m_hostname,
+              sizeof (xe.e_hostname) - 1);
+       xe.e_hostname[sizeof (xe.e_hostname) - 1] = '\0';
+       putexportent(&xe);
+       endexportent();
+       xfunlock(lockid);
+       exp->m_xtabent = 1;
+}
+
diff --git a/support/include/Makefile b/support/include/Makefile
new file mode 100644 (file)
index 0000000..e1cbfc4
--- /dev/null
@@ -0,0 +1,16 @@
+#
+# Makefile for linux-nfs/support
+#
+
+include $(TOP)rules.mk
+
+all install::  mount.h
+       @:
+
+distclean::
+       $(RM) mount.h config.h
+
+mount.h:
+       $(LN_S) ../export/mount.h .
+
+# .EXPORT_ALL_VARIABLES:
diff --git a/support/include/config.h.in b/support/include/config.h.in
new file mode 100644 (file)
index 0000000..f8c1497
--- /dev/null
@@ -0,0 +1,25 @@
+/* Define this if you have standard C headers
+ */
+#undef  STDC_HEADERS
+
+/* Define this if you have string.h */
+#undef  HAVE_STRING_H
+
+/* Define this if you have netgroup support
+ */
+#undef  HAVE_INNETGR
+
+/* Define this if you want NFSv3 support compiled in
+ */
+#undef  NFS3_SUPPORTED
+
+/* This defines the location of the NFS state files
+ * Warning: these must match definitions in config.mk!
+ */
+#define NFS_STATEDIR           "/var/lib/nfs"
+
+/* Define this if you want to enable various security
+ * checks in statd. These checks basically keep anyone
+ * but lockd from using this service.
+ */
+#undef RESTRICTED_STATD
diff --git a/support/include/exportfs.h b/support/include/exportfs.h
new file mode 100644 (file)
index 0000000..d440dc1
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * support/include/exportfs.h
+ *
+ * Declarations for exportfs and mountd
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#ifndef EXPORTFS_H
+#define EXPORTFS_H
+
+#include <netdb.h>
+#include "nfslib.h"
+
+enum {
+       MCL_FQDN = 0,
+       MCL_SUBNETWORK,
+       MCL_IPADDR = MCL_SUBNETWORK,
+       MCL_WILDCARD,
+       MCL_NETGROUP,
+       MCL_ANONYMOUS,
+       MCL_MAXTYPES
+};
+
+typedef struct mclient {
+       struct mclient *        m_next;
+       char                    m_hostname[NFSCLNT_IDMAX+1];
+       int                     m_type;
+       int                     m_naddr;
+       struct in_addr          m_addrlist[NFSCLNT_ADDRMAX];
+       int                     m_exported;     /* exported to nfsd */
+       int                     m_count;
+} nfs_client;
+
+typedef struct mexport {
+       struct mexport *        m_next;
+       struct mclient *        m_client;
+       struct exportent        m_export;
+       int                     m_exported : 1, /* known to knfsd */
+                               m_xtabent  : 1, /* xtab entry exists */
+                               m_mayexport: 1, /* derived from xtabbed */
+                               m_changed  : 1; /* options (may) have changed */
+} nfs_export;
+
+extern nfs_client *            clientlist[MCL_MAXTYPES];
+extern nfs_export *            exportlist[MCL_MAXTYPES];
+
+nfs_client *                   client_lookup(char *hname);
+nfs_client *                   client_find(struct hostent *);
+void                           client_add(nfs_client *);
+nfs_client *                   client_dup(nfs_client *, struct hostent *);
+int                            client_gettype(char *hname);
+int                            client_check(nfs_client *, struct hostent *);
+int                            client_match(nfs_client *, char *hname);
+void                           client_release(nfs_client *);
+void                           client_freeall(void);
+
+int                            export_read(char *fname);
+void                           export_add(nfs_export *);
+void                           export_reset(nfs_export *);
+nfs_export *                   export_lookup(char *hname, char *path);
+nfs_export *                   export_find(struct hostent *, char *path);
+struct exportent *             export_allowed(struct hostent *, char *path);
+nfs_export *                   export_create(struct exportent *);
+nfs_export *                   export_dup(nfs_export *, struct hostent *);
+void                           export_freeall(void);
+int                            export_export(nfs_export *);
+int                            export_unexport(nfs_export *);
+
+int                            xtab_mount_read(void);
+int                            xtab_export_read(void);
+int                            xtab_mount_write(void);
+int                            xtab_export_write(void);
+void                           xtab_append(nfs_export *);
+
+int                            rmtab_read(void);
+
+struct nfskey *                        key_lookup(char *hname);
+
+#endif /* EXPORTFS_H */
diff --git a/support/include/misc.h b/support/include/misc.h
new file mode 100644 (file)
index 0000000..a3cdcfd
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * misc.h      All that didn't fit elsewhere.
+ *
+ * Copyright (C) 1995 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#ifndef MISC_H
+#define MISC_H
+
+/*
+ * Generate random key, returning the length of the result. Currently,
+ * weakrandomkey generates a maximum of 20 bytes are generated, but this
+ * may change with future implementations.
+ */
+int    randomkey(unsigned char *keyout, int len);
+int    weakrandomkey(unsigned char *keyout, int len);
+
+int    matchhostname(const char *h1, const char *h2); 
+
+struct hostent;
+struct hostent *hostent_dup(struct hostent *hp);
+struct hostent *get_hostent (const char *addr, int len, int type);
+
+#endif /* MISC_H */
diff --git a/support/include/nfs/debug.h b/support/include/nfs/debug.h
new file mode 100644 (file)
index 0000000..876b5db
--- /dev/null
@@ -0,0 +1,75 @@
+#ifndef _NFS_DEBUG_H
+#define _NFS_DEBUG_H
+
+/*
+ * RPC debug facilities
+ */
+#define RPCDBG_XPRT            0x0001
+#define RPCDBG_CALL            0x0002
+#define RPCDBG_DEBUG           0x0004
+#define RPCDBG_NFS             0x0008
+#define RPCDBG_AUTH            0x0010
+#define RPCDBG_PMAP            0x0020
+#define RPCDBG_SCHED           0x0040
+#define RPCDBG_SVCSOCK         0x0100
+#define RPCDBG_SVCDSP          0x0200
+#define RPCDBG_MISC            0x0400
+#define RPCDBG_ALL             0x7fff
+
+/*
+ * Declarations for the sysctl debug interface, which allows to read or
+ * change the debug flags for rpc, nfs, nfsd, and lockd. Since the sunrpc
+ * module currently registers its sysctl table dynamically, the sysctl path
+ * for module FOO is <CTL_SUNRPC, CTL_FOODEBUG>.
+ */
+#define CTL_SUNRPC     7249    /* arbitrary and hopefully unused */
+
+enum {
+       CTL_RPCDEBUG = 1,
+       CTL_NFSDEBUG,
+       CTL_NFSDDEBUG,
+       CTL_NLMDEBUG,
+};
+
+
+/*
+ * knfsd debug flags
+ */
+#define NFSDDBG_SOCK           0x0001
+#define NFSDDBG_FH             0x0002
+#define NFSDDBG_EXPORT         0x0004
+#define NFSDDBG_SVC            0x0008
+#define NFSDDBG_PROC           0x0010
+#define NFSDDBG_FILEOP         0x0020
+#define NFSDDBG_AUTH           0x0040
+#define NFSDDBG_REPCACHE       0x0080
+#define NFSDDBG_XDR            0x0100
+#define NFSDDBG_LOCKD          0x0200
+#define NFSDDBG_ALL            0x7FFF
+#define NFSDDBG_NOCHANGE       0xFFFF
+
+/*
+ * Debug flags
+ */
+#define NLMDBG_SVC             0x0001
+#define NLMDBG_CLIENT          0x0002
+#define NLMDBG_CLNTLOCK                0x0004
+#define NLMDBG_SVCLOCK         0x0008
+#define NLMDBG_MONITOR         0x0010
+#define NLMDBG_CLNTSUBS                0x0020
+#define NLMDBG_SVCSUBS         0x0040
+#define NLMDBG_HOSTCACHE       0x0080
+#define NLMDBG_ALL             0x7fff
+
+
+#define NFSDBG_VFS             0x0001
+#define NFSDBG_DIRCACHE                0x0002
+#define NFSDBG_LOOKUPCACHE     0x0004
+#define NFSDBG_PAGECACHE       0x0008
+#define NFSDBG_PROC            0x0010
+#define NFSDBG_XDR             0x0020
+#define NFSDBG_FILE            0x0040
+#define NFSDBG_ROOT            0x0080
+#define NFSDBG_ALL             0xFFFF
+
+#endif /* _NFS_DEBUG_H */
diff --git a/support/include/nfs/export.h b/support/include/nfs/export.h
new file mode 100644 (file)
index 0000000..80d23fd
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef _NSF_EXPORT_H
+#define _NSF_EXPORT_H
+
+/*
+ * Important limits for the exports stuff.
+ */
+#define NFSCLNT_IDMAX          1024
+#define NFSCLNT_ADDRMAX                16
+#define NFSCLNT_KEYMAX         32
+
+/*
+ * Export flags.
+ */
+#define NFSEXP_READONLY                0x0001
+#define NFSEXP_INSECURE_PORT   0x0002
+#define NFSEXP_ROOTSQUASH      0x0004
+#define NFSEXP_ALLSQUASH       0x0008
+#define NFSEXP_ASYNC           0x0010
+#define NFSEXP_GATHERED_WRITES 0x0020
+#define NFSEXP_UIDMAP          0x0040
+#define NFSEXP_KERBEROS                0x0080          /* not available */
+#define NFSEXP_SUNSECURE       0x0100
+#define NFSEXP_CROSSMNT                0x0200          /* not available */
+#define NFSEXP_ALLFLAGS                0x03FF
+
+#endif /* _NSF_EXPORT_H */
diff --git a/support/include/nfs/nfs.h b/support/include/nfs/nfs.h
new file mode 100644 (file)
index 0000000..0cfed07
--- /dev/null
@@ -0,0 +1,145 @@
+#ifndef _NFS_NFS_H
+#define _NFS_NFS_H
+
+#include <linux/posix_types.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <rpcsvc/nfs_prot.h>
+#include <nfs/export.h>
+
+struct dentry;
+
+/*
+ * This is the new "dentry style" Linux NFSv2 file handle.
+ *
+ * The xino and xdev fields are currently used to transport the
+ * ino/dev of the exported inode.
+ */
+struct nfs_fhbase {
+       struct dentry * fb_dentry;      /* dentry cookie */
+       u_int32_t               fb_ino;         /* our inode number */
+       u_int32_t               fb_dirino;      /* dir inode number */
+       u_int32_t               fb_dev;         /* our device */
+       u_int32_t               fb_xdev;
+       u_int32_t               fb_xino;
+};
+
+#define NFS_FH_PADDING         (NFS_FHSIZE - sizeof(struct nfs_fhbase))
+struct knfs_fh {
+       struct nfs_fhbase       fh_base;
+       u_int8_t                        fh_cookie[NFS_FH_PADDING];
+};
+
+#define fh_dcookie             fh_base.fb_dentry
+#define fh_ino                 fh_base.fb_ino
+#define fh_dirino              fh_base.fb_dirino
+#define fh_dev                 fh_base.fb_dev
+#define fh_xdev                        fh_base.fb_xdev
+#define fh_xino                        fh_base.fb_xino
+
+/*
+ * Version of the syscall interface
+ */
+#define NFSCTL_VERSION         0x0201
+
+/*
+ * These are the commands understood by nfsctl().
+ */
+#define NFSCTL_SVC             0       /* This is a server process. */
+#define NFSCTL_ADDCLIENT       1       /* Add an NFS client. */
+#define NFSCTL_DELCLIENT       2       /* Remove an NFS client. */
+#define NFSCTL_EXPORT          3       /* export a file system. */
+#define NFSCTL_UNEXPORT                4       /* unexport a file system. */
+#define NFSCTL_UGIDUPDATE      5       /* update a client's uid/gid map. */
+#define NFSCTL_GETFH           6       /* get an fh (used by mountd) */
+#define NFSCTL_GETFD           7       /* get an fh by path (used by mountd) */
+
+/* Above this is for lockd. */
+#define NFSCTL_LOCKD           0x10000
+#define LOCKDCTL_SVC           NFSCTL_LOCKD
+
+
+
+/* SVC */
+struct nfsctl_svc {
+       unsigned short          svc_port;
+       int                     svc_nthreads;
+};
+
+/* ADDCLIENT/DELCLIENT */
+struct nfsctl_client {
+       char                    cl_ident[NFSCLNT_IDMAX+1];
+       int                     cl_naddr;
+       struct in_addr          cl_addrlist[NFSCLNT_ADDRMAX];
+       int                     cl_fhkeytype;
+       int                     cl_fhkeylen;
+       unsigned char           cl_fhkey[NFSCLNT_KEYMAX];
+};
+
+/* EXPORT/UNEXPORT */
+struct nfsctl_export {
+       char                    ex_client[NFSCLNT_IDMAX+1];
+       char                    ex_path[NFS_MAXPATHLEN+1];
+       __kernel_dev_t          ex_dev;
+       __kernel_ino_t          ex_ino;
+       int                     ex_flags;
+       __kernel_uid_t          ex_anon_uid;
+       __kernel_gid_t          ex_anon_gid;
+};
+
+/* UGIDUPDATE */
+struct nfsctl_uidmap {
+       char *                  ug_ident;
+       __kernel_uid_t          ug_uidbase;
+       int                     ug_uidlen;
+       __kernel_uid_t *        ug_udimap;
+       __kernel_gid_t          ug_gidbase;
+       int                     ug_gidlen;
+       __kernel_gid_t *        ug_gdimap;
+};
+
+/* GETFH */
+struct nfsctl_fhparm {
+       struct sockaddr         gf_addr;
+       __kernel_dev_t          gf_dev;
+       __kernel_ino_t          gf_ino;
+       int                     gf_version;
+};
+
+/* GETFD */
+struct nfsctl_fdparm {
+       struct sockaddr         gd_addr;
+       char                    gd_path[NFS_MAXPATHLEN+1];
+       int                     gd_version;
+};
+
+/*
+ * This is the argument union.
+ */
+struct nfsctl_arg {
+       int                     ca_version;     /* safeguard */
+       union {
+               struct nfsctl_svc       u_svc;
+               struct nfsctl_client    u_client;
+               struct nfsctl_export    u_export;
+               struct nfsctl_uidmap    u_umap;
+               struct nfsctl_fhparm    u_getfh;
+               struct nfsctl_fdparm    u_getfd;
+               unsigned int            u_debug;
+       } u;
+#define ca_svc         u.u_svc
+#define ca_client      u.u_client
+#define ca_export      u.u_export
+#define ca_umap                u.u_umap
+#define ca_getfh       u.u_getfh
+#define ca_getfd       u.u_getfd
+#define ca_authd       u.u_authd
+#define ca_debug       u.u_debug
+};
+
+union nfsctl_res {
+       struct knfs_fh          cr_getfh;
+       unsigned int            cr_debug;
+};
+
+#endif /* _NFS_NFS_H */
diff --git a/support/include/nfslib.h b/support/include/nfslib.h
new file mode 100644 (file)
index 0000000..d8be926
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * support/include/nfslib.h
+ *
+ * General support functions for NFS user-space programs.
+ *
+ * Copyright (C) 1995 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#ifndef NFSLIB_H
+#define NFSLIB_H
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <paths.h>
+#include <rpcsvc/nfs_prot.h>
+#include <nfs/nfs.h>
+#include "xlog.h"
+
+#ifndef _PATH_EXPORTS
+#define _PATH_EXPORTS          "/etc/exports"
+#endif
+#ifndef _PATH_XTAB
+#define _PATH_XTAB             NFS_STATEDIR "/xtab"
+#endif
+#ifndef _PATH_XTABTMP
+#define _PATH_XTABTMP          NFS_STATEDIR "/xtab.tmp"
+#endif
+#ifndef _PATH_ETAB
+#define _PATH_ETAB             NFS_STATEDIR "/etab"
+#endif
+#ifndef _PATH_ETABTMP
+#define _PATH_ETABTMP          NFS_STATEDIR "/etab.tmp"
+#endif
+#ifndef _PATH_RMTAB
+#define _PATH_RMTAB            NFS_STATEDIR "/rmtab"
+#endif
+#ifndef _PATH_RMTABTMP
+#define _PATH_RMTABTMP         _PATH_RMTAB ".tmp"
+#endif
+#ifndef _PATH_PROC_EXPORTS
+#define        _PATH_PROC_EXPORTS      "/proc/fs/nfs/exports"
+#endif
+
+enum cle_maptypes {
+       CLE_MAP_IDENT = 0,
+       CLE_MAP_FILE,
+       CLE_MAP_UGIDD,
+};
+
+/*
+ * Data related to a single exports entry as returned by getexportent.
+ * FIXME: export options should probably be parsed at a later time to 
+ * allow overrides when using exportfs.
+ */
+struct exportent {
+       char            e_hostname[NFSCLNT_IDMAX+1];
+       char            e_path[NFS_MAXPATHLEN+1];
+       /* The mount path may be different from the exported path due
+          to submount. It may change for every mount. The idea is we
+          set m_path every time when we process a mount. We should not
+          use it for anything else. */
+       char            m_path[NFS_MAXPATHLEN+1];
+       int             e_flags;
+       int             e_maptype;
+       int             e_anonuid;
+       int             e_anongid;
+       int *           e_squids;
+       int             e_nsquids;
+       int *           e_sqgids;
+       int             e_nsqgids;
+};
+
+struct rmtabent {
+       char            r_client[NFSCLNT_IDMAX+1];
+       char            r_path[NFS_MAXPATHLEN+1];
+};
+
+/*
+ * configuration file parsing
+ */
+void                   setexportent(char *fname, char *type);
+struct exportent *     getexportent(void);
+void                   putexportent(struct exportent *xep);
+void                   endexportent(void);
+struct exportent *     mkexportent(char *hname, char *path, char *opts);
+void                   dupexportent(struct exportent *dst,
+                                       struct exportent *src);
+int                    updateexportent(struct exportent *eep, char *options);
+
+int                    setrmtabent(char *type);
+struct rmtabent *      getrmtabent(int log);
+void                   putrmtabent(struct rmtabent *xep);
+void                   endrmtabent(void);
+void                   rewindrmtabent(void);
+FILE *                 fsetrmtabent(char *fname, char *type);
+struct rmtabent *      fgetrmtabent(FILE *fp, int log);
+void                   fputrmtabent(FILE *fp, struct rmtabent *xep);
+void                   fendrmtabent(FILE *fp);
+void                   frewindrmtabent(FILE *fp);
+
+/*
+ * wildmat borrowed from INN
+ */
+int                    wildmat(char *text, char *pattern);
+
+/*
+ * nfsd library functions.
+ */
+int                    nfsctl(int, struct nfsctl_arg *, union nfsctl_res *);
+int                    nfssvc(int port, int nrservs);
+int                    nfsaddclient(struct nfsctl_client *clp);
+int                    nfsdelclient(struct nfsctl_client *clp);
+int                    nfsexport(struct nfsctl_export *exp);
+int                    nfsunexport(struct nfsctl_export *exp);
+struct knfs_fh *       getfh_old(struct sockaddr *addr, dev_t dev, ino_t ino);
+struct knfs_fh *       getfh(struct sockaddr *addr, const char *);
+
+/* lockd. */
+int                    lockdsvc();
+
+#endif /* NFSLIB_H */
diff --git a/support/include/rpcdispatch.h b/support/include/rpcdispatch.h
new file mode 100644 (file)
index 0000000..866d4bf
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ *     nlm_dispatch    This is a generic RPC call dispatcher.
+ *                     It is loosely based on the dispatch mechanism I
+ *                     first encountered in the UNFSD source.
+ *
+ *     Cyopright (C) 1995, Olaf Kirch <okir@monad.swb.de>
+ *
+ *     24.05.95        okir
+ *
+ */
+
+#ifndef RPCDISPATCH_H
+#define RPCDISPATCH_H
+
+#include <rpc/rpc.h>
+
+#ifdef __STDC__
+#   define CONCAT(a,b)         a##b
+#   define CONCAT3(a,b,c)      a##b##c
+#   define STRING(a)           #a
+#else
+#   define CONCAT(a,b)         a/**/b
+#   define CONCAT3(a,b,c)      a/**/b/**/c
+#   define STRING(a)           "a"
+#endif
+
+#ifdef __STDC__
+typedef bool_t (*rpcsvc_fn_t)(struct svc_req *, void *argp, void *resp);
+#else
+typedef bool_t (*rpcsvc_fn_t)();
+#endif
+
+#define table_ent(func, vers, arg_type, res_type) \
+       {       STRING(func), \
+               (rpcsvc_fn_t)CONCAT(func,_svc), vers,\
+               (xdrproc_t)CONCAT(xdr_, arg_type), sizeof(arg_type), \
+               (xdrproc_t)CONCAT(xdr_, res_type), sizeof(res_type), \
+       }
+#define nlm_undef_svc  NULL
+#define xdr_nlm_void   xdr_void
+
+struct dispatch_entry {
+       const char      *name;
+       rpcsvc_fn_t     func;
+       unsigned int    versions;               /* bitmap of versions */
+       xdrproc_t       xdr_arg_fn;             /* argument XDR */
+       size_t          xdr_arg_size;
+       xdrproc_t       xdr_res_fn;             /* result XDR */
+       size_t          xdr_res_size;
+};
+
+void   rpc_dispatch(struct svc_req *rq, SVCXPRT *tp,
+                       struct dispatch_entry *dtable, int nproc,
+                       void *argp, void *resp);
+void   rpc_svcrun(void);
+
+#endif /* RPCDISPATCH_H */
diff --git a/support/include/rpcmisc.h b/support/include/rpcmisc.h
new file mode 100644 (file)
index 0000000..06970cd
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * rpcmisc     Support for RPC startup, dispatching and logging.
+ *
+ * Copyright (C) 1995 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#ifndef RPCMISC_H
+#define RPCMISC_H
+
+#include <rpc/rpc.h>
+#include <rpc/pmap_clnt.h>
+
+#ifdef __STDC__
+#   define CONCAT(a,b)         a##b
+#   define STRING(a)           #a
+#else
+#   define CONCAT(a,b)         a/**/b
+#   define STRING(a)           "a"
+#endif
+
+typedef bool_t (*rpcsvc_fn_t)(struct svc_req *, void *argp, void *resp);
+
+struct rpc_dentry {
+       const char      *name;
+       rpcsvc_fn_t     func;
+       xdrproc_t       xdr_arg_fn;             /* argument XDR */
+       size_t          xdr_arg_size;
+       xdrproc_t       xdr_res_fn;             /* result XDR */
+       size_t          xdr_res_size;
+};
+
+struct rpc_dtable {
+       struct rpc_dentry *entries;
+       int             nproc;
+};
+
+#define dtable_ent(func, vers, arg_type, res_type) \
+       {       STRING(func), \
+               (rpcsvc_fn_t)func##_##vers##_svc, \
+               (xdrproc_t)xdr_##arg_type, sizeof(arg_type), \
+               (xdrproc_t)xdr_##res_type, sizeof(res_type), \
+       }
+
+void           rpc_init(char *name, int prog, int vers,
+                               void (*dispatch)(struct svc_req *, SVCXPRT *),
+                               int defport, int bufsize);
+void           rpc_svcrun(void);
+void           rpc_dispatch(struct svc_req *rq, SVCXPRT *xprt,
+                               struct rpc_dtable *dtable, int nvers,
+                               void *argp, void *resp);
+void           rpc_logcall(struct svc_req *, char *xname, char *args);
+
+extern int     _rpcpmstart;
+extern int     _rpcfdtype;
+extern int     _rpcsvcdirty;
+
+
+#endif /* RPCMISC_H */
diff --git a/support/include/rpcsec.h b/support/include/rpcsec.h
new file mode 100644 (file)
index 0000000..84d4497
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ *  Declarations needed for the authdes library. Some of the functions
+ *  mentioned herein have been omitted from the Linux libc header files
+ */
+
+#ifndef RPCSEC_H
+#define RPCSEC_H
+
+int    netname2user(char *netname, int *uidp, int *gidp,
+                                       int *gidlenp, int *gidlist);
+int    netname2host(char *netname, char *hostname, int hostlen);
+int    getnetname(char *name);
+int    user2netname(char *netname, int uid, char *domain);
+int    host2netname(char *netname, char *hostname, char *domain);
+void   passwd2des(char *pw, char *key);
+int    getsecretkey(char *netname, char *secretkey, char *passwd);
+int    getpublickey(char *hostname, char *publickey);
+int    yp_update(char *domain, char *map, unsigned int ypop,
+                       char *key, int keylen, char *data, int datalen);
+int    key_setsecret(char *secret);
+int    xencrypt(char *secret, char *passwd);
+int    xdecrypt(char *secret, char *passwd);
+
+
+#define PUBLICKEY_MAP  "publickey.byname"
+#define NETID_MAP      "netid.byname"
+
+#ifndef DEBUG
+#define RPCSEC_BASE    "/etc/"
+#else
+#define RPCSEC_BASE    "/tmp/"
+#endif
+
+#define PUBLICKEY_FILE RPCSEC_BASE "publickey"
+#define PUBLICKEY_LOCK RPCSEC_BASE "publickey.lock"
+#define ROOTKEY_FILE   RPCSEC_BASE ".rootkey"
+#define KEYSTORE_FILE  RPCSEC_BASE "keystore"
+
+#endif /* RPCSEC_H */
diff --git a/support/include/rpcsvc/nfs_prot.h b/support/include/rpcsvc/nfs_prot.h
new file mode 100644 (file)
index 0000000..9311341
--- /dev/null
@@ -0,0 +1,661 @@
+/*
+ * Please do not edit this file.
+ * It was generated using rpcgen.
+ */
+
+#ifndef _NFS_PROT_H_RPCGEN
+#define _NFS_PROT_H_RPCGEN
+
+#include <rpc/rpc.h>
+
+#define NFS_PORT 2049
+#define NFS_MAXDATA 8192
+#define NFS_MAXPATHLEN 1024
+#define NFS_MAXNAMLEN 255
+#define NFS_FHSIZE 32
+#define NFS_COOKIESIZE 4
+#define NFS_FIFO_DEV -1
+#define NFSMODE_FMT 0170000
+#define NFSMODE_DIR 0040000
+#define NFSMODE_CHR 0020000
+#define NFSMODE_BLK 0060000
+#define NFSMODE_REG 0100000
+#define NFSMODE_LNK 0120000
+#define NFSMODE_SOCK 0140000
+#define NFSMODE_FIFO 0010000
+
+enum nfsstat {
+       NFS_OK = 0,
+       NFSERR_PERM = 1,
+       NFSERR_NOENT = 2,
+       NFSERR_IO = 5,
+       NFSERR_NXIO = 6,
+       NFSERR_ACCES = 13,
+       NFSERR_EXIST = 17,
+       NFSERR_NODEV = 19,
+       NFSERR_NOTDIR = 20,
+       NFSERR_ISDIR = 21,
+       NFSERR_FBIG = 27,
+       NFSERR_NOSPC = 28,
+       NFSERR_ROFS = 30,
+       NFSERR_NAMETOOLONG = 63,
+       NFSERR_NOTEMPTY = 66,
+       NFSERR_DQUOT = 69,
+       NFSERR_STALE = 70,
+       NFSERR_WFLUSH = 99,
+};
+typedef enum nfsstat nfsstat;
+#ifdef __cplusplus 
+extern "C" bool_t xdr_nfsstat(XDR *, nfsstat*);
+#elif __STDC__ 
+extern  bool_t xdr_nfsstat(XDR *, nfsstat*);
+#else /* Old Style C */ 
+bool_t xdr_nfsstat();
+#endif /* Old Style C */ 
+
+
+enum ftype {
+       NFNON = 0,
+       NFREG = 1,
+       NFDIR = 2,
+       NFBLK = 3,
+       NFCHR = 4,
+       NFLNK = 5,
+       NFSOCK = 6,
+       NFBAD = 7,
+       NFFIFO = 8,
+};
+typedef enum ftype ftype;
+#ifdef __cplusplus 
+extern "C" bool_t xdr_ftype(XDR *, ftype*);
+#elif __STDC__ 
+extern  bool_t xdr_ftype(XDR *, ftype*);
+#else /* Old Style C */ 
+bool_t xdr_ftype();
+#endif /* Old Style C */ 
+
+
+struct nfs_fh {
+       char data[NFS_FHSIZE];
+};
+typedef struct nfs_fh nfs_fh;
+#ifdef __cplusplus 
+extern "C" bool_t xdr_nfs_fh(XDR *, nfs_fh*);
+#elif __STDC__ 
+extern  bool_t xdr_nfs_fh(XDR *, nfs_fh*);
+#else /* Old Style C */ 
+bool_t xdr_nfs_fh();
+#endif /* Old Style C */ 
+
+
+struct nfstime {
+       u_int seconds;
+       u_int useconds;
+};
+typedef struct nfstime nfstime;
+#ifdef __cplusplus 
+extern "C" bool_t xdr_nfstime(XDR *, nfstime*);
+#elif __STDC__ 
+extern  bool_t xdr_nfstime(XDR *, nfstime*);
+#else /* Old Style C */ 
+bool_t xdr_nfstime();
+#endif /* Old Style C */ 
+
+
+struct fattr {
+       ftype type;
+       u_int mode;
+       u_int nlink;
+       u_int uid;
+       u_int gid;
+       u_int size;
+       u_int blocksize;
+       u_int rdev;
+       u_int blocks;
+       u_int fsid;
+       u_int fileid;
+       nfstime atime;
+       nfstime mtime;
+       nfstime ctime;
+};
+typedef struct fattr fattr;
+#ifdef __cplusplus 
+extern "C" bool_t xdr_fattr(XDR *, fattr*);
+#elif __STDC__ 
+extern  bool_t xdr_fattr(XDR *, fattr*);
+#else /* Old Style C */ 
+bool_t xdr_fattr();
+#endif /* Old Style C */ 
+
+
+struct sattr {
+       u_int mode;
+       u_int uid;
+       u_int gid;
+       u_int size;
+       nfstime atime;
+       nfstime mtime;
+};
+typedef struct sattr sattr;
+#ifdef __cplusplus 
+extern "C" bool_t xdr_sattr(XDR *, sattr*);
+#elif __STDC__ 
+extern  bool_t xdr_sattr(XDR *, sattr*);
+#else /* Old Style C */ 
+bool_t xdr_sattr();
+#endif /* Old Style C */ 
+
+
+typedef char *filename;
+#ifdef __cplusplus 
+extern "C" bool_t xdr_filename(XDR *, filename*);
+#elif __STDC__ 
+extern  bool_t xdr_filename(XDR *, filename*);
+#else /* Old Style C */ 
+bool_t xdr_filename();
+#endif /* Old Style C */ 
+
+
+typedef char *nfspath;
+#ifdef __cplusplus 
+extern "C" bool_t xdr_nfspath(XDR *, nfspath*);
+#elif __STDC__ 
+extern  bool_t xdr_nfspath(XDR *, nfspath*);
+#else /* Old Style C */ 
+bool_t xdr_nfspath();
+#endif /* Old Style C */ 
+
+
+struct attrstat {
+       nfsstat status;
+       union {
+               fattr attributes;
+       } attrstat_u;
+};
+typedef struct attrstat attrstat;
+#ifdef __cplusplus 
+extern "C" bool_t xdr_attrstat(XDR *, attrstat*);
+#elif __STDC__ 
+extern  bool_t xdr_attrstat(XDR *, attrstat*);
+#else /* Old Style C */ 
+bool_t xdr_attrstat();
+#endif /* Old Style C */ 
+
+
+struct sattrargs {
+       nfs_fh file;
+       sattr attributes;
+};
+typedef struct sattrargs sattrargs;
+#ifdef __cplusplus 
+extern "C" bool_t xdr_sattrargs(XDR *, sattrargs*);
+#elif __STDC__ 
+extern  bool_t xdr_sattrargs(XDR *, sattrargs*);
+#else /* Old Style C */ 
+bool_t xdr_sattrargs();
+#endif /* Old Style C */ 
+
+
+struct diropargs {
+       nfs_fh dir;
+       filename name;
+};
+typedef struct diropargs diropargs;
+#ifdef __cplusplus 
+extern "C" bool_t xdr_diropargs(XDR *, diropargs*);
+#elif __STDC__ 
+extern  bool_t xdr_diropargs(XDR *, diropargs*);
+#else /* Old Style C */ 
+bool_t xdr_diropargs();
+#endif /* Old Style C */ 
+
+
+struct diropokres {
+       nfs_fh file;
+       fattr attributes;
+};
+typedef struct diropokres diropokres;
+#ifdef __cplusplus 
+extern "C" bool_t xdr_diropokres(XDR *, diropokres*);
+#elif __STDC__ 
+extern  bool_t xdr_diropokres(XDR *, diropokres*);
+#else /* Old Style C */ 
+bool_t xdr_diropokres();
+#endif /* Old Style C */ 
+
+
+struct diropres {
+       nfsstat status;
+       union {
+               diropokres diropres;
+       } diropres_u;
+};
+typedef struct diropres diropres;
+#ifdef __cplusplus 
+extern "C" bool_t xdr_diropres(XDR *, diropres*);
+#elif __STDC__ 
+extern  bool_t xdr_diropres(XDR *, diropres*);
+#else /* Old Style C */ 
+bool_t xdr_diropres();
+#endif /* Old Style C */ 
+
+
+struct readlinkres {
+       nfsstat status;
+       union {
+               nfspath data;
+       } readlinkres_u;
+};
+typedef struct readlinkres readlinkres;
+#ifdef __cplusplus 
+extern "C" bool_t xdr_readlinkres(XDR *, readlinkres*);
+#elif __STDC__ 
+extern  bool_t xdr_readlinkres(XDR *, readlinkres*);
+#else /* Old Style C */ 
+bool_t xdr_readlinkres();
+#endif /* Old Style C */ 
+
+
+struct readargs {
+       nfs_fh file;
+       u_int offset;
+       u_int count;
+       u_int totalcount;
+};
+typedef struct readargs readargs;
+#ifdef __cplusplus 
+extern "C" bool_t xdr_readargs(XDR *, readargs*);
+#elif __STDC__ 
+extern  bool_t xdr_readargs(XDR *, readargs*);
+#else /* Old Style C */ 
+bool_t xdr_readargs();
+#endif /* Old Style C */ 
+
+
+struct readokres {
+       fattr attributes;
+       struct {
+               u_int data_len;
+               char *data_val;
+       } data;
+};
+typedef struct readokres readokres;
+#ifdef __cplusplus 
+extern "C" bool_t xdr_readokres(XDR *, readokres*);
+#elif __STDC__ 
+extern  bool_t xdr_readokres(XDR *, readokres*);
+#else /* Old Style C */ 
+bool_t xdr_readokres();
+#endif /* Old Style C */ 
+
+
+struct readres {
+       nfsstat status;
+       union {
+               readokres reply;
+       } readres_u;
+};
+typedef struct readres readres;
+#ifdef __cplusplus 
+extern "C" bool_t xdr_readres(XDR *, readres*);
+#elif __STDC__ 
+extern  bool_t xdr_readres(XDR *, readres*);
+#else /* Old Style C */ 
+bool_t xdr_readres();
+#endif /* Old Style C */ 
+
+
+struct writeargs {
+       nfs_fh file;
+       u_int beginoffset;
+       u_int offset;
+       u_int totalcount;
+       struct {
+               u_int data_len;
+               char *data_val;
+       } data;
+};
+typedef struct writeargs writeargs;
+#ifdef __cplusplus 
+extern "C" bool_t xdr_writeargs(XDR *, writeargs*);
+#elif __STDC__ 
+extern  bool_t xdr_writeargs(XDR *, writeargs*);
+#else /* Old Style C */ 
+bool_t xdr_writeargs();
+#endif /* Old Style C */ 
+
+
+struct createargs {
+       diropargs where;
+       sattr attributes;
+};
+typedef struct createargs createargs;
+#ifdef __cplusplus 
+extern "C" bool_t xdr_createargs(XDR *, createargs*);
+#elif __STDC__ 
+extern  bool_t xdr_createargs(XDR *, createargs*);
+#else /* Old Style C */ 
+bool_t xdr_createargs();
+#endif /* Old Style C */ 
+
+
+struct renameargs {
+       diropargs from;
+       diropargs to;
+};
+typedef struct renameargs renameargs;
+#ifdef __cplusplus 
+extern "C" bool_t xdr_renameargs(XDR *, renameargs*);
+#elif __STDC__ 
+extern  bool_t xdr_renameargs(XDR *, renameargs*);
+#else /* Old Style C */ 
+bool_t xdr_renameargs();
+#endif /* Old Style C */ 
+
+
+struct linkargs {
+       nfs_fh from;
+       diropargs to;
+};
+typedef struct linkargs linkargs;
+#ifdef __cplusplus 
+extern "C" bool_t xdr_linkargs(XDR *, linkargs*);
+#elif __STDC__ 
+extern  bool_t xdr_linkargs(XDR *, linkargs*);
+#else /* Old Style C */ 
+bool_t xdr_linkargs();
+#endif /* Old Style C */ 
+
+
+struct symlinkargs {
+       diropargs from;
+       nfspath to;
+       sattr attributes;
+};
+typedef struct symlinkargs symlinkargs;
+#ifdef __cplusplus 
+extern "C" bool_t xdr_symlinkargs(XDR *, symlinkargs*);
+#elif __STDC__ 
+extern  bool_t xdr_symlinkargs(XDR *, symlinkargs*);
+#else /* Old Style C */ 
+bool_t xdr_symlinkargs();
+#endif /* Old Style C */ 
+
+
+typedef char nfscookie[NFS_COOKIESIZE];
+#ifdef __cplusplus 
+extern "C" bool_t xdr_nfscookie(XDR *, nfscookie);
+#elif __STDC__ 
+extern  bool_t xdr_nfscookie(XDR *, nfscookie);
+#else /* Old Style C */ 
+bool_t xdr_nfscookie();
+#endif /* Old Style C */ 
+
+
+struct readdirargs {
+       nfs_fh dir;
+       nfscookie cookie;
+       u_int count;
+};
+typedef struct readdirargs readdirargs;
+#ifdef __cplusplus 
+extern "C" bool_t xdr_readdirargs(XDR *, readdirargs*);
+#elif __STDC__ 
+extern  bool_t xdr_readdirargs(XDR *, readdirargs*);
+#else /* Old Style C */ 
+bool_t xdr_readdirargs();
+#endif /* Old Style C */ 
+
+
+struct entry {
+       u_int fileid;
+       filename name;
+       nfscookie cookie;
+       struct entry *nextentry;
+};
+typedef struct entry entry;
+#ifdef __cplusplus 
+extern "C" bool_t xdr_entry(XDR *, entry*);
+#elif __STDC__ 
+extern  bool_t xdr_entry(XDR *, entry*);
+#else /* Old Style C */ 
+bool_t xdr_entry();
+#endif /* Old Style C */ 
+
+
+struct dirlist {
+       entry *entries;
+       bool_t eof;
+};
+typedef struct dirlist dirlist;
+#ifdef __cplusplus 
+extern "C" bool_t xdr_dirlist(XDR *, dirlist*);
+#elif __STDC__ 
+extern  bool_t xdr_dirlist(XDR *, dirlist*);
+#else /* Old Style C */ 
+bool_t xdr_dirlist();
+#endif /* Old Style C */ 
+
+
+struct readdirres {
+       nfsstat status;
+       union {
+               dirlist reply;
+       } readdirres_u;
+};
+typedef struct readdirres readdirres;
+#ifdef __cplusplus 
+extern "C" bool_t xdr_readdirres(XDR *, readdirres*);
+#elif __STDC__ 
+extern  bool_t xdr_readdirres(XDR *, readdirres*);
+#else /* Old Style C */ 
+bool_t xdr_readdirres();
+#endif /* Old Style C */ 
+
+
+struct statfsokres {
+       u_int tsize;
+       u_int bsize;
+       u_int blocks;
+       u_int bfree;
+       u_int bavail;
+};
+typedef struct statfsokres statfsokres;
+#ifdef __cplusplus 
+extern "C" bool_t xdr_statfsokres(XDR *, statfsokres*);
+#elif __STDC__ 
+extern  bool_t xdr_statfsokres(XDR *, statfsokres*);
+#else /* Old Style C */ 
+bool_t xdr_statfsokres();
+#endif /* Old Style C */ 
+
+
+struct statfsres {
+       nfsstat status;
+       union {
+               statfsokres reply;
+       } statfsres_u;
+};
+typedef struct statfsres statfsres;
+#ifdef __cplusplus 
+extern "C" bool_t xdr_statfsres(XDR *, statfsres*);
+#elif __STDC__ 
+extern  bool_t xdr_statfsres(XDR *, statfsres*);
+#else /* Old Style C */ 
+bool_t xdr_statfsres();
+#endif /* Old Style C */ 
+
+
+#define NFS_PROGRAM ((u_long)100003)
+#define NFS_VERSION ((u_long)2)
+
+#ifdef __cplusplus
+#define NFSPROC_NULL ((u_long)0)
+extern "C" void * nfsproc_null_2(void *, CLIENT *);
+extern "C" void * nfsproc_null_2_svc(void *, struct svc_req *);
+#define NFSPROC_GETATTR ((u_long)1)
+extern "C" attrstat * nfsproc_getattr_2(nfs_fh *, CLIENT *);
+extern "C" attrstat * nfsproc_getattr_2_svc(nfs_fh *, struct svc_req *);
+#define NFSPROC_SETATTR ((u_long)2)
+extern "C" attrstat * nfsproc_setattr_2(sattrargs *, CLIENT *);
+extern "C" attrstat * nfsproc_setattr_2_svc(sattrargs *, struct svc_req *);
+#define NFSPROC_ROOT ((u_long)3)
+extern "C" void * nfsproc_root_2(void *, CLIENT *);
+extern "C" void * nfsproc_root_2_svc(void *, struct svc_req *);
+#define NFSPROC_LOOKUP ((u_long)4)
+extern "C" diropres * nfsproc_lookup_2(diropargs *, CLIENT *);
+extern "C" diropres * nfsproc_lookup_2_svc(diropargs *, struct svc_req *);
+#define NFSPROC_READLINK ((u_long)5)
+extern "C" readlinkres * nfsproc_readlink_2(nfs_fh *, CLIENT *);
+extern "C" readlinkres * nfsproc_readlink_2_svc(nfs_fh *, struct svc_req *);
+#define NFSPROC_READ ((u_long)6)
+extern "C" readres * nfsproc_read_2(readargs *, CLIENT *);
+extern "C" readres * nfsproc_read_2_svc(readargs *, struct svc_req *);
+#define NFSPROC_WRITECACHE ((u_long)7)
+extern "C" void * nfsproc_writecache_2(void *, CLIENT *);
+extern "C" void * nfsproc_writecache_2_svc(void *, struct svc_req *);
+#define NFSPROC_WRITE ((u_long)8)
+extern "C" attrstat * nfsproc_write_2(writeargs *, CLIENT *);
+extern "C" attrstat * nfsproc_write_2_svc(writeargs *, struct svc_req *);
+#define NFSPROC_CREATE ((u_long)9)
+extern "C" diropres * nfsproc_create_2(createargs *, CLIENT *);
+extern "C" diropres * nfsproc_create_2_svc(createargs *, struct svc_req *);
+#define NFSPROC_REMOVE ((u_long)10)
+extern "C" nfsstat * nfsproc_remove_2(diropargs *, CLIENT *);
+extern "C" nfsstat * nfsproc_remove_2_svc(diropargs *, struct svc_req *);
+#define NFSPROC_RENAME ((u_long)11)
+extern "C" nfsstat * nfsproc_rename_2(renameargs *, CLIENT *);
+extern "C" nfsstat * nfsproc_rename_2_svc(renameargs *, struct svc_req *);
+#define NFSPROC_LINK ((u_long)12)
+extern "C" nfsstat * nfsproc_link_2(linkargs *, CLIENT *);
+extern "C" nfsstat * nfsproc_link_2_svc(linkargs *, struct svc_req *);
+#define NFSPROC_SYMLINK ((u_long)13)
+extern "C" nfsstat * nfsproc_symlink_2(symlinkargs *, CLIENT *);
+extern "C" nfsstat * nfsproc_symlink_2_svc(symlinkargs *, struct svc_req *);
+#define NFSPROC_MKDIR ((u_long)14)
+extern "C" diropres * nfsproc_mkdir_2(createargs *, CLIENT *);
+extern "C" diropres * nfsproc_mkdir_2_svc(createargs *, struct svc_req *);
+#define NFSPROC_RMDIR ((u_long)15)
+extern "C" nfsstat * nfsproc_rmdir_2(diropargs *, CLIENT *);
+extern "C" nfsstat * nfsproc_rmdir_2_svc(diropargs *, struct svc_req *);
+#define NFSPROC_READDIR ((u_long)16)
+extern "C" readdirres * nfsproc_readdir_2(readdirargs *, CLIENT *);
+extern "C" readdirres * nfsproc_readdir_2_svc(readdirargs *, struct svc_req *);
+#define NFSPROC_STATFS ((u_long)17)
+extern "C" statfsres * nfsproc_statfs_2(nfs_fh *, CLIENT *);
+extern "C" statfsres * nfsproc_statfs_2_svc(nfs_fh *, struct svc_req *);
+
+#elif __STDC__
+#define NFSPROC_NULL ((u_long)0)
+extern  void * nfsproc_null_2(void *, CLIENT *);
+extern  void * nfsproc_null_2_svc(void *, struct svc_req *);
+#define NFSPROC_GETATTR ((u_long)1)
+extern  attrstat * nfsproc_getattr_2(nfs_fh *, CLIENT *);
+extern  attrstat * nfsproc_getattr_2_svc(nfs_fh *, struct svc_req *);
+#define NFSPROC_SETATTR ((u_long)2)
+extern  attrstat * nfsproc_setattr_2(sattrargs *, CLIENT *);
+extern  attrstat * nfsproc_setattr_2_svc(sattrargs *, struct svc_req *);
+#define NFSPROC_ROOT ((u_long)3)
+extern  void * nfsproc_root_2(void *, CLIENT *);
+extern  void * nfsproc_root_2_svc(void *, struct svc_req *);
+#define NFSPROC_LOOKUP ((u_long)4)
+extern  diropres * nfsproc_lookup_2(diropargs *, CLIENT *);
+extern  diropres * nfsproc_lookup_2_svc(diropargs *, struct svc_req *);
+#define NFSPROC_READLINK ((u_long)5)
+extern  readlinkres * nfsproc_readlink_2(nfs_fh *, CLIENT *);
+extern  readlinkres * nfsproc_readlink_2_svc(nfs_fh *, struct svc_req *);
+#define NFSPROC_READ ((u_long)6)
+extern  readres * nfsproc_read_2(readargs *, CLIENT *);
+extern  readres * nfsproc_read_2_svc(readargs *, struct svc_req *);
+#define NFSPROC_WRITECACHE ((u_long)7)
+extern  void * nfsproc_writecache_2(void *, CLIENT *);
+extern  void * nfsproc_writecache_2_svc(void *, struct svc_req *);
+#define NFSPROC_WRITE ((u_long)8)
+extern  attrstat * nfsproc_write_2(writeargs *, CLIENT *);
+extern  attrstat * nfsproc_write_2_svc(writeargs *, struct svc_req *);
+#define NFSPROC_CREATE ((u_long)9)
+extern  diropres * nfsproc_create_2(createargs *, CLIENT *);
+extern  diropres * nfsproc_create_2_svc(createargs *, struct svc_req *);
+#define NFSPROC_REMOVE ((u_long)10)
+extern  nfsstat * nfsproc_remove_2(diropargs *, CLIENT *);
+extern  nfsstat * nfsproc_remove_2_svc(diropargs *, struct svc_req *);
+#define NFSPROC_RENAME ((u_long)11)
+extern  nfsstat * nfsproc_rename_2(renameargs *, CLIENT *);
+extern  nfsstat * nfsproc_rename_2_svc(renameargs *, struct svc_req *);
+#define NFSPROC_LINK ((u_long)12)
+extern  nfsstat * nfsproc_link_2(linkargs *, CLIENT *);
+extern  nfsstat * nfsproc_link_2_svc(linkargs *, struct svc_req *);
+#define NFSPROC_SYMLINK ((u_long)13)
+extern  nfsstat * nfsproc_symlink_2(symlinkargs *, CLIENT *);
+extern  nfsstat * nfsproc_symlink_2_svc(symlinkargs *, struct svc_req *);
+#define NFSPROC_MKDIR ((u_long)14)
+extern  diropres * nfsproc_mkdir_2(createargs *, CLIENT *);
+extern  diropres * nfsproc_mkdir_2_svc(createargs *, struct svc_req *);
+#define NFSPROC_RMDIR ((u_long)15)
+extern  nfsstat * nfsproc_rmdir_2(diropargs *, CLIENT *);
+extern  nfsstat * nfsproc_rmdir_2_svc(diropargs *, struct svc_req *);
+#define NFSPROC_READDIR ((u_long)16)
+extern  readdirres * nfsproc_readdir_2(readdirargs *, CLIENT *);
+extern  readdirres * nfsproc_readdir_2_svc(readdirargs *, struct svc_req *);
+#define NFSPROC_STATFS ((u_long)17)
+extern  statfsres * nfsproc_statfs_2(nfs_fh *, CLIENT *);
+extern  statfsres * nfsproc_statfs_2_svc(nfs_fh *, struct svc_req *);
+
+#else /* Old Style C */ 
+#define NFSPROC_NULL ((u_long)0)
+extern  void * nfsproc_null_2();
+extern  void * nfsproc_null_2_svc();
+#define NFSPROC_GETATTR ((u_long)1)
+extern  attrstat * nfsproc_getattr_2();
+extern  attrstat * nfsproc_getattr_2_svc();
+#define NFSPROC_SETATTR ((u_long)2)
+extern  attrstat * nfsproc_setattr_2();
+extern  attrstat * nfsproc_setattr_2_svc();
+#define NFSPROC_ROOT ((u_long)3)
+extern  void * nfsproc_root_2();
+extern  void * nfsproc_root_2_svc();
+#define NFSPROC_LOOKUP ((u_long)4)
+extern  diropres * nfsproc_lookup_2();
+extern  diropres * nfsproc_lookup_2_svc();
+#define NFSPROC_READLINK ((u_long)5)
+extern  readlinkres * nfsproc_readlink_2();
+extern  readlinkres * nfsproc_readlink_2_svc();
+#define NFSPROC_READ ((u_long)6)
+extern  readres * nfsproc_read_2();
+extern  readres * nfsproc_read_2_svc();
+#define NFSPROC_WRITECACHE ((u_long)7)
+extern  void * nfsproc_writecache_2();
+extern  void * nfsproc_writecache_2_svc();
+#define NFSPROC_WRITE ((u_long)8)
+extern  attrstat * nfsproc_write_2();
+extern  attrstat * nfsproc_write_2_svc();
+#define NFSPROC_CREATE ((u_long)9)
+extern  diropres * nfsproc_create_2();
+extern  diropres * nfsproc_create_2_svc();
+#define NFSPROC_REMOVE ((u_long)10)
+extern  nfsstat * nfsproc_remove_2();
+extern  nfsstat * nfsproc_remove_2_svc();
+#define NFSPROC_RENAME ((u_long)11)
+extern  nfsstat * nfsproc_rename_2();
+extern  nfsstat * nfsproc_rename_2_svc();
+#define NFSPROC_LINK ((u_long)12)
+extern  nfsstat * nfsproc_link_2();
+extern  nfsstat * nfsproc_link_2_svc();
+#define NFSPROC_SYMLINK ((u_long)13)
+extern  nfsstat * nfsproc_symlink_2();
+extern  nfsstat * nfsproc_symlink_2_svc();
+#define NFSPROC_MKDIR ((u_long)14)
+extern  diropres * nfsproc_mkdir_2();
+extern  diropres * nfsproc_mkdir_2_svc();
+#define NFSPROC_RMDIR ((u_long)15)
+extern  nfsstat * nfsproc_rmdir_2();
+extern  nfsstat * nfsproc_rmdir_2_svc();
+#define NFSPROC_READDIR ((u_long)16)
+extern  readdirres * nfsproc_readdir_2();
+extern  readdirres * nfsproc_readdir_2_svc();
+#define NFSPROC_STATFS ((u_long)17)
+extern  statfsres * nfsproc_statfs_2();
+extern  statfsres * nfsproc_statfs_2_svc();
+#endif /* Old Style C */ 
+
+#endif /* !_NFS_PROT_H_RPCGEN */
diff --git a/support/include/sys/fs/ext2fs.h b/support/include/sys/fs/ext2fs.h
new file mode 100644 (file)
index 0000000..93b3e2b
--- /dev/null
@@ -0,0 +1,42 @@
+#ifndef _SYS_FS_EXT2FS_H
+#define _SYS_FS_EXT2FS_H
+
+/*
+ * ioctl commands
+ */
+#define        EXT2_IOC_GETFLAGS               _IOR('f', 1, long)
+#define        EXT2_IOC_SETFLAGS               _IOW('f', 2, long)
+#define        EXT2_IOC_GETVERSION             _IOR('v', 1, long)
+#define        EXT2_IOC_SETVERSION             _IOW('v', 2, long)
+
+/*
+ * File system states
+ */
+#define        EXT2_VALID_FS                   0x0001  /* Unmounted cleanly */
+#define        EXT2_ERROR_FS                   0x0002  /* Errors detected */
+
+/*
+ * Mount flags
+ */
+#define EXT2_MOUNT_CHECK_NORMAL                0x0001  /* Do some more checks */
+#define EXT2_MOUNT_CHECK_STRICT                0x0002  /* Do again more checks */
+#define EXT2_MOUNT_CHECK               (EXT2_MOUNT_CHECK_NORMAL | \
+                                        EXT2_MOUNT_CHECK_STRICT)
+#define EXT2_MOUNT_GRPID               0x0004  /* Create files with directory's group */
+#define EXT2_MOUNT_DEBUG               0x0008  /* Some debugging messages */
+#define EXT2_MOUNT_ERRORS_CONT         0x0010  /* Continue on errors */
+#define EXT2_MOUNT_ERRORS_RO           0x0020  /* Remount fs ro on errors */
+#define EXT2_MOUNT_ERRORS_PANIC                0x0040  /* Panic on errors */
+#define EXT2_MOUNT_MINIX_DF            0x0080  /* Mimics the Minix statfs */
+
+#define clear_opt(o, opt)              o &= ~EXT2_MOUNT_##opt
+#define set_opt(o, opt)                        o |= EXT2_MOUNT_##opt
+#define test_opt(sb, opt)              ((sb)->u.ext2_sb.s_mount_opt & \
+                                        EXT2_MOUNT_##opt)
+/*
+ * Maximal mount counts between two filesystem checks
+ */
+#define EXT2_DFL_MAX_MNT_COUNT         20      /* Allow 20 mounts */
+#define EXT2_DFL_CHECKINTERVAL         0       /* Don't use interval check */
+
+#endif /* _SYS_FS_EXT2FS_H */
diff --git a/support/include/version.h b/support/include/version.h
new file mode 100644 (file)
index 0000000..a74ec35
--- /dev/null
@@ -0,0 +1 @@
+#define VERSION "1.4.7 (0.4.22)"
diff --git a/support/include/xio.h b/support/include/xio.h
new file mode 100644 (file)
index 0000000..858f5bb
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * xio.h       Declarations for simple parsing functions.
+ *
+ */
+
+#ifndef XIO_H
+#define XIO_H
+
+#include <stdio.h>
+
+typedef struct XFILE {
+       FILE            *x_fp;
+       int             x_line;
+} XFILE;
+
+XFILE  *xfopen(char *fname, char *type);
+int    xflock(char *fname, char *type);
+void   xfunlock(int lockid);
+void   xfclose(XFILE *xfp);
+int    xgettok(XFILE *xfp, char sepa, char *tok, int len);
+char   xgetc(XFILE *xfp);
+void   xungetc(char c, XFILE *xfp);
+void   xskip(XFILE *xfp, char *str);
+char   xskipcomment(XFILE *xfp);
+
+#endif /* XIO_H */
diff --git a/support/include/xlog.h b/support/include/xlog.h
new file mode 100644 (file)
index 0000000..2a839c7
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * xlog                Logging functionality
+ *
+ * Copyright (C) 1995 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#ifndef XLOG_H
+#define XLOG_H
+
+#define L_FATAL                0x0100
+#define L_ERROR                0x0200
+#define L_WARNING      0x0400
+#define L_NOTICE       0x0800
+#define L_ALL          0xFF00
+
+#define D_GENERAL      0x0001          /* general debug info */
+#define D_CALL         0x0002
+#define D_AUTH         0x0004
+#define D_FAC3         0x0008
+#define D_FAC4         0x0010
+#define D_FAC5         0x0020
+#define D_PARSE                0x0040
+#define D_FAC7         0x0080
+#define D_ALL          0x00FF
+
+/* This can be used to define symbolic log names that can be passed to
+ * xlog_config. */
+struct xlog_debugfac {
+       char            *df_name;
+       int             df_fac;
+};
+
+void                   xlog_open(char *progname);
+void                   xlog_background(void);
+void                   xlog_config(int fac, int on);
+void                   xlog_sconfig(char *, int on);
+int                    xlog_enabled(int fac);
+void                   xlog(int fac, const char *fmt, ...);
+
+#endif /* XLOG_H */
diff --git a/support/include/xmalloc.h b/support/include/xmalloc.h
new file mode 100644 (file)
index 0000000..866cfd8
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * xmalloc     Module for memory allocation. Drop in your
+ *             debugging malloc module if you feel like it.
+ *
+ * Copyright (C) 1995 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#ifndef XMALLOC_H
+#define XMALLOC_H
+
+void   *xmalloc(size_t size);
+void   *xrealloc(void *ptr, size_t size);
+char   *xstrdup(const char *s);
+void   xfree(void *ptr);
+
+#endif /* XMALLOC_H */
diff --git a/support/include/ypupdate.h b/support/include/ypupdate.h
new file mode 100644 (file)
index 0000000..e0cee15
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * ypupdate.h  This file contains the public declarations for the
+ *             ypupdate client side RPC stubs.
+ *
+ * Copyright (C) 1995 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#ifndef YPUPDATE_H
+#define YPUPDATE_H
+
+#include <rpcsvc/ypclnt.h>
+
+int    yp_update(char *domain, char *map, unsigned int ypop,
+                       char *key, int keylen, char *data, int datalen);
+
+#endif YPUPDATE_H
diff --git a/support/lib/Makefile b/support/lib/Makefile
new file mode 100644 (file)
index 0000000..b5fa14a
--- /dev/null
@@ -0,0 +1,13 @@
+
+include        $(TOP)rules.mk
+
+LIBS   = libnfs.a libexport.a
+
+all install::  $(LIBS)
+       @:
+
+clean distclean::
+       rm -f $(LIBS)
+
+lib%.a:
+       ln -sf ../$*/$@ .
diff --git a/support/nfs/Makefile b/support/nfs/Makefile
new file mode 100644 (file)
index 0000000..ed1e1ff
--- /dev/null
@@ -0,0 +1,13 @@
+#
+# linux-nfs/support/nfs/Makefile
+#
+
+LIBNAME        = libnfs.a
+OBJS   = exports.o rmtab.o xio.o \
+         rpcmisc.o rpcdispatch.o xlog.o xmalloc.o wildmat.o \
+         nfssvc.o nfsclient.o nfsexport.o getfh.o nfsctl.o lockdsvc.o
+
+include $(TOP)rules.mk
+
+install::
+       @:
diff --git a/support/nfs/clients.c b/support/nfs/clients.c
new file mode 100644 (file)
index 0000000..b1970e0
--- /dev/null
@@ -0,0 +1,324 @@
+/*
+ * support/nfs/nfsclient.c
+ *
+ * Parse the nfsclients file.
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <syslog.h>
+#include <ctype.h>
+#include "xmalloc.h"
+#include "nfslib.h"
+#include "exportfs.h"
+#include "xio.h"
+
+static XFILE   *cfp = NULL;
+static int     *squash_uids = NULL,
+               *squash_gids = NULL;
+static int     squash_uidlen = 0,
+               squash_gidlen = 0;
+static char    *hosts = NULL;
+
+static int     parsesquash(char *list, int **idp, int *lenp);
+static int     parsenum(char **cpp);
+static int     parsekey(struct nfskey *keyp, char *str);
+static int     hexdigit(char c);
+static int     gettag(char *tag, int len);
+static int     getattr(char *attr, int alen, char *value, int vlen);
+static void    syntaxerr(char *msg);
+
+#ifndef isblank
+#define isblank(c)     ((c) == ' ' || (c) == '\t')
+#endif
+
+void
+setnfsclntent(char *fname)
+{
+       if (cfp)
+               xfclose(cfp);
+       if (!fname)
+               fname = _PATH_NFSCLIENTS;
+       if ((cfp = xfopen(fname)) == NULL)
+               xlog(L_ERROR, "can't open %s for reading", fname);
+}
+
+struct nfsclntent *
+getnfsclntent(void)
+{
+       static struct nfsclntent cle;
+       static char     *hostptr = NULL;
+       char            attr[32], val[512], *sp;
+       int             ok;
+
+       if (!cfp)
+               endnfsclntent();
+
+again:
+       if (hosts) {
+               if (hostptr)
+                       goto nexthost;
+               xfree(hosts);
+               hosts = NULL;
+       }
+
+       if ((ok = gettag(cle.c_tag, sizeof(cle.c_tag))) < 0)
+               syntaxerr("expected tag");
+       if (ok <= 0)
+               return NULL;
+
+       cle.c_hostname[0] = '\0';
+       cle.c_fhkey.k_type = CLE_KEY_NONE;
+       cle.c_mapping = CLE_MAP_IDENT;
+       cle.c_anonuid = -2;
+       cle.c_anongid = -2;
+
+       if (squash_uids)
+               xfree(squash_uids);
+       if (squash_gids)
+               xfree(squash_gids);
+       squash_uids = squash_gids = NULL;
+       squash_uidlen = squash_gidlen = 0;
+
+       while (ok) {
+               if ((ok = getattr(attr, sizeof(attr), val, sizeof(val))) < 0)
+                       return NULL;
+               if (!ok)
+                       break;
+               if (attr[0] == 'h' && !strcmp(attr, "hosts")) {
+                       int     l0 = hosts? strlen(hosts) : 0;
+
+                       hosts = (char *) xrealloc(hosts, l0+strlen(val)+2);
+                       if (l0)
+                               hosts[l0++] = ':';
+                       strcpy(hosts+l0, val);
+               } else
+               if (attr[0] == 'f' && !strcmp(attr, "fhmac")) {
+                       if (!parsekey(&cle.c_fhkey, val))
+                               return NULL;
+               } else
+               if (attr[0] == 'm' && !strcmp(attr, "mapping")) {
+                       if (!strcmp(val, "identity"))
+                               cle.c_mapping = CLE_MAP_IDENT;
+                       else if (!strcmp(val, "file"))
+                               cle.c_mapping = CLE_MAP_FILE;
+                       else if (!strcmp(val, "daemon"))
+                               cle.c_mapping = CLE_MAP_UGIDD;
+                       else {
+                               syntaxerr("invalid mapping type");
+                               return NULL;
+                       }
+               } else
+               if (attr[0] == 's' && !strcmp(attr, "squash_uids")) {
+                       if (!parsesquash(val, &squash_uids, &squash_uidlen))
+                               return NULL;
+               } else
+               if (attr[0] == 's' && !strcmp(attr, "squash_gids")) {
+                       if (!parsesquash(val, &squash_gids, &squash_gidlen))
+                               return NULL;
+               } else
+               if (attr[0] == 'a' && !strcmp(attr, "anonuid"))
+                       cle.c_anonuid = atoi(val);
+               else
+               if (attr[0] == 'a' && !strcmp(attr, "anongid"))
+                       cle.c_anongid = atoi(val);
+               else
+                       syntaxerr("unknown attribute");
+       }
+
+       cle.c_squashuids = squash_uids;
+       cle.c_squashgids = squash_gids;
+
+       /* This is the anon entry */
+       if (!hosts) {
+               if (strcmp(cle.c_tag, "anonymous")) {
+                       xlog(L_ERROR, "nfsclients entry %s allows anonymous "
+                                       "access. Ignored.", cle.c_tag);
+                       goto again;
+               }
+               return &cle;
+       }
+       hostptr = hosts;
+
+nexthost:
+       if (*hostptr == ':' && strcmp(cle.c_tag, "anonymous")) {
+               xlog(L_ERROR, "nfsclients entry %s allows anonymous "
+                               "access. Ignored.", cle.c_tag);
+               while (*hostptr == ':')
+                       hostptr++;
+       }
+
+       /* Ignore trailing colons */
+       if (!*hostptr) {
+               hostptr = NULL;
+               goto again;
+       }
+
+       sp = hostptr;
+       hostptr = strchr(hostptr, ':');
+       if (hostptr)
+               *hostptr++ = '\0';
+       strncpy(cle.c_hostname, sp, sizeof(cle.c_hostname) - 1);
+       cle.c_hostname [sizeof(cle.c_hostname) - 1] = '\0';
+       return &cle;
+}
+
+void
+endnfsclntent(void)
+{
+       if (cfp)
+               xfclose(cfp);
+       if (squash_uids)
+               xfree(squash_uids);
+       if (squash_gids)
+               xfree(squash_gids);
+       if (hosts)
+               xfree(hosts);
+       cfp = NULL;
+       squash_uids = NULL;
+       squash_gids = NULL;
+       hosts = NULL;
+}
+
+static int
+parsekey(struct nfskey *keyp, char *str)
+{
+       char    *sp;
+       int     i, l, x0, x1;
+
+
+       if ((sp = strchr(str, ':')) != NULL)
+               *sp++ = '\0';
+       if (!strcmp(str, "null"))
+               keyp->k_type = CLE_KEY_NULL;
+       else if (!strcmp(str, "md5"))
+               keyp->k_type = CLE_KEY_MD5;
+       else if (!strcmp(str, "sha"))
+               keyp->k_type = CLE_KEY_SHA;
+       else {
+               syntaxerr("unknown key type");
+               return 0;
+       }
+       if (keyp->k_type == CLE_KEY_NULL) {
+               keyp->k_len = 0;
+               if (sp)
+                       syntaxerr("unexpected key data for null key");
+               return sp? 0 : 1;
+       } else if (sp) {
+               if ((l = strlen(sp)) & 1) {
+                       syntaxerr("odd key length");
+                       return 0;
+               }
+
+               l >>= 1;
+               for (i = 0; i < l && i < sizeof(keyp->k_key); i++, sp += 2) {
+                       if ((x0 = hexdigit(sp[0])) == 0xff ||
+                           (x1 = hexdigit(sp[1])) == 0xff) {
+                               syntaxerr("bad key digit");
+                               return 0;
+                       }
+                       keyp->k_key[i] = (x0 << 4) | x1;
+               }
+               keyp->k_len = i;
+               return 1;
+       }
+       return 0;
+}
+
+static int
+hexdigit(char c)
+{
+       if ((c = tolower(c)) >= '0' && c <= '9')
+               return c - '0';
+       if (c >= 'a' && c <= 'f')
+               return c - 'a' + 10;
+       return 0xff;
+}
+
+static int
+parsesquash(char *list, int **idp, int *lenp)
+{
+       char    *cp = list;
+       int     id0, id1;
+       int     len = *lenp;
+       int     *id = *idp;
+
+       do {
+               id0 = parsenum(&cp);
+               if (*cp == '-') {
+                       cp++;
+                       id1 = parsenum(&cp);
+               } else {
+                       id1 = id0;
+               }
+               if (id0 == -1 || id1 == -1) {
+                       syntaxerr("uid/gid -1 not permitted");
+                       return 0;
+               }
+               if ((len % 8) == 0)
+                       id = (int *) xrealloc(id, (len + 9) * sizeof(*id));
+               id[len++] = id0;
+               id[len++] = id1;
+               if (!*cp)
+                       break;
+               if (*cp != ',') {
+                       syntaxerr("bad uid/gid list");
+                       return 0;
+               }
+               cp++;
+       } while(1);
+
+       id[len] = -1;
+       *lenp = len;
+       *idp = id;
+       return 1;
+}
+
+static int
+parsenum(char **cpp)
+{
+       char    *cp = *cpp, c;
+       int     num = 0;
+
+       if (**cpp == '-')
+               (*cpp)++;
+       while (isdigit(**cpp))
+               (*cpp)++;
+       c = **cpp; **cpp = '\0'; num = atoi(cp); **cpp = c;
+       return num;
+}
+
+static int
+gettag(char *tag, int len)
+{
+       xskip(cfp, " \t\n");
+       return xgettok(cfp, ':', tag, len);
+}
+
+static int
+getattr(char *attr, int alen, char *value, int vlen)
+{
+       int     ok;
+
+       xskip(cfp, " \t");
+       if ((ok = xgettok(cfp, '=', attr, alen)) < 0)
+               xlog(L_ERROR, "error parsing attribute");
+       if (ok <= 0)
+               return ok;
+       xskip(cfp, " \t=");
+
+       return xgettok(cfp, 0, value, vlen);
+}
+
+static void
+syntaxerr(char *msg)
+{
+       xlog(L_ERROR, "syntax error in nfsclients file (line %d): %s",
+                               cfp->x_line, msg);
+}
+
diff --git a/support/nfs/exports.c b/support/nfs/exports.c
new file mode 100644 (file)
index 0000000..21b85be
--- /dev/null
@@ -0,0 +1,440 @@
+/*
+ * support/nfs/export.c
+ *
+ * Parse the exports file. Derived from the unfsd implementation.
+ *
+ * Authors:    Donald J. Becker, <becker@super.org>
+ *             Rick Sladkey, <jrs@world.std.com>
+ *             Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *             Olaf Kirch, <okir@monad.swb.de>
+ *             Alexander O. Yuriev, <alex@bach.cis.temple.edu>
+ *
+ *             This software maybe be used for any purpose provided
+ *             the above copyright notice is retained.  It is supplied
+ *             as is, with no warranty expressed or implied.
+ */
+
+#include "config.h"
+
+#include <sys/param.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <unistd.h>
+#include "nfslib.h"
+#include "exportfs.h"
+#include "xmalloc.h"
+#include "xlog.h"
+#include "xio.h"
+
+#define EXPORT_DEFAULT_FLAGS   \
+  (NFSEXP_ASYNC|NFSEXP_READONLY|NFSEXP_ROOTSQUASH|NFSEXP_GATHERED_WRITES)
+
+static XFILE   *efp = NULL;
+static int     first;
+static int     *squids = NULL, nsquids = 0,
+               *sqgids = NULL, nsqgids = 0;
+
+static int     getexport(char *exp, int len);
+static int     getpath(char *path, int len);
+static int     parseopts(char *cp, struct exportent *ep);
+static int     parsesquash(char *list, int **idp, int *lenp, char **ep);
+static int     parsenum(char **cpp);
+static int     parsemaptype(char *type);
+static void    freesquash(void);
+static void    syntaxerr(char *msg);
+
+void
+setexportent(char *fname, char *type)
+{
+       if (efp)
+               endexportent();
+       if (!fname)
+               fname = _PATH_EXPORTS;
+       if (!(efp = xfopen(fname, type)))
+               xlog(L_ERROR, "can't open %s for %sing",
+                               fname, strcmp(type, "r")? "writ" : "read");
+       first = 1;
+}
+
+struct exportent *
+getexportent(void)
+{
+       static struct exportent ee;
+       char            exp[512];
+       char            rpath[MAXPATHLEN+1];
+       char            *opt, *sp;
+       int             ok;
+
+       if (!efp)
+               return NULL;
+
+       freesquash();
+       ee.e_flags = EXPORT_DEFAULT_FLAGS;
+       ee.e_maptype = CLE_MAP_IDENT;
+       ee.e_anonuid = -2;
+       ee.e_anongid = -2;
+       ee.e_squids = NULL;
+       ee.e_sqgids = NULL;
+       ee.e_nsquids = 0;
+       ee.e_nsqgids = 0;
+
+       if (first || (ok = getexport(exp, sizeof(exp))) == 0) {
+               ok = getpath(ee.e_path, sizeof(ee.e_path));
+               if (ok <= 0)
+                       return NULL;
+               strncpy (ee.m_path, ee.e_path, sizeof (ee.m_path) - 1);
+               ee.m_path [sizeof (ee.m_path) - 1] = '\0';
+               ok = getexport(exp, sizeof(exp));
+       }
+       if (ok < 0) {
+               xlog(L_ERROR, "expected client(options...)");
+               return NULL;
+       }
+       first = 0;
+
+       /* Check for default client */
+       if (ok == 0)
+               exp[0] = '\0';
+       if ((opt = strchr(exp, '(')) != NULL) {
+               *opt++ = '\0';
+               if (!(sp = strchr(opt, ')')) || sp[1] != '\0') {
+                       syntaxerr("bad option list");
+                       return NULL;
+               }
+               *sp = '\0';
+               if (parseopts(opt, &ee) < 0)
+                       return NULL;
+       }
+       if (strlen(exp) >= sizeof(ee.e_hostname)) {
+               syntaxerr("client name too long");
+               return NULL;
+       }
+       strncpy(ee.e_hostname, exp, sizeof (ee.e_hostname) - 1);
+       ee.e_hostname[sizeof (ee.e_hostname) - 1] = '\0';
+
+       /* resolve symlinks */
+       if (realpath(ee.e_path, rpath) != NULL) {
+               rpath[sizeof (rpath) - 1] = '\0';
+               strncpy(ee.e_path, rpath, sizeof (ee.e_path) - 1);
+               ee.e_path[sizeof (ee.e_path) - 1] = '\0';
+               strncpy (ee.m_path, ee.e_path, sizeof (ee.m_path) - 1);
+               ee.m_path [sizeof (ee.m_path) - 1] = '\0';
+       }
+
+       return &ee;
+}
+
+void
+putexportent(struct exportent *ep)
+{
+       FILE    *fp;
+       int     *id, i;
+
+       if (!efp)
+               return;
+
+       fp = efp->x_fp;
+       fprintf(fp, "%s\t%s(", ep->e_path, ep->e_hostname);
+       fprintf(fp, "%s,", (ep->e_flags & NFSEXP_READONLY)? "ro" : "rw");
+       fprintf(fp, "%ssync,", (ep->e_flags & NFSEXP_ASYNC)? "a" : "");
+       fprintf(fp, "%swdelay,", (ep->e_flags & NFSEXP_GATHERED_WRITES)?
+                               "" : "no_");
+       fprintf(fp, "%ssecure,", (ep->e_flags & NFSEXP_INSECURE_PORT)?
+                               "in" : "");
+       fprintf(fp, "%sroot_squash,", (ep->e_flags & NFSEXP_ROOTSQUASH)?
+                               "" : "no_");
+       fprintf(fp, "%sall_squash,", (ep->e_flags & NFSEXP_ALLSQUASH)?
+                               "" : "no_");
+
+       fprintf(fp, "mapping=");
+       switch (ep->e_maptype) {
+       case CLE_MAP_IDENT:
+               fprintf(fp, "identity,");
+               break;
+       case CLE_MAP_UGIDD:
+               fprintf(fp, "ugidd,");
+               break;
+       case CLE_MAP_FILE:
+               fprintf(fp, "file,");
+               break;
+       default:
+               xlog(L_ERROR, "unknown mapping type for %s:%s",
+                                       ep->e_hostname, ep->e_path);
+       }
+       if ((id = ep->e_squids) != NULL) {
+               fprintf(fp, "squash_uids=");
+               for (i = 0; i < ep->e_nsquids; i += 2)
+                       if (id[i] != id[i+1])
+                               fprintf(fp, "%d-%d,", id[i], id[i+1]);
+                       else
+                               fprintf(fp, "%d,", id[i]);
+       }
+       if ((id = ep->e_sqgids) != NULL) {
+               fprintf(fp, "squash_gids=");
+               for (i = 0; i < ep->e_nsquids; i += 2)
+                       if (id[i] != id[i+1])
+                               fprintf(fp, "%d-%d,", id[i], id[i+1]);
+                       else
+                               fprintf(fp, "%d,", id[i]);
+       }
+       fprintf(fp, "anonuid=%d,anongid=%d)\n", ep->e_anonuid, ep->e_anongid);
+}
+
+void
+endexportent(void)
+{
+       if (efp)
+               xfclose(efp);
+       efp = NULL;
+       freesquash();
+}
+
+void
+dupexportent(struct exportent *dst, struct exportent *src)
+{
+       int     n;
+
+       *dst = *src;
+       if ((n = src->e_nsquids) != 0) {
+               dst->e_squids = (int *) xmalloc(n * sizeof(int));
+               memcpy(dst->e_squids, src->e_squids, n * sizeof(int));
+       }
+       if ((n = src->e_nsqgids) != 0) {
+               dst->e_sqgids = (int *) xmalloc(n * sizeof(int));
+               memcpy(dst->e_sqgids, src->e_sqgids, n * sizeof(int));
+       }
+}
+
+struct exportent *
+mkexportent(char *hname, char *path, char *options)
+{
+       static struct exportent ee;
+
+       ee.e_flags = EXPORT_DEFAULT_FLAGS;
+       ee.e_maptype = CLE_MAP_IDENT;
+       ee.e_anonuid = -2;
+       ee.e_anongid = -2;
+       ee.e_squids = NULL;
+       ee.e_sqgids = NULL;
+       ee.e_nsquids = 0;
+       ee.e_nsqgids = 0;
+
+       if (strlen(hname) >= sizeof(ee.e_hostname)) {
+               xlog(L_WARNING, "client name %s too long", hname);
+               return NULL;
+       }
+       strncpy(ee.e_hostname, hname, sizeof (ee.e_hostname) - 1);
+       ee.e_hostname[sizeof (ee.e_hostname) - 1] = '\0';
+       if (strlen(path) >= sizeof(ee.e_path)) {
+               xlog(L_WARNING, "path name %s too long", path);
+               return NULL;
+       }
+       strncpy(ee.e_path, path, sizeof (ee.e_path));
+       ee.e_path[sizeof (ee.e_path) - 1] = '\0';
+       strncpy (ee.m_path, ee.e_path, sizeof (ee.m_path) - 1);
+       ee.m_path [sizeof (ee.m_path) - 1] = '\0';
+       if (options && parseopts(options, &ee) < 0)
+               return NULL;
+       return &ee;
+}
+
+int
+updateexportent(struct exportent *eep, char *options)
+{
+       if (options && parseopts(options, eep) < 0)
+               return 0;
+       return 1;
+}
+
+/*
+ * Parse option string pointed to by s and set mount options accordingly.
+ */
+static int
+parseopts(char *cp, struct exportent *ep)
+{
+       char    *opt;
+
+       squids = ep->e_squids; nsquids = ep->e_nsquids;
+       sqgids = ep->e_sqgids; nsqgids = ep->e_nsqgids;
+
+       while (isblank(*cp))
+               cp++;
+       while (*cp) {
+               opt = cp;
+               while (*cp && *cp != ',')
+                       cp++;
+               if (*cp)
+                       *cp++ = '\0';
+
+               /* process keyword */
+               if (strcmp(opt, "ro") == 0)
+                       ep->e_flags |= NFSEXP_READONLY;
+               else if (strcmp(opt, "rw") == 0)
+                       ep->e_flags &= ~NFSEXP_READONLY;
+               else if (!strcmp(opt, "secure"))
+                       ep->e_flags &= ~NFSEXP_INSECURE_PORT;
+               else if (!strcmp(opt, "insecure"))
+                       ep->e_flags |= NFSEXP_INSECURE_PORT;
+               else if (!strcmp(opt, "sync"))
+                       ep->e_flags &= ~NFSEXP_ASYNC;
+               else if (!strcmp(opt, "async"))
+                       ep->e_flags |= NFSEXP_ASYNC;
+               else if (!strcmp(opt, "wdelay"))
+                       ep->e_flags |= NFSEXP_GATHERED_WRITES;
+               else if (!strcmp(opt, "no_wdelay"))
+                       ep->e_flags &= ~NFSEXP_GATHERED_WRITES;
+               else if (strcmp(opt, "root_squash") == 0)
+                       ep->e_flags |= NFSEXP_ROOTSQUASH;
+               else if (!strcmp(opt, "no_root_squash"))
+                       ep->e_flags &= ~NFSEXP_ROOTSQUASH;
+               else if (strcmp(opt, "all_squash") == 0)
+                       ep->e_flags |= NFSEXP_ALLSQUASH;
+               else if (strcmp(opt, "no_all_squash") == 0)
+                       ep->e_flags &= ~NFSEXP_ALLSQUASH;
+               else if (strncmp(opt, "mapping=", 8) == 0)
+                       ep->e_maptype = parsemaptype(opt+8);
+               else if (strcmp(opt, "map_identity") == 0)      /* old style */
+                       ep->e_maptype = CLE_MAP_IDENT;
+               else if (strcmp(opt, "map_daemon") == 0)        /* old style */
+                       ep->e_maptype = CLE_MAP_UGIDD;
+               else if (strncmp(opt, "anonuid=", 8) == 0)
+                       ep->e_anonuid = atoi(opt+8);
+               else if (strncmp(opt, "anongid=", 8) == 0)
+                       ep->e_anongid = atoi(opt+8);
+               else if (strncmp(opt, "squash_uids=", 12) == 0) {
+                       if (parsesquash(opt+12, &squids, &nsquids, &cp) < 0)
+                               return -1;
+               } else if (strncmp(opt, "squash_gids=", 12) == 0) {
+                       if (parsesquash(opt+12, &sqgids, &nsqgids, &cp) < 0)
+                               return -1;
+               } else {
+                       xlog(L_ERROR,
+                               "Unknown keyword \"%s\" in export file\n",
+                               opt);
+                       ep->e_flags |= NFSEXP_ALLSQUASH | NFSEXP_READONLY;
+                       return -1;
+               }
+               while (isblank(*cp))
+                       cp++;
+       }
+
+       ep->e_squids = squids;
+       ep->e_sqgids = sqgids;
+       ep->e_nsquids = nsquids;
+       ep->e_nsqgids = nsqgids;
+
+       return 1;
+}
+
+static int
+parsesquash(char *list, int **idp, int *lenp, char **ep)
+{
+       char    *cp = list;
+       int     id0, id1;
+       int     len = *lenp;
+       int     *id = *idp;
+
+       if (**ep)
+           *--(*ep) = ',';
+
+       do {
+               id0 = parsenum(&cp);
+               if (*cp == '-') {
+                       cp++;
+                       id1 = parsenum(&cp);
+               } else {
+                       id1 = id0;
+               }
+               if (id0 == -1 || id1 == -1) {
+                       syntaxerr("uid/gid -1 not permitted");
+                       return -1;
+               }
+               if ((len % 8) == 0)
+                       id = (int *) xrealloc(id, (len + 8) * sizeof(*id));
+               id[len++] = id0;
+               id[len++] = id1;
+               if (!*cp || *cp == ')' || (*cp == ',' && !isdigit(cp[1])))
+                       break;
+               if (*cp != ',') {
+                       syntaxerr("bad uid/gid list");
+                       return -1;
+               }
+               cp++;
+       } while(1);
+
+       if (*cp == ',') *ep = cp+1;
+       
+       *lenp = len;
+       *idp = id;
+       return 1;
+}
+
+static void
+freesquash(void)
+{
+       if (squids) {
+               xfree (squids);
+               squids = NULL;
+               nsquids = 0;
+       }
+       if (sqgids) {
+               xfree (sqgids);
+               sqgids = NULL;
+               nsqgids = 0;
+       }
+}
+
+static int
+parsenum(char **cpp)
+{
+       char    *cp = *cpp, c;
+       int     num = 0;
+
+       if (**cpp == '-')
+               (*cpp)++;
+       while (isdigit(**cpp))
+               (*cpp)++;
+       c = **cpp; **cpp = '\0'; num = atoi(cp); **cpp = c;
+       return num;
+}
+
+static int
+parsemaptype(char *type)
+{
+       if (!strcmp(type, "identity"))
+               return CLE_MAP_IDENT;
+       if (!strcmp(type, "ugidd"))
+               return CLE_MAP_UGIDD;
+       if (!strcmp(type, "file"))
+               return CLE_MAP_FILE;
+       syntaxerr("invalid map type");
+       return CLE_MAP_IDENT;   /* default */
+}
+
+static int
+getpath(char *path, int len)
+{
+       xskip(efp, " \t\n");
+       return xgettok(efp, 0, path, len);
+}
+
+static int
+getexport(char *exp, int len)
+{
+       int     ok;
+
+       xskip(efp, " \t");
+       if ((ok = xgettok(efp, 0, exp, len)) < 0)
+               xlog(L_ERROR, "error parsing export entry");
+       return ok;
+}
+
+static void
+syntaxerr(char *msg)
+{
+       xlog(L_ERROR, "syntax error in exports file (line %d): %s",
+                               efp->x_line, msg);
+}
+
diff --git a/support/nfs/getfh.c b/support/nfs/getfh.c
new file mode 100644 (file)
index 0000000..5a6f1a4
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * support/nfs/getfh.c
+ *
+ * Get the FH for a given client and directory. This function takes
+ * the NFS protocol version number as an additional argument.
+ *
+ * This function has nothing in common with the SunOS getfh function,
+ * which is a front-end to the RPC mount call.
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <sys/types.h>
+#include <errno.h>
+#include "nfslib.h"
+
+struct knfs_fh *
+getfh_old (struct sockaddr *addr, dev_t dev, ino_t ino)
+{
+       static union nfsctl_res res;
+       struct nfsctl_arg       arg;
+
+       arg.ca_version = NFSCTL_VERSION;
+       arg.ca_getfh.gf_version = 2;    /* obsolete */
+       arg.ca_getfh.gf_dev = dev;
+       arg.ca_getfh.gf_ino = ino;
+       memcpy(&arg.ca_getfh.gf_addr, addr, sizeof(struct sockaddr_in));
+
+       if (nfsctl(NFSCTL_GETFH, &arg, &res) < 0)
+               return NULL;
+
+       return &res.cr_getfh;
+}
+
+struct knfs_fh *
+getfh(struct sockaddr *addr, const char *path)
+{
+        static union nfsctl_res res;
+        struct nfsctl_arg       arg;
+
+        arg.ca_version = NFSCTL_VERSION;
+        arg.ca_getfd.gd_version = 2;    /* obsolete */
+        strncpy(arg.ca_getfd.gd_path, path,
+               sizeof(arg.ca_getfd.gd_path) - 1);
+       arg.ca_getfd.gd_path[sizeof (arg.ca_getfd.gd_path) - 1] = '\0';
+        memcpy(&arg.ca_getfd.gd_addr, addr, sizeof(struct sockaddr_in));
+
+        if (nfsctl(NFSCTL_GETFD, &arg, &res) < 0)
+                return NULL;
+
+        return &res.cr_getfh;
+}
diff --git a/support/nfs/keytab.c b/support/nfs/keytab.c
new file mode 100644 (file)
index 0000000..e33dded
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * support/nfs/keytab.c
+ *
+ * Manage the nfskeys database.
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <syslog.h>
+#include <ctype.h>
+#include "xmalloc.h"
+#include "nfslib.h"
+#include "exportfs.h"
+#include "xio.h"
+
+static FILE    *cfp = NULL;
+
+int
+setnfskeyent(char *fname)
+{
+       if (cfp)
+               fclose(cfp);
+       if (!fname)
+               fname = _PATH_NFSKEYS;
+       cfp = fsetnfskeyent(fname, "r");
+       return cfp != NULL;
+}
+
+FILE *
+fsetnfskeyent(char *fname, char *type)
+{
+#if 0
+       FILE    *fp;
+
+       if ((fp = fopen(fname, type)) == NULL)
+               xlog(L_ERROR, "can't open %s for %sing\n",
+                               fname, type[0] == 'r'? "read" : "writ");
+       return fp;
+#else
+       return fopen(fname, type);
+#endif
+}
+
+struct nfskeyent *
+getnfskeyent(void)
+{
+       return fgetnfskeyent(cfp);
+}
+
+struct nfskeyent *
+fgetnfskeyent(FILE *fp)
+{
+       static struct nfskeyent ke;
+
+       if (!fp)
+               return NULL;
+
+       do {
+               if (fread(&ke, sizeof(ke), 1, fp) != 1)
+                       return NULL;
+       } while(ke.k_hostname[0] == '\0');
+       return &ke;
+}
+
+void
+endnfskeyent(void)
+{
+       if (cfp)
+               fclose(cfp);
+       cfp = NULL;
+}
+
+void
+fendnfskeyent(FILE *fp)
+{
+       if (fp)
+               fclose(fp);
+}
+
+void
+fputnfskeyent(FILE *fp, struct nfskeyent *kep)
+{
+       fwrite(kep, sizeof(*kep), 1, fp);
+}
+
+int
+getnfskeytype(char *st)
+{
+       if (!strcasecmp(st, "null"))
+               return CLE_KEY_NULL;
+       if (!strcasecmp(st, "md5"))
+               return CLE_KEY_MD5;
+       if (!strcasecmp(st, "sha"))
+               return CLE_KEY_SHA;
+       return CLE_KEY_NONE;
+}
+
+char *
+getnfskeyname(int type)
+{
+       switch (type) {
+       case CLE_KEY_NONE:
+               return "none";
+       case CLE_KEY_NULL:
+               return "null";
+       case CLE_KEY_MD5:
+               return "md5";
+       case CLE_KEY_SHA:
+               return "sha";
+       }
+       return "unk";
+}
+
+int
+getnfskeysize(int type)
+{
+       switch (type) {
+       case CLE_KEY_MD5:
+               return 16;
+       case CLE_KEY_SHA:
+               return 20;
+       }
+       return 0;
+}
diff --git a/support/nfs/lockdsvc.c b/support/nfs/lockdsvc.c
new file mode 100644 (file)
index 0000000..532e721
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * support/nfs/nfssvc.c
+ *
+ * Run an NFS daemon.
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include "config.h"
+
+#include "nfslib.h"
+
+int
+lockdsvc()
+{
+       struct nfsctl_arg       arg;
+
+       arg.ca_version = NFSCTL_VERSION;
+       return nfsctl(LOCKDCTL_SVC, &arg, NULL);
+}
diff --git a/support/nfs/nfsclient.c b/support/nfs/nfsclient.c
new file mode 100644 (file)
index 0000000..5886484
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * support/nfs/client.c
+ *
+ * Add or delete an NFS client in knfsd.
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include "nfslib.h"
+
+int
+nfsaddclient(struct nfsctl_client *clp)
+{
+       struct nfsctl_arg       arg;
+
+       arg.ca_version = NFSCTL_VERSION;
+       memcpy(&arg.ca_client, clp, sizeof(arg.ca_client));
+       return nfsctl(NFSCTL_ADDCLIENT, &arg, NULL);
+}
+
+int
+nfsdelclient(struct nfsctl_client *clp)
+{
+       struct nfsctl_arg       arg;
+
+       arg.ca_version = NFSCTL_VERSION;
+       memcpy(&arg.ca_client, clp, sizeof(arg.ca_client));
+       return nfsctl(NFSCTL_DELCLIENT, &arg, NULL);
+}
diff --git a/support/nfs/nfsctl.c b/support/nfs/nfsctl.c
new file mode 100644 (file)
index 0000000..c04588f
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * support/nfs/nfsctl.c
+ *
+ * Central syscall to the nfsd kernel module.
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include "config.h"
+
+#include <unistd.h>
+#include <asm/unistd.h>
+#include "nfslib.h"
+
+/* compatibility hack... */
+#ifndef __NR_nfsctl
+#define __NR_nfsctl    __NR_nfsservctl
+#endif
+
+int
+nfsctl (int cmd, struct nfsctl_arg * argp, union nfsctl_res * resp)
+{
+  return syscall (__NR_nfsctl, cmd, argp, resp);
+}
diff --git a/support/nfs/nfsexport.c b/support/nfs/nfsexport.c
new file mode 100644 (file)
index 0000000..ce8b867
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * support/nfs/export.c
+ *
+ * Add or delete an NFS export in knfsd.
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include "nfslib.h"
+
+int
+nfsexport(struct nfsctl_export *exp)
+{
+       struct nfsctl_arg       arg;
+
+       arg.ca_version = NFSCTL_VERSION;
+       memcpy(&arg.ca_export, exp, sizeof(arg.ca_export));
+       return nfsctl(NFSCTL_EXPORT, &arg, NULL);
+}
+
+int
+nfsunexport(struct nfsctl_export *exp)
+{
+       struct nfsctl_arg       arg;
+
+       arg.ca_version = NFSCTL_VERSION;
+       memcpy(&arg.ca_export, exp, sizeof(arg.ca_export));
+       return nfsctl(NFSCTL_UNEXPORT, &arg, NULL);
+}
diff --git a/support/nfs/nfssvc.c b/support/nfs/nfssvc.c
new file mode 100644 (file)
index 0000000..7419baf
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * support/nfs/nfssvc.c
+ *
+ * Run an NFS daemon.
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include "config.h"
+
+#include "nfslib.h"
+
+int
+nfssvc(int port, int nrservs)
+{
+       struct nfsctl_arg       arg;
+
+       arg.ca_version = NFSCTL_VERSION;
+       arg.ca_svc.svc_nthreads = nrservs;
+       arg.ca_svc.svc_port = port;
+       return nfsctl(NFSCTL_SVC, &arg, NULL);
+}
diff --git a/support/nfs/rmtab.c b/support/nfs/rmtab.c
new file mode 100644 (file)
index 0000000..b9b5ff1
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * support/nfs/rmtab.c
+ *
+ * Handling for rmtab.
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include "config.h"
+
+#include <sys/fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <signal.h>
+#include "nfslib.h"
+
+static FILE    *rmfp = NULL;
+
+int
+setrmtabent(char *type)
+{
+       if (rmfp)
+               fclose(rmfp);
+       rmfp = fsetrmtabent(_PATH_RMTAB, type);
+       return (rmfp != NULL);
+}
+
+FILE *
+fsetrmtabent(char *fname, char *type)
+{
+       int     readonly = !strcmp(type, "r");
+       FILE    *fp;
+
+       if (!fname)
+               return NULL;
+       if ((fp = fopen(fname, type)) == NULL) {
+               xlog(L_ERROR, "can't open %s for %sing", fname,
+                               readonly ? "read" : "writ");
+               return NULL;
+       }
+       return fp;
+}
+
+struct rmtabent *
+getrmtabent(int log)
+{
+       return fgetrmtabent(rmfp, log);
+}
+
+struct rmtabent *
+fgetrmtabent(FILE *fp, int log)
+{
+       static struct rmtabent  re;
+       char    buf[2048], *sp;
+
+       errno = 0;
+       if (!fp)
+               return NULL;
+       do {
+               if (fgets(buf, sizeof(buf)-1, fp) == NULL)
+                       return NULL;
+               if ((sp = strchr(buf, '\n')) != NULL)
+                       *sp = '\0';
+               if (!(sp = strchr(buf, ':'))) {
+                       if (log)
+                               xlog(L_ERROR, "malformed entry in rmtab file");
+                       errno = EINVAL;
+                       return NULL;
+               }
+               *sp++ = '\0';
+       } while (0);
+       strncpy(re.r_client, buf, sizeof (re.r_client) - 1);
+       re.r_client[sizeof (re.r_client) - 1] = '\0';
+       strncpy(re.r_path, sp, sizeof (re.r_path) - 1);
+       re.r_path[sizeof (re.r_path) - 1] = '\0';
+       return &re;
+}
+
+void
+putrmtabent(struct rmtabent *rep)
+{
+       fputrmtabent(rmfp, rep);
+}
+
+void
+fputrmtabent(FILE *fp, struct rmtabent *rep)
+{
+       if (!fp)
+               return;
+       fprintf(fp, "%s:%s\n", rep->r_client, rep->r_path);
+}
+
+void
+endrmtabent(void)
+{
+       fendrmtabent(rmfp);
+       rmfp = NULL;
+}
+
+void
+fendrmtabent(FILE *fp)
+{
+       if (fp)
+               fclose(fp);
+}
+
+void
+rewindrmtabent(void)
+{
+       if (rmfp)
+               rewind(rmfp);
+}
+
+void
+frewindrmtabent(FILE *fp)
+{
+       if (fp)
+               rewind (fp);
+}
diff --git a/support/nfs/rpcdispatch.c b/support/nfs/rpcdispatch.c
new file mode 100644 (file)
index 0000000..e798ea5
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * support/nfs/rcpdispatch.c
+ *
+ * Generic RPC dispatcher.
+ *
+ * Copyright (C) 1995, 1996, Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <rpc/rpc.h>
+#include <rpc/pmap_clnt.h>
+#include <signal.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <string.h>
+#include "rpcmisc.h"
+#include "xlog.h"
+
+void
+rpc_dispatch(struct svc_req *rqstp, SVCXPRT *transp,
+                       struct rpc_dtable *dtable, int nvers,
+                       void *argp, void *resp)
+{
+       struct rpc_dentry       *dent;
+
+       if (rqstp->rq_vers > nvers) {
+               svcerr_progvers(transp, 1, nvers);
+               return;
+       }
+       dtable += (rqstp->rq_vers - 1);
+       if (rqstp->rq_proc > dtable->nproc) {
+               svcerr_noproc(transp);
+               return;
+       }
+
+       dent = dtable->entries + rqstp->rq_proc;
+
+       if (dent->func == NULL) {
+               svcerr_noproc(transp);
+               return;
+       }
+
+       memset(argp, 0, dent->xdr_arg_size);
+       memset(resp, 0, dent->xdr_res_size);
+
+       if (!svc_getargs(transp, dent->xdr_arg_fn, argp)) {
+               svcerr_decode(transp);
+               return;
+       }
+
+       if ((dent->func)(rqstp, argp, resp) && resp != 0) {
+               if (!svc_sendreply(transp, dent->xdr_res_fn, (caddr_t)resp)) 
+                       svcerr_systemerr(transp);
+       }
+       if (!svc_freeargs(transp, dent->xdr_arg_fn, argp)) {
+               xlog(L_ERROR, "failed to free RPC arguments");
+               exit (2);
+       }
+}
+
+#if 0
+/*
+ * This is our replacement for svc_run. It turns off some signals while
+ * executing the server procedures to avoid nasty race conditions.
+ */
+void
+rpc_svcrun(fd_set *morefds, void (*func)(int fd))
+{
+       sigset_t        block, current;
+       fd_set          readfds;
+
+       for (;;) {
+               readfds = svc_fdset;
+               if (morefds) {
+                       int     i;
+
+                       /* most efficient */
+                       for (i = 0; i < FD_SETSIZE; i++)
+                               if (FD_ISSET(i, morefds))
+                                       FD_SET(i, &readfs);
+               }
+               switch (select(FD_SETSIZE, &readfds, NULL, NULL, NULL)) {
+               case -1:
+                       if (errno == EINTR)
+                               continue;
+                       xlog(L_ERROR, "svc_run: - select failed");
+                       break;
+               case 0:
+                       continue;
+               default:
+                       if (morefds) {
+                               int     i;
+
+                               /* most efficient */
+                               for (i = 0; i < FD_SETSIZE; i++)
+                                       if (FD_ISSET(i, morefds) &&
+                                           FD_ISSET(i, &readfds))
+                                               func(i);
+                       }
+                       sigemptyset(&block);
+                       sigaddset(&block, SIGALRM);
+                       sigaddset(&block, SIGVTALRM);
+                       sigaddset(&block, SIGIO);
+                       sigprocmask(SIG_BLOCK, &block, &current);
+                       svc_getreqset(&readfds);
+                       sigprocmask(SIG_SETMASK, &current, NULL);
+               }
+       }
+}
+#endif
diff --git a/support/nfs/rpcmisc.c b/support/nfs/rpcmisc.c
new file mode 100644 (file)
index 0000000..7b182fd
--- /dev/null
@@ -0,0 +1,230 @@
+/*
+ * support/nfs/rpcmisc.c
+ *
+ * Miscellaneous functions for RPC startup and shutdown.
+ * This code is partially snarfed from rpcgen -s tcp -s udp,
+ * partly written by Mark Shand, Donald Becker, and Rick 
+ * Sladkey. It was tweaked slightly by Olaf Kirch to be
+ * usable by both unfsd and mountd.
+ *
+ * This software may be used for any purpose provided
+ * the above copyright notice is retained.  It is supplied
+ * as is, with no warranty expressed or implied.
+ */
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <rpc/rpc.h>
+#include <rpc/pmap_clnt.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <memory.h>
+#include <errno.h>
+#include <unistd.h>
+#include "nfslib.h"
+
+static void    closedown(int sig);
+static int     makesock(int port, int proto, int socksz);
+
+#define _RPCSVC_CLOSEDOWN      120
+int    _rpcpmstart = 0;
+int    _rpcfdtype = 0;
+int    _rpcsvcdirty = 0;
+
+void
+rpc_init(char *name, int prog, int vers, void (*dispatch)(), int defport,
+                                                       int bufsiz)
+{
+       struct sockaddr_in saddr;
+       SVCXPRT *transp;
+       int     sock;
+       int     asize;
+
+       asize = sizeof(saddr);
+       sock = 0;
+       _rpcfdtype = 0;
+       if (getsockname(0, (struct sockaddr *) &saddr, &asize) == 0) {
+               int ssize = sizeof (int);
+               if (saddr.sin_family != AF_INET)
+                       xlog(L_FATAL, "init: stdin is bound to non-inet addr");
+               if (getsockopt(0, SOL_SOCKET, SO_TYPE,
+                               (char *)&_rpcfdtype, &ssize) == -1)
+                       xlog(L_FATAL, "getsockopt failed: %s", strerror(errno));
+               _rpcpmstart = 1;
+       } else {
+               pmap_unset(prog, vers);
+               sock = RPC_ANYSOCK;
+       }
+
+       if ((_rpcfdtype == 0) || (_rpcfdtype == SOCK_DGRAM)) {
+               if (_rpcfdtype == 0 && defport != 0 &&
+                   ((sock = makesock(defport, IPPROTO_UDP, bufsiz)) < 0)) {
+                       xlog(L_FATAL, "%s: could not make a UDP socket\n",
+                                       name);
+               }
+               transp = svcudp_create(sock);
+               if (transp == NULL) {
+                       xlog(L_FATAL, "cannot create udp service.");
+               }
+               if (!svc_register(transp, prog, vers, dispatch, IPPROTO_UDP)) {
+                       xlog(L_FATAL, "unable to register (%s, %d, udp).",
+                                       name, vers);
+               }
+       }
+
+       if ((_rpcfdtype == 0) || (_rpcfdtype == SOCK_STREAM)) {
+               if (_rpcfdtype == 0 && defport != 0 &&
+                   ((sock = makesock(defport, IPPROTO_TCP, bufsiz)) < 0)) {
+                       xlog(L_FATAL, "%s: could not make a TCP socket\n",
+                                       name);
+               }
+               transp = svctcp_create(sock, 0, 0);
+               if (transp == NULL) {
+                       xlog(L_FATAL, "cannot create tcp service.");
+               }
+               if (!svc_register(transp, prog, vers, dispatch, IPPROTO_TCP)) {
+                       xlog(L_FATAL, "unable to register (%s, %d, tcp).",
+                                       name, vers);
+               }
+       }
+
+       if (_rpcpmstart) {
+               signal (SIGALRM, closedown);
+               alarm (_RPCSVC_CLOSEDOWN);
+       }
+}
+
+static void closedown(sig)
+int sig;
+{
+       (void) signal(sig, closedown);
+       if (_rpcsvcdirty == 0) {
+               extern fd_set svc_fdset;
+               static int size;
+               int i, openfd;
+
+               if (_rpcfdtype == SOCK_DGRAM)
+                       exit(0);
+               if (size == 0) {
+                       size = getdtablesize();
+               }
+               for (i = 0, openfd = 0; i < size && openfd < 2; i++)
+                       if (FD_ISSET(i, &svc_fdset))
+                               openfd++;
+               if (openfd <= 1)
+                       exit(0);
+       }
+       (void) alarm(_RPCSVC_CLOSEDOWN);
+}
+
+static int makesock(port, proto, socksz)
+int port;
+int proto;
+int socksz;
+{
+       struct sockaddr_in sin;
+       int     s;
+       int     sock_type;
+       int     val;
+
+       sock_type = (proto == IPPROTO_UDP) ? SOCK_DGRAM : SOCK_STREAM;
+       s = socket(AF_INET, sock_type, proto);
+       if (s < 0) {
+               xlog(L_FATAL, "Could not make a socket: %s\n",
+                                       strerror(errno));
+               return (-1);
+       }
+       memset((char *) &sin, 0, sizeof(sin));
+       sin.sin_family = AF_INET;
+       sin.sin_addr.s_addr = INADDR_ANY;
+       sin.sin_port = htons(port);
+
+       val = 1;
+       if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) < 0)
+               xlog(L_ERROR, "setsockopt failed: %s\n", strerror(errno));
+
+#ifdef SO_SNDBUF
+       {
+               int sblen, rblen;
+
+               /* 1024 for rpc & transport overheads */
+               sblen = rblen = socksz + 1024;
+               if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &sblen, sizeof sblen) < 0 ||
+                   setsockopt(s, SOL_SOCKET, SO_RCVBUF, &rblen, sizeof rblen) < 0)
+                       xlog(L_ERROR, "setsockopt failed: %s\n", strerror(errno));
+       }
+#endif                         /* SO_SNDBUF */
+
+       if (bind(s, (struct sockaddr *) &sin, sizeof(sin)) == -1) {
+               xlog(L_FATAL, "Could not bind name to socket: %s\n",
+                                       strerror(errno));
+               return (-1);
+       }
+       return (s);
+}
+
+
+/* Log an incoming call. */
+void
+rpc_logcall(struct svc_req *rqstp, char *xname, char *arg)
+{
+       char            buff[1024];
+       int             buflen=sizeof(buff);
+       int             len;
+       char            *sp;
+       int             i;
+
+       if (!xlog_enabled(D_CALL))
+               return;
+
+       sp = buff;
+       switch (rqstp->rq_cred.oa_flavor) {
+       case AUTH_NULL:
+               sprintf(sp, "NULL");
+               break;
+       case AUTH_UNIX: {
+               struct authunix_parms *unix_cred;
+               struct tm *tm;
+
+               unix_cred = (struct authunix_parms *) rqstp->rq_clntcred;
+               tm = localtime(&unix_cred->aup_time);
+               snprintf(sp, buflen, "UNIX %d/%d/%d %02d:%02d:%02d %s %d.%d",
+                       tm->tm_year, tm->tm_mon + 1, tm->tm_mday,
+                       tm->tm_hour, tm->tm_min, tm->tm_sec,
+                       unix_cred->aup_machname,
+                       unix_cred->aup_uid,
+                       unix_cred->aup_gid);
+               sp[buflen-1] = 0;
+               len = strlen(sp);
+               sp += buflen;
+               buflen -= len;
+               if ((int) unix_cred->aup_len > 0) {
+                       snprintf(sp, buflen, "+%d", unix_cred->aup_gids[0]);
+                       sp[buflen-1] = 0;
+                       len = strlen(sp);
+                       sp += buflen;
+                       buflen -= len;
+                       for (i = 1; i < unix_cred->aup_len; i++) {
+                               snprintf(sp, buflen, ",%d", 
+                                       unix_cred->aup_gids[i]);
+                               sp[buflen-1] = 0;
+                               len = strlen(sp);
+                               sp += buflen;
+                               buflen -= len;
+                       }
+               }
+               }
+               break;
+       default:
+               sprintf(sp, "CRED %d", rqstp->rq_cred.oa_flavor);
+       }
+       xlog(D_CALL, "%s [%s]\n\t%s\n", xname, buff, arg);
+}
diff --git a/support/nfs/wildmat.c b/support/nfs/wildmat.c
new file mode 100644 (file)
index 0000000..8f7b760
--- /dev/null
@@ -0,0 +1,177 @@
+/*  $Revision: 0.2.18.1 $
+**
+**  Do shell-style pattern matching for ?, \, [], and * characters.
+**  Might not be robust in face of malformed patterns; e.g., "foo[a-"
+**  could cause a segmentation violation.  It is 8bit clean.
+**
+**  Written by Rich $alz, mirror!rs, Wed Nov 26 19:03:17 EST 1986.
+**  Rich $alz is now <rsalz@osf.org>.
+**  April, 1991:  Replaced mutually-recursive calls with in-line code
+**  for the star character.
+**
+**  Special thanks to Lars Mathiesen <thorinn@diku.dk> for the ABORT code.
+**  This can greatly speed up failing wildcard patterns.  For example:
+**     pattern: -*-*-*-*-*-*-12-*-*-*-m-*-*-*
+**     text 1:  -adobe-courier-bold-o-normal--12-120-75-75-m-70-iso8859-1
+**     text 2:  -adobe-courier-bold-o-normal--12-120-75-75-X-70-iso8859-1
+**  Text 1 matches with 51 calls, while text 2 fails with 54 calls.  Without
+**  the ABORT code, it takes 22310 calls to fail.  Ugh.  The following
+**  explanation is from Lars:
+**  The precondition that must be fulfilled is that DoMatch will consume
+**  at least one character in text.  This is true if *p is neither '*' nor
+**  '\0'.)  The last return has ABORT instead of FALSE to avoid quadratic
+**  behaviour in cases like pattern "*a*b*c*d" with text "abcxxxxx".  With
+**  FALSE, each star-loop has to run to the end of the text; with ABORT
+**  only the last one does.
+**
+**  Once the control of one instance of DoMatch enters the star-loop, that
+**  instance will return either TRUE or ABORT, and any calling instance
+**  will therefore return immediately after (without calling recursively
+**  again).  In effect, only one star-loop is ever active.  It would be
+**  possible to modify the code to maintain this context explicitly,
+**  eliminating all recursive calls at the cost of some complication and
+**  loss of clarity (and the ABORT stuff seems to be unclear enough by
+**  itself).  I think it would be unwise to try to get this into a
+**  released version unless you have a good test data base to try it out
+**  on.
+*/
+
+#include "config.h"
+
+#include <ctype.h>
+
+#define TRUE                   1
+#define FALSE                  0
+#define ABORT                  -1
+
+
+    /* What character marks an inverted character class? */
+#define NEGATE_CLASS           '^'
+    /* Is "*" a common pattern? */
+#define OPTIMIZE_JUST_STAR
+    /* Do tar(1) matching rules, which ignore a trailing slash? */
+#undef MATCH_TAR_PATTERN
+
+
+/*
+**  Match text and p, return TRUE, FALSE, or ABORT.
+*/
+static int
+DoMatch(text, p)
+    register char      *text;
+    register char      *p;
+{
+    register int       last;
+    register int       matched;
+    register int       reverse;
+
+    for ( ; *p; text++, p++) {
+       if (*text == '\0' && *p != '*')
+           return ABORT;
+       switch (*p) {
+       case '\\':
+           /* Literal match with following character. */
+           p++;
+           /* FALLTHROUGH */
+       default:
+           if (toupper (*text) != toupper (*p))
+               return FALSE;
+           continue;
+       case '?':
+           /* Match anything. */
+           continue;
+       case '*':
+           while (*++p == '*')
+               /* Consecutive stars act just like one. */
+               continue;
+           if (*p == '\0')
+               /* Trailing star matches everything. */
+               return TRUE;
+           while (*text)
+               if ((matched = DoMatch(text++, p)) != FALSE)
+                   return matched;
+           return ABORT;
+       case '[':
+           reverse = p[1] == NEGATE_CLASS ? TRUE : FALSE;
+           if (reverse)
+               /* Inverted character class. */
+               p++;
+           matched = FALSE;
+           if (p[1] == ']' || p[1] == '-')
+               if (toupper (*++p) == toupper(*text))
+                   matched = TRUE;
+           for (last = *p; *++p && *p != ']'; last = *p)
+               /* This next line requires a good C compiler. */
+               if (*p == '-' && p[1] != ']'
+                   ? *text <= *++p && *text >= last
+                     : toupper (*text) == toupper (*p))
+                   matched = TRUE;
+           if (matched == reverse)
+               return FALSE;
+           continue;
+       }
+    }
+
+#ifdef MATCH_TAR_PATTERN
+    if (*text == '/')
+       return TRUE;
+#endif /* MATCH_TAR_ATTERN */
+    return *text == '\0';
+}
+
+
+/*
+**  User-level routine.  Returns TRUE or FALSE.
+*/
+int
+wildmat(text, p)
+    char       *text;
+    char       *p;
+{
+#ifdef OPTIMIZE_JUST_STAR
+    if (p[0] == '*' && p[1] == '\0')
+       return TRUE;
+#endif /* OPTIMIZE_JUST_STAR */
+    return DoMatch(text, p) == TRUE;
+}
+
+\f
+
+#if    defined(TEST)
+#include <stdio.h>
+
+/* Yes, we use gets not fgets.  Sue me. */
+extern char    *gets();
+
+
+int
+main()
+{
+    char        p[80];
+    char        text[80];
+
+    printf("Wildmat tester.  Enter pattern, then strings to test.\n");
+    printf("A blank line gets prompts for a new pattern; a blank pattern\n");
+    printf("exits the program.\n");
+
+    for ( ; ; ) {
+       printf("\nEnter pattern:  ");
+       (void)fflush(stdout);
+       if (gets(p) == NULL || p[0] == '\0')
+           break;
+       for ( ; ; ) {
+           printf("Enter text:  ");
+           (void)fflush(stdout);
+           if (gets(text) == NULL)
+               exit(0);
+           if (text[0] == '\0')
+               /* Blank line; go back and get a new pattern. */
+               break;
+           printf("      %s\n", wildmat(text, p) ? "YES" : "NO");
+       }
+    }
+
+    exit(0);
+    /* NOTREACHED */
+}
+#endif /* defined(TEST) */
diff --git a/support/nfs/xio.c b/support/nfs/xio.c
new file mode 100644 (file)
index 0000000..1bcd41b
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ * support/nfs/xio.c
+ * 
+ * Simple I/O functions for the parsing of /etc/exports and /etc/nfsclients.
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include "config.h"
+
+#include <sys/fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <signal.h>
+#include <unistd.h>
+#include "xmalloc.h"
+#include "xlog.h"
+#include "xio.h"
+
+XFILE *
+xfopen(char *fname, char *type)
+{
+       XFILE   *xfp;
+       FILE    *fp;
+
+       if (!(fp = fopen(fname, type)))
+               return NULL;
+       xfp = (XFILE *) xmalloc(sizeof(*xfp));
+       xfp->x_fp = fp;
+       xfp->x_line = 0;
+
+       return xfp;
+}
+
+void
+xfclose(XFILE *xfp)
+{
+       fclose(xfp->x_fp);
+       xfree(xfp);
+}
+
+static void
+doalarm(int sig)
+{
+       return;
+}
+
+int
+xflock(char *fname, char *type)
+{
+       struct sigaction sa, oldsa;
+       int             readonly = !strcmp(type, "r");
+       struct flock    fl = { readonly? F_RDLCK : F_WRLCK, SEEK_SET, 0, 0, 0 };
+       int             fd;
+
+       if ((fd = open(fname, readonly? O_RDONLY : O_RDWR)) < 0) {
+               xlog(L_WARNING, "could not open %s for locking", fname);
+               return -1;
+       }
+       sa.sa_handler = doalarm;
+       sa.sa_flags = 0;
+       sigemptyset(&sa.sa_mask);
+       sigaction(SIGALRM, &sa, &oldsa);
+       alarm(10);
+       if (fcntl(fd, F_SETLKW, &fl) < 0) {
+               alarm(0);
+               xlog(L_WARNING, "failed to lock %s", fname);
+               close(fd);
+               fd = 0;
+       } else {
+               alarm(0);
+       }
+       sigaction(SIGALRM, &oldsa, NULL);
+
+       return fd;
+}
+
+void
+xfunlock(int fd)
+{
+       close(fd);
+}
+
+int
+xgettok(XFILE *xfp, char sepa, char *tok, int len)
+{
+       int     i = 0;
+       char    c = 0;
+
+       while (i < len && (c = xgetc(xfp)) != EOF && c != sepa && !isspace(c))
+               tok[i++] = c;
+       if (c == '\n')
+               ungetc(c, xfp->x_fp);
+       if (!i)
+               return 0;
+       if (i >= len || (sepa && c != sepa))
+               return -1;
+       tok[i] = '\0';
+       return 1;
+}
+
+char
+xgetc(XFILE *xfp)
+{
+       char    c = getc(xfp->x_fp);
+
+       if (c == EOF)
+               return c;
+       if (c == '\\') {
+               if ((c = getc(xfp->x_fp)) != '\n') {
+                       ungetc(c, xfp->x_fp);
+                       return '\\';
+               }
+               xfp->x_line++;
+               while ((c = getc(xfp->x_fp)) == ' ' || c == '\t');
+               ungetc(c, xfp->x_fp);
+               return ' ';
+       }
+       if (c == '#')
+               c = xskipcomment(xfp);
+       if (c == '\n')
+               xfp->x_line++;
+       return c;
+}
+
+void
+xungetc(char c, XFILE *xfp)
+{
+       if (c != EOF)
+               ungetc(c, xfp->x_fp);
+}
+
+void
+xskip(XFILE *xfp, char *str)
+{
+       char    c;
+
+       while ((c = xgetc(xfp)) != EOF && strchr(str, c));
+       ungetc(c, xfp->x_fp);
+}
+
+char
+xskipcomment(XFILE *xfp)
+{
+       char    c;
+
+       while ((c = getc(xfp->x_fp)) != EOF && c != '\n');
+       return c;
+}
diff --git a/support/nfs/xlog.c b/support/nfs/xlog.c
new file mode 100644 (file)
index 0000000..90c7e63
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * support/nfs/xlog.c
+ *
+ * This module handles the logging of requests.
+ *
+ * TODO:       Merge the two "XXX_log() calls.
+ *
+ * Authors:    Donald J. Becker, <becker@super.org>
+ *             Rick Sladkey, <jrs@world.std.com>
+ *             Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *             Olaf Kirch, <okir@monad.swb.de>
+ *
+ *             This software maybe be used for any purpose provided
+ *             the above copyright notice is retained.  It is supplied
+ *             as is, with no warranty expressed or implied.
+ */
+
+#include "config.h"
+
+#include <unistd.h>
+#include <signal.h>
+#include <time.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <syslog.h>
+#include "nfslib.h"
+
+#undef VERBOSE_PRINTF
+
+static int  foreground = 1;            /* not a daemon initially       */
+static int  logging = 0;               /* enable/disable DEBUG logs    */
+static int  logmask = 0;               /* What will be logged          */
+static char log_name[256];             /* name of this program         */
+static int  log_pid = -1;              /* PID of this program          */
+static FILE *log_fp = (FILE *)NULL;    /* fp for the log file          */
+
+static void    xlog_toggle(int sig);
+static struct xlog_debugfac    debugnames[] = {
+       { "general",    D_GENERAL, },
+       { "call",       D_CALL, },
+       { "auth",       D_AUTH, },
+       { "parse",      D_PARSE, },
+       { "all",        D_ALL, },
+       { NULL,         0, },
+};
+
+void
+xlog_open(char *progname)
+{
+       openlog(progname, LOG_PID, LOG_DAEMON);
+       if (foreground) {
+               log_fp = stderr;
+               if (log_fp != NULL)
+                       setbuf(log_fp, NULL);
+       }
+
+       strncpy(log_name, progname, sizeof (log_name) - 1);
+       log_name [sizeof (log_name) - 1] = '\0';
+       log_pid = getpid();
+
+       signal(SIGUSR1, xlog_toggle);
+       signal(SIGUSR2, xlog_toggle);
+}
+
+void
+xlog_background(void)
+{
+       foreground = 0;
+}
+
+static void
+xlog_toggle(int sig)
+{
+       unsigned int    tmp, i;
+
+       if (sig == SIGUSR1) {
+               if ((logmask & D_ALL) && !logging) {
+                       xlog(D_GENERAL, "turned on logging");
+                       logging = 1;
+                       return;
+               }
+               tmp = ~logmask;
+               logmask |= ((logmask & D_ALL) << 1) | D_GENERAL;
+               for (i = -1, tmp &= logmask; tmp; tmp >>= 1, i++)
+                       if (tmp & 1)
+                               xlog(D_GENERAL,
+                                       "turned on logging level %d", i);
+       } else {
+               xlog(D_GENERAL, "turned off logging");
+               logging = 0;
+       }
+       signal(sig, xlog_toggle);
+}
+
+void
+xlog_config(int fac, int on)
+{
+       if (on)
+               logmask |= fac;
+       else
+               logmask &= ~fac;
+       if (on)
+               logging = 1;
+}
+
+void
+xlog_sconfig(char *kind, int on)
+{
+       struct xlog_debugfac    *tbl = debugnames;
+
+       while (tbl->df_name != NULL && strcasecmp(tbl->df_name, kind)) 
+               tbl++;
+       if (!tbl->df_name) {
+               xlog (L_WARNING, "Invalid debug facility: %s\n", kind);
+               return;
+       }
+       xlog_config(tbl->df_fac, on);
+}
+
+int
+xlog_enabled(int fac)
+{
+       return (logging && (fac & logmask));
+}
+
+
+/* Write something to the system logfile. */
+void
+xlog(int kind, const char *fmt, ...)
+{
+       char            buff[1024];
+       va_list         args;
+       int             logged = 1, n;
+#ifdef VERBOSE_PRINTF
+       time_t          now;
+       struct tm       *tm;
+#endif
+
+       if (!(kind & (L_ALL)) && !(logging && (kind & logmask)))
+               return;
+
+       va_start(args, fmt);
+       vsnprintf(buff, sizeof (buff), fmt, args);
+       va_end(args);
+       buff[sizeof (buff) - 1] = 0;
+
+       if ((n = strlen(buff)) > 0 && buff[n-1] != '\n') {
+               buff[n++] = '\n'; buff[n++] = '\0';
+       }
+
+       switch (kind) {
+       case L_FATAL:
+               syslog(LOG_ERR, "%s", buff);
+               break;
+       case L_ERROR:
+               syslog(LOG_ERR, "%s", buff);
+               break;
+       case L_WARNING:
+               syslog(LOG_WARNING, "%s", buff);
+               break;
+       case L_NOTICE:
+               syslog(LOG_NOTICE, "%s", buff);
+               break;
+       default:
+               logged = 0;
+               break;
+       }
+       if (!logged || foreground) {
+               if (!logged && log_fp == NULL) {
+                       syslog(LOG_DEBUG, "%s", buff);
+               } else if (log_fp != NULL) {
+#ifdef VERBOSE_PRINTF
+                       time(&now);
+                       tm = localtime(&now);
+                       fprintf(log_fp, "%s[%d] %02d/%02d/%02d %02d:%02d %s\n",
+                                       log_name, log_pid,
+                                       tm->tm_mon + 1, tm->tm_mday,
+                                       tm->tm_year, tm->tm_hour, tm->tm_min,
+                                       buff);
+#else
+                       fprintf(log_fp, "%s: %s", log_name, buff);
+#endif
+               }
+       }
+       if (kind == L_FATAL)
+               exit(1);
+}
diff --git a/support/nfs/xmalloc.c b/support/nfs/xmalloc.c
new file mode 100644 (file)
index 0000000..9523afc
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * support/nfs/xmalloc.c
+ *
+ * malloc with NULL checking.
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include "xmalloc.h"
+#include "xlog.h"
+
+void *
+xmalloc(size_t size)
+{
+       void    *ptr;
+
+       if (!(ptr = malloc(size)))
+               xlog(L_FATAL, "malloc: out of memory");
+       return ptr;
+}
+
+void *
+xrealloc(void *ptr, size_t size)
+{
+       if (!(ptr = realloc(ptr, size)))
+               xlog(L_FATAL, "realloc: out of memory");
+       return ptr;
+}
+
+void
+xfree(void *ptr)
+{
+       free(ptr);
+}
+
+char *
+xstrdup(const char *str)
+{
+       char    *ret;
+
+       if (!(ret = strdup(str)))
+               xlog(L_FATAL, "strdup: out of memory");
+       return ret;
+}
diff --git a/support/nfs/ypupdate_xdr.c b/support/nfs/ypupdate_xdr.c
new file mode 100644 (file)
index 0000000..9fe1098
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * support/nfs/ypupdate_xdr.c
+ *
+ * This file contains the XDR code for the ypupdate protocol.
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include "config.h"
+
+#include <ypupdate.h>
+
+bool_t
+xdr_ypupdate_args(XDR *xdrs, ypupdate_args *objp)
+{
+        return xdr_string(xdrs, &objp->mapname, MAXMAPNAMELEN) &&
+               xdr_bytes(xdrs, &objp->key.yp_buf_val,
+                               &objp->key.yp_buf_len, MAXYPDATALEN) &&
+               xdr_bytes(xdrs, &objp->datum.yp_buf_val,
+                               &objp->datum.yp_buf_len, MAXYPDATALEN);
+}
+
+bool_t
+xdr_ypdelete_args(XDR *xdrs, ypdelete_args *objp)
+{
+        return xdr_string(xdrs, &objp->mapname, MAXMAPNAMELEN) &&
+               xdr_bytes(xdrs, &objp->key.yp_buf_val,
+                               &objp->key.yp_buf_len, MAXYPDATALEN);
+}
diff --git a/tools/Makefile b/tools/Makefile
new file mode 100644 (file)
index 0000000..6378850
--- /dev/null
@@ -0,0 +1,7 @@
+#
+# Various debugging/testing tools
+#
+
+SUBDIRS        = rpcgen getiversion getkversion nlmtest rpcdebug locktest
+
+include $(TOP)rules.mk
diff --git a/tools/getiversion/Makefile b/tools/getiversion/Makefile
new file mode 100644 (file)
index 0000000..46c7150
--- /dev/null
@@ -0,0 +1,11 @@
+#
+# knfsd tools
+#
+
+TOOL   = getiversion
+OBJS   = getiversion.o
+
+include $(TOP)rules.mk
+
+install::
+       @:
diff --git a/tools/getiversion/getiversion.c b/tools/getiversion/getiversion.c
new file mode 100644 (file)
index 0000000..e9cb391
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * getiversion
+ *
+ * Get version number for an inode on an ext2 file system.
+ */
+
+#include "config.h"
+
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/fs/ext2fs.h>
+
+int
+main(int argc, char **argv)
+{
+       int     i, fd;
+       u_int32_t       vers;
+
+       if (argc <= 1) {
+               fprintf(stderr, "usage: getiversion file ...\n");
+               return 1;
+       }
+
+       for (i = 1; i < argc; i++) {
+               if ((fd = open(argv[i], O_RDONLY)) < 0
+                || ioctl(fd, EXT2_IOC_GETVERSION, &vers) < 0) {
+                       perror(argv[i]);
+                       continue;
+               } else {
+                       printf("%-20s %d\n", argv[i], vers);
+               }
+               close(fd);
+       }
+       return 0;
+}
diff --git a/tools/getkversion/Makefile b/tools/getkversion/Makefile
new file mode 100644 (file)
index 0000000..be813ad
--- /dev/null
@@ -0,0 +1,12 @@
+#
+# getkversion - print the kernel version for which the modules were
+#              compiled.
+#
+
+TOOL   = getkversion
+OBJS   = getkversion.o
+
+include $(TOP)rules.mk
+
+install::
+       @:
diff --git a/tools/getkversion/getkversion.c b/tools/getkversion/getkversion.c
new file mode 100644 (file)
index 0000000..f8faf0a
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Get version number of the kernel this was compiled for.
+ * This is NOT the same as calling uname(), because we may be
+ * running on a different kernel.
+ */
+
+#include "config.h"
+
+#include <linux/version.h>
+#include <stdio.h>
+
+int
+main(void)     /* This is for Dan Popp ;) */
+{
+       printf("%s\n", UTS_RELEASE);
+       return 0;
+}
diff --git a/tools/locktest/Makefile b/tools/locktest/Makefile
new file mode 100644 (file)
index 0000000..e18f0b1
--- /dev/null
@@ -0,0 +1,11 @@
+#
+# testlk -     lock a file to test client side locking.
+#
+
+TOOL   = testlk
+OBJS   = testlk.o
+
+include $(TOP)rules.mk
+
+install::
+       @:
diff --git a/tools/locktest/testlk.c b/tools/locktest/testlk.c
new file mode 100644 (file)
index 0000000..47eb40a
--- /dev/null
@@ -0,0 +1,105 @@
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#ifdef linux
+#include <getopt.h>
+#endif
+#include <fcntl.h>
+
+static void    usage(int exval);
+static void    fatal(char *);
+
+int
+main(int argc, char **argv)
+{
+       unsigned long   start = 0, len = 0;
+       struct flock    fl;
+       int             c, fd, cmd, typ;
+       char            *fname;
+
+       typ = F_RDLCK;
+       cmd = F_SETLK;
+
+       while ((c = getopt(argc, argv, "bhrtw")) != EOF) {
+               switch (c) {
+               case 'h':
+                       usage(0);
+               case 'r':
+                       cmd = F_SETLK;
+                       typ = F_RDLCK;
+                       break;
+               case 'w':
+                       cmd = F_SETLK;
+                       typ = F_WRLCK;
+                       break;
+               case 'b':
+                       cmd = F_SETLKW;
+                       typ = F_WRLCK;
+                       break;
+               case 't':
+                       cmd = F_GETLK;
+                       break;
+               case '?':
+                       usage(1);
+               }
+       }
+
+       argc -= optind;
+       argv += optind;
+
+       if (argc <= 0 || argc > 3)
+               usage(1);
+
+       fname = argv[0];
+       /* printf("TP\n"); */
+       if (argc > 1)
+               start = atoi(argv[1]);
+       /* printf("TP\n"); */
+       if (argc > 2)
+               len   = atoi(argv[2]);
+       /* printf("TP\n"); */
+
+       if ((fd = open(fname, O_RDWR, 0644)) < 0)
+               fatal(fname);
+
+       /* printf("TP1\n"); */
+       fl.l_type = typ;
+       fl.l_whence = 0;
+       fl.l_start = start;
+       fl.l_len = len;
+
+       if (fcntl(fd, cmd, &fl) < 0)
+               fatal("fcntl");
+       printf("fcntl: ok\n");
+       
+       /* printf("TP2\n"); */
+       if (cmd == F_GETLK) {
+               if (fl.l_type == F_UNLCK) {
+                       printf("%s: no conflicting lock\n", fname);
+               } else {
+                       printf("%s: conflicting lock by %d on (%ld;%ld)\n",
+                               fname, fl.l_pid, fl.l_start, fl.l_len);
+               }
+               return 0;
+       }
+
+       /* printf("TP3\n"); */
+       pause();
+       return 0;
+}
+
+static void
+usage(int exval)
+{
+       fprintf(stderr, "usage: testlk filename [start [len]]\n");
+       exit(exval);
+}
+
+static void
+fatal(char *msg)
+{
+       perror(msg);
+       exit(2);
+}
diff --git a/tools/nlmtest/Makefile b/tools/nlmtest/Makefile
new file mode 100644 (file)
index 0000000..6f29afb
--- /dev/null
@@ -0,0 +1,26 @@
+#
+# nlmtest      Exercise some NLM calls to test the lockd server.
+#
+
+TOOL   = nlmtest
+SRCS   = $(RPCSRCS) nlmtest.c
+OBJS   = $(SRCS:.c=.o)
+
+RPCSRCS        = nlm_prot_clnt.c nlm_prot_xdr.c
+RPCHDRS        = nlm_prot.h
+
+#LIBS  = -lnfs
+
+include $(TOP)rules.mk
+
+install::
+       @:
+
+$(RPCHDRS) $(RPCSRCS): nlm_prot.x
+       $(RM) $(RPCHDRS) $(RPCSRCS)
+       $(RPCGEN) -h -o nlm_prot.h $<
+       $(RPCGEN) -l -o nlm_prot_clnt.c $<
+       $(RPCGEN) -c -o nlm_prot_xdr.c $<
+
+clean distclean::
+       $(RM) $(RPCHDRS) $(RPCSRCS)
diff --git a/tools/nlmtest/README b/tools/nlmtest/README
new file mode 100644 (file)
index 0000000..b54cb43
--- /dev/null
@@ -0,0 +1,5 @@
+
+This is a simple tool to test your lockd server. This is a very
+primitive program. To use it on your system, you have to edit
+host.h and adjust the inode and device numbers in nltest.c to
+suit your nfs server.
diff --git a/tools/nlmtest/host.h b/tools/nlmtest/host.h
new file mode 100644 (file)
index 0000000..b4f30df
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * host.h
+ *
+ * Defaults for nlmtest
+ */
+
+#ifndef NLMTEST_HOST_H
+#define NLMTEST_HOST_H
+
+/*
+ * The host on which lockd runs
+ */
+#define NLMTEST_HOST           "crutch"
+
+/*
+ * NFS mount point
+ */
+#define NLMTEST_DIR            "../../mount/"
+
+/*
+ * The default file name and its inode version number.
+ * There's no way the test program can find out the version number,
+ * so you have to add it here.
+ */
+#define NLMTEST_FILE           NLMTEST_DIR "COPYING"
+#define NLMTEST_VERSION                1
+
+#endif /* NLMTEST_HOST_H */
diff --git a/tools/nlmtest/nlm_prot.x b/tools/nlmtest/nlm_prot.x
new file mode 100644 (file)
index 0000000..a425912
--- /dev/null
@@ -0,0 +1,183 @@
+/* @(#)nlm_prot.x      2.1 88/08/01 4.0 RPCSRC */
+/* @(#)nlm_prot.x 1.8 87/09/21 Copyr 1987 Sun Micro */
+
+/*
+ * Network lock manager protocol definition
+ * Copyright (C) 1986 Sun Microsystems, Inc.
+ *
+ * protocol used between local lock manager and remote lock manager
+ */
+
+#ifdef RPC_CLNT
+%#include <string.h>
+#endif
+
+#ifdef RPC_HDR
+%#define LM_MAXSTRLEN  1024
+%#define MAXNAMELEN    LM_MAXSTRLEN+1
+#endif
+
+/*
+ * status of a call to the lock manager
+ */
+enum nlm_stats {
+       nlm_granted = 0,
+       nlm_denied = 1,
+       nlm_denied_nolocks = 2,
+       nlm_blocked = 3,
+       nlm_denied_grace_period = 4
+};
+
+struct nlm_holder {
+       bool exclusive;
+       int svid;
+       netobj oh;
+       unsigned l_offset;
+       unsigned l_len;
+};
+
+union nlm_testrply switch (nlm_stats stat) {
+       case nlm_denied:
+               struct nlm_holder holder;
+       default:
+               void;
+};
+
+struct nlm_stat {
+       nlm_stats stat;
+};
+
+struct nlm_res {
+       netobj cookie;
+       nlm_stat stat;
+};
+
+struct nlm_testres {
+       netobj cookie;
+       nlm_testrply stat;
+};
+
+struct nlm_lock {
+       string caller_name<LM_MAXSTRLEN>;
+       netobj fh;              /* identify a file */
+       netobj oh;              /* identify owner of a lock */
+       int svid;               /* generated from pid for svid */
+       unsigned l_offset;
+       unsigned l_len;
+};
+
+struct nlm_lockargs {
+       netobj cookie;
+       bool block;
+       bool exclusive;
+       struct nlm_lock alock;
+       bool reclaim;           /* used for recovering locks */
+       int state;              /* specify local status monitor state */
+};
+
+struct nlm_cancargs {
+       netobj cookie;          
+       bool block;
+       bool exclusive;
+       struct nlm_lock alock;
+};
+
+struct nlm_testargs {
+       netobj cookie;          
+       bool exclusive;
+       struct nlm_lock alock;
+};
+
+struct nlm_unlockargs {
+       netobj cookie;          
+       struct nlm_lock alock;
+};
+
+
+#ifdef RPC_HDR
+%/*
+% * The following enums are actually bit encoded for efficient
+% * boolean algebra.... DON'T change them.....
+% */
+#endif
+enum   fsh_mode {
+       fsm_DN  = 0,    /* deny none */
+       fsm_DR  = 1,    /* deny read */
+       fsm_DW  = 2,    /* deny write */
+       fsm_DRW = 3     /* deny read/write */
+};
+
+enum   fsh_access {
+       fsa_NONE = 0,   /* for completeness */
+       fsa_R    = 1,   /* read only */
+       fsa_W    = 2,   /* write only */
+       fsa_RW   = 3    /* read/write */
+};
+
+struct nlm_share {
+       string caller_name<LM_MAXSTRLEN>;
+       netobj  fh;
+       netobj  oh;
+       fsh_mode        mode;
+       fsh_access      access;
+};
+
+struct nlm_shareargs {
+       netobj  cookie;
+       nlm_share       share;
+       bool    reclaim;
+};
+
+struct nlm_shareres {
+       netobj  cookie;
+       nlm_stats       stat;
+       int     sequence;
+};
+
+struct nlm_notify {
+       string name<MAXNAMELEN>;
+       long state;
+};
+
+/*
+ * Over-the-wire protocol used between the network lock managers
+ */
+
+program NLM_PROG {
+       version NLM_VERS {
+
+               nlm_testres     NLM_TEST(struct nlm_testargs) = 1;
+
+               nlm_res         NLM_LOCK(struct nlm_lockargs) = 2;
+
+               nlm_res         NLM_CANCEL(struct nlm_cancargs) = 3;
+               nlm_res         NLM_UNLOCK(struct nlm_unlockargs) =     4;
+
+               /*
+                * remote lock manager call-back to grant lock
+                */
+               nlm_res         NLM_GRANTED(struct nlm_testargs)= 5;
+               /*
+                * message passing style of requesting lock
+                */
+               void            NLM_TEST_MSG(struct nlm_testargs) = 6;
+               void            NLM_LOCK_MSG(struct nlm_lockargs) = 7;
+               void            NLM_CANCEL_MSG(struct nlm_cancargs) =8;
+               void            NLM_UNLOCK_MSG(struct nlm_unlockargs) = 9;
+               void            NLM_GRANTED_MSG(struct nlm_testargs) = 10;
+               void            NLM_TEST_RES(nlm_testres) = 11;
+               void            NLM_LOCK_RES(nlm_res) = 12;
+               void            NLM_CANCEL_RES(nlm_res) = 13;
+               void            NLM_UNLOCK_RES(nlm_res) = 14;
+               void            NLM_GRANTED_RES(nlm_res) = 15;
+       } = 1;
+
+       version NLM_VERSX {
+               nlm_shareres    NLM_SHARE(nlm_shareargs) = 20;
+               nlm_shareres    NLM_UNSHARE(nlm_shareargs) = 21;
+               nlm_res         NLM_NM_LOCK(nlm_lockargs) = 22;
+               void            NLM_FREE_ALL(nlm_notify) = 23;
+       } = 3;
+
+} = 100021;
+
diff --git a/tools/nlmtest/nlmtest.c b/tools/nlmtest/nlmtest.c
new file mode 100644 (file)
index 0000000..77dd889
--- /dev/null
@@ -0,0 +1,264 @@
+/*
+ * nlmtest
+ *
+ * Simple tool for NLM testing. You will have to adjust the values in
+ * host.h to your test system.
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include "config.h"
+
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <nfs/nfs.h>
+#include <getopt.h>
+#include "nlm_prot.h"
+#include "host.h"
+
+static char            myhostname[256];
+static int             hostnamelen;
+
+static void            makelock(struct nlm_lock *, u_int32_t, off_t, off_t);
+static void            makeowner(struct netobj *, u_int32_t);
+static void            makefileh(struct netobj *);
+static char *          nlm_stat_name(int status);
+static char *          holderstr(struct netobj *oh);
+
+int
+main(int argc, char **argv)
+{
+       CLIENT          *client;
+       nlm_testargs    testargs;
+       nlm_lockargs    lockargs;
+       nlm_unlockargs  unlockargs;
+       nlm_lock        alock;
+       nlm_testres     *testres;
+       nlm_res         *lockres;
+       char            *filename = NLMTEST_FILE;
+       char            *svchost = NLMTEST_HOST;
+       unsigned long   offset = 0, length = 0;
+       int             exclusive = 0;
+       int             blocking = 0;
+       int             unlock = 0;
+       u_int32_t               cookie = 4321;
+       u_int32_t               mypid = 1234;
+       int             c;
+
+       while ((c = getopt(argc, argv, "bf:h:l:o:p:ux")) != EOF) {
+               switch(c) {
+               case 'b':
+                       blocking = 1;
+                       break;
+               case 'f':
+                       filename = optarg;
+                       break;
+               case 'h':
+                       svchost = optarg;
+                       break;
+               case 'l':
+                       length = atoi(optarg);
+                       break;
+               case 'o':
+                       offset = atoi(optarg);
+                       break;
+               case 'p':
+                       mypid = atoi(optarg);
+                       break;
+               case 'u':
+                       unlock = 1;
+                       break;
+               case 'x':
+                       exclusive = 1;
+                       break;
+               default:
+                       fprintf(stderr, "nlmtest: bad option %c\n", c);
+                       exit (2);
+               }
+       }
+
+       client = clnt_create(svchost, NLM_PROG, NLM_VERS, "udp");
+       if (client == NULL) {
+               clnt_pcreateerror("localhost");
+               exit(1);
+       }
+
+       /* Get local host name */
+       if (gethostname(myhostname, sizeof(myhostname)) < 0)
+               strcpy(myhostname, "unknown");
+       hostnamelen = strlen(myhostname);
+
+       makelock(&alock, mypid, offset, length);
+
+       testargs.cookie.n_bytes = (void*)&cookie;
+       testargs.cookie.n_len   = 4;
+       testargs.exclusive      = exclusive;
+       testargs.alock          = alock;
+
+       if ((testres = nlm_test_1(&testargs, client)) == NULL) {
+               clnt_perror(client, "nlm_test call failed:");
+               exit (1);
+       }
+       printf ("nlm_test reply:\n"
+               "\tcookie:      %d\n"
+               "\tstatus:      %s\n",
+                       *(int*)(testres->cookie.n_bytes),
+                       nlm_stat_name(testres->stat.stat)
+               );
+
+       if (testres->stat.stat == nlm_denied) {
+               nlm_holder *holder = &(testres->stat.nlm_testrply_u.holder);
+               printf ("\tconflicting lock:\n"
+                       "\t oh:         %s\n"
+                       "\t pid:        %d\n"
+                       "\t offset:     %d\n"
+                       "\t length:     %d\n"
+                       "\t exclusive:  %d\n",
+                       holderstr(&holder->oh),
+                       holder->svid,
+                       holder->l_offset,
+                       holder->l_len,
+                       holder->exclusive);
+       }
+
+       if (testres->stat.stat != nlm_granted && !unlock && !blocking)
+               return 1;
+
+       if (unlock) {
+               unlockargs.cookie.n_bytes = (void*)&cookie;
+               unlockargs.cookie.n_len   = sizeof(cookie);
+               unlockargs.alock          = alock;
+
+               if ((lockres = nlm_unlock_1(&unlockargs, client)) == NULL) {
+                       clnt_perror(client, "nlm_unlock call failed:");
+                       exit (1);
+               }
+               printf ("nlm_unlock reply:\n"
+                       "\tcookie:      %d\n"
+                       "\tstatus:      %s\n",
+                               *(int*)(lockres->cookie.n_bytes),
+                               nlm_stat_name(lockres->stat.stat)
+                       );
+       } else {
+               lockargs.cookie.n_bytes = (void*)&cookie;
+               lockargs.cookie.n_len   = sizeof(cookie);
+               lockargs.exclusive      = exclusive;
+               lockargs.alock          = alock;
+               lockargs.reclaim        = 0;
+               lockargs.state          = 0;
+
+               if ((lockres = nlm_lock_1(&lockargs, client)) == NULL) {
+                       clnt_perror(client, "nlm_lock call failed:");
+                       exit (1);
+               }
+               printf ("nlm_lock reply:\n"
+                       "\tcookie:      %d\n"
+                       "\tstatus:      %s\n",
+                               *(int*)(lockres->cookie.n_bytes),
+                               nlm_stat_name(lockres->stat.stat)
+                       );
+       }
+
+       return 0;
+}
+
+static char *
+nlm_stat_name(int status)
+{
+       static char     buf[12];
+
+       switch (status) {
+       case nlm_granted:
+               return "nlm_granted";
+       case nlm_denied:
+               return "nlm_denied";
+       case nlm_denied_nolocks:
+               return "nlm_denied_nolocks";
+       case nlm_blocked:
+               return "nlm_blocked";
+       case nlm_denied_grace_period:
+               return "nlm_denied_grace_period";
+       }
+       sprintf(buf, "%d", status);
+       return buf;
+}
+
+static char *
+holderstr(struct netobj *oh)
+{
+       static char     buffer[4096];
+       unsigned char   c, *sp;
+       int             i;
+
+       for (i = 0, sp = buffer; i < oh->n_len; i++) {
+               c = (unsigned char) oh->n_bytes[i];
+               if (c < 0x20 || c > 0x7f)
+                       sp += sprintf(sp, "\\%03o", c);
+               else
+                       *sp++ = c;
+       }
+       *sp++ = '\0';
+
+       return buffer;
+}
+
+static void
+makelock(struct nlm_lock *alock, u_int32_t mypid, off_t offset, off_t length)
+{
+       makeowner(&alock->oh, mypid);   /* Create owner handle */
+       makefileh(&alock->fh);          /* Create file handle */
+
+       alock->caller_name = myhostname;
+       alock->svid        = mypid;
+       alock->l_offset    = offset;
+       alock->l_len       = length;
+}
+
+static void
+makeowner(struct netobj *oh, u_int32_t mypid)
+{
+       static char     ohdata[1024];
+
+       oh->n_bytes = ohdata;
+       oh->n_len   = hostnamelen + 1 + 4;
+
+       strcpy(ohdata, myhostname);
+       memcpy(ohdata + hostnamelen + 1, &mypid, 4);
+}
+
+static void
+makefileh(struct netobj *fh)
+{
+       static struct knfs_fh   f;
+       struct stat             stb;
+
+       memset(&f, 0, sizeof(f));
+#if 0
+       if (stat(NLMTEST_DIR, &stb) < 0) {
+               perror("couldn't stat mount point " NLMTEST_DIR);
+               exit(1);
+       }
+       f.fh_xdev = stb.st_dev;
+       f.fh_xino = stb.st_ino;
+
+       if (stat(NLMTEST_DIR, &stb) < 0) {
+               perror("couldn't stat mount point " NLMTEST_DIR);
+               exit(1);
+       }
+       f.fh_dev = stb.st_dev;
+       f.fh_ino = stb.st_ino;
+
+       f.fh_version = NLMTEST_VERSION;
+#else
+       f.fh_xdev = 0x801;
+       f.fh_xino = 37596;
+       f.fh_dev  = 0x801;
+       f.fh_ino  = 37732;
+#endif
+
+       fh->n_len   = 32;
+       fh->n_bytes = (void *) &f;
+}
diff --git a/tools/rpcdebug/Makefile b/tools/rpcdebug/Makefile
new file mode 100644 (file)
index 0000000..af2e530
--- /dev/null
@@ -0,0 +1,11 @@
+#
+# knfsd tools
+#
+
+TOOL   = rpcdebug
+OBJS   = rpcdebug.o
+
+include $(TOP)rules.mk
+
+install::
+       @:
diff --git a/tools/rpcdebug/neat_idea.c b/tools/rpcdebug/neat_idea.c
new file mode 100644 (file)
index 0000000..ddaee2e
--- /dev/null
@@ -0,0 +1,334 @@
+/*
+ * Get or set RPC debug flags.
+ *
+ * I would have loved to write this without recourse to the sysctl
+ * interface, but the only plausible approach (reading and writing
+ * /dev/kmem at the offsets indicated by the *_debug symbols from
+ * /proc/ksyms) didn't work, because /dev/kmem doesn't translate virtual
+ * addresses on write. Unfortunately, modules are stuffed into memory
+ * allocated via vmalloc.
+ *
+ * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <nfs/debug.h>
+#include "nfslib.h"
+
+static int             verbose = 0;
+static int             memfd;
+static off_t           flagpos;
+
+static void            find_offset(char *module);
+static unsigned int    find_flag(char **module, char *name);
+static unsigned int    get_flags(void);
+static void            set_flags(unsigned int value);
+static void            print_flags(char *module, unsigned int flags);
+static void            usage(int excode);
+
+int
+main(int argc, char **argv)
+{
+       int             opt_s = 0,
+                       opt_c = 0;
+       unsigned int    flags = 0, oflags;
+       char *          module = NULL;
+       int             c;
+
+       while ((c = getopt(argc, argv, "chm:sv")) != EOF) {
+               switch (c) {
+               case 'c':
+                       opt_c = 1;
+                       break;
+               case 'h':
+                       usage(0);
+               case 'm':
+                       module = optarg;
+                       break;
+               case 's':
+                       opt_s = 1;
+                       break;
+               case 'v':
+                       verbose++;
+                       break;
+               default:
+                       fprintf(stderr, "rpcdebug: unknown option -%c\n",
+                                               optopt);
+                       usage(1);
+               }
+       }
+
+       if (opt_c + opt_s > 1) {
+               fprintf(stderr, "You can use at most one of -c and -s\n");
+               usage(1);
+       }
+
+       if (argc == optind) {
+               flags = ~(unsigned int) 0;
+       } else {
+               for (; optind < argc; optind++) {
+                       unsigned int    temp;
+
+                       if (!(temp = find_flag(&module, argv[optind]))) {
+                               fprintf(stderr, "rpcdebug: unknown flag %s\n",
+                                                       argv[optind]);
+                               exit(1);
+                       }
+                       flags |= temp;
+               }
+       }
+
+       if (!module) {
+               fprintf(stderr, "rpcdebug: no module name specified, and "
+                               "could not be inferred.\n");
+               usage(1);
+       }
+
+       if ((memfd = open("/dev/kmem", O_RDWR)) < 0) {
+               perror("can't open /dev/mem");
+               exit(1);
+       }
+
+       find_offset(module);
+
+       oflags = get_flags();
+
+       if (opt_c) {
+               set_flags(oflags & ~flags);
+       } else if (opt_s) {
+               set_flags(oflags | flags);
+       } else {
+               print_flags(module, oflags);
+       }
+
+       close(memfd);
+       return 0;
+}
+
+#define FLAG(mname, fname)     \
+      { #mname, #fname, mname##DBG_##fname }
+
+static struct flagmap {
+       char *          module;
+       char *          name;
+       unsigned int    value;
+}                      flagmap[] = {
+       /* rpc */
+       FLAG(RPC,       XPRT),
+       FLAG(RPC,       CALL),
+       FLAG(RPC,       TYPES),
+       FLAG(RPC,       NFS),
+       FLAG(RPC,       AUTH),
+       FLAG(RPC,       PMAP),
+       FLAG(RPC,       SCHED),
+       FLAG(RPC,       SVCSOCK),
+       FLAG(RPC,       SVCDSP),
+       FLAG(RPC,       MISC),
+       FLAG(RPC,       ALL),
+
+       /* nfs */
+       /* currently handled by RPCDBG_NFS */
+
+       /* nfsd */
+       FLAG(NFSD,      SOCK),
+       FLAG(NFSD,      FH),
+       FLAG(NFSD,      EXPORT),
+       FLAG(NFSD,      SVC),
+       FLAG(NFSD,      PROC),
+       FLAG(NFSD,      FILEOP),
+       FLAG(NFSD,      AUTH),
+       FLAG(NFSD,      REPCACHE),
+       FLAG(NFSD,      XDR),
+       FLAG(NFSD,      LOCKD),
+       FLAG(NFSD,      ALL),
+
+       /* lockd */
+       FLAG(NLM,       SVC),
+       FLAG(NLM,       CLIENT),
+       FLAG(NLM,       CLNTLOCK),
+       FLAG(NLM,       SVCLOCK),
+       FLAG(NLM,       MONITOR),
+       FLAG(NLM,       CLNTSUBS),
+       FLAG(NLM,       SVCSUBS),
+       FLAG(NLM,       ALL),
+
+      { NULL,          NULL,           0 }
+};
+
+static unsigned int
+find_flag(char **module, char *name)
+{
+       char            *mod = *module;
+       unsigned int    value = 0;
+       int             i;
+
+       for (i = 0; flagmap[i].module; i++) {
+               if ((mod && strcasecmp(mod, flagmap[i].module))
+                || strcasecmp(name, flagmap[i].name))
+                       continue;
+               if (value) {
+                       fprintf(stderr,
+                               "rpcdebug: ambiguous symbol name %s.\n"
+                               "This name is used by more than one module, "
+                               "please specify the module name using\n"
+                               "the -m option.\n",
+                               name);
+                       usage(1);
+               }
+               value = flagmap[i].value;
+               if (*module)
+                       return value;
+               mod = flagmap[i].module;
+       }
+
+       *module = mod;
+       return value;
+}
+
+static unsigned int
+get_flags(void)
+{
+       unsigned int    value;
+       int             count;
+
+       if (lseek(memfd, flagpos, SEEK_SET) < 0) {
+               perror("lseek");
+               exit(1);
+       }
+       if ((count = read(memfd, &value, sizeof(value))) < 0) {
+               perror("read");
+               exit(1);
+       }
+       if (count != sizeof(value)) {
+               fprintf(stderr, "read failed (only %d bytes read)\n",
+                               count);
+               exit(1);
+       }
+       if (verbose)
+               printf("getting flags 0x%x\n", value);
+       return value;
+}
+
+static void
+set_flags(unsigned int value)
+{
+       int     count;
+
+       if (verbose)
+               printf("setting flags 0x%x\n", value);
+       if (lseek(memfd, flagpos, SEEK_SET) < 0) {
+               perror("lseek");
+               exit(1);
+       }
+       if ((count = write(memfd, &value, sizeof(value))) < 0) {
+               perror("write");
+               exit(1);
+       }
+       if (count != sizeof(value)) {
+               fprintf(stderr, "write failed (only %d bytes written)\n",
+                               count);
+               exit(1);
+       }
+}
+
+static void
+find_offset(char *module)
+{
+       char    buffer[512], *sp;
+       char    symbol[64];
+       FILE    *fp;
+       int     len;
+
+       len = sprintf(symbol, "%s_debug", module);
+
+       if ((fp = fopen("/proc/ksyms", "r")) < 0) {
+               perror("rpcdebug: can't open /proc/ksyms");
+               exit(1);
+       }
+
+       while (fgets(buffer, sizeof(buffer), fp) != NULL) {
+               if (!(sp = strchr(buffer, ' ')))
+                       continue;
+               if (strncmp(++sp, symbol, len))
+                       continue;
+               if (sp[len] != '\n' && sp[len] != '\t'
+                && strncmp(sp+len, "_R", 2))
+                       continue;
+               flagpos = (unsigned long) strtol(buffer, &sp, 16);
+               /* printf("position is %lx\n", flagpos); */
+               if (sp && *sp == ' ')
+                       return;
+               fprintf(stderr, "rpcdebug: weird line in /proc/ksyms: %s\n",
+                               buffer);
+               exit(1);
+       }
+
+       fprintf(stderr, "rpcdebug: debug symbol for module %s not found.\n",
+                       module);
+       exit(1);
+}
+
+static char *
+strtolower(char *str)
+{
+       static char     temp[64];
+       char            *sp;
+
+       strcpy(temp, str);
+       for (sp = temp; *sp; sp++)
+               *sp = tolower(*sp);
+       return temp;
+}
+
+static void
+print_flags(char *module, unsigned int flags)
+{
+       char    *lastmod = NULL;
+       int     i;
+
+       if (module) {
+               printf("%-10s", strtolower(module));
+               if (!flags) {
+                       printf("<no flags set>\n");
+                       return;
+               }
+               /* printf(" <%x>", flags); */
+       }
+
+       for (i = 0; flagmap[i].module; i++) {
+               if (module) {
+                       if (strcasecmp(flagmap[i].module, module))
+                               continue;
+               } else if (!lastmod || strcmp(lastmod, flagmap[i].module)) {
+                       if (lastmod)
+                               printf("\n");
+                       printf("%-10s", strtolower(flagmap[i].module));
+                       lastmod = flagmap[i].module;
+               }
+               if (!(flags & flagmap[i].value)
+                || (module && !strcasecmp(flagmap[i].name, "all")))
+                       continue;
+               printf(" %s", strtolower(flagmap[i].name));
+       }
+       printf("\n");
+}
+
+static void
+usage(int excode)
+{
+       fprintf(stderr, "usage: rpcdebug [-m module] [-cs] flags ...\n");
+       if (verbose) {
+               printf("\nModule     Valid flags\n");
+               print_flags(NULL, ~(unsigned int) 0);
+       }
+       exit (excode);
+}
+
diff --git a/tools/rpcdebug/rpcdebug.c b/tools/rpcdebug/rpcdebug.c
new file mode 100644 (file)
index 0000000..b7e7511
--- /dev/null
@@ -0,0 +1,334 @@
+/*
+ * Get or set RPC debug flags.
+ *
+ * I would have loved to write this without recourse to the sysctl
+ * interface, but the only plausible approach (reading and writing
+ * /dev/kmem at the offsets indicated by the _debug symbols from
+ * /proc/ksyms) didn't work, because /dev/kmem doesn't translate virtual
+ * addresses on write. Unfortunately, modules are stuffed into memory
+ * allocated via vmalloc.
+ *
+ * Copyright (C) 1996, 1997, Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <nfs/debug.h>
+
+static int             verbose = 0;
+
+static int             find_sysname(char *module);
+static unsigned int    find_flag(char **module, char *name);
+static unsigned int    get_flags(char *);
+static unsigned int    set_flags(char *, unsigned int value);
+static void            print_flags(FILE *, char *, unsigned int, int);
+static char *          strtolower(char *str);
+static void            usage(int excode);
+
+int
+main(int argc, char **argv)
+{
+       int             opt_s = 0,
+                       opt_c = 0;
+       unsigned int    flags = 0, oflags;
+       char *          module = NULL;
+       int             c;
+
+       while ((c = getopt(argc, argv, "chm:sv")) != EOF) {
+               switch (c) {
+               case 'c':
+                       opt_c = 1;
+                       break;
+               case 'h':
+                       usage(0);
+               case 'm':
+                       module = optarg;
+                       break;
+               case 's':
+                       opt_s = 1;
+                       break;
+               case 'v':
+                       verbose++;
+                       break;
+               default:
+                       fprintf(stderr, "rpcdebug: unknown option -%c\n",
+                                               optopt);
+                       usage(1);
+               }
+       }
+
+       if (opt_c + opt_s > 1) {
+               fprintf(stderr, "You can use at most one of -c and -s\n");
+               usage(1);
+       }
+
+       if (argc == optind) {
+               flags = ~(unsigned int) 0;
+       } else {
+               for (; optind < argc; optind++)
+                       flags |= find_flag(&module, argv[optind]);
+               if (flags && !opt_c)
+                       opt_s = 1;
+       }
+
+       if (!module) {
+               fprintf(stderr, "rpcdebug: no module name specified, and "
+                               "could not be inferred.\n");
+               usage(1);
+       }
+
+       oflags = get_flags(module);
+
+       if (opt_c) {
+               oflags = set_flags(module, oflags & ~flags);
+       } else if (opt_s) {
+               oflags = set_flags(module, oflags | flags);
+       }
+       print_flags(stdout, module, oflags, 0);
+
+       return 0;
+}
+
+#define FLAG(mname, fname)     \
+      { #mname, #fname, mname##DBG_##fname }
+#define SHORTFLAG(mname, fname, dbgname)       \
+      { #mname, #fname, mname##DBG_##dbgname }
+
+static struct flagmap {
+       char *          module;
+       char *          name;
+       unsigned int    value;
+}                      flagmap[] = {
+       /* rpc */
+       FLAG(RPC,       XPRT),
+       FLAG(RPC,       CALL),
+       FLAG(RPC,       DEBUG),
+       FLAG(RPC,       NFS),
+       FLAG(RPC,       AUTH),
+       FLAG(RPC,       PMAP),
+       FLAG(RPC,       SCHED),
+       FLAG(RPC,       SVCSOCK),
+       FLAG(RPC,       SVCDSP),
+       FLAG(RPC,       MISC),
+       FLAG(RPC,       ALL),
+
+       /* nfs */
+       FLAG(NFS,       VFS),
+       FLAG(NFS,       DIRCACHE),
+       FLAG(NFS,       LOOKUPCACHE),
+       FLAG(NFS,       PAGECACHE),
+       FLAG(NFS,       PROC),
+       FLAG(NFS,       ALL),
+       SHORTFLAG(NFS,  dir,    DIRCACHE),
+       SHORTFLAG(NFS,  lookup, LOOKUPCACHE),
+       SHORTFLAG(NFS,  page,   PAGECACHE),
+
+       /* nfsd */
+       FLAG(NFSD,      SOCK),
+       FLAG(NFSD,      FH),
+       FLAG(NFSD,      EXPORT),
+       FLAG(NFSD,      SVC),
+       FLAG(NFSD,      PROC),
+       FLAG(NFSD,      FILEOP),
+       FLAG(NFSD,      AUTH),
+       FLAG(NFSD,      REPCACHE),
+       FLAG(NFSD,      XDR),
+       FLAG(NFSD,      LOCKD),
+       FLAG(NFSD,      ALL),
+
+       /* lockd */
+       FLAG(NLM,       SVC),
+       FLAG(NLM,       CLIENT),
+       FLAG(NLM,       CLNTLOCK),
+       FLAG(NLM,       SVCLOCK),
+       FLAG(NLM,       MONITOR),
+       FLAG(NLM,       CLNTSUBS),
+       FLAG(NLM,       SVCSUBS),
+       FLAG(NLM,       ALL),
+
+      { NULL,          NULL,           0 }
+};
+
+static unsigned int
+find_flag(char **module, char *name)
+{
+       char            *mod = *module;
+       unsigned int    value = 0;
+       int             i;
+
+       for (i = 0; flagmap[i].module; i++) {
+               if ((mod && strcasecmp(mod, flagmap[i].module))
+                || strcasecmp(name, flagmap[i].name))
+                       continue;
+               if (value) {
+                       fprintf(stderr,
+                               "rpcdebug: ambiguous symbol name %s.\n"
+                               "This name is used by more than one module, "
+                               "please specify the module name using\n"
+                               "the -m option.\n",
+                               name);
+                       usage(1);
+               }
+               value = flagmap[i].value;
+               if (*module)
+                       return value;
+               mod = flagmap[i].module;
+       }
+
+       if (!value) {
+               if (*module)
+                       fprintf(stderr,
+                               "rpcdebug: unknown module or flag %s/%s\n",
+                               *module, name);
+               else
+                       fprintf(stderr,
+                               "rpcdebug: unknown flag %s\n",
+                               name);
+               exit(1);
+       }
+
+       *module = mod;
+       return value;
+}
+
+static unsigned int
+get_flags(char *module)
+{
+       char    buffer[256], *sp;
+       int     sysfd, len, namelen;
+
+       if ((sysfd = open("/proc/net/rpc/debug", O_RDONLY)) < 0) {
+               perror("/proc/net/rpc/debug");
+               exit(1);
+       }
+       if ((len = read(sysfd, buffer, sizeof(buffer))) < 0) {
+               perror("read");
+               exit(1);
+       }
+       close(sysfd);
+       buffer[len - 1] = '\0';
+
+       namelen = strlen(module);
+       for (sp = strtok(buffer, " \t"); sp; sp = strtok(NULL, " \t")) {
+               if (!strncmp(sp, module, namelen) && sp[namelen] == '=') {
+
+                       return strtoul(sp + namelen + 1, NULL, 0);
+               }
+       }
+
+       fprintf(stderr, "Unknown module %s\n", module);
+       exit(1);
+}
+
+static unsigned int
+set_flags(char *module, unsigned int value)
+{
+       char    buffer[64];
+       int     sysfd, len, ret;
+
+       len = sprintf(buffer, "%s=%u\n", module, value);
+       if ((sysfd = open("/proc/net/rpc/debug", O_WRONLY)) < 0) {
+               perror("/proc/net/rpc/debug");
+               exit(1);
+       }
+       if ((ret = write(sysfd, buffer, len)) < 0) {
+               perror("write");
+               exit(1);
+       }
+       if (ret < len) {
+               fprintf(stderr, "error: short write in set_flags!\n");
+               exit(1);
+       }
+       close(sysfd);
+       return value;
+}
+
+static int
+find_sysname(char *module)
+{
+       char    procname[1024];
+       int     fd;
+
+       module = strtolower(module);
+
+       sprintf(procname, "/proc/sys/sunrpc/%s_debug", module);
+       if ((fd = open(procname, O_RDWR)) < 0) {
+               perror(procname);
+               exit(1);
+       }
+
+       return fd;
+}
+
+static char *
+strtolower(char *str)
+{
+       static char     temp[64];
+       char            *sp;
+
+       strcpy(temp, str);
+       for (sp = temp; *sp; sp++)
+               *sp = tolower(*sp);
+       return temp;
+}
+
+static void
+print_flags(FILE *ofp, char *module, unsigned int flags, int show_all)
+{
+       char            *lastmod = NULL;
+       unsigned int    shown = 0;
+       int             i;
+
+       if (module) {
+               fprintf(ofp, "%-10s", strtolower(module));
+               if (!flags) {
+                       fprintf(ofp, "<no flags set>\n");
+                       return;
+               }
+               /* printf(" <%x>", flags); */
+       }
+
+       for (i = 0, shown = 0; flagmap[i].module; i++) {
+               if (module) {
+                       if (strcasecmp(flagmap[i].module, module))
+                               continue;
+               } else if (!lastmod || strcmp(lastmod, flagmap[i].module)) {
+                       if (lastmod) {
+                               fprintf(ofp, "\n");
+                               shown = 0;
+                       }
+                       fprintf(ofp, "%-10s", strtolower(flagmap[i].module));
+                       lastmod = flagmap[i].module;
+               }
+               if (!(flags & flagmap[i].value)
+                || (!show_all && (shown & flagmap[i].value))
+                || (module && !strcasecmp(flagmap[i].name, "all")))
+                       continue;
+               fprintf(ofp, " %s", strtolower(flagmap[i].name));
+               shown |= flagmap[i].value;
+       }
+       fprintf(ofp, "\n");
+}
+
+static void
+usage(int excode)
+{
+       fprintf(stderr, "usage: rpcdebug [-m module] [-cs] flags ...\n");
+       if (verbose) {
+               fprintf(stderr, "\nModule     Valid flags\n");
+               print_flags(stderr, NULL, ~(unsigned int) 0, 1);
+       } else {
+               fprintf(stderr,
+                   "       (use rpcdebug -vh to get a list of valid flags)\n");
+       }
+       exit (excode);
+}
+
diff --git a/tools/rpcgen/Makefile b/tools/rpcgen/Makefile
new file mode 100644 (file)
index 0000000..defa7cb
--- /dev/null
@@ -0,0 +1,55 @@
+#
+# This is a slightly patched rpcgen.new from the tirpc package
+# 8 oct 96 --okir
+#              
+
+#
+# Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+# unrestricted use provided that this legend is included on all tape
+# media and as a part of the software program in whole or part.  Users
+# may copy or modify Sun RPC without charge, but are not authorized
+# to license or distribute it to anyone else except as part of a product or
+# program developed by the user or with the express written consent of
+# Sun Microsystems, Inc.
+#
+# SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+# WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+#
+# Sun RPC is provided with no support and without any obligation on the
+# part of Sun Microsystems, Inc. to assist in its use, correction,
+# modification or enhancement.
+#
+# SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+# INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+# OR ANY PART THEREOF.
+#
+# In no event will Sun Microsystems, Inc. be liable for any lost revenue
+# or profits or other special, indirect and consequential damages, even if
+# Sun has been advised of the possibility of such damages.
+#
+# Sun Microsystems, Inc.
+# 2550 Garcia Avenue
+# Mountain View, California  94043
+# 
+#      @(#)Makefile 1.14 89/03/30 (C) 1987 SMI <MODIFIED>
+#
+# Makefile for rpc protocol compiler
+# Copyright (C) 1987, Sun Microsystems, Inc.
+#
+
+TOOL   = rpcgen
+OBJS   = rpc_clntout.o rpc_cout.o rpc_hout.o rpc_main.o rpc_parse.o \
+         rpc_scan.o rpc_svcout.o rpc_tblout.o rpc_util.o rpc_sample.o
+
+include $(TOP)rules.mk
+
+
+all::  $(TOOL)
+       if [ ! -d $(TOP)bin ]; then \
+         rm -rf $(TOP)bin; mkdir -p $(TOP)bin; \
+       fi
+       cp $(TOOL) $(TOP)bin
+
+install::
+       @:
diff --git a/tools/rpcgen/README b/tools/rpcgen/README
new file mode 100644 (file)
index 0000000..2f6bbf3
--- /dev/null
@@ -0,0 +1,8 @@
+
+ This directory contains the source for rpcgen.new from Sun TIRPC
+ source distribution. I cleaned it up a little so that it will
+ compile with gcc (without using -traditional), and modified the
+ output to avoid those silly warnings you usually get when compiling
+ an rpcgen-generated C file.
+
+ Olaf Kirch <okir@monad.swb.de> 8 Oct 1996
diff --git a/tools/rpcgen/rpc_clntout.c b/tools/rpcgen/rpc_clntout.c
new file mode 100644 (file)
index 0000000..3ea267a
--- /dev/null
@@ -0,0 +1,219 @@
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part.  Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user or with the express written consent of
+ * Sun Microsystems, Inc.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California  94043
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)rpc_clntout.c 1.11 89/02/22 (C) 1987 SMI";
+#endif
+
+/*
+ * rpc_clntout.c, Client-stub outputter for the RPC protocol compiler
+ * Copyright (C) 1987, Sun Microsytsems, Inc.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <rpc/types.h>
+#include "rpc_parse.h"
+#include "rpc_util.h"
+#include "rpc_output.h"
+
+/* extern pdeclaration(); */
+/* void printarglist(); */
+
+#define DEFAULT_TIMEOUT 25     /* in seconds */
+static char RESULT[] = "clnt_res";
+
+static void    write_program(definition *def);
+static void    printbody(proc_list *proc);
+
+
+void
+write_stubs(void)
+{
+       list *l;
+       definition *def;
+
+       f_print(fout, 
+               "\n/* Default timeout can be changed using clnt_control() */\n");
+       f_print(fout, "static struct timeval TIMEOUT = { %d, 0 };\n",
+               DEFAULT_TIMEOUT);
+       for (l = defined; l != NULL; l = l->next) {
+               def = (definition *) l->val;
+               if (def->def_kind == DEF_PROGRAM) {
+                       write_program(def);
+               }
+       }
+}
+
+static void
+write_program(definition *def)
+{
+       version_list   *vp;
+       proc_list      *proc;
+
+       for (vp = def->def.pr.versions; vp != NULL; vp = vp->next) {
+               for (proc = vp->procs; proc != NULL; proc = proc->next) {
+                       f_print(fout, "\n");
+                       ptype(proc->res_prefix, proc->res_type, 1);
+                       f_print(fout, "*\n");
+                       pvname(proc->proc_name, vp->vers_num);
+                       printarglist(proc, "clnt", "CLIENT *");
+                       f_print(fout, "{\n");
+                       printbody(proc);
+                       f_print(fout, "}\n");
+               }
+       }
+}
+
+/*
+ * Writes out declarations of procedure's argument list.
+ * In either ANSI C style, in one of old rpcgen style (pass by reference),
+ * or new rpcgen style (multiple arguments, pass by value);
+ */
+
+/* sample addargname = "clnt"; sample addargtype = "CLIENT * " */
+
+void
+printarglist(proc_list *proc, char *addargname, char *addargtype)
+{
+
+       decl_list      *l;
+
+       if (!newstyle) {        /* old style: always pass arg by reference */
+               if (Cflag) {    /* C++ style heading */
+                       f_print(fout, "(");
+                       ptype(proc->args.decls->decl.prefix, proc->args.decls->decl.type, 1);
+                       f_print(fout, "*argp, %s%s)\n", addargtype, addargname);
+               } else {
+                       f_print(fout, "(argp, %s)\n", addargname);
+                       f_print(fout, "\t");
+                       ptype(proc->args.decls->decl.prefix, proc->args.decls->decl.type, 1);
+                       f_print(fout, "*argp;\n");
+               }
+       } else if (streq(proc->args.decls->decl.type, "void")) {
+               /* newstyle, 0 argument */
+               if (Cflag)
+                       f_print(fout, "(%s%s)\n", addargtype, addargname);
+               else
+                       f_print(fout, "(%s)\n", addargname);
+       } else {
+               /* new style, 1 or multiple arguments */
+               if (!Cflag) {
+                       f_print(fout, "(");
+                       for (l = proc->args.decls; l != NULL; l = l->next)
+                               f_print(fout, "%s, ", l->decl.name);
+                       f_print(fout, "%s)\n", addargname);
+                       for (l = proc->args.decls; l != NULL; l = l->next) {
+                               pdeclaration(proc->args.argname, &l->decl, 1, ";\n");
+                       }
+               } else {        /* C++ style header */
+                       f_print(fout, "(");
+                       for (l = proc->args.decls; l != NULL; l = l->next) {
+                               pdeclaration(proc->args.argname, &l->decl, 0, ", ");
+                       }
+                       f_print(fout, " %s%s)\n", addargtype, addargname);
+               }
+       }
+
+       if (!Cflag)
+               f_print(fout, "\t%s%s;\n", addargtype, addargname);
+}
+
+
+
+static char *
+ampr(char *type)
+{
+       if (isvectordef(type, REL_ALIAS)) {
+               return ("");
+       } else {
+               return ("&");
+       }
+}
+
+static void
+printbody(proc_list *proc)
+{
+       decl_list      *l;
+       bool_t          args2 = (proc->arg_num > 1);
+
+       /* For new style with multiple arguments, need a structure in which
+         * to stuff the arguments. */
+       if (newstyle && args2) {
+               f_print(fout, "\t%s", proc->args.argname);
+               f_print(fout, " arg;\n");
+       }
+       f_print(fout, "\tstatic ");
+       if (streq(proc->res_type, "void")) {
+               f_print(fout, "char ");
+       } else {
+               ptype(proc->res_prefix, proc->res_type, 0);
+       }
+       f_print(fout, "%s;\n", RESULT);
+       f_print(fout, "\n");
+       f_print(fout, "\tmemset((char *)%s%s, 0, sizeof(%s));\n",
+               ampr(proc->res_type), RESULT, RESULT);
+       if (newstyle && !args2 && (streq(proc->args.decls->decl.type, "void"))) {
+               /* newstyle, 0 arguments */
+               f_print(fout,
+                       "\tif (clnt_call(clnt, %s, (xdrproc_t) xdr_void, (caddr_t) NULL, "
+                       "(xdrproc_t) xdr_%s, (caddr_t) %s%s, TIMEOUT) != RPC_SUCCESS) {\n",
+                       proc->proc_name,
+                       stringfix(proc->res_type), ampr(proc->res_type), RESULT);
+
+       } else if (newstyle && args2) {
+               /* newstyle, multiple arguments:  stuff arguments into structure */
+               for (l = proc->args.decls; l != NULL; l = l->next) {
+                       f_print(fout, "\targ.%s = %s;\n",
+                               l->decl.name, l->decl.name);
+               }
+               f_print(fout,
+                       "\tif (clnt_call(clnt, %s, (xdrproc_t) xdr_%s, (caddr_t) &arg, "
+                       "(xdrproc_t) xdr_%s, (caddr_t) %s%s, TIMEOUT) != RPC_SUCCESS) {\n",
+                       proc->proc_name, proc->args.argname,
+                       stringfix(proc->res_type), ampr(proc->res_type), RESULT);
+       } else {                /* single argument, new or old style */
+               f_print(fout,
+                       "\tif (clnt_call(clnt, %s, (xdrproc_t) xdr_%s, "
+                       "(caddr_t) %s%s, (xdrproc_t) xdr_%s, (caddr_t) %s%s, TIMEOUT) != RPC_SUCCESS) {\n",
+                       proc->proc_name,
+                       stringfix(proc->args.decls->decl.type),
+                       (newstyle ? "&" : ""),
+                       (newstyle ? proc->args.decls->decl.name : "argp"),
+                       stringfix(proc->res_type), ampr(proc->res_type), RESULT);
+       }
+       f_print(fout, "\t\treturn (NULL);\n");
+       f_print(fout, "\t}\n");
+       if (streq(proc->res_type, "void")) {
+               f_print(fout, "\treturn ((void *)%s%s);\n",
+                       ampr(proc->res_type), RESULT);
+       } else {
+               f_print(fout, "\treturn (%s%s);\n", ampr(proc->res_type), RESULT);
+       }
+}
diff --git a/tools/rpcgen/rpc_cout.c b/tools/rpcgen/rpc_cout.c
new file mode 100644 (file)
index 0000000..9bc20bb
--- /dev/null
@@ -0,0 +1,715 @@
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part.  Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user or with the express written consent of
+ * Sun Microsystems, Inc.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California  94043
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)rpc_cout.c 1.13 89/02/22 (C) 1987 SMI";
+#endif
+
+/*
+ * rpc_cout.c, XDR routine outputter for the RPC protocol compiler 
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <malloc.h>
+#include <ctype.h>
+#include "rpc_parse.h"
+#include "rpc_util.h"
+
+static int     findtype(definition *def, char *type);
+static int     undefined(char *type);
+static void    print_generic_header(char *procname, int pointerp);
+static void    print_header(definition *def);
+static void    print_prog_header(proc_list *plist);
+static void    print_trailer(void);
+static void    print_ifopen(int indent, char *name);
+static void    print_ifarg(char *arg);
+static void    print_ifsizeof(char *prefix, char *type);
+static void    print_ifclose(int indent);
+static void    print_ifstat(int indent, char *prefix, char *type, relation rel,
+                       char *amax, char *objname, char *name);
+static void    emit_enum(definition *def);
+static void    emit_program(definition *def);
+static void    emit_union(definition *def);
+static void    emit_struct(definition *def);
+static void    emit_typedef(definition *def);
+static void    print_stat(int indent, declaration *dec);
+static void    emit_inline(declaration *decl, int flag);
+static void    emit_single_in_line(declaration *decl, int flag, relation rel);
+static char *  upcase(char *str);
+
+/*
+ * Emit the C-routine for the given definition 
+ */
+void
+emit(definition *def)
+{
+       if (def->def_kind == DEF_CONST) {
+               return;
+       }
+       if (def->def_kind == DEF_PROGRAM) {
+               emit_program(def);
+               return;
+       }
+       if (def->def_kind == DEF_TYPEDEF) {
+               /* now we need to handle declarations like
+                * struct typedef foo foo;
+                * since we dont want this to be expanded into 2 calls
+                * to xdr_foo */
+
+               if (strcmp(def->def.ty.old_type, def->def_name) == 0)
+                       return;
+       };
+
+       print_header(def);
+       switch (def->def_kind) {
+       case DEF_UNION:
+               emit_union(def);
+               break;
+       case DEF_ENUM:
+               emit_enum(def);
+               break;
+       case DEF_STRUCT:
+               emit_struct(def);
+               break;
+       case DEF_TYPEDEF:
+               emit_typedef(def);
+               break;
+       default:
+               break;
+       }
+       print_trailer();
+}
+
+static int
+findtype(definition *def, char *type)
+{
+
+       if (def->def_kind == DEF_PROGRAM || def->def_kind == DEF_CONST) {
+               return (0);
+       } else {
+               return (streq(def->def_name, type));
+       }
+}
+
+static int
+undefined(char *type)
+{
+       definition     *def;
+
+       def = (definition *) FINDVAL(defined, type, findtype);
+
+       return (def == NULL);
+}
+
+
+static void
+print_generic_header(char *procname, int pointerp)
+{
+       f_print(fout, "\n");
+       f_print(fout, "bool_t\n");
+       if (Cflag) {
+               f_print(fout, "xdr_%s(", procname);
+               f_print(fout, "XDR *xdrs, ");
+               f_print(fout, "%s ", procname);
+               if (pointerp)
+                       f_print(fout, "*");
+               f_print(fout, "objp)\n{\n\n");
+       } else {
+               f_print(fout, "xdr_%s(xdrs, objp)\n", procname);
+               f_print(fout, "\tXDR *xdrs;\n");
+               f_print(fout, "\t%s ", procname);
+               if (pointerp)
+                       f_print(fout, "*");
+               f_print(fout, "objp;\n{\n\n");
+       }
+}
+
+static void
+print_header(definition *def)
+{
+       decl_list      *dl;
+       bas_type       *ptr;
+       int             i;
+
+
+       print_generic_header(def->def_name,
+               def->def_kind != DEF_TYPEDEF ||
+               !isvectordef(def->def.ty.old_type, def->def.ty.rel));
+
+       /* Now add Inline support */
+
+
+       if (Inline == 0)
+               return;
+       /* May cause lint to complain. but  ... */
+       f_print(fout, "\t register long *buf;\n\n");
+}
+
+static void
+print_prog_header(proc_list *plist)
+{
+       print_generic_header(plist->args.argname, 1);
+}
+
+static void
+print_trailer(void)
+{
+       f_print(fout, "\treturn (TRUE);\n");
+       f_print(fout, "}\n");
+}
+
+
+static void
+print_ifopen(int indent, char *name)
+{
+       tabify(fout, indent);
+       f_print(fout, " if (!xdr_%s(xdrs", name);
+}
+
+static void
+print_ifarg(char *arg)
+{
+       f_print(fout, ", %s", arg);
+}
+
+static void
+print_ifsizeof(char *prefix, char *type)
+{
+       if (streq(type, "bool")) {
+               f_print(fout, ", sizeof(bool_t), (xdrproc_t)xdr_bool");
+       } else {
+               f_print(fout, ", sizeof(");
+               if (undefined(type) && prefix) {
+                       f_print(fout, "%s ", prefix);
+               }
+               f_print(fout, "%s), (xdrproc_t)xdr_%s", type, type);
+       }
+}
+
+static void
+print_ifclose(int indent)
+{
+       f_print(fout, ")) {\n");
+       tabify(fout, indent);
+       f_print(fout, "\t return (FALSE);\n");
+       tabify(fout, indent);
+       f_print(fout, " }\n");
+}
+
+static void
+print_ifstat(int indent, char *prefix, char *type, relation rel,
+                       char *amax, char *objname, char *name)
+{
+       char *alt = NULL;
+
+       switch (rel) {
+       case REL_POINTER:
+               print_ifopen(indent, "pointer");
+               print_ifarg("(char **)");
+               f_print(fout, "%s", objname);
+               print_ifsizeof(prefix, type);
+               break;
+       case REL_VECTOR:
+               if (streq(type, "string")) {
+                       alt = "string";
+               } else if (streq(type, "opaque")) {
+                       alt = "opaque";
+               }
+               if (alt) {
+                       print_ifopen(indent, alt);
+                       print_ifarg(objname);
+               } else {
+                       print_ifopen(indent, "vector");
+                       print_ifarg("(char *)");
+                       f_print(fout, "%s", objname);
+               }
+               print_ifarg(amax);
+               if (!alt) {
+                       print_ifsizeof(prefix, type);
+               }
+               break;
+       case REL_ARRAY:
+               if (streq(type, "string")) {
+                       alt = "string";
+               } else if (streq(type, "opaque")) {
+                       alt = "bytes";
+               }
+               if (streq(type, "string")) {
+                       print_ifopen(indent, alt);
+                       print_ifarg(objname);
+               } else {
+                       if (alt) {
+                               print_ifopen(indent, alt);
+                       } else {
+                               print_ifopen(indent, "array");
+                       }
+                       print_ifarg("(char **)");
+                       if (*objname == '&') {
+                               f_print(fout, "%s.%s_val, (u_int *)%s.%s_len",
+                                       objname, name, objname, name);
+                       } else {
+                               f_print(fout, "&%s->%s_val, (u_int *)&%s->%s_len",
+                                       objname, name, objname, name);
+                       }
+               }
+               print_ifarg(amax);
+               if (!alt) {
+                       print_ifsizeof(prefix, type);
+               }
+               break;
+       case REL_ALIAS:
+               print_ifopen(indent, type);
+               print_ifarg(objname);
+               break;
+       }
+       print_ifclose(indent);
+}
+
+static void
+emit_enum(definition *def)
+{
+       print_ifopen(1, "enum");
+       print_ifarg("(enum_t *)objp");
+       print_ifclose(1);
+}
+
+static void
+emit_program(definition *def)
+{
+       decl_list      *dl;
+       version_list   *vlist;
+       proc_list      *plist;
+
+       for (vlist = def->def.pr.versions; vlist != NULL; vlist = vlist->next)
+               for (plist = vlist->procs; plist != NULL; plist = plist->next) {
+                       if (!newstyle || plist->arg_num < 2)
+                               continue;/* old style, or single argument */
+                       print_prog_header(plist);
+                       for (dl = plist->args.decls; dl != NULL; dl = dl->next)
+                               print_stat(1, &dl->decl);
+                       print_trailer();
+               }
+}
+
+
+static void
+emit_union(definition *def)
+{
+  declaration *dflt;
+  case_list *cl;
+  declaration *cs;
+  char *object;
+  char *vecformat = "objp->%s_u.%s";
+  char *format = "&objp->%s_u.%s";
+
+  print_stat(1,&def->def.un.enum_decl);
+  f_print(fout, "\tswitch (objp->%s) {\n", def->def.un.enum_decl.name);
+  for (cl = def->def.un.cases; cl != NULL; cl = cl->next) {
+
+    f_print(fout, "\tcase %s:\n", cl->case_name);
+    if(cl->contflag == 1)      /* a continued case statement */
+      continue;
+    cs = &cl->case_decl;
+    if (!streq(cs->type, "void")) {
+      object = alloc(strlen(def->def_name) + strlen(format) +
+                    strlen(cs->name) + 1);
+      if (isvectordef (cs->type, cs->rel)) {
+       s_print(object, vecformat, def->def_name, 
+               cs->name);
+      } else {
+       s_print(object, format, def->def_name, 
+               cs->name);
+      }
+      print_ifstat(2, cs->prefix, cs->type, cs->rel, cs->array_max,
+                  object, cs->name);
+      free(object);
+    }
+    f_print(fout, "\t\tbreak;\n");
+  }
+  dflt = def->def.un.default_decl;
+  if (dflt != NULL) {
+    if (!streq(dflt->type, "void")) {
+      f_print(fout, "\tdefault:\n");
+      object = alloc(strlen(def->def_name) + strlen(format) +
+                    strlen(dflt->name) + 1);
+      if (isvectordef (dflt->type, dflt->rel)) {
+       s_print(object, vecformat, def->def_name, 
+               dflt->name);
+      } else {
+       s_print(object, format, def->def_name, 
+               dflt->name);
+      }
+
+      print_ifstat(2, dflt->prefix, dflt->type, dflt->rel,
+                  dflt->array_max, object, dflt->name);
+      free(object);
+      f_print(fout, "\t\tbreak;\n");
+    } else {
+      /* Avoid gcc warnings about `value not handled in switch' */
+      f_print(fout, "\tdefault:\n");
+      f_print(fout, "\t\tbreak;\n");
+    }
+  } else {
+    f_print(fout, "\tdefault:\n");
+    f_print(fout, "\t\treturn (FALSE);\n");
+  }
+
+  f_print(fout, "\t}\n");
+}
+
+static void
+emit_struct(definition *def)
+{
+       decl_list      *dl;
+       int             i, j, size, flag;
+       decl_list      *cur = NULL, *psav;
+       bas_type       *ptr;
+       char           *sizestr, *plus;
+       char            ptemp[256];
+       int             can_inline;
+
+
+       if (Inline == 0) {
+               for (dl = def->def.st.decls; dl != NULL; dl = dl->next)
+                       print_stat(1, &dl->decl);
+       } else {
+
+               for (dl = def->def.st.decls; dl != NULL; dl = dl->next)
+                       if (dl->decl.rel == REL_VECTOR) {
+                               f_print(fout, "\t int i;\n");
+                               break;
+                       }
+               size = 0;
+               can_inline = 0;
+               for (dl = def->def.st.decls; dl != NULL; dl = dl->next)
+                       if ((dl->decl.prefix == NULL) && ((ptr = find_type(dl->decl.type)) != NULL) && ((dl->decl.rel == REL_ALIAS) || (dl->decl.rel == REL_VECTOR))) {
+
+                               if (dl->decl.rel == REL_ALIAS)
+                                       size += ptr->length;
+                               else {
+                                       can_inline = 1;
+                                       break;  /* can be inlined */
+                               };
+                       } else {
+                               if (size >= Inline) {
+                                       can_inline = 1;
+                                       break;  /* can be inlined */
+                               }
+                               size = 0;
+                       }
+               if (size > Inline)
+                       can_inline = 1;
+
+               if (can_inline == 0) {  /* can not inline, drop back to old mode */
+                       for (dl = def->def.st.decls; dl != NULL; dl = dl->next)
+                               print_stat(1, &dl->decl);
+                       return;
+               };
+
+
+
+
+               flag = PUT;
+               for (j = 0; j < 2; j++) {
+
+                       if (flag == PUT)
+                               f_print(fout, "\n\t if (xdrs->x_op == XDR_ENCODE) {\n");
+                       else
+                               f_print(fout, "\n \t return (TRUE);\n\t} else if (xdrs->x_op == XDR_DECODE) {\n");
+
+
+                       i = 0;
+                       size = 0;
+                       sizestr = NULL;
+                       for (dl = def->def.st.decls; dl != NULL; dl = dl->next) {       /* xxx */
+
+                               /* now walk down the list and check for basic types */
+                               if ((dl->decl.prefix == NULL) && ((ptr = find_type(dl->decl.type)) != NULL) && ((dl->decl.rel == REL_ALIAS) || (dl->decl.rel == REL_VECTOR))) {
+                                       if (i == 0)
+                                               cur = dl;
+                                       i++;
+
+                                       if (dl->decl.rel == REL_ALIAS)
+                                               size += ptr->length;
+                                       else {
+                                               /* this is required to handle arrays */
+
+                                               if (sizestr == NULL)
+                                                       plus = " ";
+                                               else
+                                                       plus = "+";
+
+                                               if (ptr->length != 1)
+                                                       s_print(ptemp, " %s %s * %d", plus, dl->decl.array_max, ptr->length);
+                                               else
+                                                       s_print(ptemp, " %s %s ", plus, dl->decl.array_max);
+
+                                               /*now concatenate to sizestr !!!! */
+                                               if (sizestr == NULL)
+                                                       sizestr = strdup(ptemp);
+                                               else {
+                                                       sizestr = realloc(sizestr, strlen(sizestr) + strlen(ptemp) + 1);
+                                                       if (sizestr == NULL) {
+
+                                                               f_print(stderr, "Fatal error : no memory \n");
+                                                               crash();
+                                                       };
+                                                       sizestr = strcat(sizestr, ptemp);       /*build up length of array */
+
+                                               }
+                                       }
+
+                               } else {
+                                       if (i > 0)
+                                           {
+                                               if (sizestr == NULL && size < Inline) {
+                                                       /* don't expand into inline code if size < inline */
+                                                       while (cur != dl) {
+                                                               print_stat(1, &cur->decl);
+                                                               cur = cur->next;
+                                                       }
+                                               } else {
+
+
+
+                                                       /* were already looking at a xdr_inlineable structure */
+                                                       if (sizestr == NULL)
+                                                               f_print(fout, "\t buf = XDR_INLINE(xdrs,%d * BYTES_PER_XDR_UNIT);",
+                                                                       size);
+                                                       else if (size == 0)
+                                                               f_print(fout,
+                                                                       "\t buf = XDR_INLINE(xdrs,%s * BYTES_PER_XDR_UNIT);",
+                                                                       sizestr);
+                                                       else
+                                                               f_print(fout,
+                                                                       "\t buf = XDR_INLINE(xdrs,(%d + %s)* BYTES_PER_XDR_UNIT);",
+                                                                       size, sizestr);
+
+                                                       f_print(fout, "\n\t   if (buf == NULL) {\n");
+
+                                                       psav = cur;
+                                                       while (cur != dl) {
+                                                               print_stat(2, &cur->decl);
+                                                               cur = cur->next;
+                                                       }
+
+                                                       f_print(fout, "\n\t  }\n\t  else {\n");
+
+                                                       cur = psav;
+                                                       while (cur != dl) {
+                                                               emit_inline(&cur->decl, flag);
+                                                               cur = cur->next;
+                                                       }
+
+                                                       f_print(fout, "\t  }\n");
+                                               }
+                                           }
+                                       size = 0;
+                                       i = 0;
+                                       sizestr = NULL;
+                                       print_stat(1, &dl->decl);
+                               }
+
+                       }
+                       if (i > 0)
+                           {
+                               if (sizestr == NULL && size < Inline) {
+                                       /* don't expand into inline code if size < inline */
+                                       while (cur != dl) {
+                                               print_stat(1, &cur->decl);
+                                               cur = cur->next;
+                                       }
+                               } else {
+
+                                       /* were already looking at a xdr_inlineable structure */
+                                       if (sizestr == NULL)
+                                               f_print(fout, "\t\tbuf = XDR_INLINE(xdrs,%d * BYTES_PER_XDR_UNIT);",
+                                                       size);
+                                       else if (size == 0)
+                                               f_print(fout,
+                                                       "\t\tbuf = XDR_INLINE(xdrs,%s * BYTES_PER_XDR_UNIT);",
+                                                       sizestr);
+                                       else
+                                               f_print(fout,
+                                                       "\t\tbuf = XDR_INLINE(xdrs,(%d + %s)* BYTES_PER_XDR_UNIT);",
+                                                       size, sizestr);
+
+                                       f_print(fout, "\n\t\tif (buf == NULL) {\n");
+
+                                       psav = cur;
+                                       while (cur != NULL) {
+                                               print_stat(2, &cur->decl);
+                                               cur = cur->next;
+                                       }
+                                       f_print(fout, "\n\t  }\n\t  else {\n");
+
+                                       cur = psav;
+                                       while (cur != dl) {
+                                               emit_inline(&cur->decl, flag);
+                                               cur = cur->next;
+                                       }
+
+                                       f_print(fout, "\t  }\n");
+
+                               }
+                           }
+                       flag = GET;
+               }
+               f_print(fout, "\t return(TRUE);\n\t}\n\n");
+
+               /* now take care of XDR_FREE case */
+
+               for (dl = def->def.st.decls; dl != NULL; dl = dl->next)
+                       print_stat(1, &dl->decl);
+       }
+}
+
+
+
+static void
+emit_typedef(definition *def)
+{
+       char *prefix = def->def.ty.old_prefix;
+       char *type = def->def.ty.old_type;
+       char *amax = def->def.ty.array_max;
+       relation rel = def->def.ty.rel;
+
+
+         print_ifstat(1, prefix, type, rel, amax, "objp", def->def_name);
+}
+
+static void
+print_stat(int indent, declaration *dec)
+{
+       char *prefix = dec->prefix;
+       char *type = dec->type;
+       char *amax = dec->array_max;
+       relation rel = dec->rel;
+       char name[256];
+
+       if (isvectordef(type, rel)) {
+               s_print(name, "objp->%s", dec->name);
+       } else {
+               s_print(name, "&objp->%s", dec->name);
+       }
+       print_ifstat(indent, prefix, type, rel, amax, name, dec->name);
+}
+
+
+static void
+emit_inline(declaration *decl, int flag)
+{
+
+       /*check whether an array or not */
+
+       switch (decl->rel) {
+       case REL_ALIAS:
+               emit_single_in_line(decl, flag, REL_ALIAS);
+               break;
+       case REL_VECTOR:
+               f_print(fout, "\t\t{ register %s *genp; \n", decl->type);
+               f_print(fout, "\t\t  for ( i = 0,genp=objp->%s;\n \t\t\ti < %s; i++){\n\t\t",
+                       decl->name, decl->array_max);
+               emit_single_in_line(decl, flag, REL_VECTOR);
+               f_print(fout, "\t\t   }\n\t\t };\n");
+               break;
+       default:
+               break;
+       }
+}
+
+static void
+emit_single_in_line(declaration *decl, int flag, relation rel)
+{
+       char *upp_case;
+       int freed=0;
+
+       if(flag == PUT)
+               f_print(fout,"\t\t IXDR_PUT_");
+       else    
+               if(rel== REL_ALIAS)
+                       f_print(fout,"\t\t objp->%s = IXDR_GET_",decl->name);
+               else
+                       f_print(fout,"\t\t *genp++ = IXDR_GET_");
+
+       upp_case=upcase(decl->type);
+
+       /* hack  - XX */
+       if(strcmp(upp_case,"INT") == 0)
+       {
+               free(upp_case);
+               freed=1;
+               upp_case="LONG";
+       }
+
+       if(strcmp(upp_case,"U_INT") == 0)
+       {
+               free(upp_case);
+               freed=1;
+               upp_case="U_LONG";
+       }
+
+
+       if(flag == PUT) 
+               if(rel== REL_ALIAS)
+                       f_print(fout,"%s(buf,objp->%s);\n",upp_case,decl->name);
+               else
+                       f_print(fout,"%s(buf,*genp++);\n",upp_case);
+
+       else
+               f_print(fout,"%s(buf);\n",upp_case);
+       if(!freed)
+               free(upp_case);
+
+}
+
+
+static char *
+upcase(char *str)
+{
+       char           *ptr, *hptr;
+
+
+       ptr = (char *) malloc(strlen(str));
+       if (ptr == (char *) NULL) {
+               f_print(stderr, "malloc failed \n");
+               exit(1);
+       };
+
+       hptr = ptr;
+       while (*str != '\0')
+               *ptr++ = toupper(*str++);
+
+       *ptr = '\0';
+       return (hptr);
+
+}
diff --git a/tools/rpcgen/rpc_hout.c b/tools/rpcgen/rpc_hout.c
new file mode 100644 (file)
index 0000000..06835cd
--- /dev/null
@@ -0,0 +1,492 @@
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part.  Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user or with the express written consent of
+ * Sun Microsystems, Inc.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California  94043
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)rpc_hout.c 1.12 89/02/22 (C) 1987 SMI";
+#endif
+
+/*
+ * rpc_hout.c, Header file outputter for the RPC protocol compiler 
+ */
+#include <stdio.h>
+#include <ctype.h>
+#include "rpc_parse.h"
+#include "rpc_util.h"
+#include "rpc_output.h"
+
+
+static int     undefined2(char *type, char *stop);
+static void    pxdrfuncdecl(char *name, int pointerp);
+static void    pconstdef(definition *def);
+static void    pargdef(definition *def);
+static void    pstructdef(definition *def);
+static void    puniondef(definition *def);
+static void    pdefine(char *name, char *num);
+static void    puldefine(char *name, char *num);
+static int     define_printed(proc_list *stop, version_list *start);
+static void    pprogramdef(definition *def);
+static void    pprocdef(proc_list *proc, version_list *vp,
+                               char *addargtype, int server_p, int mode);
+static void    parglist(proc_list *proc, char *addargtype);
+static void    penumdef(definition *def);
+static void    ptypedef(definition *def);
+
+/*
+ * Print the C-version of an xdr definition 
+ */
+void
+print_datadef(definition *def)
+{
+
+       if (def->def_kind == DEF_PROGRAM )  /* handle data only */
+               return;
+
+       if (def->def_kind != DEF_CONST) {
+               f_print(fout, "\n");
+       }
+       switch (def->def_kind) {
+       case DEF_STRUCT:
+               pstructdef(def);
+               break;
+       case DEF_UNION:
+               puniondef(def);
+               break;
+       case DEF_ENUM:
+               penumdef(def);
+               break;
+       case DEF_TYPEDEF:
+               ptypedef(def);
+               break;
+       case DEF_PROGRAM:
+               pprogramdef(def);
+               break;
+       case DEF_CONST:
+               pconstdef(def);
+               break;
+       }
+       if (def->def_kind != DEF_PROGRAM && def->def_kind != DEF_CONST) {
+         pxdrfuncdecl( def->def_name,
+                      def->def_kind != DEF_TYPEDEF ||
+                      !isvectordef(def->def.ty.old_type, def->def.ty.rel));
+
+       }
+}
+
+
+void
+print_funcdef(definition *def)
+{
+       switch (def->def_kind) {
+       case DEF_PROGRAM:
+               f_print(fout, "\n");
+               pprogramdef(def);
+               break;
+       default:
+               break;
+       }
+}
+
+static void
+pxdrfuncdecl(char *name, int pointerp)
+{
+       f_print(fout,
+       "#ifdef __cplusplus \n"
+       "extern \"C\" bool_t xdr_%s(XDR *, %s%s);\n"
+       "#elif __STDC__ \n"
+       "extern  bool_t xdr_%s(XDR *, %s%s);\n"
+       "#else /* Old Style C */ \n"
+       "bool_t xdr_%s();\n"
+       "#endif /* Old Style C */ \n\n",
+       name, name, pointerp ? "*" : "",
+       name, name, pointerp ? "*" : "",
+       name);
+}
+
+
+static void
+pconstdef(definition *def)
+{
+       pdefine(def->def_name, def->def.co);
+}
+
+/* print out the definitions for the arguments of functions in the 
+   header file 
+*/
+static  void
+pargdef(definition *def)
+{
+       decl_list *l;
+       version_list *vers;
+       char *name;
+       proc_list *plist;
+
+       
+       for (vers = def->def.pr.versions; vers != NULL; vers = vers->next) {
+                       for(plist = vers->procs; plist != NULL; 
+                           plist = plist->next) {
+                               
+                               if (!newstyle || plist->arg_num < 2) {
+                                       continue; /* old style or single args */
+                               }
+                               name = plist->args.argname;
+                               f_print(fout, "struct %s {\n", name);
+                               for (l = plist->args.decls; 
+                                    l != NULL; l = l->next) {
+                                       pdeclaration(name, &l->decl, 1, ";\n" );
+                               }
+                               f_print(fout, "};\n");
+                               f_print(fout, "typedef struct %s %s;\n", name, name);
+                               pxdrfuncdecl(name, 0);
+                               f_print( fout, "\n" );
+                       }
+               }
+
+}
+
+
+static void
+pstructdef(definition *def)
+{
+       decl_list *l;
+       char *name = def->def_name;
+
+       f_print(fout, "struct %s {\n", name);
+       for (l = def->def.st.decls; l != NULL; l = l->next) {
+               pdeclaration(name, &l->decl, 1, ";\n");
+       }
+       f_print(fout, "};\n");
+       f_print(fout, "typedef struct %s %s;\n", name, name);
+}
+
+static void
+puniondef(definition *def)
+{
+       case_list      *l;
+       char           *name = def->def_name;
+       declaration    *decl;
+
+       f_print(fout, "struct %s {\n", name);
+       decl = &def->def.un.enum_decl;
+       if (streq(decl->type, "bool")) {
+               f_print(fout, "\tbool_t %s;\n", decl->name);
+       } else {
+               f_print(fout, "\t%s %s;\n", decl->type, decl->name);
+       }
+       f_print(fout, "\tunion {\n");
+       for (l = def->def.un.cases; l != NULL; l = l->next) {
+               if (l->contflag == 0)
+                       pdeclaration(name, &l->case_decl, 2, ";\n");
+       }
+       decl = def->def.un.default_decl;
+       if (decl && !streq(decl->type, "void")) {
+               pdeclaration(name, decl, 2, ";\n");
+       }
+       f_print(fout, "\t} %s_u;\n", name);
+       f_print(fout, "};\n");
+       f_print(fout, "typedef struct %s %s;\n", name, name);
+}
+
+static void
+pdefine(char *name, char *num)
+{
+       f_print(fout, "#define %s %s\n", name, num);
+}
+
+static void
+puldefine(char *name, char *num)
+{
+       f_print(fout, "#define %s ((u_long)%s)\n", name, num);
+}
+
+static int
+define_printed(proc_list *stop, version_list *start)
+{
+       version_list *vers;
+       proc_list *proc;
+
+       for (vers = start; vers != NULL; vers = vers->next) {
+               for (proc = vers->procs; proc != NULL; proc = proc->next) {
+                       if (proc == stop) {
+                               return (0);
+                       } else if (streq(proc->proc_name, stop->proc_name)) {
+                               return (1);
+                       }
+               }
+       }
+       abort();
+       /* NOTREACHED */
+}
+
+static void
+pprogramdef(definition *def)
+{
+       version_list   *vers;
+       proc_list      *proc;
+       int             i;
+       char           *ext;
+
+       pargdef(def);
+
+       puldefine(def->def_name, def->def.pr.prog_num);
+       for (vers = def->def.pr.versions; vers != NULL; vers = vers->next) {
+               if (tblflag) {
+                       f_print(fout, "extern struct rpcgen_table %s_%s_table[];\n",
+                               locase(def->def_name), vers->vers_num);
+                       f_print(fout, "extern %s_%s_nproc;\n",
+                               locase(def->def_name), vers->vers_num);
+               }
+               puldefine(vers->vers_name, vers->vers_num);
+
+               /*
+                * Print out 3 definitions, one for ANSI-C, another for C++,
+                * a third for old style C
+                */
+
+               for (i = 0; i < 3; i++) {
+                       if (i == 0) {
+                               f_print(fout, "\n#ifdef __cplusplus\n");
+                               ext = "extern \"C\" ";
+                       } else if (i == 1) {
+                               f_print(fout, "\n#elif __STDC__\n");
+                               ext = "extern  ";
+                       } else {
+                               f_print(fout, "\n#else /* Old Style C */ \n");
+                               ext = "extern  ";
+                       }
+
+
+                       for (proc = vers->procs; proc != NULL; proc = proc->next) {
+                               if (!define_printed(proc, def->def.pr.versions)) {
+                                       puldefine(proc->proc_name, proc->proc_num);
+                               }
+                               f_print(fout, "%s", ext);
+                               pprocdef(proc, vers, "CLIENT *", 0, i);
+                               f_print(fout, "%s", ext);
+                               pprocdef(proc, vers, "struct svc_req *", 1, i);
+
+                       }
+
+               }
+               f_print(fout, "#endif /* Old Style C */ \n");
+       }
+}
+
+static void
+pprocdef(proc_list *proc, version_list *vp, char *addargtype,
+                               int server_p, int mode)
+{
+       ptype(proc->res_prefix, proc->res_type, 1);
+       f_print(fout, "* ");
+       if (server_p)
+               pvname_svc(proc->proc_name, vp->vers_num);
+       else
+               pvname(proc->proc_name, vp->vers_num);
+
+       /*
+        * mode  0 == cplusplus, mode  1 = ANSI-C, mode 2 = old style C
+        */
+       if (mode == 0 || mode == 1)
+               parglist(proc, addargtype);
+       else
+               f_print(fout, "();\n");
+}
+
+
+
+/* print out argument list of procedure */
+static void
+parglist(proc_list *proc, char *addargtype)
+{
+       decl_list      *dl;
+
+       f_print(fout, "(");
+
+       if (proc->arg_num < 2 && newstyle &&
+               streq(proc->args.decls->decl.type, "void")) {
+               /* 0 argument in new style:  do nothing */
+       } else {
+               for (dl = proc->args.decls; dl != NULL; dl = dl->next) {
+                       ptype(dl->decl.prefix, dl->decl.type, 1);
+                       if (!newstyle)
+                               f_print(fout, "*");     /* old style passes by reference */
+
+                       f_print(fout, ", ");
+               }
+       }
+
+       f_print(fout, "%s);\n", addargtype);
+}
+
+static void
+penumdef(definition *def)
+{
+       char *name = def->def_name;
+       enumval_list *l;
+       char *last = NULL;
+       int count = 0;
+
+       f_print(fout, "enum %s {\n", name);
+       for (l = def->def.en.vals; l != NULL; l = l->next) {
+               f_print(fout, "\t%s", l->name);
+               if (l->assignment) {
+                       f_print(fout, " = %s", l->assignment);
+                       last = l->assignment;
+                       count = 1;
+               } else {
+                       if (last == NULL) {
+                               f_print(fout, " = %d", count++);
+                       } else {
+                               f_print(fout, " = %s + %d", last, count++);
+                       }
+               }
+               f_print(fout, ",\n");
+       }
+       f_print(fout, "};\n");
+       f_print(fout, "typedef enum %s %s;\n", name, name);
+}
+
+static void
+ptypedef(definition *def)
+{
+       char *name = def->def_name;
+       char *old = def->def.ty.old_type;
+       char prefix[8]; /* enough to contain "struct ", including NUL */
+       relation rel = def->def.ty.rel;
+
+
+       if (!streq(name, old)) {
+               if (streq(old, "string")) {
+                       old = "char";
+                       rel = REL_POINTER;
+               } else if (streq(old, "opaque")) {
+                       old = "char";
+               } else if (streq(old, "bool")) {
+                       old = "bool_t";
+               }
+               if (undefined2(old, name) && def->def.ty.old_prefix) {
+                       s_print(prefix, "%s ", def->def.ty.old_prefix);
+               } else {
+                       prefix[0] = 0;
+               }
+               f_print(fout, "typedef ");
+               switch (rel) {
+               case REL_ARRAY:
+                       f_print(fout, "struct {\n");
+                       f_print(fout, "\tu_int %s_len;\n", name);
+                       f_print(fout, "\t%s%s *%s_val;\n", prefix, old, name);
+                       f_print(fout, "} %s", name);
+                       break;
+               case REL_POINTER:
+                       f_print(fout, "%s%s *%s", prefix, old, name);
+                       break;
+               case REL_VECTOR:
+                       f_print(fout, "%s%s %s[%s]", prefix, old, name,
+                               def->def.ty.array_max);
+                       break;
+               case REL_ALIAS:
+                       f_print(fout, "%s%s %s", prefix, old, name);
+                       break;
+               }
+               f_print(fout, ";\n");
+       }
+}
+
+void
+pdeclaration(char *name, declaration *dec, int tab, char *separator)
+{
+       char buf[8];    /* enough to hold "struct ", include NUL */
+       char *prefix;
+       char *type;
+
+       if (streq(dec->type, "void")) {
+               return;
+       }
+       tabify(fout, tab);
+       if (streq(dec->type, name) && !dec->prefix) {
+               f_print(fout, "struct ");
+       }
+       if (streq(dec->type, "string")) {
+               f_print(fout, "char *%s", dec->name);
+       } else {
+               prefix = "";
+               if (streq(dec->type, "bool")) {
+                       type = "bool_t";
+               } else if (streq(dec->type, "opaque")) {
+                       type = "char";
+               } else {
+                       if (dec->prefix) {
+                               s_print(buf, "%s ", dec->prefix);
+                               prefix = buf;
+                       }
+                       type = dec->type;
+               }
+               switch (dec->rel) {
+               case REL_ALIAS:
+                       f_print(fout, "%s%s %s", prefix, type, dec->name);
+                       break;
+               case REL_VECTOR:
+                       f_print(fout, "%s%s %s[%s]", prefix, type, dec->name,
+                               dec->array_max);
+                       break;
+               case REL_POINTER:
+                       f_print(fout, "%s%s *%s", prefix, type, dec->name);
+                       break;
+               case REL_ARRAY:
+                       f_print(fout, "struct {\n");
+                       tabify(fout, tab);
+                       f_print(fout, "\tu_int %s_len;\n", dec->name);
+                       tabify(fout, tab);
+                       f_print(fout, "\t%s%s *%s_val;\n", prefix, type, dec->name);
+                       tabify(fout, tab);
+                       f_print(fout, "} %s", dec->name);
+                       break;
+               }
+       }
+       f_print(fout, separator );
+}
+
+static int
+undefined2(char *type, char *stop)
+{
+       list *l;
+       definition *def;
+
+       for (l = defined; l != NULL; l = l->next) {
+               def = (definition *) l->val;
+               if (def->def_kind != DEF_PROGRAM) {
+                       if (streq(def->def_name, stop)) {
+                               return (1);
+                       } else if (streq(def->def_name, type)) {
+                               return (0);
+                       }
+               }
+       }
+       return (1);
+}
diff --git a/tools/rpcgen/rpc_main.c b/tools/rpcgen/rpc_main.c
new file mode 100644 (file)
index 0000000..bd1a2c0
--- /dev/null
@@ -0,0 +1,1066 @@
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part.  Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user or with the express written consent of
+ * Sun Microsystems, Inc.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California  94043
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)rpc_main.c 1.30 89/03/30 (C) 1987 SMI";
+#endif
+
+/*
+ * rpc_main.c, Top level of the RPC protocol compiler. 
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ctype.h>
+#include "rpc_parse.h"
+#include "rpc_util.h"
+#include "rpc_scan.h"
+
+struct commandline {
+       int cflag;              /* xdr C routines */
+       int hflag;              /* header file */
+       int lflag;              /* client side stubs */
+       int mflag;              /* server side stubs */
+       int nflag;              /* netid flag */
+       int sflag;              /* server stubs for the given transport */
+       int tflag;              /* dispatch Table file */
+       int Ssflag;             /* produce server sample code */
+       int Scflag;             /* produce client sample code */
+       char *infile;           /* input module name */
+       char *outfile;          /* output module name */
+};
+
+static char *  extendfile(char *file, char *ext);
+static void    open_output(char *infile, char *outfile);
+static void    add_warning(void);
+static void    clear_args(void);
+static void    find_cpp(void);
+static void    open_input(char *infile, char *define);
+static int     check_nettype(char *name, char **list_to_check);
+static void    c_output(char *infile, char *define, int extend, char *outfile);
+static void    c_initialize(void);
+static char *  generate_guard(char *pathname);
+static void    h_output(char *infile, char *define, int extend, char *outfile);
+static void    s_output(int argc, char **argv, char *infile,
+                       char *define, int extend, char *outfile,
+                       int nomain, int netflag);
+static void    l_output(char *infile, char *define, int extend, char *outfile);
+static void    t_output(char *infile, char *define, int extend, char *outfile);
+static void    svc_output(char *, char *, int, char *);
+static void    clnt_output(char *, char *, int, char *);
+static int     do_registers(int argc, char **argv);
+static void    addarg(char *cp);
+static void    putarg(int where, char *cp);
+static void    checkfiles(char *infile, char *outfile);
+static int     parseargs(int argc, char **argv, struct commandline *cmd);
+static void    usage(void);
+static void    options_usage(void);
+
+/*
+extern void  write_sample_svc();
+int write_sample_clnt();
+void write_sample_clnt_main();
+
+static svc_output();
+ */
+
+#define EXTEND 1               /* alias for TRUE */
+#define DONT_EXTEND    0               /* alias for FALSE */
+
+#define SVR4_CPP "/usr/ccs/lib/cpp"
+#define SUNOS_CPP "/lib/cpp"
+static int cppDefined = 0;          /* explicit path for C preprocessor */
+
+
+static char *cmdname;
+
+static char *svcclosetime = "120";
+static char *CPP = SVR4_CPP;
+static char CPPFLAGS[] = "-C";
+static char pathbuf[MAXPATHLEN + 1];
+static char *allv[] = {
+       "rpcgen", "-s", "udp", "-s", "tcp",
+};
+static int allc = sizeof(allv)/sizeof(allv[0]);
+static char *allnv[] = {
+       "rpcgen", "-s", "netpath",
+};
+static int allnc = sizeof(allnv)/sizeof(allnv[0]);
+
+/*
+ * machinations for handling expanding argument list
+ */
+#if 0
+static void addarg();          /* add another argument to the list */
+static void putarg();          /* put argument at specified location  */
+static void clear_args();      /* clear argument list */
+static void checkfiles();      /* check if out file already exists */
+#endif
+
+
+
+#define ARGLISTLEN     20
+#define FIXEDARGS         2
+
+static char *arglist[ARGLISTLEN];
+static int argcount = FIXEDARGS;
+
+
+int nonfatalerrors;    /* errors */
+int inetdflag/* = 1*/; /* Support for inetd */ /* is now the default */
+int pmflag;            /* Support for port monitors */
+int logflag;           /* Use syslog instead of fprintf for errors */
+int tblflag;           /* Support for dispatch table file */
+
+/* length at which to start doing an inline */
+#define INLINE 3
+
+int Inline = INLINE;   /* length at which to start doing an inline. 3 = default
+                        * if 0, no xdr_inline code */
+
+int indefinitewait;    /* If started by port monitors, hang till it wants */
+int exitnow;           /* If started by port monitors, exit after the call */
+int timerflag;         /* TRUE if !indefinite && !exitnow */
+int newstyle;           /* newstyle of passing arguments (by value) */
+int Cflag = 0 ;         /* ANSI C syntax */
+static int allfiles;    /* generate all files */
+#ifdef linux
+int tirpcflag = 0;     /* no tirpc by default */
+#else
+int tirpcflag = 1;      /* generating code for tirpc, by default */
+#endif
+
+int
+main(int argc, char **argv)
+{
+       struct commandline cmd;
+
+       (void) memset((char *) &cmd, 0, sizeof(struct commandline));
+       clear_args();
+       if (!parseargs(argc, argv, &cmd))
+               usage();
+
+       if (cmd.cflag || cmd.hflag || cmd.lflag || cmd.tflag || cmd.sflag ||
+               cmd.mflag || cmd.nflag || cmd.Ssflag || cmd.Scflag) {
+               checkfiles(cmd.infile, cmd.outfile);
+       } else
+               checkfiles(cmd.infile, NULL);
+
+       if (cmd.cflag) {
+               c_output(cmd.infile, "-DRPC_XDR", DONT_EXTEND, cmd.outfile);
+       } else if (cmd.hflag) {
+               h_output(cmd.infile, "-DRPC_HDR", DONT_EXTEND, cmd.outfile);
+       } else if (cmd.lflag) {
+               l_output(cmd.infile, "-DRPC_CLNT", DONT_EXTEND, cmd.outfile);
+       } else if (cmd.sflag || cmd.mflag || (cmd.nflag)) {
+               s_output(argc, argv, cmd.infile, "-DRPC_SVC", DONT_EXTEND,
+                       cmd.outfile, cmd.mflag, cmd.nflag);
+       } else if (cmd.tflag) {
+               t_output(cmd.infile, "-DRPC_TBL", DONT_EXTEND, cmd.outfile);
+       } else if (cmd.Ssflag) {
+               svc_output(cmd.infile, "-DRPC_SERVER", DONT_EXTEND, cmd.outfile);
+       } else if (cmd.Scflag) {
+               clnt_output(cmd.infile, "-DRPC_CLIENT", DONT_EXTEND, cmd.outfile);
+       } else {
+               /* the rescans are required, since cpp may effect input */
+               c_output(cmd.infile, "-DRPC_XDR", EXTEND, "_xdr.c");
+               reinitialize();
+               h_output(cmd.infile, "-DRPC_HDR", EXTEND, ".h");
+               reinitialize();
+               l_output(cmd.infile, "-DRPC_CLNT", EXTEND, "_clnt.c");
+               reinitialize();
+               if (inetdflag || !tirpcflag)
+                       s_output(allc, allv, cmd.infile, "-DRPC_SVC", EXTEND,
+                               "_svc.c", cmd.mflag, cmd.nflag);
+               else
+                       s_output(allnc, allnv, cmd.infile, "-DRPC_SVC",
+                               EXTEND, "_svc.c", cmd.mflag, cmd.nflag);
+               if (tblflag) {
+                       reinitialize();
+                       t_output(cmd.infile, "-DRPC_TBL", EXTEND, "_tbl.i");
+               }
+               if (allfiles) {
+                       reinitialize();
+                       svc_output(cmd.infile, "-DRPC_SERVER", EXTEND, "_server.c");
+               }
+               if (allfiles) {
+                       reinitialize();
+                       clnt_output(cmd.infile, "-DRPC_CLIENT", EXTEND, "_client.c");
+               }
+       }
+       exit(nonfatalerrors);
+       /* NOTREACHED */
+}
+
+/*
+ * add extension to filename 
+ */
+static char *
+extendfile(char *file, char *ext)
+{
+       char *res;
+       char *p;
+
+       res = alloc(strlen(file) + strlen(ext) + 1);
+       if (res == NULL) {
+               abort();
+       }
+       p = strrchr(file, '.');
+       if (p == NULL) {
+               p = file + strlen(file);
+       }
+       (void) strcpy(res, file);
+       (void) strcpy(res + (p - file), ext);
+       return (res);
+}
+
+/*
+ * Open output file with given extension 
+ */
+static void
+open_output(char *infile, char *outfile)
+{
+
+       if (outfile == NULL) {
+               fout = stdout;
+               return;
+       }
+
+       if (infile != NULL && streq(outfile, infile)) {
+               f_print(stderr, "%s: output would overwrite %s\n", cmdname,
+                       infile);
+               crash();
+       }
+       fout = fopen(outfile, "w");
+       if (fout == NULL) {
+               f_print(stderr, "%s: unable to open ", cmdname);
+               perror(outfile);
+               crash();
+       }
+       record_open(outfile);
+
+}
+
+static void
+add_warning(void)
+{
+       f_print(fout, "/*\n");
+       f_print(fout, " * Please do not edit this file.\n");
+       f_print(fout, " * It was generated using rpcgen.\n");
+       f_print(fout, " */\n\n");
+}
+
+/* clear list of arguments */
+static void
+clear_args(void)
+{
+  int i;
+  for( i=FIXEDARGS; i<ARGLISTLEN; i++ )
+    arglist[i] = NULL;
+  argcount = FIXEDARGS;
+}
+
+/* make sure that a CPP exists */
+static void
+find_cpp(void)
+{
+       struct stat     buf;
+
+       if (stat(CPP, &buf) < 0) {      /* SVR4 or explicit cpp does not exist */
+               if (cppDefined) {
+                       fprintf(stderr, "cannot find C preprocessor: %s \n", CPP);
+                       crash();
+               } else {        /* try the other one */
+                       CPP = SUNOS_CPP;
+                       if (stat(CPP, &buf) < 0) {      /* can't find any cpp */
+                               fprintf(stderr, "cannot find any C preprocessor (cpp)\n");
+                               crash();
+                       }
+               }
+       }
+}
+
+/*
+ * Open input file with given define for C-preprocessor 
+ */
+static void
+open_input(char *infile, char *define)
+{
+       int pd[2];
+
+       infilename = (infile == NULL) ? "<stdin>" : infile;
+       (void) pipe(pd);
+       switch (fork()) {
+       case 0:
+               find_cpp();
+               putarg(0, CPP);
+               putarg(1, CPPFLAGS);
+               addarg(define);
+               addarg(infile);
+               addarg((char *)NULL);
+               (void) close(1);
+               (void) dup2(pd[1], 1);
+               (void) close(pd[0]);
+               execv(arglist[0], arglist);
+               perror("execv");
+               exit(1);
+       case -1:
+               perror("fork");
+               exit(1);
+       }
+       (void) close(pd[1]);
+       fin = fdopen(pd[0], "r");
+       if (fin == NULL) {
+               f_print(stderr, "%s: ", cmdname);
+               perror(infilename);
+               crash();
+       }
+}
+
+/* valid tirpc nettypes */
+static char*   valid_ti_nettypes[] =
+{
+       "netpath",
+       "visible",
+       "circuit_v",
+       "datagram_v",
+       "circuit_n",
+       "datagram_n",
+       "udp",
+       "tcp",
+       "raw",
+       NULL
+};
+
+/* valid inetd nettypes */
+static char* valid_i_nettypes[] =
+{
+       "udp",
+       "tcp",
+       NULL
+};
+
+static int
+check_nettype(char *name, char **list_to_check)
+{
+  int i;
+  for( i = 0; list_to_check[i] != NULL; i++ ) {
+         if( strcmp( name, list_to_check[i] ) == 0 ) {
+           return 1;
+         }
+  }
+  f_print( stderr, "illegal nettype :\'%s\'\n", name );
+  return 0;
+}
+
+/*
+ * Compile into an XDR routine output file
+ */
+
+static void
+c_output(char *infile, char *define, int extend, char *outfile)
+{
+       definition *def;
+       char *include;
+       char *outfilename;
+       long tell;
+
+       c_initialize();
+       open_input(infile, define);     
+       outfilename = extend ? extendfile(infile, outfile) : outfile;
+       open_output(infile, outfilename);
+       add_warning();
+       if (infile && (include = extendfile(infile, ".h"))) {
+               f_print(fout, "#include \"%s\"\n", include);
+               free(include);
+               /* .h file already contains rpc/rpc.h */
+       } else
+         f_print(fout, "#include <rpc/rpc.h>\n");
+       tell = ftell(fout);
+       while ((def = get_definition()) != NULL) {
+               emit(def);
+       }
+       if (extend && tell == ftell(fout)) {
+               (void) unlink(outfilename);
+       }
+}
+
+
+static void
+c_initialize(void)
+{
+
+  /* add all the starting basic types */
+
+  add_type(1,"int");
+  add_type(1,"long");
+  add_type(1,"short");
+  add_type(1,"bool");
+
+  add_type(1,"u_int");
+  add_type(1,"u_long");
+  add_type(1,"u_short");
+
+}
+
+char rpcgen_table_dcl[] = "struct rpcgen_table {\n\
+       char    *(*proc)();\n\
+       xdrproc_t       xdr_arg;\n\
+       unsigned        len_arg;\n\
+       xdrproc_t       xdr_res;\n\
+       unsigned        len_res;\n\
+};\n";
+
+
+static char *
+generate_guard(char *pathname)
+{
+        char* filename, *guard, *tmp;
+
+       filename = strrchr(pathname, '/' );  /* find last component */
+       filename = ((filename == 0) ? pathname : filename+1);
+       guard = strdup(filename);
+       /* convert to upper case */
+       tmp = guard;
+       while (*tmp) {
+               if (islower(*tmp))
+                       *tmp = toupper(*tmp);
+               tmp++;
+       }
+               
+       guard = extendfile(guard, "_H_RPCGEN");
+       return( guard );
+}
+
+/*
+ * Compile into an XDR header file
+ */
+static void
+h_output(char *infile, char *define, int extend, char *outfile)
+{
+       definition *def;
+       char *outfilename;
+       long tell;
+       char *guard;
+       list *l;
+
+       open_input(infile, define);
+       outfilename =  extend ? extendfile(infile, outfile) : outfile;
+       open_output(infile, outfilename);
+       add_warning();
+       guard = generate_guard(  outfilename ? outfilename: infile );
+
+       f_print(fout,"#ifndef _%s\n#define _%s\n\n", guard,
+               guard);
+
+       f_print(fout, "#include <rpc/rpc.h>\n\n");
+
+       tell = ftell(fout);
+       /* print data definitions */
+       while ((def = get_definition()) != NULL) {
+               print_datadef(def);
+       }
+
+       /* print function declarations.  
+          Do this after data definitions because they might be used as
+          arguments for functions */
+       for (l = defined; l != NULL; l = l->next) {
+               print_funcdef(l->val);
+       }
+       if (extend && tell == ftell(fout)) {
+               (void) unlink(outfilename);
+       } else if (tblflag) {
+               f_print(fout, rpcgen_table_dcl);
+       }
+       f_print(fout, "\n#endif /* !_%s */\n", guard);
+}
+
+/*
+ * Compile into an RPC service
+ */
+static void
+s_output(int argc, char **argv, char *infile, char *define, int extend,
+                       char *outfile, int nomain, int netflag)
+{
+       char *include;
+       definition *def;
+       int foundprogram = 0;
+       char *outfilename;
+
+       open_input(infile, define);
+       outfilename = extend ? extendfile(infile, outfile) : outfile;
+       open_output(infile, outfilename);
+       add_warning();
+       if (infile && (include = extendfile(infile, ".h"))) {
+               f_print(fout, "#include \"%s\"\n", include);
+               free(include);
+       } else
+         f_print(fout, "#include <rpc/rpc.h>\n");
+
+       f_print(fout, "#include <stdio.h>\n");
+       f_print(fout, "#include <stdlib.h>/* getenv, exit */\n"); 
+       if (Cflag) {
+               f_print (fout, "#include <rpc/pmap_clnt.h> /* for pmap_unset */\n");
+               f_print (fout, "#include <string.h> /* strcmp */ \n"); 
+       }
+       if (strcmp(svcclosetime, "-1") == 0)
+               indefinitewait = 1;
+       else if (strcmp(svcclosetime, "0") == 0)
+               exitnow = 1;
+       else if (inetdflag || pmflag) {
+               f_print(fout, "#include <signal.h>\n");
+         timerflag = 1;
+       }
+
+#ifndef linux
+       if( !tirpcflag && inetdflag )
+         f_print(fout, "#include <sys/ttycom.h>/* TIOCNOTTY */\n");
+#endif
+       if( Cflag && (inetdflag || pmflag ) ) {
+         f_print(fout, "#ifdef __cplusplus\n");
+         f_print(fout, "#include <sysent.h> /* getdtablesize, open */\n"); 
+         f_print(fout, "#endif /* __cplusplus */\n");
+         
+         if( tirpcflag )
+           f_print(fout, "#include <unistd.h> /* setsid */\n");
+       }
+       if( tirpcflag )
+         f_print(fout, "#include <sys/types.h>\n");
+
+       f_print(fout, "#include <memory.h>\n");
+#ifndef linux
+       f_print(fout, "#include <stropts.h>\n");
+#endif
+       if (inetdflag || !tirpcflag ) {
+               f_print(fout, "#include <sys/socket.h>\n");
+               f_print(fout, "#include <netinet/in.h>\n");
+       } 
+
+       if ( (netflag || pmflag) && tirpcflag ) {
+               f_print(fout, "#include <netconfig.h>\n");
+       }
+       if (/*timerflag &&*/ tirpcflag)
+               f_print(fout, "#include <sys/resource.h> /* rlimit */\n");
+       if (logflag || inetdflag || pmflag) {
+#ifdef linux
+               f_print(fout, "#include <syslog.h>\n");
+#else
+               f_print(fout, "#ifdef SYSLOG\n");
+               f_print(fout, "#include <syslog.h>\n");
+               f_print(fout, "#else\n");
+               f_print(fout, "#define LOG_ERR 1\n");
+               f_print(fout, "#define openlog(a, b, c)\n");
+               f_print(fout, "#endif\n");
+#endif
+       }
+
+       /* for ANSI-C */
+       f_print(fout, "\n#ifdef __STDC__\n#define SIG_PF void(*)(int)\n#endif\n");
+
+       f_print(fout, "\n#ifdef DEBUG\n#define RPC_SVC_FG\n#endif\n");
+       if (timerflag)
+               f_print(fout, "\n#define _RPCSVC_CLOSEDOWN %s\n", svcclosetime);
+       while ((def = get_definition()) != NULL) {
+               foundprogram |= (def->def_kind == DEF_PROGRAM);
+       }
+       if (extend && !foundprogram) {
+               (void) unlink(outfilename);
+               return;
+       }
+       write_most(infile, netflag, nomain);
+       if (!nomain) {
+               if( !do_registers(argc, argv) ) {
+                 if (outfilename)
+                   (void) unlink(outfilename);
+                 usage();
+               }
+               write_rest();
+       }
+}
+
+/*
+ * generate client side stubs
+ */
+static void
+l_output(char *infile, char *define, int extend, char *outfile)
+{
+       char *include;
+       definition *def;
+       int foundprogram = 0;
+       char *outfilename;
+
+       open_input(infile, define);
+       outfilename = extend ? extendfile(infile, outfile) : outfile;
+       open_output(infile, outfilename);
+       add_warning();
+       if (Cflag)
+         f_print (fout, "#include <memory.h> /* for memset */\n");
+       if (infile && (include = extendfile(infile, ".h"))) {
+               f_print(fout, "#include \"%s\"\n", include);
+               free(include);
+       } else
+         f_print(fout, "#include <rpc/rpc.h>\n");
+       while ((def = get_definition()) != NULL) {
+               foundprogram |= (def->def_kind == DEF_PROGRAM);
+       }
+       if (extend && !foundprogram) {
+               (void) unlink(outfilename);
+               return;
+       }
+       write_stubs();
+}
+
+/*
+ * generate the dispatch table
+ */
+static void
+t_output(char *infile, char *define, int extend, char *outfile)
+{
+       definition *def;
+       int foundprogram = 0;
+       char *outfilename;
+
+       open_input(infile, define);
+       outfilename = extend ? extendfile(infile, outfile) : outfile;
+       open_output(infile, outfilename);
+       add_warning();
+       while ((def = get_definition()) != NULL) {
+               foundprogram |= (def->def_kind == DEF_PROGRAM);
+       }
+       if (extend && !foundprogram) {
+               (void) unlink(outfilename);
+               return;
+       }
+       write_tables();
+}
+
+/* sample routine for the server template */
+static  void
+svc_output(char *infile, char *define, int extend, char *outfile)
+{
+  definition *def;
+  char *include;
+  char *outfilename;
+  long tell;
+  
+  open_input(infile, define);  
+  outfilename = extend ? extendfile(infile, outfile) : outfile;
+  checkfiles(infile,outfilename); /*check if outfile already exists.
+                                 if so, print an error message and exit*/
+  open_output(infile, outfilename);
+  add_sample_msg();
+
+  if (infile && (include = extendfile(infile, ".h"))) {
+    f_print(fout, "#include \"%s\"\n", include);
+    free(include);
+  } else
+    f_print(fout, "#include <rpc/rpc.h>\n");
+
+  tell = ftell(fout);
+  while ((def = get_definition()) != NULL) {
+         write_sample_svc(def);
+  }
+  if (extend && tell == ftell(fout)) {
+         (void) unlink(outfilename);
+  }
+}
+
+
+/* sample main routine for client */
+static  void
+clnt_output(char *infile, char *define, int extend, char *outfile)
+{
+       definition     *def;
+       char           *include;
+       char           *outfilename;
+       long            tell;
+       int             has_program = 0;
+
+       open_input(infile, define);
+       outfilename = extend ? extendfile(infile, outfile) : outfile;
+       checkfiles(infile, outfilename);        /*check if outfile already exists.
+                                 if so, print an error message and exit*/
+
+       open_output(infile, outfilename);
+       add_sample_msg();
+       if (infile && (include = extendfile(infile, ".h"))) {
+               f_print(fout, "#include \"%s\"\n", include);
+               free(include);
+       } else
+               f_print(fout, "#include <rpc/rpc.h>\n");
+       tell = ftell(fout);
+       while ((def = get_definition()) != NULL) {
+               has_program += write_sample_clnt(def);
+       }
+
+       if (has_program)
+               write_sample_clnt_main();
+
+       if (extend && tell == ftell(fout)) {
+               (void) unlink(outfilename);
+       }
+}
+
+/*
+ * Perform registrations for service output 
+ * Return 0 if failed; 1 otherwise.
+ */
+static int
+do_registers(int argc, char **argv)
+{
+       int             i;
+
+       if (inetdflag || !tirpcflag) {
+               for (i = 1; i < argc; i++) {
+                       if (streq(argv[i], "-s")) {
+                               if (!check_nettype(argv[i + 1], valid_i_nettypes))
+                                       return 0;
+                               write_inetd_register(argv[i + 1]);
+                               i++;
+                       }
+               }
+       } else {
+               for (i = 1; i < argc; i++)
+                       if (streq(argv[i], "-s")) {
+                               if (!check_nettype(argv[i + 1], valid_ti_nettypes))
+                                       return 0;
+                               write_nettype_register(argv[i + 1]);
+                               i++;
+                       } else if (streq(argv[i], "-n")) {
+                               write_netid_register(argv[i + 1]);
+                               i++;
+                       }
+       }
+       return 1;
+}
+
+/*
+ * Add another argument to the arg list
+ */
+static void
+addarg(char *cp)
+{
+       if (argcount >= ARGLISTLEN) {
+               f_print(stderr, "rpcgen: too many defines\n");
+               crash();
+               /*NOTREACHED*/
+       }
+       arglist[argcount++] = cp;
+
+}
+
+static void
+putarg(int where, char *cp)
+{
+       if (where >= ARGLISTLEN) {
+               f_print(stderr, "rpcgen: arglist coding error\n");
+               crash();
+               /*NOTREACHED*/
+       }
+       arglist[where] = cp;
+       
+}
+
+/*
+ * if input file is stdin and an output file is specified then complain
+ * if the file already exists. Otherwise the file may get overwritten
+ * If input file does not exist, exit with an error 
+ */
+
+static void
+checkfiles(char *infile, char *outfile) 
+{
+
+  struct stat buf;
+
+  if(infile)                   /* infile ! = NULL */
+    if(stat(infile,&buf) < 0)
+      {
+       perror(infile);
+       crash();
+      };
+  if (outfile) {
+    if (stat(outfile, &buf) < 0) 
+      return;                  /* file does not exist */
+    else {
+      f_print(stderr, 
+             "file '%s' already exists and may be overwritten\n", outfile);
+      crash();
+    }
+  }
+}
+
+/*
+ * Parse command line arguments 
+ */
+static int
+parseargs(int argc, char **argv, struct commandline *cmd)
+{
+       int i;
+       int j;
+       char c;
+       char flag[(1 << 8 * sizeof(char))];
+       int nflags;
+
+       cmdname = argv[0];
+       cmd->infile = cmd->outfile = NULL;
+       if (argc < 2) {
+               return (0);
+       }
+       allfiles = 0;
+       flag['c'] = 0;
+       flag['h'] = 0;
+       flag['l'] = 0;
+       flag['m'] = 0;
+       flag['o'] = 0;
+       flag['s'] = 0;
+       flag['n'] = 0;
+       flag['t'] = 0;
+       flag['S'] = 0;
+       flag['C'] = 0;
+       for (i = 1; i < argc; i++) {
+               if (argv[i][0] != '-') {
+                       if (cmd->infile) {
+                               f_print( stderr, "Cannot specify more than one input file!\n");
+
+                               return (0);
+                       }
+                       cmd->infile = argv[i];
+               } else {
+                       for (j = 1; argv[i][j] != 0; j++) {
+                               c = argv[i][j];
+                               switch (c) {
+                               case 'a':
+                                       allfiles = 1;
+                                       break;
+                               case 'c':
+                               case 'h':
+                               case 'l':
+                               case 'm':
+                               case 't':
+                                       if (flag[(int) c]) {
+                                               return (0);
+                                       }
+                                       flag[(int) c] = 1;
+                                       break;
+                               case 'S':  
+                                       /* sample flag: Ss or Sc.
+                                          Ss means set flag['S'];
+                                          Sc means set flag['C']; */
+                                       c = argv[i][++j];  /* get next char */
+                                       if( c == 's' )
+                                         c = 'S';
+                                       else if( c == 'c' )
+                                         c = 'C';
+                                       else
+                                         return( 0 );
+
+                                       if (flag[(int) c]) {
+                                               return (0);
+                                       }
+                                       flag[(int) c] = 1;
+                                       break;
+                               case 'C':  /* ANSI C syntax */
+                                       Cflag = 1;
+                                       break;
+
+                               case 'b':  /* turn TIRPC flag off for
+                                           generating backward compatible
+                                           */
+                                       tirpcflag = 0;
+                                       break;
+
+                               case 'I':
+                                       inetdflag = 1;
+                                       break;
+                               case 'N':
+                                       newstyle = 1;
+                                       break;
+                               case 'L':
+                                       logflag = 1;
+                                       break;
+                               case 'K':
+                                       if (++i == argc) {
+                                               return (0);
+                                       }
+                                       svcclosetime = argv[i];
+                                       goto nextarg;
+                               case 'T':
+                                       tblflag = 1;
+                                       break;
+                               case 'i' :
+                                       if (++i == argc) {
+                                               return (0);
+                                       }
+                                       Inline = atoi(argv[i]);
+                                       goto nextarg;
+                               case 'n':
+                               case 'o':
+                               case 's':
+                                       if (argv[i][j - 1] != '-' || 
+                                           argv[i][j + 1] != 0) {
+                                               return (0);
+                                       }
+                                       flag[(int) c] = 1;
+                                       if (++i == argc) {
+                                               return (0);
+                                       }
+                                       if (c == 's') {
+                                               if (!streq(argv[i], "udp") &&
+                                                   !streq(argv[i], "tcp")) {
+                                                       return (0);
+                                               }
+                                       } else if (c == 'o') {
+                                               if (cmd->outfile) {
+                                                       return (0);
+                                               }
+                                               cmd->outfile = argv[i];
+                                       }
+                                       goto nextarg;
+                               case 'D':
+                                       if (argv[i][j - 1] != '-') {
+                                               return (0);
+                                       }
+                                       (void) addarg(argv[i]);
+                                       goto nextarg;
+                               case 'Y':
+                                       if (++i == argc) {
+                                               return (0);
+                                       }
+                                       (void) strcpy(pathbuf, argv[i]);
+                                       (void) strcat(pathbuf, "/cpp");
+                                       CPP = pathbuf;
+                                       cppDefined = 1;
+                                       goto nextarg;
+
+
+
+                               default:
+                                       return (0);
+                               }
+                       }
+       nextarg:
+                       ;
+               }
+       }
+
+       cmd->cflag = flag['c'];
+       cmd->hflag = flag['h'];
+       cmd->lflag = flag['l'];
+       cmd->mflag = flag['m'];
+       cmd->nflag = flag['n'];
+       cmd->sflag = flag['s'];
+       cmd->tflag = flag['t'];
+       cmd->Ssflag = flag['S'];
+       cmd->Scflag = flag['C'];
+
+       if( tirpcflag ) {
+         pmflag = inetdflag ? 0 : 1;     /* pmflag or inetdflag is always TRUE */
+         if( (inetdflag && cmd->nflag)) { /* netid not allowed with inetdflag */
+           f_print(stderr, "Cannot use netid flag with inetd flag!\n");
+           return (0);
+         }
+       } else {  /* 4.1 mode */
+         pmflag = 0;               /* set pmflag only in tirpcmode */
+         inetdflag = 1;            /* inetdflag is TRUE by default */
+         if( cmd->nflag ) {          /* netid needs TIRPC */
+           f_print( stderr, "Cannot use netid flag without TIRPC!\n");
+           return( 0 );
+         }
+       }
+
+       if( newstyle && ( tblflag || cmd->tflag) ) {
+         f_print( stderr, "Cannot use table flags with newstyle!\n");
+         return( 0 );
+       }
+
+       /* check no conflicts with file generation flags */
+       nflags = cmd->cflag + cmd->hflag + cmd->lflag + cmd->mflag +
+               cmd->sflag + cmd->nflag + cmd->tflag + cmd->Ssflag + cmd->Scflag;
+
+       if (nflags == 0) {
+               if (cmd->outfile != NULL || cmd->infile == NULL) {
+                       return (0);
+               }
+       } else if (nflags > 1) {
+               f_print( stderr, "Cannot have more than one file generation flag!\n");
+               return (0);
+       }
+       return (1);
+}
+
+static void
+usage(void)
+{
+       f_print(stderr, "usage:  %s infile\n", cmdname);
+       f_print(stderr, "\t%s [-a][-b][-C][-Dname[=value]] -i size  [-I [-K seconds]] [-L][-N][-T] infile\n",
+                       cmdname);
+       f_print(stderr, "\t%s [-c | -h | -l | -m | -t | -Sc | -Ss] [-o outfile] [infile]\n",
+                       cmdname);
+       f_print(stderr, "\t%s [-s nettype]* [-o outfile] [infile]\n", cmdname);
+       f_print(stderr, "\t%s [-n netid]* [-o outfile] [infile]\n", cmdname);
+       options_usage();
+       exit(1);
+}
+
+static void
+options_usage(void)
+{
+       f_print(stderr, "options:\n");
+       f_print(stderr, "-a\t\tgenerate all files, including samples\n");
+       f_print(stderr, "-b\t\tbackward compatibility mode (generates code for SunOS 4.1)\n");
+       f_print(stderr, "-c\t\tgenerate XDR routines\n");
+       f_print(stderr, "-C\t\tANSI C mode\n");
+       f_print(stderr, "-Dname[=value]\tdefine a symbol (same as #define)\n");
+       f_print(stderr, "-h\t\tgenerate header file\n");
+       f_print(stderr, "-i size\t\tsize at which to start generating inline code\n");
+       f_print(stderr, "-I\t\tgenerate code for inetd support in server (for SunOS 4.1)\n");
+       f_print(stderr, "-K seconds\tserver exits after K seconds of inactivity\n");
+       f_print(stderr, "-l\t\tgenerate client side stubs\n");
+       f_print(stderr, "-L\t\tserver errors will be printed to syslog\n");
+       f_print(stderr, "-m\t\tgenerate server side stubs\n");
+       f_print(stderr, "-n netid\tgenerate server code that supports named netid\n");
+       f_print(stderr, "-N\t\tsupports multiple arguments and call-by-value\n");
+       f_print(stderr, "-o outfile\tname of the output file\n");
+       f_print(stderr, "-s nettype\tgenerate server code that supports named nettype\n");
+       f_print(stderr, "-Sc\t\tgenerate sample client code that uses remote procedures\n");
+       f_print(stderr, "-Ss\t\tgenerate sample server code that defines remote procedures\n");
+       f_print(stderr, "-t\t\tgenerate RPC dispatch table\n");
+       f_print(stderr, "-T\t\tgenerate code to support RPC dispatch tables\n");
+       f_print(stderr, "-Y path\t\tdirectory name to find C preprocessor (cpp)\n");
+
+       exit(1);
+}
diff --git a/tools/rpcgen/rpc_output.h b/tools/rpcgen/rpc_output.h
new file mode 100644 (file)
index 0000000..eb25a60
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * rpc_output.h
+ *
+ * Declarations for output functions
+ *
+ */
+
+#ifndef RPCGEN_NEW_OUTPUT_H
+#define RPCGEN_NEW_OUTPUT_H
+
+void   write_msg_out(void);
+int    nullproc(proc_list *);
+void   printarglist(proc_list *, char *, char *);
+void   pdeclaration(char *, declaration *, int, char *);
+
+#endif /* RPCGEN_NEW_OUTPUT_H */
diff --git a/tools/rpcgen/rpc_parse.c b/tools/rpcgen/rpc_parse.c
new file mode 100644 (file)
index 0000000..1e5b80a
--- /dev/null
@@ -0,0 +1,620 @@
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part.  Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user or with the express written consent of
+ * Sun Microsystems, Inc.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California  94043
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)rpc_parse.c 1.8 89/02/22 (C) 1987 SMI";
+#endif
+
+/*
+ * rpc_parse.c, Parser for the RPC protocol compiler 
+ * Copyright (C) 1987 Sun Microsystems, Inc.
+ */
+#include <stdio.h>
+#include <string.h>
+#include "rpc/types.h"
+#include "rpc_scan.h"
+#include "rpc_parse.h"
+#include "rpc_util.h"
+
+#define ARGNAME "arg"
+
+/*
+extern char *make_argname();
+extern char *strdup();
+ */
+
+static void    isdefined(definition *defp);
+static void    def_struct(definition *defp);
+static void    def_program(definition *defp);
+static void    def_enum(definition *defp);
+static void    def_const(definition *defp);
+static void    def_union(definition *defp);
+static void    check_type_name(char *name, int new_type);
+static void    def_typedef(definition *defp);
+static void    get_declaration(declaration *dec, defkind dkind);
+static void    get_prog_declaration(declaration *dec, defkind dkind, int num);
+static void    get_type(char **prefixp, char **typep, defkind dkind);
+static void    unsigned_dec(char **typep);
+
+/*
+ * return the next definition you see
+ */
+definition *
+get_definition(void)
+{
+       definition *defp;
+       token tok;
+
+       defp = ALLOC(definition);
+       get_token(&tok);
+       switch (tok.kind) {
+       case TOK_STRUCT:
+               def_struct(defp);
+               break;
+       case TOK_UNION:
+               def_union(defp);
+               break;
+       case TOK_TYPEDEF:
+               def_typedef(defp);
+               break;
+       case TOK_ENUM:
+               def_enum(defp);
+               break;
+       case TOK_PROGRAM:
+               def_program(defp);
+               break;
+       case TOK_CONST:
+               def_const(defp);
+               break;
+       case TOK_EOF:
+               return (NULL);
+       default:
+               error("definition keyword expected");
+       }
+       scan(TOK_SEMICOLON, &tok);
+       isdefined(defp);
+       return (defp);
+}
+
+static void
+isdefined(definition *defp)
+{
+       STOREVAL(&defined, defp);
+}
+
+static void
+def_struct(definition *defp)
+{
+       token tok;
+       declaration dec;
+       decl_list *decls;
+       decl_list **tailp;
+
+       defp->def_kind = DEF_STRUCT;
+
+       scan(TOK_IDENT, &tok);
+       defp->def_name = tok.str;
+       scan(TOK_LBRACE, &tok);
+       tailp = &defp->def.st.decls;
+       do {
+               get_declaration(&dec, DEF_STRUCT);
+               decls = ALLOC(decl_list);
+               decls->decl = dec;
+               *tailp = decls;
+               tailp = &decls->next;
+               scan(TOK_SEMICOLON, &tok);
+               peek(&tok);
+       } while (tok.kind != TOK_RBRACE);
+       get_token(&tok);
+       *tailp = NULL;
+}
+
+static void
+def_program(definition *defp)
+{
+       token tok;
+       declaration dec;
+       decl_list *decls;
+       decl_list **tailp;
+       version_list *vlist;
+       version_list **vtailp;
+       proc_list *plist;
+       proc_list **ptailp;
+       int num_args;
+       bool_t isvoid = FALSE; /* whether first argument is void */
+       defp->def_kind = DEF_PROGRAM;
+       scan(TOK_IDENT, &tok);
+       defp->def_name = tok.str;
+       scan(TOK_LBRACE, &tok);
+       vtailp = &defp->def.pr.versions;
+       tailp = &defp->def.st.decls;
+       scan(TOK_VERSION, &tok);
+       do {
+               scan(TOK_IDENT, &tok);
+               vlist = ALLOC(version_list);
+               vlist->vers_name = tok.str;
+               scan(TOK_LBRACE, &tok);
+               ptailp = &vlist->procs;
+               do {
+                       /* get result type */
+                       plist = ALLOC(proc_list);
+                       get_type(&plist->res_prefix, &plist->res_type, 
+                                DEF_PROGRAM);
+                       if (streq(plist->res_type, "opaque")) {
+                               error("illegal result type");
+                       }
+                       scan(TOK_IDENT, &tok);
+                       plist->proc_name = tok.str;
+                       scan(TOK_LPAREN, &tok);
+                       /* get args - first one*/
+                       num_args = 1;
+                       isvoid = FALSE;
+                       /* type of DEF_PROGRAM in the first 
+                        * get_prog_declaration and DEF_STURCT in the next
+                        * allows void as argument if it is the only argument
+                        */
+                       get_prog_declaration(&dec, DEF_PROGRAM, num_args);
+                       if (streq(dec.type, "void"))
+                         isvoid = TRUE;
+                       decls = ALLOC(decl_list);
+                       plist->args.decls = decls;
+                       decls->decl = dec;
+                       tailp = &decls->next;
+                       /* get args */
+                       while(peekscan(TOK_COMMA, &tok)) {
+                         num_args++;
+                         get_prog_declaration(&dec, DEF_STRUCT, 
+                                              num_args);
+                         decls = ALLOC(decl_list);
+                         decls->decl = dec;
+                         *tailp = decls;
+                         if (streq(dec.type, "void"))
+                           isvoid = TRUE;
+                         tailp = &decls->next;
+                       }
+                       /* multiple arguments are only allowed in newstyle */
+                       if( !newstyle && num_args > 1 ) {
+                         error("only one argument is allowed" );
+                       }
+                       if (isvoid && num_args > 1) { 
+                         error("illegal use of void in program definition");
+                       }
+                       *tailp = NULL;
+                       scan(TOK_RPAREN, &tok);
+                       scan(TOK_EQUAL, &tok);
+                       scan_num(&tok);
+                       scan(TOK_SEMICOLON, &tok);
+                       plist->proc_num = tok.str;
+                       plist->arg_num = num_args;
+                       *ptailp = plist;
+                       ptailp = &plist->next;
+                       peek(&tok);
+               } while (tok.kind != TOK_RBRACE);
+               *ptailp = NULL;
+               *vtailp = vlist;
+               vtailp = &vlist->next;
+               scan(TOK_RBRACE, &tok);
+               scan(TOK_EQUAL, &tok);
+               scan_num(&tok);
+               vlist->vers_num = tok.str;
+               /* make the argument structure name for each arg*/
+               for(plist = vlist->procs; plist != NULL; 
+                   plist = plist->next) {
+                       plist->args.argname = make_argname(plist->proc_name,
+                                                          vlist->vers_num); 
+                       /* free the memory ??*/
+               }
+               scan(TOK_SEMICOLON, &tok);
+               scan2(TOK_VERSION, TOK_RBRACE, &tok);
+       } while (tok.kind == TOK_VERSION);
+       scan(TOK_EQUAL, &tok);
+       scan_num(&tok);
+       defp->def.pr.prog_num = tok.str;
+       *vtailp = NULL;
+}
+
+
+static void
+def_enum(definition *defp)
+{
+       token tok;
+       enumval_list *elist;
+       enumval_list **tailp;
+
+       defp->def_kind = DEF_ENUM;
+       scan(TOK_IDENT, &tok);
+       defp->def_name = tok.str;
+       scan(TOK_LBRACE, &tok);
+       tailp = &defp->def.en.vals;
+       do {
+               scan(TOK_IDENT, &tok);
+               elist = ALLOC(enumval_list);
+               elist->name = tok.str;
+               elist->assignment = NULL;
+               scan3(TOK_COMMA, TOK_RBRACE, TOK_EQUAL, &tok);
+               if (tok.kind == TOK_EQUAL) {
+                       scan_num(&tok);
+                       elist->assignment = tok.str;
+                       scan2(TOK_COMMA, TOK_RBRACE, &tok);
+               }
+               *tailp = elist;
+               tailp = &elist->next;
+       } while (tok.kind != TOK_RBRACE);
+       *tailp = NULL;
+}
+
+static void
+def_const(definition *defp)
+{
+       token tok;
+
+       defp->def_kind = DEF_CONST;
+       scan(TOK_IDENT, &tok);
+       defp->def_name = tok.str;
+       scan(TOK_EQUAL, &tok);
+       scan2(TOK_IDENT, TOK_STRCONST, &tok);
+       defp->def.co = tok.str;
+}
+
+static void
+def_union(definition *defp)
+{
+  token tok;
+  declaration dec;
+  case_list *cases,*tcase;
+  case_list **tailp;
+  int flag;
+
+  defp->def_kind = DEF_UNION;
+  scan(TOK_IDENT, &tok);
+  defp->def_name = tok.str;
+  scan(TOK_SWITCH, &tok);
+  scan(TOK_LPAREN, &tok);
+  get_declaration(&dec, DEF_UNION);
+  defp->def.un.enum_decl = dec;
+  tailp = &defp->def.un.cases;
+  scan(TOK_RPAREN, &tok);
+  scan(TOK_LBRACE, &tok);
+  scan(TOK_CASE, &tok);
+  while (tok.kind == TOK_CASE) {
+    scan2(TOK_IDENT, TOK_CHARCONST, &tok);
+    cases = ALLOC(case_list);
+    cases->case_name = tok.str;
+    scan(TOK_COLON, &tok);
+    /* now peek at next token */
+    flag=0;
+    if(peekscan(TOK_CASE,&tok))
+      {
+
+       do 
+         {
+           scan2(TOK_IDENT, TOK_CHARCONST, &tok);
+           cases->contflag=1;  /* continued case statement */
+           *tailp = cases;
+           tailp = &cases->next;
+           cases = ALLOC(case_list);
+           cases->case_name = tok.str;
+           scan(TOK_COLON, &tok);
+      
+         }while(peekscan(TOK_CASE,&tok));
+      }
+    else
+      if(flag)
+       {
+
+         *tailp = cases;
+         tailp = &cases->next;
+         cases = ALLOC(case_list);
+       };
+
+    get_declaration(&dec, DEF_UNION);
+    cases->case_decl = dec;
+    cases->contflag=0;         /* no continued case statement */
+    *tailp = cases;
+    tailp = &cases->next;
+    scan(TOK_SEMICOLON, &tok);
+
+    scan3(TOK_CASE, TOK_DEFAULT, TOK_RBRACE, &tok);
+  }
+  *tailp = NULL;
+  if (tok.kind == TOK_DEFAULT) {
+    scan(TOK_COLON, &tok);
+    get_declaration(&dec, DEF_UNION);
+    defp->def.un.default_decl = ALLOC(declaration);
+    *defp->def.un.default_decl = dec;
+    scan(TOK_SEMICOLON, &tok);
+    scan(TOK_RBRACE, &tok);
+  } else {
+    defp->def.un.default_decl = NULL;
+  }
+}
+
+static char* reserved_words[] =
+{
+  "array",
+  "bytes",
+  "destroy",
+  "free",
+  "getpos",
+  "inline",
+  "pointer",
+  "reference",
+  "setpos",
+  "sizeof",
+  "union",
+  "vector",
+  NULL
+  };
+
+static char* reserved_types[] =
+{
+  "opaque",
+  "string",
+  NULL
+  };
+
+/* check that the given name is not one that would eventually result in
+   xdr routines that would conflict with internal XDR routines. */
+static void
+check_type_name(char *name, int new_type)
+{
+  int i;
+  char tmp[100];
+
+  for( i = 0; reserved_words[i] != NULL; i++ ) {
+    if( strcmp( name, reserved_words[i] ) == 0 ) {
+      sprintf(tmp, 
+             "illegal (reserved) name :\'%s\' in type definition", name );
+      error(tmp);
+    }
+  }
+  if( new_type ) {
+    for( i = 0; reserved_types[i] != NULL; i++ ) {
+      if( strcmp( name, reserved_types[i] ) == 0 ) {
+       sprintf(tmp, 
+               "illegal (reserved) name :\'%s\' in type definition", name );
+       error(tmp);
+      }
+    }
+  }
+}
+
+static void
+def_typedef(definition *defp)
+{
+       declaration dec;
+
+       defp->def_kind = DEF_TYPEDEF;
+       get_declaration(&dec, DEF_TYPEDEF);
+       defp->def_name = dec.name;
+       check_type_name( dec.name, 1 );
+       defp->def.ty.old_prefix = dec.prefix;
+       defp->def.ty.old_type = dec.type;
+       defp->def.ty.rel = dec.rel;
+       defp->def.ty.array_max = dec.array_max;
+}
+
+static void
+get_declaration(declaration *dec, defkind dkind)
+{
+       token tok;
+
+       get_type(&dec->prefix, &dec->type, dkind);
+       dec->rel = REL_ALIAS;
+       if (streq(dec->type, "void")) {
+               return;
+       }
+
+       check_type_name( dec->type, 0 );
+
+       scan2(TOK_STAR, TOK_IDENT, &tok);
+       if (tok.kind == TOK_STAR) {
+               dec->rel = REL_POINTER;
+               scan(TOK_IDENT, &tok);
+       }
+       dec->name = tok.str;
+       if (peekscan(TOK_LBRACKET, &tok)) {
+               if (dec->rel == REL_POINTER) {
+                       error("no array-of-pointer declarations -- use typedef");
+               }
+               dec->rel = REL_VECTOR;
+               scan_num(&tok);
+               dec->array_max = tok.str;
+               scan(TOK_RBRACKET, &tok);
+       } else if (peekscan(TOK_LANGLE, &tok)) {
+               if (dec->rel == REL_POINTER) {
+                       error("no array-of-pointer declarations -- use typedef");
+               }
+               dec->rel = REL_ARRAY;
+               if (peekscan(TOK_RANGLE, &tok)) {
+                       dec->array_max = "~0";  /* unspecified size, use max */
+               } else {
+                       scan_num(&tok);
+                       dec->array_max = tok.str;
+                       scan(TOK_RANGLE, &tok);
+               }
+       }
+       if (streq(dec->type, "opaque")) {
+               if (dec->rel != REL_ARRAY && dec->rel != REL_VECTOR) {
+                       error("array declaration expected");
+               }
+       } else if (streq(dec->type, "string")) {
+               if (dec->rel != REL_ARRAY) {
+                       error("variable-length array declaration expected");
+               }
+       }
+}
+
+
+static void
+get_prog_declaration(declaration *dec, defkind dkind, int num)
+{
+       token tok;
+       char name[10]; /* argument name */
+
+       if (dkind == DEF_PROGRAM) { 
+         peek(&tok);
+         if (tok.kind == TOK_RPAREN) { /* no arguments */
+               dec->rel = REL_ALIAS;
+               dec->type = "void";
+               dec->prefix = NULL;
+               dec->name = NULL;
+               return;
+             }
+       }
+       get_type(&dec->prefix, &dec->type, dkind);
+       dec->rel = REL_ALIAS;
+       if (peekscan(TOK_IDENT, &tok))  /* optional name of argument */
+               strcpy(name, tok.str);
+       else 
+               sprintf(name, "%s%d", ARGNAME, num); /* default name of argument */
+
+       dec->name = (char *) strdup(name); 
+       
+       if (streq(dec->type, "void")) {
+               return;
+       }
+
+       if (streq(dec->type, "opaque")) {
+               error("opaque -- illegal argument type");
+       }
+       if (peekscan(TOK_STAR, &tok)) { 
+         if (streq(dec->type, "string")) {
+           error("pointer to string not allowed in program arguments\n");
+         }
+               dec->rel = REL_POINTER;
+               if (peekscan(TOK_IDENT, &tok))  /* optional name of argument */
+                 dec->name = strdup(tok.str);
+      }
+         if (peekscan(TOK_LANGLE, &tok)) {
+           if (!streq(dec->type, "string")) {
+             error("arrays cannot be declared as arguments to procedures -- use typedef");
+           }
+               dec->rel = REL_ARRAY;
+               if (peekscan(TOK_RANGLE, &tok)) {
+                       dec->array_max = "~0";/* unspecified size, use max */
+               } else {
+                       scan_num(&tok);
+                       dec->array_max = tok.str;
+                       scan(TOK_RANGLE, &tok);
+               }
+       }
+       if (streq(dec->type, "string")) {
+               if (dec->rel != REL_ARRAY) {  /* .x specifies just string as
+                                              * type of argument 
+                                              * - make it string<>
+                                              */
+                       dec->rel = REL_ARRAY;
+                       dec->array_max = "~0";/* unspecified size, use max */
+               }
+       }
+}
+
+
+
+static void
+get_type(char **prefixp, char **typep, defkind dkind)
+{
+       token tok;
+
+       *prefixp = NULL;
+       get_token(&tok);
+       switch (tok.kind) {
+       case TOK_IDENT:
+               *typep = tok.str;
+               break;
+       case TOK_STRUCT:
+       case TOK_ENUM:
+       case TOK_UNION:
+               *prefixp = tok.str;
+               scan(TOK_IDENT, &tok);
+               *typep = tok.str;
+               break;
+       case TOK_UNSIGNED:
+               unsigned_dec(typep);
+               break;
+       case TOK_SHORT:
+               *typep = "short";
+               (void) peekscan(TOK_INT, &tok);
+               break;
+       case TOK_LONG:
+               *typep = "long";
+               (void) peekscan(TOK_INT, &tok);
+               break;
+       case TOK_VOID:
+               if (dkind != DEF_UNION && dkind != DEF_PROGRAM) {
+                       error("voids allowed only inside union and program definitions with one argument");
+               }
+               *typep = tok.str;
+               break;
+       case TOK_STRING:
+       case TOK_OPAQUE:
+       case TOK_CHAR:
+       case TOK_INT:
+       case TOK_FLOAT:
+       case TOK_DOUBLE:
+       case TOK_BOOL:
+               *typep = tok.str;
+               break;
+       default:
+               error("expected type specifier");
+       }
+}
+
+static void
+unsigned_dec(char **typep)
+{
+       token tok;
+
+       peek(&tok);
+       switch (tok.kind) {
+       case TOK_CHAR:
+               get_token(&tok);
+               *typep = "u_char";
+               break;
+       case TOK_SHORT:
+               get_token(&tok);
+               *typep = "u_short";
+               (void) peekscan(TOK_INT, &tok);
+               break;
+       case TOK_LONG:
+               get_token(&tok);
+               *typep = "u_long";
+               (void) peekscan(TOK_INT, &tok);
+               break;
+       case TOK_INT:
+               get_token(&tok);
+               *typep = "u_int";
+               break;
+       default:
+               *typep = "u_int";
+               break;
+       }
+}
diff --git a/tools/rpcgen/rpc_parse.h b/tools/rpcgen/rpc_parse.h
new file mode 100644 (file)
index 0000000..be72495
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part.  Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user or with the express written consent of
+ * Sun Microsystems, Inc.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California  94043
+ */
+
+/*      @(#)rpc_parse.h  1.3  90/08/29  (C) 1987 SMI   */
+
+/*
+ * rpc_parse.h, Definitions for the RPCL parser 
+ */
+
+enum defkind {
+       DEF_CONST,
+       DEF_STRUCT,
+       DEF_UNION,
+       DEF_ENUM,
+       DEF_TYPEDEF,
+       DEF_PROGRAM
+};
+typedef enum defkind defkind;
+
+typedef char *const_def;
+
+enum relation {
+       REL_VECTOR,     /* fixed length array */
+       REL_ARRAY,      /* variable length array */
+       REL_POINTER,    /* pointer */
+       REL_ALIAS,      /* simple */
+};
+typedef enum relation relation;
+
+struct typedef_def {
+       char *old_prefix;
+       char *old_type;
+       relation rel;
+       char *array_max;
+};
+typedef struct typedef_def typedef_def;
+
+struct enumval_list {
+       char *name;
+       char *assignment;
+       struct enumval_list *next;
+};
+typedef struct enumval_list enumval_list;
+
+struct enum_def {
+       enumval_list *vals;
+};
+typedef struct enum_def enum_def;
+
+struct declaration {
+       char *prefix;
+       char *type;
+       char *name;
+       relation rel;
+       char *array_max;
+};
+typedef struct declaration declaration;
+
+struct decl_list {
+       declaration decl;
+       struct decl_list *next;
+};
+typedef struct decl_list decl_list;
+
+struct struct_def {
+       decl_list *decls;
+};
+typedef struct struct_def struct_def;
+
+struct case_list {
+       char *case_name;
+       int contflag;
+       declaration case_decl;
+       struct case_list *next;
+};
+typedef struct case_list case_list;
+
+struct union_def {
+       declaration enum_decl;
+       case_list *cases;
+       declaration *default_decl;
+};
+typedef struct union_def union_def;
+
+struct arg_list {
+       char *argname; /* name of struct for arg*/
+       decl_list *decls;
+};
+       
+typedef struct arg_list arg_list;
+
+struct proc_list {
+       char *proc_name;
+       char *proc_num;
+       arg_list args;
+       int arg_num;
+       char *res_type;
+       char *res_prefix;
+       struct proc_list *next;
+};
+typedef struct proc_list proc_list;
+
+struct version_list {
+       char *vers_name;
+       char *vers_num;
+       proc_list *procs;
+       struct version_list *next;
+};
+typedef struct version_list version_list;
+
+struct program_def {
+       char *prog_num;
+       version_list *versions;
+};
+typedef struct program_def program_def;
+
+struct definition {
+       char *def_name;
+       defkind def_kind;
+       union {
+               const_def co;
+               struct_def st;
+               union_def un;
+               enum_def en;
+               typedef_def ty;
+               program_def pr;
+       } def;
+};
+typedef struct definition definition;
+
+definition *get_definition();
+
+
+struct bas_type
+{
+  char *name;
+  int length;
+  struct bas_type *next;
+};
+
+typedef struct bas_type bas_type;
diff --git a/tools/rpcgen/rpc_sample.c b/tools/rpcgen/rpc_sample.c
new file mode 100644 (file)
index 0000000..97b2cd0
--- /dev/null
@@ -0,0 +1,249 @@
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part.  Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user or with the express written consent of
+ * Sun Microsystems, Inc.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California  94043
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)rpc_sample.c  1.1  90/08/30  (C) 1987 SMI";
+
+#endif
+
+/*
+ * rpc_sample.c, Sample client-server code outputter for the RPC protocol compiler
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "rpc_parse.h"
+#include "rpc_util.h"
+
+
+static char RQSTP[] = "rqstp";
+
+static void    write_sample_client(char *program_name, version_list *vp);
+static void    write_sample_server(definition * def);
+static void    return_type(proc_list *plist);
+
+void
+write_sample_svc(definition *def)
+{
+       if (def->def_kind != DEF_PROGRAM)
+               return;
+       write_sample_server(def);
+}
+
+
+int
+write_sample_clnt(definition *def)
+{
+       version_list   *vp;
+       int             count = 0;
+
+       if (def->def_kind != DEF_PROGRAM)
+               return (0);
+       /* generate sample code for each version */
+       for (vp = def->def.pr.versions; vp != NULL; vp = vp->next) {
+               write_sample_client(def->def_name, vp);
+               ++count;
+       }
+       return (count);
+}
+
+
+static void
+write_sample_client(char *program_name, version_list *vp)
+{
+       proc_list      *proc;
+       int             i;
+       decl_list      *l;
+
+       f_print(fout, "\n\nvoid\n");
+       pvname(program_name, vp->vers_num);
+       if (Cflag)
+               f_print(fout, "( char* host )\n{\n");
+       else
+               f_print(fout, "(host)\nchar *host;\n{\n");
+       f_print(fout, "\tCLIENT *clnt;\n");
+
+       i = 0;
+       for (proc = vp->procs; proc != NULL; proc = proc->next) {
+               f_print(fout, "\t");
+               ptype(proc->res_prefix, proc->res_type, 1);
+               f_print(fout, " *result_%d;\n", ++i);
+               /* print out declarations for arguments */
+               if (proc->arg_num < 2 && !newstyle) {
+                       f_print(fout, "\t");
+                       if (!streq(proc->args.decls->decl.type, "void"))
+                               ptype(proc->args.decls->decl.prefix, proc->args.decls->decl.type, 1);
+                       else
+                               f_print(fout, "char* ");        /* cannot have "void" type */
+                       f_print(fout, " ");
+                       pvname(proc->proc_name, vp->vers_num);
+                       f_print(fout, "_arg;\n");
+               } else if (!streq(proc->args.decls->decl.type, "void")) {
+                       for (l = proc->args.decls; l != NULL; l = l->next) {
+                               f_print(fout, "\t");
+                               ptype(l->decl.prefix, l->decl.type, 1);
+                               f_print(fout, " ");
+                               pvname(proc->proc_name, vp->vers_num);
+                               f_print(fout, "_%s;\n", l->decl.name);
+                               /*        pdeclaration(proc->args.argname, &l->decl, 1, ";\n" );*/
+                       }
+               }
+       }
+
+       /* generate creation of client handle */
+       f_print(fout, "\tclnt = clnt_create(host, %s, %s, \"%s\");\n",
+               program_name, vp->vers_name, tirpcflag ? "netpath" : "udp");
+       f_print(fout, "\tif (clnt == NULL) {\n");
+       f_print(fout, "\t\tclnt_pcreateerror(host);\n");
+       f_print(fout, "\t\texit(1);\n\t}\n");
+
+       /* generate calls to procedures */
+       i = 0;
+       for (proc = vp->procs; proc != NULL; proc = proc->next) {
+               f_print(fout, "\tresult_%d = ", ++i);
+               pvname(proc->proc_name, vp->vers_num);
+               if (proc->arg_num < 2 && !newstyle) {
+                       f_print(fout, "(");
+                       if (streq(proc->args.decls->decl.type, "void")) /* cast to void* */
+                               f_print(fout, "(void*)");
+                       f_print(fout, "&");
+                       pvname(proc->proc_name, vp->vers_num);
+                       f_print(fout, "_arg, clnt);\n");
+               } else if (streq(proc->args.decls->decl.type, "void")) {
+                       f_print(fout, "(clnt);\n");
+               } else {
+                       f_print(fout, "(");
+                       for (l = proc->args.decls; l != NULL; l = l->next) {
+                               pvname(proc->proc_name, vp->vers_num);
+                               f_print(fout, "_%s, ", l->decl.name);
+                       }
+                       f_print(fout, "clnt);\n");
+               }
+               f_print(fout, "\tif (result_%d == NULL) {\n", i);
+               f_print(fout, "\t\tclnt_perror(clnt, \"call failed:\");\n");
+               f_print(fout, "\t}\n");
+       }
+
+       f_print(fout, "\tclnt_destroy( clnt );\n");
+       f_print(fout, "}\n");
+}
+
+static void
+write_sample_server(definition * def)
+{
+       version_list   *vp;
+       proc_list      *proc;
+
+       for (vp = def->def.pr.versions; vp != NULL; vp = vp->next) {
+               for (proc = vp->procs; proc != NULL; proc = proc->next) {
+                       f_print(fout, "\n");
+                       /*                      if( Cflag )
+                         f_print( fout, "extern \"C\"{\n");
+*/
+                       return_type(proc);
+                       f_print(fout, "* \n");
+                       if (Cflag)
+                               pvname_svc(proc->proc_name, vp->vers_num);
+                       else
+                               pvname(proc->proc_name, vp->vers_num);
+                       printarglist(proc, RQSTP, "struct svc_req *");
+
+                       f_print(fout, "{\n");
+                       f_print(fout, "\n\tstatic ");
+                       if (!streq(proc->res_type, "void"))
+                               return_type(proc);
+                       else
+                               f_print(fout, "char*"); /* cannot have void type */
+                       /* f_print(fout, " result;\n", proc->res_type); */
+                       f_print(fout, " result;\n");
+                       f_print(fout,
+                               "\n\t/*\n\t * insert server code here\n\t */\n\n");
+                       if (!streq(proc->res_type, "void"))
+                               f_print(fout, "\treturn(&result);\n}\n");
+                       else    /* cast back to void * */
+                               f_print(fout, "\treturn((void*) &result);\n}\n");
+                       /*                      if( Cflag)
+                         f_print( fout, "};\n");
+*/
+
+               }
+       }
+}
+
+
+
+static void
+return_type(proc_list *plist)
+{
+       ptype( plist->res_prefix, plist->res_type, 1 );
+}
+
+void
+add_sample_msg(void)
+{
+       f_print(fout, "/*\n");
+       f_print(fout, " * This is sample code generated by rpcgen.\n");
+       f_print(fout, " * These are only templates and you can use them\n");
+       f_print(fout, " * as a guideline for developing your own functions.\n");
+       f_print(fout, " */\n\n");
+}
+
+void
+write_sample_clnt_main(void)
+{
+  list *l;
+  definition *def;
+  version_list *vp;
+
+  f_print(fout, "\n\n" );
+  if( Cflag )
+    f_print(fout,"main( int argc, char* argv[] )\n{\n" );
+  else
+    f_print(fout, "main(argc, argv)\nint argc;\nchar *argv[];\n{\n" );
+
+  f_print(fout, "\tchar *host;");
+  f_print(fout, "\n\n\tif(argc < 2) {");
+  f_print(fout, "\n\t\tprintf(\"usage: %%s server_host\\n\", argv[0]);\n" );
+  f_print(fout, "\t\texit(1);\n\t}");
+  f_print(fout, "\n\thost = argv[1];\n");
+
+  for (l = defined; l != NULL; l = l->next) {
+               def = l->val;
+               if (def->def_kind != DEF_PROGRAM) {
+                       continue;
+               }
+               for (vp = def->def.pr.versions; vp != NULL; vp = vp->next) {
+                       f_print( fout, "\t" );
+                       pvname(def->def_name, vp->vers_num);
+                       f_print( fout, "( host );\n" );
+                     }
+               }
+  f_print(fout, "}\n");
+}
diff --git a/tools/rpcgen/rpc_scan.c b/tools/rpcgen/rpc_scan.c
new file mode 100644 (file)
index 0000000..07565a1
--- /dev/null
@@ -0,0 +1,475 @@
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part.  Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user or with the express written consent of
+ * Sun Microsystems, Inc.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California  94043
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)rpc_scan.c 1.11 89/02/22 (C) 1987 SMI";
+#endif
+
+/*
+ * rpc_scan.c, Scanner for the RPC protocol compiler 
+ * Copyright (C) 1987, Sun Microsystems, Inc. 
+ */
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include "rpc_scan.h"
+#include "rpc_parse.h"
+#include "rpc_util.h"
+
+static void    unget_token(token *tokp);
+static void    findstrconst(char **str, char **val);
+static void    findchrconst(char **str, char **val);
+static void    findconst(char **str, char **val);
+static void    findkind(char **mark, token *tokp);
+static int     cppline(char *line);
+static int     directive(char *line);
+static void    printdirective(char *line);
+static void    docppline(char *line, int *lineno, char **fname);
+
+#define startcomment(where) (where[0] == '/' && where[1] == '*')
+#define endcomment(where) (where[-1] == '*' && where[0] == '/')
+
+static int pushed = 0; /* is a token pushed */
+static token lasttok;  /* last token, if pushed */
+
+/*
+ * scan expecting 1 given token 
+ */
+void
+scan(tok_kind expect, token *tokp)
+{
+       get_token(tokp);
+       if (tokp->kind != expect) {
+               expected1(expect);
+       }
+}
+
+/*
+ * scan expecting any of the 2 given tokens 
+ */
+void
+scan2(tok_kind expect1, tok_kind expect2, token *tokp)
+{
+       get_token(tokp);
+       if (tokp->kind != expect1 && tokp->kind != expect2) {
+               expected2(expect1, expect2);
+       }
+}
+
+/*
+ * scan expecting any of the 3 given token 
+ */
+void
+scan3(tok_kind expect1, tok_kind expect2, tok_kind expect3, token *tokp)
+{
+       get_token(tokp);
+       if (tokp->kind != expect1 && tokp->kind != expect2
+           && tokp->kind != expect3) {
+               expected3(expect1, expect2, expect3);
+       }
+}
+
+/*
+ * scan expecting a constant, possibly symbolic 
+ */
+void
+scan_num(token *tokp)
+{
+       get_token(tokp);
+       switch (tokp->kind) {
+       case TOK_IDENT:
+               break;
+       default:
+               error("constant or identifier expected");
+       }
+}
+
+/*
+ * Peek at the next token 
+ */
+void
+peek(token *tokp)
+{
+       get_token(tokp);
+       unget_token(tokp);
+}
+
+/*
+ * Peek at the next token and scan it if it matches what you expect 
+ */
+int
+peekscan(tok_kind expect, token *tokp)
+{
+       peek(tokp);
+       if (tokp->kind == expect) {
+               get_token(tokp);
+               return (1);
+       }
+       return (0);
+}
+
+/*
+ * Get the next token, printing out any directive that are encountered. 
+ */
+void
+get_token(token *tokp)
+{
+       int commenting;
+
+       if (pushed) {
+               pushed = 0;
+               *tokp = lasttok;
+               return;
+       }
+       commenting = 0;
+       for (;;) {
+               if (*where == 0) {
+                       for (;;) {
+                               if (!fgets(curline, MAXLINESIZE, fin)) {
+                                       tokp->kind = TOK_EOF;
+                                       *where = 0;
+                                       return;
+                               }
+                               linenum++;
+                               if (commenting) {
+                                       break;
+                               } else if (cppline(curline)) {
+                                       docppline(curline, &linenum, 
+                                                 &infilename);
+                               } else if (directive(curline)) {
+                                       printdirective(curline);
+                               } else {
+                                       break;
+                               }
+                       }
+                       where = curline;
+               } else if (isspace(*where)) {
+                       while (isspace(*where)) {
+                               where++;        /* eat */
+                       }
+               } else if (commenting) {
+                       for (where++; *where; where++) {
+                               if (endcomment(where)) {
+                                       where++;
+                                       commenting--;
+                                       break;
+                               }
+                       }
+               } else if (startcomment(where)) {
+                       where += 2;
+                       commenting++;
+               } else {
+                       break;
+               }
+       }
+
+       /*
+        * 'where' is not whitespace, comment or directive Must be a token! 
+        */
+       switch (*where) {
+       case ':':
+               tokp->kind = TOK_COLON;
+               where++;
+               break;
+       case ';':
+               tokp->kind = TOK_SEMICOLON;
+               where++;
+               break;
+       case ',':
+               tokp->kind = TOK_COMMA;
+               where++;
+               break;
+       case '=':
+               tokp->kind = TOK_EQUAL;
+               where++;
+               break;
+       case '*':
+               tokp->kind = TOK_STAR;
+               where++;
+               break;
+       case '[':
+               tokp->kind = TOK_LBRACKET;
+               where++;
+               break;
+       case ']':
+               tokp->kind = TOK_RBRACKET;
+               where++;
+               break;
+       case '{':
+               tokp->kind = TOK_LBRACE;
+               where++;
+               break;
+       case '}':
+               tokp->kind = TOK_RBRACE;
+               where++;
+               break;
+       case '(':
+               tokp->kind = TOK_LPAREN;
+               where++;
+               break;
+       case ')':
+               tokp->kind = TOK_RPAREN;
+               where++;
+               break;
+       case '<':
+               tokp->kind = TOK_LANGLE;
+               where++;
+               break;
+       case '>':
+               tokp->kind = TOK_RANGLE;
+               where++;
+               break;
+
+       case '"':
+               tokp->kind = TOK_STRCONST;
+               findstrconst(&where, &tokp->str);
+               break;
+       case '\'':
+               tokp->kind = TOK_CHARCONST;
+               findchrconst(&where, &tokp->str);
+               break;
+
+       case '-':
+       case '0':
+       case '1':
+       case '2':
+       case '3':
+       case '4':
+       case '5':
+       case '6':
+       case '7':
+       case '8':
+       case '9':
+               tokp->kind = TOK_IDENT;
+               findconst(&where, &tokp->str);
+               break;
+
+       default:
+               if (!(isalpha(*where) || *where == '_')) {
+                       char buf[100];
+                       char *p;
+
+                       s_print(buf, "illegal character in file: ");
+                       p = buf + strlen(buf);
+                       if (isprint(*where)) {
+                               s_print(p, "%c", *where);
+                       } else {
+                               s_print(p, "%d", *where);
+                       }
+                       error(buf);
+               }
+               findkind(&where, tokp);
+               break;
+       }
+}
+
+static void
+unget_token(token *tokp)
+{
+       lasttok = *tokp;
+       pushed = 1;
+}
+
+static void
+findstrconst(char **str, char **val)
+{
+       char *p;
+       int size;
+
+       p = *str;
+       do {
+               *p++;
+       } while (*p && *p != '"');
+       if (*p == 0) {
+               error("unterminated string constant");
+       }
+       p++;
+       size = p - *str;
+       *val = alloc(size + 1);
+       (void) strncpy(*val, *str, size);
+       (*val)[size] = 0;
+       *str = p;
+}
+
+static void
+findchrconst(char **str, char **val)
+{
+       char *p;
+       int size;
+
+       p = *str;
+       do {
+               *p++;
+       } while (*p && *p != '\'');
+       if (*p == 0) {
+               error("unterminated string constant");
+       }
+       p++;
+       size = p - *str;
+       if (size != 3) {
+               error("empty char string");
+       }
+       *val = alloc(size + 1);
+       (void) strncpy(*val, *str, size);
+       (*val)[size] = 0;
+       *str = p;
+}
+
+static void
+findconst(char **str, char **val)
+{
+       char *p;
+       int size;
+
+       p = *str;
+       if (*p == '0' && *(p + 1) == 'x') {
+               p++;
+               do {
+                       p++;
+               } while (isxdigit(*p));
+       } else {
+               do {
+                       p++;
+               } while (isdigit(*p));
+       }
+       size = p - *str;
+       *val = alloc(size + 1);
+       (void) strncpy(*val, *str, size);
+       (*val)[size] = 0;
+       *str = p;
+}
+
+static token symbols[] = {
+                         {TOK_CONST, "const"},
+                         {TOK_UNION, "union"},
+                         {TOK_SWITCH, "switch"},
+                         {TOK_CASE, "case"},
+                         {TOK_DEFAULT, "default"},
+                         {TOK_STRUCT, "struct"},
+                         {TOK_TYPEDEF, "typedef"},
+                         {TOK_ENUM, "enum"},
+                         {TOK_OPAQUE, "opaque"},
+                         {TOK_BOOL, "bool"},
+                         {TOK_VOID, "void"},
+                         {TOK_CHAR, "char"},
+                         {TOK_INT, "int"},
+                         {TOK_UNSIGNED, "unsigned"},
+                         {TOK_SHORT, "short"},
+                         {TOK_LONG, "long"},
+                         {TOK_FLOAT, "float"},
+                         {TOK_DOUBLE, "double"},
+                         {TOK_STRING, "string"},
+                         {TOK_PROGRAM, "program"},
+                         {TOK_VERSION, "version"},
+                         {TOK_EOF, "??????"},
+};
+
+static void
+findkind(char **mark, token *tokp)
+{
+       int len;
+       token *s;
+       char *str;
+
+       str = *mark;
+       for (s = symbols; s->kind != TOK_EOF; s++) {
+               len = strlen(s->str);
+               if (strncmp(str, s->str, len) == 0) {
+                       if (!isalnum(str[len]) && str[len] != '_') {
+                               tokp->kind = s->kind;
+                               tokp->str = s->str;
+                               *mark = str + len;
+                               return;
+                       }
+               }
+       }
+       tokp->kind = TOK_IDENT;
+       for (len = 0; isalnum(str[len]) || str[len] == '_'; len++);
+       tokp->str = alloc(len + 1);
+       (void) strncpy(tokp->str, str, len);
+       tokp->str[len] = 0;
+       *mark = str + len;
+}
+
+static int
+cppline(char *line)
+{
+       return (line == curline && *line == '#');
+}
+
+static int
+directive(char *line)
+{
+       return (line == curline && *line == '%');
+}
+
+static void
+printdirective(char *line)
+{
+       f_print(fout, "%s", line + 1);
+}
+
+static void
+docppline(char *line, int *lineno, char **fname)
+{
+       char *file;
+       int num;
+       char *p;
+
+       line++;
+       while (isspace(*line)) {
+               line++;
+       }
+       num = atoi(line);
+       while (isdigit(*line)) {
+               line++;
+       }
+       while (isspace(*line)) {
+               line++;
+       }
+       if (*line != '"') {
+               error("preprocessor error");
+       }
+       line++;
+       p = file = alloc(strlen(line) + 1);
+       while (*line && *line != '"') {
+               *p++ = *line++;
+       }
+       if (*line == 0) {
+               error("preprocessor error");
+       }
+       *p = 0;
+       if (*file == 0) {
+               *fname = NULL;
+       } else {
+               *fname = file;
+       }
+       *lineno = num - 1;
+}
diff --git a/tools/rpcgen/rpc_scan.h b/tools/rpcgen/rpc_scan.h
new file mode 100644 (file)
index 0000000..0683dbe
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part.  Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user or with the express written consent of
+ * Sun Microsystems, Inc.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California  94043
+ */
+
+/*      @(#)rpc_scan.h  1.3  90/08/29  (C) 1987 SMI   */
+
+/*
+ * rpc_scan.h, Definitions for the RPCL scanner 
+ */
+
+/*
+ * kinds of tokens 
+ */
+enum tok_kind {
+       TOK_IDENT,
+       TOK_CHARCONST,
+       TOK_STRCONST,
+       TOK_LPAREN,
+       TOK_RPAREN,
+       TOK_LBRACE,
+       TOK_RBRACE,
+       TOK_LBRACKET,
+       TOK_RBRACKET,
+       TOK_LANGLE,
+       TOK_RANGLE,
+       TOK_STAR,
+       TOK_COMMA,
+       TOK_EQUAL,
+       TOK_COLON,
+       TOK_SEMICOLON,
+       TOK_CONST,
+       TOK_STRUCT,
+       TOK_UNION,
+       TOK_SWITCH,
+       TOK_CASE,
+       TOK_DEFAULT,
+       TOK_ENUM,
+       TOK_TYPEDEF,
+       TOK_INT,
+       TOK_SHORT,
+       TOK_LONG,
+       TOK_UNSIGNED,
+       TOK_FLOAT,
+       TOK_DOUBLE,
+       TOK_OPAQUE,
+       TOK_CHAR,
+       TOK_STRING,
+       TOK_BOOL,
+       TOK_VOID,
+       TOK_PROGRAM,
+       TOK_VERSION,
+       TOK_EOF
+};
+typedef enum tok_kind tok_kind;
+
+/*
+ * a token 
+ */
+struct token {
+       tok_kind kind;
+       char *str;
+};
+typedef struct token token;
+
+
+/*
+ * routine interface 
+ */
+void            scan();
+void            scan2();
+void            scan3();
+void            scan_num();
+void            peek();
+int             peekscan();
+void            get_token();
+void            expected1(tok_kind);
+void            expected2(tok_kind, tok_kind);
+void            expected3(tok_kind, tok_kind, tok_kind);
+
diff --git a/tools/rpcgen/rpc_svcout.c b/tools/rpcgen/rpc_svcout.c
new file mode 100644 (file)
index 0000000..50c4ff9
--- /dev/null
@@ -0,0 +1,885 @@
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part.  Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user or with the express written consent of
+ * Sun Microsystems, Inc.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California  94043
+ */
+
+#ifndef lint
+ static char sccsid[] = "@(#)rpc_svcout.c 1.29 89/03/30 (C) 1987 SMI";
+#endif
+
+/*
+ * rpc_svcout.c, Server-skeleton outputter for the RPC protocol compiler
+ */
+#include <stdio.h>
+#include <string.h>
+#include "rpc_parse.h"
+#include "rpc_util.h"
+#include "rpc_output.h"
+
+static void    write_real_program(definition *def);
+static void    write_program(definition *def, char *storage);
+static void    printerr(char *err, char *transp);
+static void    printif(char *proc, char *transp, char *prefix, char *arg);
+static void    write_inetmost(char *infile);
+static void    print_return(char *space);
+static void    print_pmapunset(char *space);
+static void    print_err_message(char *space);
+static void    write_timeout_func(void);
+static void    write_pm_most(char *infile, int netflag);
+static void    write_rpc_svc_fg(char *infile, char *sp);
+static void    open_log_file(char *infile, char *sp);
+
+static char RQSTP[] = "rqstp";
+static char TRANSP[] = "transp";
+static char ARG[] = "argument";
+static char RESULT[] = "result";
+static char ROUTINE[] = "local";
+
+char _errbuf[256];     /* For all messages */
+
+static void
+p_xdrfunc(char *rname, char *typename)
+{
+       if (Cflag)
+               f_print(fout, "\t\txdr_%s = (xdrproc_t) xdr_%s;\n", rname,
+                       stringfix(typename));
+       else
+               f_print(fout, "\t\txdr_%s = xdr_%s;\n", rname, stringfix(typename));
+}
+
+void
+internal_proctype(proc_list *plist)
+{
+       f_print(fout, "static ");
+       ptype( plist->res_prefix, plist->res_type, 1 );
+       f_print( fout, "*" );
+}
+
+
+/*
+ * write most of the service, that is, everything but the registrations. 
+ */
+void
+write_most(char *infile, int netflag, int nomain)
+{
+       if (inetdflag || pmflag) {
+               char* var_type;
+               var_type = (nomain? "extern" : "static");
+               f_print(fout, "%s int _rpcpmstart;", var_type );
+               f_print(fout, "\t\t/* Started by a port monitor ? */\n"); 
+               f_print(fout, "%s int _rpcfdtype;", var_type );
+               f_print(fout, "\t\t/* Whether Stream or Datagram ? */\n");
+               if (timerflag) {
+                       f_print(fout, "%s int _rpcsvcdirty;", var_type );
+                       f_print(fout, "\t/* Still serving ? */\n");
+               }
+               write_svc_aux( nomain );
+       }
+       /* write out dispatcher and stubs */
+       write_programs( nomain? (char *)NULL : "static" );
+
+        if( nomain ) 
+         return;
+
+       f_print(fout, "\nmain()\n");
+       f_print(fout, "{\n");
+       if (inetdflag) {
+               write_inetmost(infile); /* Includes call to write_rpc_svc_fg() */
+       } else {
+         if( tirpcflag ) {
+               if (netflag) {
+                       f_print(fout, "\tregister SVCXPRT *%s;\n", TRANSP);
+                       f_print(fout, "\tstruct netconfig *nconf = NULL;\n");
+               }
+               f_print(fout, "\tpid_t pid;\n");
+               f_print(fout, "\tint i;\n");
+               f_print(fout, "\tchar mname[FMNAMESZ + 1];\n\n");
+               write_pm_most(infile, netflag);
+               f_print(fout, "\telse {\n");
+               write_rpc_svc_fg(infile, "\t\t");
+               f_print(fout, "\t}\n");
+             } else {
+               f_print(fout, "\tregister SVCXPRT *%s;\n", TRANSP);
+               f_print(fout, "\n");
+               print_pmapunset("\t");
+             }
+       }
+
+       if (logflag && !inetdflag) {
+               open_log_file(infile, "\t");
+       }
+}
+
+/*
+ * write a registration for the given transport 
+ */
+void
+write_netid_register(char *transp)
+{
+       list *l;
+       definition *def;
+       version_list *vp;
+       char *sp;
+       char tmpbuf[32];
+
+       sp = "";
+       f_print(fout, "\n");
+       f_print(fout, "%s\tnconf = getnetconfigent(\"%s\");\n", sp, transp);
+       f_print(fout, "%s\tif (nconf == NULL) {\n", sp);
+       (void) sprintf(_errbuf, "cannot find %s netid.", transp);
+       sprintf(tmpbuf, "%s\t\t", sp);
+       print_err_message(tmpbuf);
+       f_print(fout, "%s\t\texit(1);\n", sp);
+       f_print(fout, "%s\t}\n", sp);
+       f_print(fout, "%s\t%s = svc_tli_create(RPC_ANYFD, nconf, 0, 0, 0);\n",
+                       sp, TRANSP);
+       f_print(fout, "%s\tif (%s == NULL) {\n", sp, TRANSP);
+       (void) sprintf(_errbuf, "cannot create %s service.", transp);
+       print_err_message(tmpbuf);
+       f_print(fout, "%s\t\texit(1);\n", sp);
+       f_print(fout, "%s\t}\n", sp);
+
+       for (l = defined; l != NULL; l = l->next) {
+               def = (definition *) l->val;
+               if (def->def_kind != DEF_PROGRAM) {
+                       continue;
+               }
+               for (vp = def->def.pr.versions; vp != NULL; vp = vp->next) {
+                       f_print(fout,
+                               "%s\t(void) rpcb_unset(%s, %s, nconf);\n",
+                               sp, def->def_name, vp->vers_name);
+                       f_print(fout,
+                               "%s\tif (!svc_reg(%s, %s, %s, ",
+                               sp, TRANSP, def->def_name, vp->vers_name);
+                       pvname(def->def_name, vp->vers_num);
+                       f_print(fout, ", nconf)) {\n");
+                       (void) sprintf(_errbuf, "unable to register (%s, %s, %s).",
+                                       def->def_name, vp->vers_name, transp);
+                       print_err_message(tmpbuf);
+                       f_print(fout, "%s\t\texit(1);\n", sp);
+                       f_print(fout, "%s\t}\n", sp);
+               }
+       }
+       f_print(fout, "%s\tfreenetconfigent(nconf);\n", sp);
+}
+
+/*
+ * write a registration for the given transport for TLI
+ */
+void
+write_nettype_register(char *transp)
+{
+       list *l;
+       definition *def;
+       version_list *vp;
+
+       for (l = defined; l != NULL; l = l->next) {
+               def = (definition *) l->val;
+               if (def->def_kind != DEF_PROGRAM) {
+                       continue;
+               }
+               for (vp = def->def.pr.versions; vp != NULL; vp = vp->next) {
+                       f_print(fout, "\tif (!svc_create(");
+                       pvname(def->def_name, vp->vers_num);
+                       f_print(fout, ", %s, %s, \"%s\")) {\n ",
+                               def->def_name, vp->vers_name, transp);
+                       (void) sprintf(_errbuf,
+                               "unable to create (%s, %s) for %s.",
+                                       def->def_name, vp->vers_name, transp);
+                       print_err_message("\t\t");
+                       f_print(fout, "\t\texit(1);\n");
+                       f_print(fout, "\t}\n");
+               }
+       }
+}
+
+/*
+ * write the rest of the service 
+ */
+void
+write_rest(void)
+{
+       f_print(fout, "\n");
+       if (inetdflag) {
+               f_print(fout, "\tif (%s == (SVCXPRT *)NULL) {\n", TRANSP);
+               (void) sprintf(_errbuf, "could not create a handle");
+               print_err_message("\t\t");
+               f_print(fout, "\t\texit(1);\n");
+               f_print(fout, "\t}\n");
+               if (timerflag) {
+                       f_print(fout, "\tif (_rpcpmstart) {\n");
+                       f_print(fout, 
+                               "\t\t(void) signal(SIGALRM, %s closedown);\n",
+                               Cflag? "(SIG_PF)" : "(void(*)())" );
+                       f_print(fout, "\t\t(void) alarm(_RPCSVC_CLOSEDOWN);\n");
+                       f_print(fout, "\t}\n");
+               }
+       }
+       f_print(fout, "\tsvc_run();\n");
+       (void) sprintf(_errbuf, "svc_run returned");
+       print_err_message("\t");
+       f_print(fout, "\texit(1);\n");
+       f_print(fout, "\t/* NOTREACHED */\n");
+       f_print(fout, "}\n");
+}
+
+void
+write_programs(char *storage)
+{
+       list *l;
+       definition *def;
+
+       /* write out stubs for procedure  definitions */
+       for (l = defined; l != NULL; l = l->next) {
+               def = (definition *) l->val;
+               if (def->def_kind == DEF_PROGRAM) {
+                       write_real_program(def);
+               }
+       }
+
+       /* write out dispatcher for each program */
+       for (l = defined; l != NULL; l = l->next) {
+               def = (definition *) l->val;
+               if (def->def_kind == DEF_PROGRAM) {
+                       write_program(def, storage);
+               }
+       }
+
+
+}
+
+/* write out definition of internal function (e.g. _printmsg_1(...))
+   which calls server's defintion of actual function (e.g. printmsg_1(...)).
+   Unpacks single user argument of printmsg_1 to call-by-value format
+   expected by printmsg_1. */
+static void
+write_real_program(definition *def)
+{
+       version_list *vp;
+       proc_list *proc;
+       decl_list *l;
+
+       if( !newstyle ) return;  /* not needed for old style */
+       for (vp = def->def.pr.versions; vp != NULL; vp = vp->next) {
+               for (proc = vp->procs; proc != NULL; proc = proc->next) {
+                       f_print(fout, "\n");
+                       internal_proctype(proc);
+                       f_print(fout, "\n_");
+                       pvname(proc->proc_name, vp->vers_num);
+                       if( Cflag ) {
+                         f_print(fout, "(" );
+                         /* arg name */
+                         if (proc->arg_num > 1)
+                           f_print(fout, proc->args.argname);
+                         else
+                           ptype(proc->args.decls->decl.prefix, 
+                                 proc->args.decls->decl.type, 0);
+                         f_print(fout, " *argp, struct svc_req *%s)\n", 
+                                 RQSTP);
+                       } else {
+                         f_print(fout, "(argp, %s)\n", RQSTP );
+                         /* arg name */
+                         if (proc->arg_num > 1)
+                           f_print(fout, "\t%s *argp;\n", proc->args.argname);
+                         else {
+                           f_print(fout, "\t");
+                           ptype(proc->args.decls->decl.prefix, 
+                                 proc->args.decls->decl.type, 0);
+                           f_print(fout, " *argp;\n");
+                         }
+                         f_print(fout, "       struct svc_req *%s;\n", RQSTP);
+                       }
+
+                       f_print(fout, "{\n");
+                       f_print(fout, "\treturn(");
+                       if( Cflag )
+                         pvname_svc(proc->proc_name, vp->vers_num);
+                       else
+                         pvname(proc->proc_name, vp->vers_num);
+                       f_print(fout, "(");
+                       if (proc->arg_num < 2) { /* single argument */
+                         if (!streq( proc->args.decls->decl.type, "void"))
+                           f_print(fout, "*argp, ");  /* non-void */
+                       } else {
+                         for (l = proc->args.decls;  l != NULL; l = l->next) 
+                           f_print(fout, "argp->%s, ", l->decl.name);
+                       }
+                       f_print(fout, "%s));\n}\n", RQSTP);
+               }               
+       }
+}
+
+static void
+write_program(definition *def, char *storage)
+{
+       version_list *vp;
+       proc_list *proc;
+       int filled;
+
+       for (vp = def->def.pr.versions; vp != NULL; vp = vp->next) {
+               f_print(fout, "\n");
+               if (storage != NULL) {
+                       f_print(fout, "%s ", storage);
+               }
+               f_print(fout, "void\n");
+               pvname(def->def_name, vp->vers_num);
+
+               if (Cflag) {
+                  f_print(fout, "(struct svc_req *%s, ", RQSTP);
+                  f_print(fout, "register SVCXPRT *%s)\n", TRANSP);
+               } else {
+                  f_print(fout, "(%s, %s)\n", RQSTP, TRANSP);
+                  f_print(fout, "      struct svc_req *%s;\n", RQSTP);
+                  f_print(fout, "      register SVCXPRT *%s;\n", TRANSP);
+               }
+
+               f_print(fout, "{\n");
+
+               filled = 0;
+               f_print(fout, "\tunion {\n");
+               for (proc = vp->procs; proc != NULL; proc = proc->next) {
+                       if (proc->arg_num < 2) { /* single argument */
+                               if (streq(proc->args.decls->decl.type, 
+                                         "void")) {
+                                       continue;
+                               }
+                               filled = 1;
+                               f_print(fout, "\t\t");
+                               ptype(proc->args.decls->decl.prefix, 
+                                     proc->args.decls->decl.type, 0);
+                               pvname(proc->proc_name, vp->vers_num);
+                               f_print(fout, "_arg;\n");
+
+                       }
+                       else {
+                               filled = 1;
+                               f_print(fout, "\t\t%s", proc->args.argname);
+                               f_print(fout, " ");
+                               pvname(proc->proc_name, vp->vers_num);
+                               f_print(fout, "_arg;\n");
+                       }
+               }
+               if (!filled) {
+                       f_print(fout, "\t\tint fill;\n");
+               }
+               f_print(fout, "\t} %s;\n", ARG);
+               f_print(fout, "\tchar *%s;\n", RESULT);
+
+               if (Cflag) {
+                   f_print(fout, "\txdrproc_t xdr_%s, xdr_%s;\n", ARG, RESULT);
+                   f_print(fout,
+                           "\tchar *(*%s)(char *, struct svc_req *);\n",
+                           ROUTINE);
+               } else {
+                   f_print(fout, "\tbool_t (*xdr_%s)(), (*xdr_%s)();\n", ARG, RESULT);
+                   f_print(fout, "\tchar *(*%s)();\n", ROUTINE);
+               }
+
+               f_print(fout, "\n");
+
+               if (timerflag)
+                       f_print(fout, "\t_rpcsvcdirty = 1;\n");
+               f_print(fout, "\tswitch (%s->rq_proc) {\n", RQSTP);
+               if (!nullproc(vp->procs)) {
+                       f_print(fout, "\tcase NULLPROC:\n");
+                       f_print(fout,
+                       "\t\t(void) svc_sendreply(%s, (xdrproc_t) xdr_void, (char *)NULL);\n",
+                                       TRANSP);
+                       print_return("\t\t");
+                       f_print(fout, "\n");
+               }
+               for (proc = vp->procs; proc != NULL; proc = proc->next) {
+                       f_print(fout, "\tcase %s:\n", proc->proc_name);
+                       if (proc->arg_num < 2) { /* single argument */
+                         p_xdrfunc( ARG, proc->args.decls->decl.type);
+                       } else {
+                         p_xdrfunc( ARG, proc->args.argname);
+                       }
+                       p_xdrfunc( RESULT, proc->res_type);
+                       if( Cflag )
+                           f_print(fout,
+                                   "\t\t%s = (char *(*)(char *, struct svc_req *)) ",
+                                   ROUTINE);
+                       else
+                           f_print(fout, "\t\t%s = (char *(*)()) ", ROUTINE);
+
+                       if (newstyle) { /* new style: calls internal routine */
+                               f_print(fout,"_");
+                       }
+                       /* Not sure about the following...
+                        * rpc_hout always generates foobar_1_svc for
+                        * the service procedure, so why should we use
+                        * foobar_1 here?! --okir */
+#if 0
+                       if( Cflag && !newstyle )
+                         pvname_svc(proc->proc_name, vp->vers_num);
+                       else
+                         pvname(proc->proc_name, vp->vers_num);
+#else
+                       pvname_svc(proc->proc_name, vp->vers_num);
+#endif
+                       f_print(fout, ";\n");
+                       f_print(fout, "\t\tbreak;\n\n");
+               }
+               f_print(fout, "\tdefault:\n");
+               printerr("noproc", TRANSP);
+               print_return("\t\t");
+               f_print(fout, "\t}\n");
+
+               f_print(fout, "\t(void) memset((char *)&%s, 0, sizeof (%s));\n", ARG, ARG);
+               if (Cflag)
+                   printif("getargs", TRANSP, "(caddr_t) &", ARG);
+               else
+                   printif("getargs", TRANSP, "&", ARG);
+               printerr("decode", TRANSP);
+               print_return("\t\t");
+               f_print(fout, "\t}\n");
+
+               if (Cflag)
+                   f_print(fout, "\t%s = (*%s)((char *)&%s, %s);\n",
+                           RESULT, ROUTINE, ARG, RQSTP);
+               else
+                   f_print(fout, "\t%s = (*%s)(&%s, %s);\n",
+                           RESULT, ROUTINE, ARG, RQSTP);
+               f_print(fout, 
+                       "\tif (%s != NULL && !svc_sendreply(%s, "
+                       "(xdrproc_t) xdr_%s, %s)) {\n",
+                       RESULT, TRANSP, RESULT, RESULT);
+               printerr("systemerr", TRANSP);
+               f_print(fout, "\t}\n");
+
+               if (Cflag)
+                   printif("freeargs", TRANSP, "(caddr_t) &", ARG);
+               else
+                   printif("freeargs", TRANSP, "&", ARG);
+               (void) sprintf(_errbuf, "unable to free arguments");
+               print_err_message("\t\t");
+               f_print(fout, "\t\texit(1);\n");
+               f_print(fout, "\t}\n");
+               print_return("\t");
+               f_print(fout, "}\n");
+       }
+}
+
+static void
+printerr(char *err, char *transp)
+{
+       f_print(fout, "\t\tsvcerr_%s(%s);\n", err, transp);
+}
+
+static void
+printif(char *proc, char *transp, char *prefix, char *arg)
+{
+       f_print(fout, "\tif (!svc_%s(%s, (xdrproc_t) xdr_%s, (caddr_t) %s%s)) {\n",
+               proc, transp, arg, prefix, arg);
+}
+
+int
+nullproc(proc_list *proc)
+{
+       for (; proc != NULL; proc = proc->next) {
+               if (streq(proc->proc_num, "0")) {
+                       return (1);
+               }
+       }
+       return (0);
+}
+
+static void
+write_inetmost(char *infile)
+{
+       f_print(fout, "\tregister SVCXPRT *%s;\n", TRANSP);
+       f_print(fout, "\tint sock;\n");
+       f_print(fout, "\tint proto;\n");
+       f_print(fout, "\tstruct sockaddr_in saddr;\n");
+       f_print(fout, "\tint asize = sizeof (saddr);\n");
+       f_print(fout, "\n");
+       f_print(fout, 
+       "\tif (getsockname(0, (struct sockaddr *)&saddr, &asize) == 0) {\n");
+       f_print(fout, "\t\tint ssize = sizeof (int);\n\n");
+       f_print(fout, "\t\tif (saddr.sin_family != AF_INET)\n");
+       f_print(fout, "\t\t\texit(1);\n");
+       f_print(fout, "\t\tif (getsockopt(0, SOL_SOCKET, SO_TYPE,\n");
+       f_print(fout, "\t\t\t\t(char *)&_rpcfdtype, &ssize) == -1)\n");
+       f_print(fout, "\t\t\texit(1);\n");
+       f_print(fout, "\t\tsock = 0;\n");
+       f_print(fout, "\t\t_rpcpmstart = 1;\n");
+       f_print(fout, "\t\tproto = 0;\n");
+       open_log_file(infile, "\t\t");
+       f_print(fout, "\t} else {\n");
+       write_rpc_svc_fg(infile, "\t\t");
+       f_print(fout, "\t\tsock = RPC_ANYSOCK;\n");
+       print_pmapunset("\t\t");
+       f_print(fout, "\t}\n");
+}
+
+static void
+print_return(char *space)
+{
+       if (exitnow)
+               f_print(fout, "%sexit(0);\n", space);
+       else {
+               if (timerflag)
+                       f_print(fout, "%s_rpcsvcdirty = 0;\n", space);
+               f_print(fout, "%sreturn;\n", space);
+       }
+}
+
+static void
+print_pmapunset(char *space)
+{
+       list *l;
+       definition *def;
+       version_list *vp;
+
+       for (l = defined; l != NULL; l = l->next) {
+               def = (definition *) l->val;
+               if (def->def_kind == DEF_PROGRAM) {
+                       for (vp = def->def.pr.versions; vp != NULL;
+                                       vp = vp->next) {
+                               f_print(fout, "%s(void) pmap_unset(%s, %s);\n",
+                                       space, def->def_name, vp->vers_name);
+                       }
+               }
+       }
+}
+
+static void
+print_err_message(char *space)
+{
+       if (logflag)
+               f_print(fout, "%ssyslog(LOG_ERR, \"%s\");\n", space, _errbuf);
+       else if (inetdflag || pmflag)
+               f_print(fout, "%s_msgout(\"%s\");\n", space, _errbuf);
+       else
+               f_print(fout, "%sfprintf(stderr, \"%s\");\n", space, _errbuf);
+}
+
+/*
+ * Write the server auxiliary function ( _msgout, timeout)
+ */
+void
+write_svc_aux(int nomain)
+{
+       if (!logflag)
+               write_msg_out();
+       if( !nomain )
+         write_timeout_func();
+}
+
+/*
+ * Write the _msgout function
+ */
+void
+write_msg_out(void)
+{
+       f_print(fout, "\n");
+       f_print(fout, "static\n");
+       if( !Cflag ) {
+         f_print(fout, "void _msgout(msg)\n");
+         f_print(fout, "\tchar *msg;\n");
+       } else {
+         f_print(fout, "void _msgout(char* msg)\n");
+       }
+       f_print(fout, "{\n");
+       f_print(fout, "#ifdef RPC_SVC_FG\n");
+       if (inetdflag || pmflag)
+               f_print(fout, "\tif (_rpcpmstart)\n");
+       f_print(fout, "\t\tsyslog(LOG_ERR, msg);\n");
+       f_print(fout, "\telse\n");
+       f_print(fout, "\t\t(void) fprintf(stderr, \"%%s\\n\", msg);\n");
+       f_print(fout, "#else\n");
+       f_print(fout, "\tsyslog(LOG_ERR, msg);\n");
+       f_print(fout, "#endif\n");
+       f_print(fout, "}\n");
+}
+
+/*
+ * Write the timeout function
+ */
+static void
+write_timeout_func(void)
+{
+       if (!timerflag)
+               return;
+       f_print(fout, "\n");
+       f_print(fout, "static void\n");
+       f_print(fout, "closedown()\n");
+       f_print(fout, "{\n");
+       f_print(fout, "\tif (_rpcsvcdirty == 0) {\n");
+       f_print(fout, "\t\textern fd_set svc_fdset;\n");
+       f_print(fout, "\t\tstatic int size;\n");
+       f_print(fout, "\t\tint i, openfd;\n");
+       if (tirpcflag && pmflag) {
+               f_print(fout, "\t\tstruct t_info tinfo;\n\n");
+               f_print(fout, "\t\tif (!t_getinfo(0, &tinfo) && (tinfo.servtype == T_CLTS))\n");
+       } else {
+               f_print(fout, "\n\t\tif (_rpcfdtype == SOCK_DGRAM)\n");
+       }
+       f_print(fout, "\t\t\texit(0);\n");
+       f_print(fout, "\t\tif (size == 0) {\n");
+       if( tirpcflag ) {
+         f_print(fout, "\t\t\tstruct rlimit rl;\n\n");
+         f_print(fout, "\t\t\trl.rlim_max = 0;\n");
+         f_print(fout, "\t\t\tgetrlimit(RLIMIT_NOFILE, &rl);\n");
+         f_print(fout, "\t\t\tif ((size = rl.rlim_max) == 0)\n");
+         f_print(fout, "\t\t\t\treturn;\n");
+       } else {
+         f_print(fout, "\t\t\tsize = getdtablesize();\n");
+       }
+       f_print(fout, "\t\t}\n");
+       f_print(fout, "\t\tfor (i = 0, openfd = 0; i < size && openfd < 2; i++)\n");
+       f_print(fout, "\t\t\tif (FD_ISSET(i, &svc_fdset))\n");
+       f_print(fout, "\t\t\t\topenfd++;\n");
+       f_print(fout, "\t\tif (openfd <= 1)\n");
+       f_print(fout, "\t\t\texit(0);\n");
+       f_print(fout, "\t}\n");
+       f_print(fout, "\t(void) alarm(_RPCSVC_CLOSEDOWN);\n");
+       f_print(fout, "}\n");
+}
+
+/*
+ * Write the most of port monitor support
+ */
+static void
+write_pm_most(char *infile, int netflag)
+{
+       list *l;
+       definition *def;
+       version_list *vp;
+
+       f_print(fout, "\tif (!ioctl(0, I_LOOK, mname) &&\n");
+       f_print(fout, "\t\t(!strcmp(mname, \"sockmod\") ||");
+       f_print(fout, " !strcmp(mname, \"timod\"))) {\n");
+       f_print(fout, "\t\tchar *netid;\n");
+       if (!netflag) { /* Not included by -n option */
+               f_print(fout, "\t\tstruct netconfig *nconf = NULL;\n");
+               f_print(fout, "\t\tSVCXPRT *%s;\n", TRANSP);
+       }
+       if( timerflag )
+         f_print(fout, "\t\tint pmclose;\n");
+/* not necessary, defined in /usr/include/stdlib */
+/*     f_print(fout, "\t\textern char *getenv();\n");*/
+       f_print(fout, "\n");
+       f_print(fout, "\t\t_rpcpmstart = 1;\n");
+       if (logflag)
+               open_log_file(infile, "\t\t");
+       f_print(fout, "\t\tif ((netid = getenv(\"NLSPROVIDER\")) == NULL) {\n");
+       sprintf(_errbuf, "cannot get transport name");
+       print_err_message("\t\t\t");
+       f_print(fout, "\t\t} else if ((nconf = getnetconfigent(netid)) == NULL) {\n");
+       sprintf(_errbuf, "cannot get transport info");
+       print_err_message("\t\t\t");
+       f_print(fout, "\t\t}\n");
+       /*
+        * A kludgy support for inetd services. Inetd only works with
+        * sockmod, and RPC works only with timod, hence all this jugglery
+        */
+       f_print(fout, "\t\tif (strcmp(mname, \"sockmod\") == 0) {\n");
+       f_print(fout, "\t\t\tif (ioctl(0, I_POP, 0) || ioctl(0, I_PUSH, \"timod\")) {\n");
+       sprintf(_errbuf, "could not get the right module");
+       print_err_message("\t\t\t\t");
+       f_print(fout, "\t\t\t\texit(1);\n");
+       f_print(fout, "\t\t\t}\n");
+       f_print(fout, "\t\t}\n");
+       if( timerflag )
+         f_print(fout, "\t\tpmclose = (t_getstate(0) != T_DATAXFER);\n");
+       f_print(fout, "\t\tif ((%s = svc_tli_create(0, nconf, NULL, 0, 0)) == NULL) {\n",
+                       TRANSP);
+       sprintf(_errbuf, "cannot create server handle");
+       print_err_message("\t\t\t");
+       f_print(fout, "\t\t\texit(1);\n");
+       f_print(fout, "\t\t}\n");
+       f_print(fout, "\t\tif (nconf)\n");
+       f_print(fout, "\t\t\tfreenetconfigent(nconf);\n");
+       for (l = defined; l != NULL; l = l->next) {
+               def = (definition *) l->val;
+               if (def->def_kind != DEF_PROGRAM) {
+                       continue;
+               }
+               for (vp = def->def.pr.versions; vp != NULL; vp = vp->next) {
+                       f_print(fout,
+                               "\t\tif (!svc_reg(%s, %s, %s, ",
+                               TRANSP, def->def_name, vp->vers_name);
+                       pvname(def->def_name, vp->vers_num);
+                       f_print(fout, ", 0)) {\n");
+                       (void) sprintf(_errbuf, "unable to register (%s, %s).",
+                                       def->def_name, vp->vers_name);
+                       print_err_message("\t\t\t");
+                       f_print(fout, "\t\t\texit(1);\n");
+                       f_print(fout, "\t\t}\n");
+               }
+       }
+       if (timerflag) {
+               f_print(fout, "\t\tif (pmclose) {\n");
+               f_print(fout, "\t\t\t(void) signal(SIGALRM, %s closedown);\n",
+                               Cflag? "(SIG_PF)" : "(void(*)())" );
+               f_print(fout, "\t\t\t(void) alarm(_RPCSVC_CLOSEDOWN);\n");
+               f_print(fout, "\t\t}\n");
+       }
+       f_print(fout, "\t\tsvc_run();\n");
+       f_print(fout, "\t\texit(1);\n");
+       f_print(fout, "\t\t/* NOTREACHED */\n");
+       f_print(fout, "\t}\n");
+}
+
+/*
+ * Support for backgrounding the server if self started.
+ */
+static void
+write_rpc_svc_fg(char *infile, char *sp)
+{
+       f_print(fout, "#ifndef RPC_SVC_FG\n");
+       f_print(fout, "%sint size;\n", sp);
+       if( tirpcflag )
+               f_print(fout, "%sstruct rlimit rl;\n", sp);
+       if (inetdflag)
+               f_print(fout, "%sint pid, i;\n\n", sp);
+       f_print(fout, "%spid = fork();\n", sp);
+       f_print(fout, "%sif (pid < 0) {\n", sp);
+       f_print(fout, "%s\tperror(\"cannot fork\");\n", sp);
+       f_print(fout, "%s\texit(1);\n", sp);
+       f_print(fout, "%s}\n", sp);
+       f_print(fout, "%sif (pid)\n", sp);
+       f_print(fout, "%s\texit(0);\n", sp);
+       /* get number of file descriptors */
+       if( tirpcflag ) {
+         f_print(fout, "%srl.rlim_max = 0;\n", sp);
+         f_print(fout, "%sgetrlimit(RLIMIT_NOFILE, &rl);\n", sp);
+         f_print(fout, "%sif ((size = rl.rlim_max) == 0)\n", sp);
+         f_print(fout, "%s\texit(1);\n", sp);
+       } else {
+         f_print(fout, "%ssize = getdtablesize();\n", sp);
+       }
+
+       f_print(fout, "%sfor (i = 0; i < size; i++)\n", sp);
+       f_print(fout, "%s\t(void) close(i);\n", sp);
+       /* Redirect stderr and stdout to console */
+       f_print(fout, "%si = open(\"/dev/console\", 2);\n", sp);
+       f_print(fout, "%s(void) dup2(i, 1);\n", sp);
+       f_print(fout, "%s(void) dup2(i, 2);\n", sp);
+       /* This removes control of the controlling terminal */
+       if( tirpcflag )
+         f_print(fout, "%ssetsid();\n", sp);
+       else {
+         f_print(fout, "%si = open(\"/dev/tty\", 2);\n", sp);
+         f_print(fout, "%sif (i >= 0) {\n", sp);
+         f_print(fout, "%s\t(void) ioctl(i, TIOCNOTTY, (char *)NULL);\n", sp);;
+         f_print(fout, "%s\t(void) close(i);\n", sp);
+         f_print(fout, "%s}\n", sp);
+       }
+       if (!logflag)
+               open_log_file(infile, sp);
+       f_print(fout, "#endif\n");
+       if (logflag)
+               open_log_file(infile, sp);
+}
+
+static void
+open_log_file(char *infile, char *sp)
+{
+       char *s;
+
+       s = strrchr(infile, '.');
+       if (s) 
+               *s = '\0';
+       f_print(fout,"%sopenlog(\"%s\", LOG_PID, LOG_DAEMON);\n", sp, infile);
+       if (s)
+               *s = '.';
+}
+
+
+
+
+/*
+ * write a registration for the given transport for Inetd
+ */
+void
+write_inetd_register(char *transp)
+{
+       list *l;
+       definition *def;
+       version_list *vp;
+       char *sp;
+       int isudp;
+       char tmpbuf[32];
+
+       if (inetdflag)
+               sp = "\t";
+       else
+               sp = "";
+       if (streq(transp, "udp"))
+               isudp = 1;
+       else
+               isudp = 0;
+       f_print(fout, "\n");
+       if (inetdflag) {
+               f_print(fout, "\tif ((_rpcfdtype == 0) || (_rpcfdtype == %s)) {\n",
+                               isudp ? "SOCK_DGRAM" : "SOCK_STREAM");
+       }
+       f_print(fout, "%s\t%s = svc%s_create(%s",
+               sp, TRANSP, transp, inetdflag? "sock": "RPC_ANYSOCK");
+       if (!isudp)
+               f_print(fout, ", 0, 0");
+       f_print(fout, ");\n");
+       f_print(fout, "%s\tif (%s == NULL) {\n", sp, TRANSP);
+       (void) sprintf(_errbuf, "cannot create %s service.", transp);
+       (void) sprintf(tmpbuf, "%s\t\t", sp);
+       print_err_message(tmpbuf);
+       f_print(fout, "%s\t\texit(1);\n", sp);
+       f_print(fout, "%s\t}\n", sp);
+
+       if (inetdflag) {
+               f_print(fout, "%s\tif (!_rpcpmstart)\n\t", sp);
+               f_print(fout, "%s\tproto = IPPROTO_%s;\n",
+                               sp, isudp ? "UDP": "TCP");
+       }
+       for (l = defined; l != NULL; l = l->next) {
+               def = (definition *) l->val;
+               if (def->def_kind != DEF_PROGRAM) {
+                       continue;
+               }
+               for (vp = def->def.pr.versions; vp != NULL; vp = vp->next) {
+                       f_print(fout, "%s\tif (!svc_register(%s, %s, %s, ",
+                               sp, TRANSP, def->def_name, vp->vers_name);
+                       pvname(def->def_name, vp->vers_num);
+                       if (inetdflag)
+                               f_print(fout, ", proto)) {\n");
+                       else 
+                               f_print(fout, ", IPPROTO_%s)) {\n",
+                                       isudp ? "UDP": "TCP");
+                       (void) sprintf(_errbuf, "unable to register (%s, %s, %s).",
+                                       def->def_name, vp->vers_name, transp);
+                       print_err_message(tmpbuf);
+                       f_print(fout, "%s\t\texit(1);\n", sp);
+                       f_print(fout, "%s\t}\n", sp);
+               }
+       }
+       if (inetdflag)
+               f_print(fout, "\t}\n");
+}
diff --git a/tools/rpcgen/rpc_tblout.c b/tools/rpcgen/rpc_tblout.c
new file mode 100644 (file)
index 0000000..5ce4015
--- /dev/null
@@ -0,0 +1,167 @@
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part.  Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user or with the express written consent of
+ * Sun Microsystems, Inc.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California  94043
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)rpc_tblout.c 1.4 89/02/22 (C) 1988 SMI";
+#endif
+
+/*
+ * rpc_tblout.c, Dispatch table outputter for the RPC protocol compiler
+ */
+#include <stdio.h>
+#include <string.h>
+#include "rpc_parse.h"
+#include "rpc_util.h"
+#include "rpc_output.h"
+
+static void    write_table(definition *def);
+static void    printit(char *prefix, char *type);
+
+#define TABSIZE                8
+#define TABCOUNT       5
+#define TABSTOP                (TABSIZE*TABCOUNT)
+
+static char tabstr[TABCOUNT+1] = "\t\t\t\t\t";
+
+static char tbl_hdr[] = "struct rpcgen_table %s_table[] = {\n";
+static char tbl_end[] = "};\n";
+
+static char null_entry[] = "\n\t(char *(*)())0,\n\
+ \t(xdrproc_t) xdr_void,\t\t\t0,\n\
+ \t(xdrproc_t) xdr_void,\t\t\t0,\n";
+
+
+static char tbl_nproc[] = "int %s_nproc =\n\tsizeof(%s_table)/sizeof(%s_table[0]);\n\n";
+
+void
+write_tables(void)
+{
+       list *l;
+       definition *def;
+
+       f_print(fout, "\n");
+       for (l = defined; l != NULL; l = l->next) {
+               def = (definition *) l->val;
+               if (def->def_kind == DEF_PROGRAM) {
+                       write_table(def);
+               }
+       }
+}
+
+static void
+write_table(definition *def)
+{
+       version_list *vp;
+       proc_list *proc;
+       int current;
+       int expected;
+       char progvers[100];
+       int warning;
+
+       for (vp = def->def.pr.versions; vp != NULL; vp = vp->next) {
+               warning = 0;
+               s_print(progvers, "%s_%s",
+                   locase(def->def_name), vp->vers_num);
+               /* print the table header */
+               f_print(fout, tbl_hdr, progvers);
+
+               if (nullproc(vp->procs)) {
+                       expected = 0;
+               } else {
+                       expected = 1;
+                       f_print(fout, null_entry);
+               }
+               for (proc = vp->procs; proc != NULL; proc = proc->next) {
+                       current = atoi(proc->proc_num);
+                       if (current != expected++) {
+                               f_print(fout,
+                       "\n/*\n * WARNING: table out of order\n */\n");
+                               if (warning == 0) {
+                                       f_print(stderr,
+                                   "WARNING %s table is out of order\n",
+                                           progvers);
+                                       warning = 1;
+                                       nonfatalerrors = 1;
+                               }
+                               expected = current + 1;
+                       }
+                       f_print(fout, "\n\t(char *(*)())RPCGEN_ACTION(");
+
+                       /* routine to invoke */
+                       if( Cflag && !newstyle )
+                         pvname_svc(proc->proc_name, vp->vers_num);
+                       else {
+                         if( newstyle )
+                           f_print( fout, "_");   /* calls internal func */
+                         pvname(proc->proc_name, vp->vers_num);
+                       }
+                       f_print(fout, "),\n");
+
+                       /* argument info */
+                       if( proc->arg_num > 1 )
+                         printit((char*) NULL, proc->args.argname );
+                       else  
+                         /* do we have to do something special for newstyle */
+                         printit( proc->args.decls->decl.prefix,
+                                 proc->args.decls->decl.type );
+                       /* result info */
+                       printit(proc->res_prefix, proc->res_type);
+               }
+
+               /* print the table trailer */
+               f_print(fout, tbl_end);
+               f_print(fout, tbl_nproc, progvers, progvers, progvers);
+       }
+}
+
+static void
+printit(char *prefix, char *type)
+{
+       int len;
+       int tabs;
+
+
+       len = fprintf(fout, "\txdr_%s,", stringfix(type));
+       /* account for leading tab expansion */
+       len += TABSIZE - 1;
+       /* round up to tabs required */
+       tabs = (TABSTOP - len + TABSIZE - 1)/TABSIZE;
+       f_print(fout, "%s", &tabstr[TABCOUNT-tabs]);
+
+       if (streq(type, "void")) {
+               f_print(fout, "0");
+       } else {
+               f_print(fout, "sizeof ( ");
+               /* XXX: should "follow" be 1 ??? */
+               ptype(prefix, type, 0);
+               f_print(fout, ")");
+       }
+       f_print(fout, ",\n");
+}
diff --git a/tools/rpcgen/rpc_util.c b/tools/rpcgen/rpc_util.c
new file mode 100644 (file)
index 0000000..2fd5f59
--- /dev/null
@@ -0,0 +1,481 @@
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part.  Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user or with the express written consent of
+ * Sun Microsystems, Inc.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California  94043
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)rpc_util.c 1.11 89/02/22 (C) 1987 SMI";
+#endif
+
+/*
+ * rpc_util.c, Utility routines for the RPC protocol compiler 
+ */
+#include <stdio.h>
+#include <memory.h>
+#include <ctype.h>
+#include <unistd.h>
+#include "rpc_scan.h"
+#include "rpc_parse.h"
+#include "rpc_util.h"
+
+static void    printwhere(void);
+
+
+#define ARGEXT "argument"
+
+char curline[MAXLINESIZE];     /* current read line */
+char *where = curline;         /* current point in line */
+int linenum = 0;               /* current line number */
+
+char *infilename;              /* input filename */
+
+#define NFILES 7
+char *outfiles[NFILES];                /* output file names */
+int nfiles;
+
+FILE *fout;                    /* file pointer of current output */
+FILE *fin;                     /* file pointer of current input */
+
+list *defined;                 /* list of defined things */
+
+/*
+ * Reinitialize the world 
+ */
+void
+reinitialize(void)
+{
+       memset(curline, 0, MAXLINESIZE);
+       where = curline;
+       linenum = 0;
+       defined = NULL;
+}
+
+/*
+ * string equality 
+ */
+int
+streq(char *a, char *b)
+{
+       return (strcmp(a, b) == 0);
+}
+
+/*
+ * find a value in a list 
+ */
+definition *
+findval(list *lst, char *val, int (*cmp)(definition *, char *))
+{
+         
+       for (; lst != NULL; lst = lst->next) {
+               if ((*cmp) (lst->val, val)) {
+                       return (lst->val);
+               }
+       }
+       return (NULL);
+}
+
+/*
+ * store a value in a list 
+ */
+void
+storeval(lstp, val)
+       list **lstp;
+       definition *val;
+{
+       list **l;
+       list *lst;
+
+       
+       for (l = lstp; *l != NULL; l = (list **) & (*l)->next);
+       lst = ALLOC(list);
+       lst->val = val;
+       lst->next = NULL;
+       *l = lst;
+}
+
+static int
+findit(definition *def, char *type)
+{
+       return (streq(def->def_name, type));
+}
+
+static char *
+fixit(char *type, char *orig)
+{
+       definition *def;
+
+       def = (definition *) FINDVAL(defined, type, findit);
+       if (def == NULL || def->def_kind != DEF_TYPEDEF) {
+               return (orig);
+       }
+       switch (def->def.ty.rel) {
+       case REL_VECTOR:
+               return (def->def.ty.old_type);
+       case REL_ALIAS:
+               return (fixit(def->def.ty.old_type, orig));
+       default:
+               return (orig);
+       }
+}
+
+char *
+fixtype(char *type)
+{
+       return (fixit(type, type));
+}
+
+char *
+stringfix(char *type)
+{
+       if (streq(type, "string")) {
+               return ("wrapstring");
+       } else {
+               return (type);
+       }
+}
+
+void
+ptype(char *prefix, char *type, int follow)
+{
+       if (prefix != NULL) {
+               if (streq(prefix, "enum")) {
+                       f_print(fout, "enum ");
+               } else {
+                       f_print(fout, "struct ");
+               }
+       }
+       if (streq(type, "bool")) {
+               f_print(fout, "bool_t ");
+       } else if (streq(type, "string")) {
+               f_print(fout, "char *");
+       } else {
+               f_print(fout, "%s ", follow ? fixtype(type) : type);
+       }
+}
+
+static int
+typedefed(definition *def, char *type)
+{
+       if (def->def_kind != DEF_TYPEDEF || def->def.ty.old_prefix != NULL) {
+               return (0);
+       } else {
+               return (streq(def->def_name, type));
+       }
+}
+
+int
+isvectordef(char *type, relation rel)
+{
+       definition *def;
+
+       for (;;) {
+               switch (rel) {
+               case REL_VECTOR:
+                       return (!streq(type, "string"));
+               case REL_ARRAY:
+                       return (0);
+               case REL_POINTER:
+                       return (0);
+               case REL_ALIAS:
+                       def = (definition *) FINDVAL(defined, type, typedefed);
+                       if (def == NULL) {
+                               return (0);
+                       }
+                       type = def->def.ty.old_type;
+                       rel = def->def.ty.rel;
+               }
+       }
+}
+
+char *
+locase(char *str)
+{
+       char c;
+       static char buf[100];
+       char *p = buf;
+
+       while ((c = *str++) != '\0') {
+               *p++ = (c >= 'A' && c <= 'Z') ? (c - 'A' + 'a') : c;
+       }
+       *p = 0;
+       return (buf);
+}
+
+void
+pvname_svc(char *pname, char *vnum)
+{
+       f_print(fout, "%s_%s_svc", locase(pname), vnum);
+}
+
+void
+pvname(char *pname, char *vnum)
+{
+       f_print(fout, "%s_%s", locase(pname), vnum);
+}
+
+/*
+ * print a useful (?) error message, and then die 
+ */
+void
+error(char *msg)
+{
+       printwhere();
+       f_print(stderr, "%s, line %d: ", infilename, linenum);
+       f_print(stderr, "%s\n", msg);
+       crash();
+}
+
+/*
+ * Something went wrong, unlink any files that we may have created and then
+ * die. 
+ */
+void
+crash(void)
+{
+       int i;
+
+       for (i = 0; i < nfiles; i++) {
+               (void) unlink(outfiles[i]);
+       }
+       exit(1);
+}
+
+void
+record_open(char *file)
+{
+       if (nfiles < NFILES) {
+               outfiles[nfiles++] = file;
+       } else {
+               f_print(stderr, "too many files!\n");
+               crash();
+       }
+}
+
+static char expectbuf[100];
+static char *toktostr();
+
+/*
+ * error, token encountered was not the expected one 
+ */
+void
+expected1(exp1)
+       tok_kind exp1;
+{
+       s_print(expectbuf, "expected '%s'",
+               toktostr(exp1));
+       error(expectbuf);
+}
+
+/*
+ * error, token encountered was not one of two expected ones 
+ */
+void
+expected2(exp1, exp2)
+       tok_kind exp1, exp2;
+{
+       s_print(expectbuf, "expected '%s' or '%s'",
+               toktostr(exp1),
+               toktostr(exp2));
+       error(expectbuf);
+}
+
+/*
+ * error, token encountered was not one of 3 expected ones 
+ */
+void
+expected3(exp1, exp2, exp3)
+       tok_kind exp1, exp2, exp3;
+{
+       s_print(expectbuf, "expected '%s', '%s' or '%s'",
+               toktostr(exp1),
+               toktostr(exp2),
+               toktostr(exp3));
+       error(expectbuf);
+}
+
+void
+tabify(f, tab)
+       FILE *f;
+       int tab;
+{
+       while (tab--) {
+               (void) fputc('\t', f);
+       }
+}
+
+
+static token tokstrings[] = {
+                            {TOK_IDENT, "identifier"},
+                            {TOK_CONST, "const"},
+                            {TOK_RPAREN, ")"},
+                            {TOK_LPAREN, "("},
+                            {TOK_RBRACE, "}"},
+                            {TOK_LBRACE, "{"},
+                            {TOK_LBRACKET, "["},
+                            {TOK_RBRACKET, "]"},
+                            {TOK_STAR, "*"},
+                            {TOK_COMMA, ","},
+                            {TOK_EQUAL, "="},
+                            {TOK_COLON, ":"},
+                            {TOK_SEMICOLON, ";"},
+                            {TOK_UNION, "union"},
+                            {TOK_STRUCT, "struct"},
+                            {TOK_SWITCH, "switch"},
+                            {TOK_CASE, "case"},
+                            {TOK_DEFAULT, "default"},
+                            {TOK_ENUM, "enum"},
+                            {TOK_TYPEDEF, "typedef"},
+                            {TOK_INT, "int"},
+                            {TOK_SHORT, "short"},
+                            {TOK_LONG, "long"},
+                            {TOK_UNSIGNED, "unsigned"},
+                            {TOK_DOUBLE, "double"},
+                            {TOK_FLOAT, "float"},
+                            {TOK_CHAR, "char"},
+                            {TOK_STRING, "string"},
+                            {TOK_OPAQUE, "opaque"},
+                            {TOK_BOOL, "bool"},
+                            {TOK_VOID, "void"},
+                            {TOK_PROGRAM, "program"},
+                            {TOK_VERSION, "version"},
+                            {TOK_EOF, "??????"}
+};
+
+static char *
+toktostr(kind)
+       tok_kind kind;
+{
+       token *sp;
+
+       for (sp = tokstrings; sp->kind != TOK_EOF && sp->kind != kind; sp++);
+       return (sp->str);
+}
+
+static void
+printbuf(void)
+{
+       char c;
+       int i;
+       int cnt;
+
+#      define TABSIZE 4
+
+       for (i = 0; (c = curline[i]) != '\0'; i++) {
+               if (c == '\t') {
+                       cnt = 8 - (i % TABSIZE);
+                       c = ' ';
+               } else {
+                       cnt = 1;
+               }
+               while (cnt--) {
+                       (void) fputc(c, stderr);
+               }
+       }
+}
+
+static void
+printwhere(void)
+{
+       int i;
+       char c;
+       int cnt;
+
+       printbuf();
+       for (i = 0; i < where - curline; i++) {
+               c = curline[i];
+               if (c == '\t') {
+                       cnt = 8 - (i % TABSIZE);
+               } else {
+                       cnt = 1;
+               }
+               while (cnt--) {
+                       (void) fputc('^', stderr);
+               }
+       }
+       (void) fputc('\n', stderr);
+}
+
+char * 
+make_argname(char *pname, char *vname) 
+{
+       char *name;
+       
+       name = malloc(strlen(pname) + strlen(vname) + strlen(ARGEXT) + 3);
+       if (!name) {
+               fprintf(stderr, "failed in malloc");
+               exit(1);
+       }
+       sprintf(name, "%s_%s_%s", locase(pname), vname, ARGEXT);
+       return(name);
+}
+
+bas_type *typ_list_h;
+bas_type *typ_list_t;
+
+void
+add_type(int len, char *type)
+{
+       bas_type       *ptr;
+
+
+       if ((ptr = (bas_type *) malloc(sizeof(bas_type))) == (bas_type *) NULL) {
+               fprintf(stderr, "failed in malloc");
+               exit(1);
+       }
+       ptr->name = type;
+       ptr->length = len;
+       ptr->next = NULL;
+       if (typ_list_t == NULL) {
+
+               typ_list_t = ptr;
+               typ_list_h = ptr;
+       } else {
+
+               typ_list_t->next = ptr;
+               typ_list_t = ptr;
+       }
+}
+
+
+bas_type *
+find_type(char *type)
+{
+       bas_type       *ptr;
+
+       ptr = typ_list_h;
+
+
+       while (ptr != NULL) {
+               if (strcmp(ptr->name, type) == 0)
+                       return (ptr);
+               else
+                       ptr = ptr->next;
+       };
+       return (NULL);
+}
+
diff --git a/tools/rpcgen/rpc_util.h b/tools/rpcgen/rpc_util.h
new file mode 100644 (file)
index 0000000..b0268bb
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part.  Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user or with the express written consent of
+ * Sun Microsystems, Inc.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California  94043
+ */
+
+/*      @(#)rpc_util.h  1.5  90/08/29  (C) 1987 SMI   */
+
+/*
+ * rpc_util.h, Useful definitions for the RPC protocol compiler 
+ */
+
+#include <stdlib.h>
+
+#define alloc(size)            malloc((unsigned)(size))
+#define ALLOC(object)   (object *) malloc(sizeof(object))
+
+#define s_print        (void) sprintf
+#define f_print (void) fprintf
+
+struct list {
+       definition *val;
+       struct list *next;
+};
+typedef struct list list;
+
+#define PUT 1
+#define GET 2
+
+/*
+ * Global variables 
+ */
+#define MAXLINESIZE 1024
+extern char curline[MAXLINESIZE];
+extern char *where;
+extern int linenum;
+
+extern char *infilename;
+extern FILE *fout;
+extern FILE *fin;
+
+extern list *defined;
+
+
+extern bas_type *typ_list_h;
+extern bas_type *typ_list_t;
+
+/*
+ * All the option flags
+ */
+extern int inetdflag;
+extern int pmflag;   
+extern int tblflag;
+extern int logflag;
+extern int newstyle;
+extern int Cflag;      /* C++ flag */
+extern int tirpcflag;  /* flag for generating tirpc code */
+extern int Inline;     /* if this is 0, then do not generate inline code */
+
+/*
+ * Other flags related with inetd jumpstart.
+ */
+extern int indefinitewait;
+extern int exitnow;
+extern int timerflag;
+
+extern int nonfatalerrors;
+
+/*
+ * rpc_util routines 
+ */
+void storeval();
+
+#define STOREVAL(list,item)    \
+       storeval(list,item)
+
+definition *findval();
+
+#define FINDVAL(list,item,finder) \
+       findval(list, item, finder)
+
+
+/*
+ * rpc_cout routines
+ */
+void            cprint(void);
+void            emit(definition *);
+
+/*
+ * rpc_hout routines
+ */
+void            print_datadef(definition *);
+void            print_funcdef(definition *);
+
+/*
+ * rpc_svcout routines
+ */
+void            write_most(char *, int, int);
+void            write_register(void);
+void           write_netid_register(char *);
+void           write_nettype_register(char *);
+void           write_inetd_register(char *);
+void            write_rest(void);
+void            write_programs(char *);
+void            write_svc_aux(int);
+
+/*
+ * rpc_clntout routines
+ */
+void            write_stubs(void);
+void           printarglist(proc_list *, char *, char *);
+
+/*
+ * rpc_tblout routines
+ */
+void            write_tables(void);
+
+/*
+ * rpc_util
+ */
+void            pvname_svc(char *, char *);
+void            pvname(char *, char *);
+void            ptype(char *, char *, int);
+char *         make_argname(char *, char *);
+void           add_type(int, char *);
+void           reinitialize(void);
+void           crash(void);
+void           error(char *);
+char           *fixtype(char *);
+char           *stringfix(char *);
+char           *locase(char *);
+int             isvectordef(char *, relation);
+int             streq(char *, char *);
+void            tabify(FILE *, int);
+void            record_open(char *);
+bas_type       *find_type(char *type);
+
+/*
+ * rpc_sample
+ */
+void           write_sample_svc(definition *);
+int            write_sample_clnt(definition *);
+void           write_sample_clnt_main(void);
+void           add_sample_msg(void);
diff --git a/tools/rpcgen/rpcgen.new.1 b/tools/rpcgen/rpcgen.new.1
new file mode 100644 (file)
index 0000000..6f4897f
--- /dev/null
@@ -0,0 +1,422 @@
+.\" @(#)rpcgen.new.1   1.1 90/11/09 TIRPC 1.0; from 40.10 of 10/10/89
+.\" Copyright (c) 1988,1990 Sun Microsystems, Inc. - All Rights Reserved.
+.nr X
+.if \nX=0 .ds x} rpcgen 1 "" "\&"
+.if \nX=1 .ds x} rpcgen 1 ""
+.if \nX=2 .ds x} rpcgen 1 "" "\&"
+.if \nX=3 .ds x} rpcgen "" "" "\&"
+.TH \*(x}
+.SH NAME
+\f4rpcgen\f1 \- an RPC protocol compiler
+.SH SYNOPSIS
+.ft 4
+.nf
+rpcgen \f2infile\f4
+.fi
+.ft 1
+.br
+.ft 4
+.nf
+rpcgen [\-D\f2name\f4[=\f2value\f4]] [\-T] [\-K \f2secs\fP] \f2infile\f4
+.fi
+.ft 1
+.br
+.ft 4
+.nf
+rpcgen \-c|\-h|\-l|\-m|\-t [\-o \f2outfile\f4 ] \f2infile\f4
+.fi
+.ft 1
+.br
+.ft 4
+.nf
+rpcgen \-s \f2nettype\f4 [\-o \f2outfile\f4] \f2infile\f4
+.fi
+.ft 1
+.br
+.ft 4
+.nf
+rpcgen \-n \f2netid\f4 [\-o \f2outfile\f4] \f2infile\f4
+.ft 1
+.SH DESCRIPTION
+.P
+\f4rpcgen\f1
+is a tool that generates C code to implement an RPC protocol.
+The input to
+\f4rpcgen\f1
+is a language similar to C known as
+RPC Language (Remote Procedure Call Language).
+.P
+\f4rpcgen\f1
+is normally used as in the first synopsis where 
+it takes an input file and generates up to four output files.
+If the
+\f2infile\f1
+is named
+\f4proto.x\f1,
+then
+\f4rpcgen\f1
+will generate a header file in
+\f4proto.h\f1,
+XDR routines in
+\f4proto_xdr.c\f1,
+server-side stubs in
+\f4proto_svc.c\f1,
+and client-side stubs in
+\f4proto_clnt.c\f1.
+With the
+\f4\-T\f1
+option,
+it will also generate the RPC dispatch table in
+\f4proto_tbl.i\f1.
+With the
+\f4\-Sc\f1
+option,
+it will also generate  sample code which would illustrate how to use the
+remote procedures on the client side. This code would be created in 
+\f4proto_client.c\f1.
+With the
+\f4\-Ss\f1
+option,
+it will also generate a sample server code which would illustrate how to write
+the remote procedures. This code would be created in 
+\f4proto_server.c\f1.
+.P
+The server created can be started both by the port monitors
+(for example, \f4inetd\f1 or \f4listen\f1)
+or by itself.
+When it is started by a port monitor,
+it creates servers only for the transport for which 
+the file descriptor \f40\fP was passed.
+The name of the transport must be specified
+by setting up the environmental variable
+\f4PM_TRANSPORT\f1.
+When the server generated by
+\f4rpcgen\f1
+is executed,
+it creates server handles for all the transports
+specified in
+\f4NETPATH\f1
+environment variable,
+or if it is unset,
+it creates server handles for all the visible transports from
+\f4/etc/netconfig\f1
+file.
+Note:
+the transports are chosen at run time and not at compile time.
+When the server is self-started,
+it backgrounds itself by default.
+A special define symbol
+\f4RPC_SVC_FG\f1
+can be used to run the server process in foreground.
+.P
+The second synopsis provides special features which allow
+for the creation of more sophisticated RPC servers.
+These features include support for user provided
+\f4#defines\f1
+and RPC dispatch tables.
+The entries in the RPC dispatch table contain:
+.RS
+.PD 0
+.TP 3
+\(bu
+pointers to the service routine corresponding to that procedure,
+.TP
+\(bu
+a pointer to the input and output arguments
+.TP
+\(bu
+the size of these routines
+.PD
+.RE
+A server can use the dispatch table to check authorization 
+and then to execute the service routine; 
+a client library may use it to deal with the details of storage
+management and XDR data conversion.
+.P
+The other three synopses shown above are used when 
+one does not want to generate all the output files,
+but only a particular one.
+Some examples of their usage is described in the
+EXAMPLE
+section below.
+When 
+\f4rpcgen\f1
+is executed with the
+\f4\-s\f1
+option,
+it creates servers for that particular class of transports.
+When
+executed with the
+\f4\-n\f1
+option,
+it creates a server for the transport specified by
+\f2netid\f1.
+If
+\f2infile\f1
+is not specified,
+\f4rpcgen\f1
+accepts the standard input.
+.P
+The C preprocessor,
+\f4cc \-E\f1
+[see \f4cc\fP(1)],
+is run on the input file before it is actually interpreted by
+\f4rpcgen\f1.
+For each type of output file,
+\f4rpcgen\f1
+defines a special preprocessor symbol for use by the
+\f4rpcgen\f1
+programmer:
+.P
+.PD 0
+.TP 12
+\f4RPC_HDR\f1
+defined when compiling into header files
+.TP
+\f4RPC_XDR\f1
+defined when compiling into XDR routines
+.TP
+\f4RPC_SVC\f1
+defined when compiling into server-side stubs
+.TP
+\f4RPC_CLNT\f1
+defined when compiling into client-side stubs
+.TP
+\f4RPC_TBL\f1
+defined when compiling into RPC dispatch tables
+.PD
+.P
+Any line beginning with
+`\f4%\f1'
+is passed directly into the output file,
+uninterpreted by
+\f4rpcgen\f1.
+.P
+For every data type referred to in
+\f2infile\f1,
+\f4rpcgen\f1
+assumes that there exists a
+routine with the string
+\f4xdr_\f1
+prepended to the name of the data type.
+If this routine does not exist in the RPC/XDR
+library, it must be provided.
+Providing an undefined data type
+allows customization of XDR routines.
+.br
+.ne 10
+.P
+The following options are available:
+.TP
+\f4\-a\f1
+Generate all the files including sample code for client and server side.
+.TP
+\f4\-b\f1
+This generates code for the SunOS4.1 style of rpc. It is only
+for backward compatibilty. By default rpcgen generates code for
+Transport Independent RPC that is in Svr4 systems.
+.TP
+\f4\-c\f1
+Compile into XDR routines.
+.TP
+\f4\-C\f1
+Generate code in ANSI C. This option also generates code that could be
+compiled with the C++ compiler.
+.TP
+\f4\-D\f2name\f4[=\f2value\f4]\f1
+Define a symbol
+\f2name\f1.
+Equivalent to the
+\f4#define\f1
+directive in the source.
+If no
+\f2value\f1
+is given,
+\f2value\f1
+is defined as \f41\f1.
+This option may be specified more than once.
+.TP
+\f4\-h\f1
+Compile into
+\f4C\f1
+data-definitions (a header file).
+\f4\-T\f1
+option can be used in conjunction to produce a 
+header file which supports RPC dispatch tables.
+.TP
+\f4-K\f2 secs\f1
+By default, services created using \f4rpcgen\fP wait \f4120\fP seconds
+after servicing a request before exiting.
+That interval can be changed using the \f4-K\fP flag.
+To create a server that exits immediately upon servicing a request,
+\f4-K\ 0\fP can be used.
+To create a server that never exits, the appropriate argument is
+\f4-K\ -1\fP.
+.IP
+When monitoring for a server,
+some portmonitors, like
+\f4listen\fP(1M),
+.I always
+spawn a new process in response to a service request.
+If it is known that a server will be used with such a monitor, the
+server should exit immediately on completion.
+For such servers, \f4rpcgen\fP should be used with \f4-K\ -1\fP.
+.TP
+\f4\-l\f1
+Compile into client-side stubs.
+.TP
+\f4\-m\f1
+Compile into server-side stubs,
+but do not generate a \(lqmain\(rq routine.
+This option is useful for doing callback-routines 
+and for users who need to write their own 
+\(lqmain\(rq routine to do initialization.
+.TP
+\f4\-n \f2netid\f1
+Compile into server-side stubs for the transport
+specified by
+\f2netid\f1.
+There should be an entry for
+\f2netid\f1
+in the
+netconfig database.
+This option may be specified more than once,
+so as to compile a server that serves multiple transports.
+.TP
+\f4\-N\f1
+Use the newstyle of rpcgen. This allows procedures to have multiple arguments. 
+It also uses the style of parameter passing that closely resembles C. So, when 
+passing an argument to a remote procedure you do not have to pass a pointer to
+the argument but the argument itself. This behaviour is different from the oldstyle
+of rpcgen generated code. The newstyle is not the default case because of 
+backward compatibility.
+.TP
+\f4\-o \f2outfile\f1
+Specify the name of the output file.
+If none is specified,
+standard output is used
+(\f4\-c\f1,
+\f4\-h\f1,
+\f4\-l\f1,
+\f4\-m\f1,
+\f4\-n\f1,
+\f4\-s\f1,
+\f4\-s\Sc,
+\f4\-s\Ss
+and
+\f4\-t\f1
+modes only).
+.TP
+\f4\-s \f2nettype\f1
+Compile into server-side stubs for all the 
+transports belonging to the class
+\f2nettype\f1.
+The supported classes are
+\f4netpath\f1,
+\f4visible\f1,
+\f4circuit_n\f1,
+\f4circuit_v\f1,
+\f4datagram_n\f1,
+\f4datagram_v\f1,
+\f4tcp\f1,
+and
+\f4udp\f1
+[see \f4rpc\fP(3N)
+for the meanings associated with these classes].
+This option may be specified more than once.
+Note:
+the transports are chosen at run time and not at compile time.
+.TP
+\f4\-Sc\f1
+Generate sample code to show the use of remote procedure and how to bind
+to the server before calling the client side stubs generated by rpcgen.
+.TP
+\f4\-Ss\f1
+Generate skeleton code for the remote procedures on the server side. You would need
+to fill in the actual code for the remote procedures.
+.TP
+\f4\-t\f1
+Compile into RPC dispatch table.
+.TP
+\f4\-T\f1
+Generate the code to support RPC dispatch tables.
+.P
+The options 
+\f4\-c\f1,
+\f4\-h\f1,
+\f4\-l\f1,
+\f4\-m\f1,
+\f4\-s\f1
+and
+\f4\-t\f1
+are used exclusively to generate a particular type of file,
+while the options
+\f4\-D\f1
+and
+\f4\-T\f1
+are global and can be used with the other options.
+.br
+.ne 5
+.SH NOTES
+The RPC Language does not support nesting of structures.
+As a work-around,
+structures can be declared at the top-level,
+and their name used inside other structures in 
+order to achieve the same effect.
+.P
+Name clashes can occur when using program definitions,
+since the apparent scoping does not really apply.
+Most of these can be avoided by giving 
+unique names for programs,
+versions,
+procedures and types.
+.P
+The server code generated with
+\f4\-n\f1
+option refers to the transport indicated by
+\f2netid\f1
+and hence is very site specific.
+.SH EXAMPLE
+The following example:
+.IP
+.ft 4
+$ rpcgen \-T prot.x
+.ft 1
+.P
+generates the five files:
+\f4prot.h\f1,
+\f4prot_clnt.c\f1,
+\f4prot_svc.c\f1,
+\f4prot_xdr.c\f1
+and
+\f4prot_tbl.i\f1.
+.P
+The following example sends the C data-definitions (header file)
+to the standard output.
+.IP
+.ft 4
+$ rpcgen \-h prot.x
+.ft 1
+.P
+To send the test version of the
+\f4-DTEST\f1,
+server side stubs for 
+all the transport belonging to the class
+\f4datagram_n\f1
+to standard output, use:
+.IP
+.ft 4
+$ rpcgen \-s datagram_n \-DTEST prot.x
+.ft 1
+.P
+To create the server side stubs for the transport indicated
+by
+\f2netid\f1
+\f4tcp\f1,
+use:
+.IP
+.ft 4
+$ rpcgen \-n tcp \-o prot_svc.c prot.x
+.ft 1
+.SH "SEE ALSO"
+\f4cc\fP(1).
diff --git a/utils/Makefile b/utils/Makefile
new file mode 100644 (file)
index 0000000..7e58325
--- /dev/null
@@ -0,0 +1,10 @@
+#
+# Makefile for linux-nfs/support
+#
+
+SUBDIRS        = exportfs mountd nfsd statd nfsstat rquotad showmount \
+         nhfsstone lockd
+
+include $(TOP)rules.mk
+
+# .EXPORT_ALL_VARIABLES:
diff --git a/utils/exportfs/Makefile b/utils/exportfs/Makefile
new file mode 100644 (file)
index 0000000..851a294
--- /dev/null
@@ -0,0 +1,13 @@
+#
+# dummy Makefile
+#
+
+PROGRAM        = exportfs
+OBJS   = exportfs.o
+LIBDEPS        = $(TOP)support/lib/libexport.a $(TOP)/support/lib/libnfs.a
+LIBS   = -lexport -lnfs
+MAN8   = exportfs
+MAN5   = exports
+
+include $(TOP)rules.mk
+
diff --git a/utils/exportfs/exportfs.c b/utils/exportfs/exportfs.c
new file mode 100644 (file)
index 0000000..44761f8
--- /dev/null
@@ -0,0 +1,391 @@
+/*
+ * utils/exportfs/exportfs.c
+ *
+ * Export file systems to knfsd
+ *
+ * Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de>
+ *
+ * Extensive changes, 1999, Neil Brown <neilb@cse.unsw.edu.au>
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <getopt.h>
+#include <netdb.h>
+#include <errno.h>
+#include "xmalloc.h"
+#include "misc.h"
+#include "nfslib.h"
+#include "exportfs.h"
+#include "xmalloc.h"
+#include "xlog.h"
+
+static void    export_all(int verbose);
+static void    unexport_all(int verbose);
+static void    exportfs(char *arg, char *options, int verbose);
+static void    unexportfs(char *arg, int verbose);
+static void    exports_update(int verbose);
+static void    dump(int verbose);
+static void    error(nfs_export *exp, int err);
+static void    usage(void);
+
+
+int
+main(int argc, char **argv)
+{
+       char    *options = NULL;
+       int     f_export = 1;
+       int     f_all = 0;
+       int     f_verbose = 0;
+       int     f_reexport = 0;
+       int     f_ignore = 0;
+       int     i, c;
+
+       xlog_open("exportfs");
+
+       while ((c = getopt(argc, argv, "aio:ruv")) != EOF) {
+               switch(c) {
+               case 'a':
+                       f_all = 1;
+                       break;
+               case 'i':
+                       f_ignore = 1;
+                       break;
+               case 'o':
+                       options = optarg;
+                       break;
+               case 'r':
+                       f_reexport = 1;
+                       f_all = 1;
+                       break;
+               case 'u':
+                       f_export = 0;
+                       break;
+               case 'v':
+                       f_verbose = 1;
+                       break;
+               default:
+                       usage();
+                       break;
+               }
+       }
+
+       if (optind != argc && f_all) {
+               fprintf(stderr,"exportfs: extra arguments are not permitted with -a or -r.\n");
+               return 1;
+       }
+       if (f_ignore && (f_all || ! f_export)) {
+               fprintf(stderr,"exportfs: -i not meaningful with -a, -r or -u.\n");
+               return 1;
+       }
+       if (f_reexport && ! f_export) {
+               fprintf(stderr, "exportfs: -r and -u are incompatible.\n");
+               return 1;
+       }
+       if (optind == argc && ! f_all) {
+               xtab_export_read();
+               dump(f_verbose);
+               return 0;
+       }
+
+       if (f_export && ! f_ignore)
+               export_read(_PATH_EXPORTS);
+       if (f_export) {
+               if (f_all)
+                       export_all(f_verbose);
+               else
+                       for (i = optind; i < argc ; i++)
+                               exportfs(argv[i], options, f_verbose);
+       }
+       /* note: xtab_*_read does not update entries if they already exist,
+        * so this will not lose new options
+        */
+       if (!f_reexport)
+               xtab_export_read();
+       if (!f_export) {
+               if (f_all)
+                       unexport_all(f_verbose);
+               else
+                       for (i = optind ; i < argc ; i++)
+                               unexportfs(argv[i], f_verbose);
+       }
+       rmtab_read();
+       xtab_mount_read();
+       exports_update(f_verbose);
+       xtab_export_write();
+       xtab_mount_write();
+
+       return 0;
+}
+
+/* we synchronise intention with reality.
+ * entries with m_mayexport get exported
+ * entries with m_exported but not m_mayexport get unexported
+ * looking at m_client->m_type == MCL_FQDN only
+ */
+static void
+exports_update(int verbose)
+{
+       nfs_export      *exp;
+
+       for (exp = exportlist[MCL_FQDN]; exp; exp=exp->m_next) {
+               if (exp->m_mayexport && (!exp->m_exported || exp->m_changed)) {
+                       if (verbose)
+                               printf("%sexporting %s:%s to kernel\n",
+                                      exp->m_exported ?"re":"",
+                                      exp->m_client->m_hostname,
+                                      exp->m_export.e_path);
+                       if (!export_export(exp))
+                               error(exp, errno);
+               }
+               if (exp->m_exported && ! exp->m_mayexport) {
+                       if (verbose)
+                               printf("unexporting %s:%s from kernel\n",
+                                      exp->m_client->m_hostname,
+                                      exp->m_export.e_path);
+                       if (!export_unexport(exp))
+                               error(exp, errno);
+               }
+       }
+}
+                       
+/*
+ * export_all finds all entries and
+ *    marks them xtabent and mayexport so that they get exported
+ */
+static void
+export_all(int verbose)
+{
+       nfs_export      *exp;
+       int             i;
+
+       for (i = 0; i < MCL_MAXTYPES; i++) {
+               for (exp = exportlist[i]; exp; exp = exp->m_next) {
+                       if (verbose)
+                               printf("exporting %s:%s\n",
+                                      exp->m_client->m_hostname, 
+                                      exp->m_export.e_path);
+                       exp->m_xtabent = 1;
+                       exp->m_mayexport = 1;
+                       exp->m_changed = 1;
+               }
+       }
+}
+/*
+ * unexport_all finds all entries that are mayexport, and
+ *    marks them not xtabent and not mayexport
+ */
+static void
+unexport_all(int verbose)
+{
+       nfs_export      *exp;
+       int             i;
+
+       for (i = 0; i < MCL_MAXTYPES; i++) {
+               for (exp = exportlist[i]; exp; exp = exp->m_next)
+                       if (exp->m_mayexport) {
+                               if (verbose) {
+                                       if (exp->m_exported) {
+                                               printf("unexporting %s:%s from kernel\n",
+                                                      exp->m_client->m_hostname,
+                                                      exp->m_export.e_path);
+                                       }
+                                       else {
+                                               printf("unexporting %s:%s\n",
+                                                       exp->m_client->m_hostname, 
+                                                       exp->m_export.e_path);
+                                       }
+                               }
+                               if (exp->m_exported && !export_unexport(exp))
+                                       error(exp, errno);
+                               exp->m_xtabent = 0;
+                               exp->m_mayexport = 0;
+                       }
+       }
+}
+
+
+static void
+exportfs(char *arg, char *options, int verbose)
+{
+       struct exportent *eep;
+       nfs_export      *exp;
+       struct hostent  *hp = NULL;
+       char            *path;
+       char            *hname = arg;
+       int             htype;
+
+       if ((path = strchr(arg, ':')) != NULL)
+               *path++ = '\0';
+
+       if (!path || *path != '/') {
+               fprintf(stderr, "Invalid exporting option: %s\n", arg);
+               return;
+       }
+
+       if ((htype = client_gettype(hname)) == MCL_FQDN &&
+           (hp = gethostbyname(hname)) != NULL) {
+               hp = hostent_dup (hp);
+               exp = export_find(hp, path);
+       } else {
+               exp = export_lookup(hname, path);
+       }
+
+       if (!exp) {
+               if (!(eep = mkexportent(hname, path, options)) ||
+                   !(exp = export_create(eep))) {
+                       if (hp) free (hp);
+                       return;
+               }
+       } else if (!updateexportent(&exp->m_export, options)) {
+               if (hp) free (hp);
+               return;
+       }
+
+       if (verbose)
+               printf("exporting %s:%s\n", exp->m_client->m_hostname, 
+                       exp->m_export.e_path);
+       exp->m_xtabent = 1;
+       exp->m_mayexport = 1;
+       exp->m_changed = 1;
+       if (hp) free (hp);
+}
+
+static void
+unexportfs(char *arg, int verbose)
+{
+       nfs_export      *exp;
+       struct hostent  *hp = NULL;
+       char            *path;
+       char            *hname = arg;
+       int             htype;
+
+       if ((path = strchr(arg, ':')) != NULL)
+               *path++ = '\0';
+
+       if (!path || *path != '/') {
+               fprintf(stderr, "Invalid unexporting option: %s\n",
+                       arg);
+               return;
+       }
+
+       if ((htype = client_gettype(hname)) == MCL_FQDN) {
+               if ((hp = gethostbyname(hname)) != 0) {
+                       hp = hostent_dup (hp);
+                       hname = (char *) hp->h_name;
+               }
+       }
+
+       for (exp = exportlist[htype]; exp; exp = exp->m_next) {
+               if (path && strcmp(path, exp->m_export.e_path))
+                       continue;
+               if (htype != exp->m_client->m_type
+                   || (htype == MCL_FQDN
+                       && !matchhostname(exp->m_export.e_hostname,
+                                         hname)))
+                       continue;
+               if (verbose) {
+                       if (exp->m_exported) {
+                               printf("unexporting %s:%s from kernel\n",
+                                      exp->m_client->m_hostname,
+                                      exp->m_export.e_path);
+                       }
+                       else {
+                               printf("unexporting %s:%s\n",
+                                       exp->m_client->m_hostname, 
+                                       exp->m_export.e_path);
+                       }
+               }
+               if (exp->m_exported && !export_unexport(exp))
+                       error(exp, errno);
+               exp->m_xtabent = 0;
+               exp->m_mayexport = 0;
+       }
+
+       if (hp) free (hp);
+}
+
+static char
+dumpopt(char c, char *fmt, ...)
+{
+       va_list ap;
+
+       va_start(ap, fmt);
+       printf("%c", c);
+       vprintf(fmt, ap);
+       va_end(ap);
+       return ',';
+}
+
+static void
+dump(int verbose)
+{
+       nfs_export      *exp;
+       struct exportent *ep;
+       int             htype;
+       char            *hname, c;
+
+       for (htype = 0; htype < MCL_MAXTYPES; htype++) {
+               for (exp = exportlist[htype]; exp; exp = exp->m_next) {
+                       ep = &exp->m_export;
+                       if (!exp->m_xtabent)
+                           continue; /* neilb */
+                       if (htype == MCL_ANONYMOUS)
+                               hname = "<world>";
+                       else
+                               hname = ep->e_hostname;
+                       if (strlen(ep->e_path) > 14)
+                               printf("%-14s\n\t\t%s", ep->e_path, hname);
+                       else
+                               printf("%-14s\t%s", ep->e_path, hname);
+                       if (!verbose) {
+                               printf("\n");
+                               continue;
+                       }
+                       c = '(';
+                       if (ep->e_flags & NFSEXP_READONLY)
+                               c = dumpopt(c, "ro");
+                       else
+                               c = dumpopt(c, "rw");
+                       if (ep->e_flags & NFSEXP_ASYNC)
+                               c = dumpopt(c, "async");
+                       if (ep->e_flags & NFSEXP_GATHERED_WRITES)
+                               c = dumpopt(c, "wdelay");
+                       if (ep->e_flags & NFSEXP_INSECURE_PORT)
+                               c = dumpopt(c, "insecure");
+                       if (ep->e_flags & NFSEXP_ROOTSQUASH)
+                               c = dumpopt(c, "root_squash");
+                       else
+                               c = dumpopt(c, "no_root_squash");
+                       if (ep->e_flags & NFSEXP_ALLSQUASH)
+                               c = dumpopt(c, "all_squash");
+                       if (ep->e_maptype == CLE_MAP_UGIDD)
+                               c = dumpopt(c, "mapping=ugidd");
+                       else if (ep->e_maptype == CLE_MAP_FILE)
+                               c = dumpopt(c, "mapping=file");
+                       if (ep->e_anonuid != -2)
+                               c = dumpopt(c, "anonuid=%d", ep->e_anonuid);
+                       if (ep->e_anongid != -2)
+                               c = dumpopt(c, "anongid=%d", ep->e_anongid);
+
+                       printf("%c\n", (c != '(')? ')' : ' ');
+               }
+       }
+}
+
+static void
+error(nfs_export *exp, int err)
+{
+       fprintf(stderr, "%s:%s: %s\n", exp->m_client->m_hostname, 
+               exp->m_export.e_path, strerror(err));
+}
+
+static void
+usage(void)
+{
+       fprintf(stderr, "usage: exportfs [-aruv] [host:/path]\n");
+       exit(1);
+}
diff --git a/utils/exportfs/exportfs.man b/utils/exportfs/exportfs.man
new file mode 100644 (file)
index 0000000..0cd5cca
--- /dev/null
@@ -0,0 +1,195 @@
+.\"
+.\" exportfs(8)
+.\" 
+.\" Copyright (C) 1995 Olaf Kirch <okir@monad.swb.de>
+.\" Modifications 1999 Neil Brown <neilb@cse.unsw.edu.au>
+.TH exportfs 8 "7 Sep 1999"
+.SH NAME
+exportfs \- maintain list of NFS exported file systems
+.SH SYNOPSIS
+.BI "/usr/sbin/exportfs [-avi] [-o " "options,.." "] [" "client:/path" " ..]
+.br
+.BI "/usr/sbin/exportfs -r [-v]"
+.br
+.BI "/usr/sbin/exportfs [-av] -u [" "client:/path" " ..]
+.br
+.BI "/usr/sbin/exportfs [-v]
+.br
+.SH DESCRIPTION
+The
+.B exportfs
+command is used to maintain the current table of exported file systems for
+NFS. This list is kept in a separate file named
+.BR /var/lib/nfs/xtab
+which is read by
+.B mountd
+when a remote host requests access to mount a file tree, and parts of
+the list which are active are kept in the kernel's export table.
+.P
+Normally this 
+.B xtab
+file is initialized with the list of all file systems named in
+.B /etc/exports 
+by invoking
+.BR "exportfs -a" .
+.P
+However, administrators can choose to add and delete individual file systems
+without modifying
+.B /etc/exports
+using
+.BR exportfs .
+.P
+Any export requests which identify a specific host (rather than a
+subnet or netgroup etc) are entered directly into the kernel's export
+table as well as being written to
+.BR /var/lib/nfs/xtab .
+Further, any mount points listed in
+.B /var/lib/nfs/rmtab
+which match a non host-specific export request will cause an
+appropriate export entry for the host given in
+.B rmtab
+to be entered
+into the kernel's export table.
+.SH OPTIONS
+.TP 
+.B -a
+Export or unexport all directories.
+.TP
+.BI "-o " options,...
+Specify a list of export options in the same manner as in
+.BR exports(5) .
+.TP
+.B -i
+Ignore the
+.B /etc/exports
+file, so that only default options and options given on the command
+line are used.
+.TP
+.B -r
+Reexport all directories. It synchronizes /var/lib/nfs/xtab
+with /etc/exports. It removes entries in /var/lib/nfs/xtab
+which are deleted from /etc/exports, and remove any entries from the
+kernel export table which are no longer valid.
+.TP
+.TP
+.B -u
+Unexport one or more directories.
+.TP
+.B -v
+Be verbose. When exporting or unexporting, show what's going on. When
+displaying the current export list, also display the list of export
+options.
+.SH DISCUSSION
+.\" -------------------- Exporting Directories --------------------
+.SS Exporting Directories
+The first synopsis shows how to invoke the command when adding new
+entries to the export table.  When using 
+.BR "exportfs -a" ,
+all directories in
+.B exports(5)
+are added to
+.B xtab
+and the resulting list is pushed into the kernel.
+.P
+The
+.I host:/path
+argument specifies the directory to export along with the host or hosts to
+export it to. All formats described in
+.B exports(5)
+are supported; to export a directory to the world, simply specify
+.IR :/path .
+.P
+The export options for a particular host/directory pair derive from
+several sources.  There is a set of default options which can be overridden by
+entries in
+.B /etc/exports
+(unless the
+.B -i
+option is given).
+In addition, the administrator may overide any options from these sources
+using the
+.B -o
+argument which takes a comma-separated list of options in the same fashion
+as one would specify them in
+.BR exports(5) .
+Thus,
+.B exportfs
+can also be used to modify the export options of an already exported
+directory.
+.P
+Modifications of the kernel export table used by
+.B nfsd(8)
+take place immediately after parsing the command line and updating the
+.B xtab
+file.
+.P
+The default export options are
+.BR async,ro,root_squash,no_delay .
+.\" -------------------- Unexporting Directories ------------------
+.SS Unexporting Directories
+The third synopsis shows how to unexported a currently exported directory.
+When using
+.BR "exportfs -ua" ,
+all entries listed in
+.B xtab
+are removed from the kernel export tables, and the file is cleared. This
+effectively shuts down all NFS activity.
+.P
+To remove individial export entries, one can specify a
+.I host:/path
+pair. This deletes the specified entry from
+.B xtab
+and removes the corresponding kernel entry (if any).
+.P
+.\" -------------------- Dumping the Export Table -----------------
+.SS Dumping the Export Table 
+Invoking
+.B exportfs
+without further options shows the current list of exported file systems.
+When giving the
+.B -v
+option, the list of flags pertaining to each export are shown in addition.
+.\" -------------------- EXAMPLES ---------------------------------
+.SH EXAMPLES
+The following adds all directories listed in
+.B /etc/exports to /var/lib/nfs/xtab
+and pushes the resulting export entries into the kernel:
+.P
+.nf
+.B "# exportfs -a
+.fi
+.P
+To export the
+.B /usr/tmp
+directory to host 
+.BR djando ,
+allowing asynchronous writes, one would do this:
+.P
+.nf
+.B "# exportfs -o async django:/usr/tmp
+.fi
+.\" -------------------- DEPENDENCIES -----------------------------
+.SH DEPENDENCIES
+Exporting to IP networks, DNS and NIS domains does not enable clients
+from these groups to access NFS immediately; rather, these sorts of
+exports are hints to
+.B mountd(8)
+to grant any mount requests from these clients.
+This is usually not a big problem, because any existing mounts are preserved
+in
+.B rmtab
+across reboots.
+.P
+When unexporting a network or domain entry, any current exports to members
+of this group will be checked against the remaining valid exports and
+if they themselves are nolonger valid they will be removed.
+.P
+.\" -------------------- SEE ALSO --------------------------------
+.SH SEE ALSO
+.BR exports(5) ", " mountd(8)
+.\" -------------------- AUTHOR ----------------------------------
+.SH AUTHORS
+Olaf Kirch, <okir@monad.swb.de>
+.br
+Neil Brown, <neilb@cse.unsw.edu.au>
+
diff --git a/utils/exportfs/exports.man b/utils/exportfs/exports.man
new file mode 100644 (file)
index 0000000..2863fea
--- /dev/null
@@ -0,0 +1,306 @@
+.TH EXPORTS 5 "11 August 1997"
+.UC 5
+.SH NAME
+exports \- NFS file systems being exported
+.SH SYNOPSIS
+.B /etc/exports
+.SH DESCRIPTION
+The file
+.I /etc/exports
+serves as the access control list for file systems which may be
+exported to NFS clients.  It it used by both the NFS mount daemon,
+.IR mountd (8)
+and the NFS file server daemon
+.IR nfsd (8).
+.PP
+The file format is similar to the SunOS
+.I exports
+file, except that several additional options are permitted.  Each line
+contains a mount point and a list of machine or netgroup names allowed
+to mount the file system at that point.  An optional parenthesized list
+of mount parameters may follow each machine name.  Blank lines are
+ignored, and a # introduces a comment to the end of the line. Entries may
+be continued across newlines using a backslash.
+.PP
+.SS Machine Name Formats
+NFS clients may be specified in a number of ways:
+.IP "single host
+This is the most common format. You may specify a host either by an
+abbreviated name recognizued be the resolver, the fully qualified domain
+name, or an IP address.
+.IP "netgroups
+NIS netgroups may be given as
+.IR @group .
+Only the host part of all
+netgroup members is extracted and added to the access list. Empty host
+parts or those containing a single dash (\-) are ignored.
+.IP "wildcards
+Machine names may contain the wildcard characters \fI*\fR and \fI?\fR.
+This can be used to make the \fIexports\fR file more compact; for instance,
+\fI*.cs.foo.edu\fR matches all hosts in the domain \fIcs.foo.edu\fR. However,
+these wildcard characters do not match the dots in a domain name, so the
+above pattern does not include hosts such as \fIa.b.cs.foo.edu\fR.
+.IP "IP networks
+You can also export directories to all hosts on an IP (sub-) network
+simultaneously. This is done by specifying an IP address and netmask pair
+as
+.IR address/netmask .
+.TP
+.B =public
+This is a special ``hostname'' that identifies the given directory name
+as the public root directory (see the section on WebNFS in
+.BR nfsd (8)
+for a discussion of WebNFS and the public root handle). When using this
+convention,
+.B =public
+must be the only entry on this line, and must have no export options
+associated with it. Note that this does
+.I not
+actually export the named directory; you still have to set the exports
+options in a separate entry.
+.PP
+The public root path can also be specified by invoking
+.I nfsd
+with the
+.B \-\-public\-root
+option. Multiple specifications of a public root will be ignored.
+.PP
+.SS General Options
+.IR mountd " and " nfsd
+understand the following export options:
+.TP
+.IR secure "\*d
+This option requires that requests originate on an internet port less
+than IPPORT_RESERVED (1024). This option is on by default. To turn it
+off, specify
+.IR insecure .
+.TP
+.IR ro
+Allow only read-only requests on this NFS volume. The default is to
+allow write requests as well, which can also be made explicit by using
+the
+.IR rw " option.
+.TP
+.I noaccess
+This makes everything below the directory inaccessible for the named
+client.  This is useful when you want to export a directory hierarchy to
+a client, but exclude certain subdirectories. The client's view of a
+directory flagged with noaccess is very limited; it is allowed to read
+its attributes, and lookup `.' and `..'. These are also the only entries
+returned by a readdir.
+.TP
+.IR link_relative
+Convert absolute symbolic links (where the link contents start with a
+slash) into relative links by prepending the necessary number of ../'s
+to get from the directory containing the link to the root on the
+server.  This has subtle, perhaps questionable, semantics when the file
+hierarchy is not mounted at its root.
+.TP
+.IR link_absolute
+Leave all symbolic link as they are. This is the default operation.
+.SS User ID Mapping
+.PP
+.I nfsd
+bases its access control to files on the server machine on the uid and
+gid provided in each NFS RPC request. The normal behavior a user would
+expect is that she can access her files on the server just as she would
+on a normal file system. This requires that the same uids and gids are
+used on the client and the server machine. This is not always true, nor
+is it always desirable.
+.PP
+Very often, it is not desirable that the root user on a client machine
+is also treated as root when accessing files on the NFS server. To this
+end, uid 0 is normally mapped to a different id: the so-called
+anonymous or
+.I nobody
+uid. This mode of operation (called `root squashing') is the default,
+and can be turned off with
+.IR no_root_squash .
+.PP
+By default,
+.I nfsd
+tries to obtain the anonymous uid and gid by looking up user
+.I nobody
+in the password file at startup time. If it isn't found, a uid and gid
+of -2 (i.e. 65534) is used. These values can also be overridden by
+the
+.IR anonuid " and " anongid
+options.
+.PP
+In addition to this, 
+.I nfsd
+lets you specify arbitrary uids and gids that should be mapped to user
+nobody as well. Finally, you can map all user requests to the
+anonymous uid by specifying the
+.IR all_squash " option.
+.PP 
+For the benefit of installations where uids differ between different
+machines, 
+.I nfsd
+provides several mechanism to dynamically map server uids to client
+uids and vice versa: static mapping files, NIS-based mapping, and
+.IR ugidd -based
+mapping.
+.PP
+.IR ugidd -based
+mapping is enabled with the 
+.I map_daemon
+option, and uses the UGID RPC protocol. For this to work, you have to run
+the
+.IR ugidd (8)
+mapping daemon on the client host. It is the least secure of the three methods,
+because by running
+.IR ugidd ,
+everybody can query the client host for a list of valid user names. You
+can protect yourself by restricting access to
+.I ugidd
+to valid hosts only. This can be done by entering the list of valid
+hosts into the
+.I hosts.allow
+or 
+.I hosts.deny
+file. The service name is
+.IR ugidd .
+For a description of the file's syntax, please read
+.IR hosts_access (5).
+.PP
+Static mapping is enabled by using the
+.I map_static
+option, which takes a file name as an argument that describes the mapping.
+NIS-based mapping queries the client's NIS server to obtain a mapping from
+user and group names on the server host to user and group names on the
+client.
+.PP
+Here's the complete list of mapping options:
+.TP
+.IR root_squash
+Map requests from uid/gid 0 to the anonymous uid/gid. Note that this does
+not apply to any other uids that might be equally sensitive, such as user
+.IR bin .
+.TP
+.IR no_root_squash
+Turn off root squashing. This option is mainly useful for diskless clients.
+.TP
+.IR squash_uids " and " squash_gids
+This option specifies a list of uids or gids that should be subject to
+anonymous mapping. A valid list of ids looks like this:
+.IP
+.IR squash_uids=0-15,20,25-50
+.IP
+Usually, your squash lists will look a lot simpler.
+.TP
+.IR all_squash
+Map all uids and gids to the anonymous user. Useful for NFS-exported
+public FTP directories, news spool directories, etc. The opposite option
+is 
+.IR no_all_squash ,
+which is the default setting.
+.TP
+.IR map_daemon
+This option turns on dynamic uid/gid mapping. Each uid in an NFS request
+will be translated to the equivalent server uid, and each uid in an
+NFS reply will be mapped the other way round. This option requires that
+.IR rpc.ugidd (8)
+runs on the client host. The default setting is
+.IR map_identity ,
+which leaves all uids untouched. The normal squash options apply regardless
+of whether dynamic mapping is requested or not.
+.TP
+.IR map_static
+This option enables static mapping. It specifies the name of the file
+that describes the uid/gid mapping, e.g.
+.IP
+.IR map_static=/etc/nfs/foobar.map
+.IP
+The file's format looks like this
+.IP
+.nf
+.ta +3i
+# Mapping for client foobar:
+#    remote     local
+uid  0-99       -       # squash these
+uid  100-500    1000    # map 100-500 to 1000-1500
+gid  0-49       -       # squash these
+gid  50-100     700     # map 50-100 to 700-750
+.fi
+.TP
+.IR map_nis
+This option enables NIS-based uid/gid mapping. For instance, when
+the server encounters the uid 123 on the server, it will obtain the
+login name associated with it, and contact the NFS client's NIS server
+to obtain the uid the client associates with the name.
+.IP
+In order to do this, the NFS server must know the client's NIS domain.
+This is specified as an argument to the
+.I map_nis
+options, e.g.
+.IP
+.I map_nis=foo.com
+.IP
+Note that it may not be sufficient to simply specify the NIS domain
+here; you may have to take additional actions before
+.I nfsd
+is actually able to contact the server. If your distribution uses
+the NYS library, you can specify one or more NIS servers for the
+client's domain in
+.IR /etc/yp.conf .
+If you are using a different NIS library, you may have to obtain a
+special
+.IR ypbind (8)
+daemon that can be configured via
+.IR yp.conf .
+.TP
+.IR anonuid " and " anongid
+These options explicitly set the uid and gid of the anonymous account.
+This option is primarily useful for PC/NFS clients, where you might want
+all requests appear to be from one user. As an example, consider the
+export entry for
+.B /home/joe
+in the example section below, which maps all requests to uid 150 (which
+is supposedly that of user joe).
+.IP
+.SH EXAMPLE
+.PP
+.nf
+.ta +3i
+# sample /etc/exports file
+/               master(rw) trusty(rw,no_root_squash)
+/projects       proj*.local.domain(rw)
+/usr            *.local.domain(ro) @trusted(rw)
+/home/joe       pc001(rw,all_squash,anonuid=150,anongid=100)
+/pub            (ro,insecure,all_squash)
+/pub/private    (noaccess)
+.fi
+.PP
+The first line exports the entire filesystem to machines master and trusty.
+In addition to write access, all uid squashing is turned off for host
+trusty. The second and third entry show examples for wildcard hostnames
+and netgroups (this is the entry `@trusted'). The fourth line shows the
+entry for the PC/NFS client discussed above. Line 5 exports the
+public FTP directory to every host in the world, executing all requests
+under the nobody account. The
+.I insecure 
+option in this entry also allows clients with NFS implementations that
+don't use a reserved port for NFS. The last line denies all NFS clients
+access to the private directory.
+.SH CAVEATS
+Unlike other NFS server implementations, this
+.I nfsd
+allows you to export both a directory and a subdirectory thereof to
+the same host, for instance 
+.IR /usr " and " /usr/X11R6 .
+In this case, the mount options of the most specific entry apply. For
+instance, when a user on the client host accesses a file in 
+.IR /usr/X11R6 ,
+the mount options given in the 
+.I /usr/X11R6 
+entry apply. This is also true when the latter is a wildcard or netgroup
+entry.
+.SH FILES
+/etc/exports
+.SH DIAGNOSTICS
+An error parsing the file is reported using syslogd(8) as level NOTICE from
+a DAEMON whenever nfsd(8) or mountd(8) is started up.  Any unknown
+host is reported at that time, but often not all hosts are not yet known
+to named(8) at boot time, thus as hosts are found they are reported
+with the same syslogd(8) parameters.
diff --git a/utils/lockd/Makefile b/utils/lockd/Makefile
new file mode 100644 (file)
index 0000000..557eebe
--- /dev/null
@@ -0,0 +1,12 @@
+#
+# Makefile for lockd
+#
+
+PROGRAM        = lockd
+PREFIX = rpc.
+OBJS   = lockd.o
+DEPLIBS = $(TOP)support/lib/libfs.a
+LIBS   = -lnfs
+#MAN8  = lockd
+
+include $(TOP)rules.mk
diff --git a/utils/lockd/lockd.c b/utils/lockd/lockd.c
new file mode 100644 (file)
index 0000000..05bc999
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * lockd
+ *
+ * This is the user level part of lockd. This is very primitive, because
+ * all the work is now done in the kernel module.
+ *
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include "nfslib.h"
+
+static void    usage(const char *);
+
+int
+main(int argc, char **argv)
+{
+       int error;
+
+       if (argc > 1)
+               usage (argv [0]);
+
+       if ((error = lockdsvc()) < 0)
+               perror("lockdsvc");
+
+       return (error != 0);
+}
+
+static void
+usage(const char *prog)
+{
+       fprintf(stderr, "usage:\n%s\n", prog);
+       exit(2);
+}
diff --git a/utils/mountd/Makefile b/utils/mountd/Makefile
new file mode 100644 (file)
index 0000000..93529a0
--- /dev/null
@@ -0,0 +1,12 @@
+#
+# Makefile for rpc.mountd
+#
+
+PROGRAM        = mountd
+PREFIX = rpc.
+OBJS   = mountd.o mount_dispatch.o auth.o rmtab.o
+LIBDEPS        = $(TOP)support/lib/libexport.a $(TOP)/support/lib/libnfs.a
+LIBS   = -lexport -lnfs
+MAN8   = mountd
+
+include $(TOP)rules.mk
diff --git a/utils/mountd/auth.c b/utils/mountd/auth.c
new file mode 100644 (file)
index 0000000..9f63120
--- /dev/null
@@ -0,0 +1,215 @@
+/*
+ * utils/mountd/auth.c
+ *
+ * Authentication procedures for mountd.
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include "config.h"
+
+#include <sys/stat.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include "misc.h"
+#include "nfslib.h"
+#include "exportfs.h"
+#include "mountd.h"
+
+enum auth_error
+{
+  bad_path,
+  unknown_host,
+  no_entry,
+  not_exported,
+  illegal_port,
+  faked_hostent,
+  success
+};
+
+static void            auth_fixpath(char *path);
+static nfs_export*     auth_authenticate_internal
+  (char *what, struct sockaddr_in *caller, char *path,
+   struct hostent **hpp, enum auth_error *error);
+static char    *export_file = NULL;
+
+void
+auth_init(char *exports)
+{
+
+       export_file = exports;
+       auth_reload();
+       xtab_mount_write();
+}
+
+int
+auth_reload()
+{
+       struct stat             stb;
+       static time_t           last_modified = 0;
+
+       if (stat(_PATH_ETAB, &stb) < 0)
+               xlog(L_FATAL, "couldn't stat %s", _PATH_ETAB);
+       if (stb.st_mtime == last_modified)
+               return 0;
+       last_modified = stb.st_mtime;
+
+       export_freeall();
+       // export_read(export_file);
+       xtab_export_read();
+
+       return 1;
+}
+
+static nfs_export *
+auth_authenticate_internal(char *what, struct sockaddr_in *caller,
+                          char *path, struct hostent **hpp,
+                          enum auth_error *error)
+{
+       struct in_addr          addr = caller->sin_addr;
+       nfs_export              *exp;
+
+       if (path[0] != '/') {
+               *error = bad_path;
+               return NULL;
+       }
+       auth_fixpath(path);
+
+       if (!(*hpp = gethostbyaddr((const char *)&addr, sizeof(addr), AF_INET)))
+               *hpp = get_hostent((const char *)&addr, sizeof(addr),
+                                  AF_INET);
+       else {
+               /* must make sure the hostent is authorative. */
+               char *name = strdup((*hpp)->h_name);
+               char **sp;
+               *hpp = gethostbyname(name);
+               /* now make sure the "addr" is in the list */
+               for (sp = (*hpp)->h_addr_list ; *sp ; sp++) {
+                       if (memcmp(*sp, &addr, (*hpp)->h_length)==0)
+                               break;
+               }
+               
+               if (!*sp) {
+                       free(name);
+                       /* it was a FAKE */
+                       *error = faked_hostent;
+                       *hpp = NULL;
+                       return NULL;
+               }
+               *hpp = hostent_dup (*hpp);
+               free(name);
+       }
+
+       if (!(exp = export_find(*hpp, path))) {
+               *error = no_entry;
+               return NULL;
+       }
+       if (!exp->m_mayexport) {
+               *error = not_exported;
+               return NULL;
+       }
+
+       if (!(exp->m_export.e_flags & NFSEXP_INSECURE_PORT) &&
+           (ntohs(caller->sin_port) <  IPPORT_RESERVED/2 ||
+            ntohs(caller->sin_port) >= IPPORT_RESERVED)) {
+               *error = illegal_port;
+               return NULL;
+       }
+
+       *error = success;
+
+       return exp;
+}
+
+nfs_export *
+auth_authenticate(char *what, struct sockaddr_in *caller, char *path)
+{
+       nfs_export      *exp = NULL;
+       char            epath[MAXPATHLEN+1];
+       char            *p = NULL;
+       struct hostent  *hp = NULL;
+       struct in_addr  addr = caller->sin_addr;
+       enum auth_error error;
+
+       if (path [0] != '/') return exp;
+
+       strncpy(epath, path, sizeof (epath) - 1);
+       epath[sizeof (epath) - 1] = '\0';
+
+       /* Try the longest matching exported pathname. */
+       while (1) {
+               if (hp) {
+                       free (hp);
+                       hp = NULL;
+               }
+               exp = auth_authenticate_internal(what, caller, epath,
+                                                &hp, &error);
+               if (exp || (error != not_exported && error != no_entry))
+                       break;
+               /* We have to treat the root, "/", specially. */
+               if (p == &epath[1]) break;
+               p = strrchr(epath, '/');
+               if (p == epath) p++;
+               *p = '\0';
+       }
+
+       switch (error) {
+       case bad_path:
+               xlog(L_WARNING, "bad path in %s request from %s: \"%s\"",
+                    what, inet_ntoa(addr), path);
+               break;
+
+       case unknown_host:
+               xlog(L_WARNING, "%s request from unknown host %s for %s (%s)",
+                    what, inet_ntoa(addr), path, epath);
+               break;
+
+       case no_entry:
+               xlog(L_WARNING, "refused %s request from %s for %s (%s): no export entry",
+                    what, hp->h_name, path, epath);
+               break;
+
+       case not_exported:
+               xlog(L_WARNING, "refused %s request from %s for %s (%s): not exported",
+                    what, hp->h_name, path, epath);
+               break;
+
+       case illegal_port:
+               xlog(L_WARNING, "refused %s request from %s for %s (%s): illegal port %d",
+                    what, hp->h_name, path, epath, ntohs(caller->sin_port));
+               break;
+
+       case faked_hostent:
+               xlog(L_WARNING, "refused %s request from %s for %s (%s): faked hostent",
+                    what, inet_ntoa(addr), path, epath);
+               break;
+
+       case success:
+               xlog(L_NOTICE, "authenticated %s request from %s:%d for %s (%s)",
+                    what, hp->h_name, ntohs(caller->sin_port), path, epath);
+               break;
+       default:
+               xlog(L_NOTICE, "%s request from %s:%d for %s (%s) gave %d",
+                    what, hp->h_name, ntohs(caller->sin_port), path, epath, error);
+       }
+
+       if (hp)
+               free (hp);
+
+       return exp;
+}
+
+static void
+auth_fixpath(char *path)
+{
+       char    *sp, *cp;
+
+       for (sp = cp = path; *sp; sp++) {
+               if (*sp != '/' || sp[1] != '/')
+                       *cp++ = *sp;
+       }
+       while (cp > path+1 && cp[-1] == '/')
+               cp--;
+       *cp = '\0';
+}
diff --git a/utils/mountd/mount_dispatch.c b/utils/mountd/mount_dispatch.c
new file mode 100644 (file)
index 0000000..cee1981
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * mount_dispatch      This file contains the function dispatch table.
+ *
+ * Copyright (C) 1995 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include "config.h"
+
+#include "mountd.h"
+#include "rpcmisc.h"
+
+/*
+ * Procedures for MNTv1
+ */
+static struct rpc_dentry mnt_1_dtable[] = {
+       dtable_ent(mount_null,1,void,void),             /* NULL */
+       dtable_ent(mount_mnt,1,dirpath,fhstatus),       /* MNT */
+       dtable_ent(mount_dump,1,void,mountlist),        /* DUMP */
+       dtable_ent(mount_umnt,1,dirpath,void),          /* UMNT */
+       dtable_ent(mount_umntall,1,void,void),          /* UMNTALL */
+       dtable_ent(mount_export,1,void,exports),        /* EXPORT */
+       dtable_ent(mount_exportall,1,void,exports),     /* EXPORTALL */
+};
+
+/*
+ * Procedures for MNTv2
+ */
+static struct rpc_dentry mnt_2_dtable[] = {
+       dtable_ent(mount_null,1,void,void),             /* NULL */
+       dtable_ent(mount_mnt,1,dirpath,fhstatus),       /* MNT */
+       dtable_ent(mount_dump,1,void,mountlist),        /* DUMP */
+       dtable_ent(mount_umnt,1,dirpath,void),          /* UMNT */
+       dtable_ent(mount_umntall,1,void,void),          /* UMNTALL */
+       dtable_ent(mount_export,1,void,exports),        /* EXPORT */
+       dtable_ent(mount_exportall,1,void,exports),     /* EXPORTALL */
+       dtable_ent(mount_pathconf,2,dirpath,ppathcnf),  /* PATHCONF */
+};
+
+/*
+ * Procedures for MNTv3
+ */
+static struct rpc_dentry mnt_3_dtable[] = {
+       dtable_ent(mount_null,1,void,void),             /* NULL */
+       dtable_ent(mount_mnt,3,dirpath,mountres3),      /* MNT */
+       dtable_ent(mount_dump,1,void,mountlist),        /* DUMP */
+       dtable_ent(mount_umnt,1,dirpath,void),          /* UMNT */
+       dtable_ent(mount_umntall,1,void,void),          /* UMNTALL */
+       dtable_ent(mount_export,1,void,exports),        /* EXPORT */
+};
+
+#define number_of(x)   (sizeof(x)/sizeof(x[0]))
+
+static struct rpc_dtable       dtable[] = {
+       { mnt_1_dtable,         number_of(mnt_1_dtable) },
+       { mnt_2_dtable,         number_of(mnt_2_dtable) },
+       { mnt_3_dtable,         number_of(mnt_3_dtable) },
+};
+
+/*
+ * The main dispatch routine.
+ */
+void
+mount_dispatch(struct svc_req *rqstp, SVCXPRT *transp)
+{
+       union mountd_arguments  argument;
+       union mountd_results    result;
+
+       rpc_dispatch(rqstp, transp, dtable, number_of(dtable),
+                       &argument, &result);
+}
diff --git a/utils/mountd/mount_xdr.c b/utils/mountd/mount_xdr.c
new file mode 100644 (file)
index 0000000..87adfa6
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * mount_xdr   XDR procedures for mountd.
+ *
+ * Originally generated by rpcgen; edited to get rid of warnings.
+ */
+
+#include "config.h"
+
+#include "mount.h"
+
+inline bool_t
+xdr_fhandle(XDR *xdrs, fhandle objp)
+{
+       return xdr_opaque(xdrs, objp, FHSIZE);
+}
+
+bool_t
+xdr_fhstatus(XDR *xdrs, fhstatus *objp)
+{
+       return xdr_u_int(xdrs, &objp->fhs_status) &&
+              (objp->fhs_status != 0 ||
+               xdr_fhandle(xdrs, objp->fhstatus_u.fhs_fhandle));
+}
+
+bool_t
+xdr_dirpath(XDR *xdrs, dirpath *objp)
+{
+       return xdr_string(xdrs, objp, MNTPATHLEN);
+}
+
+inline bool_t
+xdr_name(XDR *xdrs, name *objp)
+{
+       return xdr_string(xdrs, objp, MNTPATHLEN);
+}
+
+bool_t
+xdr_mountlist(XDR *xdrs, mountlist *objp)
+{
+       return xdr_pointer(xdrs, (char **)objp, sizeof(struct mountbody),
+                       (xdrproc_t)xdr_mountbody);
+}
+
+bool_t
+xdr_mountbody(XDR *xdrs, mountbody *objp)
+{
+       return xdr_name(xdrs, &objp->ml_hostname) &&
+              xdr_dirpath(xdrs, &objp->ml_directory) &&
+              xdr_mountlist(xdrs, &objp->ml_next);
+}
+
+bool_t
+xdr_groups(XDR *xdrs, groups *objp)
+{
+       return xdr_pointer(xdrs, (char **)objp, sizeof(struct groupnode),
+                       (xdrproc_t)xdr_groupnode);
+}
+
+bool_t
+xdr_groupnode(XDR *xdrs, groupnode *objp)
+{
+       return xdr_name(xdrs, &objp->gr_name) &&
+              xdr_groups(xdrs, &objp->gr_next);
+}
+
+bool_t
+xdr_exports(XDR *xdrs, exports *objp)
+{
+       return xdr_pointer(xdrs, (char **)objp, sizeof(struct exportnode),
+                       (xdrproc_t)xdr_exportnode);
+}
+
+bool_t
+xdr_exportnode(XDR *xdrs, exportnode *objp)
+{
+       return xdr_dirpath(xdrs, &objp->ex_dir) &&
+              xdr_groups(xdrs, &objp->ex_groups) &&
+              xdr_exports(xdrs, &objp->ex_next);
+}
diff --git a/utils/mountd/mountd.c b/utils/mountd/mountd.c
new file mode 100644 (file)
index 0000000..9f467d1
--- /dev/null
@@ -0,0 +1,489 @@
+/*
+ * utils/mountd/mountd.c
+ *
+ * Authenticate mount requests and retrieve file handle.
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include "config.h"
+
+#include <signal.h>
+#include <sys/stat.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <errno.h>
+#include <fcntl.h>
+#include "xmalloc.h"
+#include "misc.h"
+#include "mountd.h"
+#include "rpcmisc.h"
+
+static void            usage(const char *, int exitcode);
+static exports         get_exportlist(void);
+static struct knfs_fh *        get_rootfh(struct svc_req *, dirpath *, int *);
+
+static struct option longopts[] =
+{
+       { "foreground", 0, 0, 'F' },
+       { "debug", 1, 0, 'd' },
+       { "help", 0, 0, 'h' },
+       { "exports-file", 1, 0, 'f' },
+       { "nfs-version", 1, 0, 'V' },
+       { "no-nfs-version", 1, 0, 'N' },
+       { "version", 0, 0, 'v' },
+       { "port", 1, 0, 'p' },
+       { NULL, 0, 0, 0 }
+};
+
+static int nfs_version = -1;
+
+/*
+ * Signal handler.
+ */
+static void 
+killer (int sig)
+{
+  if (nfs_version & 0x1)
+    pmap_unset (MOUNTPROG, MOUNTVERS);
+  if (nfs_version & (0x1 << 1))
+    pmap_unset (MOUNTPROG, MOUNTVERS_POSIX);
+  if (nfs_version & (0x1 << 2))
+    pmap_unset (MOUNTPROG, MOUNTVERS_NFSV3);
+  xlog (L_FATAL, "Caught signal %d, un-registering and exiting.", sig);
+}
+
+bool_t
+mount_null_1_svc(struct svc_req *rqstp, void *argp, void *resp)
+{
+       return 1;
+}
+
+bool_t
+mount_mnt_1_svc(struct svc_req *rqstp, dirpath *path, fhstatus *res)
+{
+       struct knfs_fh  *fh;
+
+       xlog(D_CALL, "MNT1(%s) called", *path);
+       if ((fh = get_rootfh(rqstp, path, &res->fhs_status)) != NULL)
+               memcpy(&res->fhstatus_u.fhs_fhandle, fh, 32);
+       return 1;
+}
+
+bool_t
+mount_dump_1_svc(struct svc_req *rqstp, void *argp, mountlist *res)
+{
+       xlog(L_NOTICE, "dump request from %s",
+               inet_ntoa(svc_getcaller(rqstp->rq_xprt)->sin_addr));
+
+       *res = mountlist_list();
+       return 1;
+}
+
+bool_t
+mount_umnt_1_svc(struct svc_req *rqstp, dirpath *argp, void *resp)
+{
+       struct sockaddr_in *sin = svc_getcaller(rqstp->rq_xprt);
+       nfs_export      *exp;
+       char            *p = *argp;
+       char            rpath[MAXPATHLEN+1];
+
+       if (*p == '\0')
+               p = "/";
+
+       if (realpath(p, rpath) != NULL) {
+               rpath[sizeof (rpath) - 1] = '\0';
+               p = rpath;
+       }
+
+       if (!(exp = auth_authenticate("unmount", sin, p))) {
+               return 1;
+       }
+       mountlist_del(exp, p);
+       export_reset (exp);
+       return 1;
+}
+
+bool_t
+mount_umntall_1_svc(struct svc_req *rqstp, void *argp, void *resp)
+{
+       /* Reload /etc/xtab if necessary */
+       auth_reload();
+
+       mountlist_del_all(svc_getcaller(rqstp->rq_xprt));
+       return 1;
+}
+
+bool_t
+mount_export_1_svc(struct svc_req *rqstp, void *argp, exports *resp)
+{
+       xlog(L_NOTICE, "export request from %s",
+               inet_ntoa(svc_getcaller(rqstp->rq_xprt)->sin_addr));
+       *resp = get_exportlist();
+       return 1;
+}
+
+bool_t
+mount_exportall_1_svc(struct svc_req *rqstp, void *argp, exports *resp)
+{
+       xlog(L_NOTICE, "exportall request from %s",
+               inet_ntoa(svc_getcaller(rqstp->rq_xprt)->sin_addr));
+       *resp = get_exportlist();
+       return 1;
+}
+
+/*
+ * MNTv2 pathconf procedure
+ *
+ * The protocol doesn't include a status field, so Sun apparently considers
+ * it good practice to let anyone snoop on your system, even if it's
+ * pretty harmless data such as pathconf. We don't.
+ *
+ * Besides, many of the pathconf values don't make much sense on NFS volumes.
+ * FIFOs and tty device files represent devices on the *client*, so there's
+ * no point in getting the server's buffer sizes etc.
+ */
+bool_t
+mount_pathconf_2_svc(struct svc_req *rqstp, dirpath *path, ppathcnf *res)
+{
+       struct sockaddr_in *sin = svc_getcaller(rqstp->rq_xprt);
+       struct stat     stb;
+       nfs_export      *exp;
+       char            rpath[MAXPATHLEN+1];
+       char            *p = *path;
+
+       memset(res, 0, sizeof(*res));
+
+       if (*p == '\0')
+               p = "/";
+
+       /* Reload /etc/xtab if necessary */
+       auth_reload();
+
+       /* Resolve symlinks */
+       if (realpath(p, rpath) != NULL) {
+               rpath[sizeof (rpath) - 1] = '\0';
+               p = rpath;
+       }
+
+       /* Now authenticate the intruder... */
+       if (!(exp = auth_authenticate("mount", sin, p))) {
+               return 1;
+       } else if (stat(p, &stb) < 0) {
+               xlog(L_WARNING, "can't stat exported dir %s: %s",
+                               p, strerror(errno));
+               export_reset (exp);
+               return 1;
+       }
+
+       res->pc_link_max  = pathconf(p, _PC_LINK_MAX);
+       res->pc_max_canon = pathconf(p, _PC_MAX_CANON);
+       res->pc_max_input = pathconf(p, _PC_MAX_INPUT);
+       res->pc_name_max  = pathconf(p, _PC_NAME_MAX);
+       res->pc_path_max  = pathconf(p, _PC_PATH_MAX);
+       res->pc_pipe_buf  = pathconf(p, _PC_PIPE_BUF);
+       res->pc_vdisable  = pathconf(p, _PC_VDISABLE);
+
+       /* Can't figure out what to do with pc_mask */
+       res->pc_mask[0]   = 0;
+       res->pc_mask[1]   = 0;
+
+       export_reset (exp);
+
+       return 1;
+}
+
+/*
+ * NFSv3 MOUNT procedure
+ */
+bool_t
+mount_mnt_3_svc(struct svc_req *rqstp, dirpath *path, mountres3 *res)
+{
+       static int      flavors[] = { AUTH_NULL, AUTH_UNIX };
+       struct knfs_fh  *fh;
+
+       xlog(D_CALL, "MNT3(%s) called", *path);
+       if ((fh = get_rootfh(rqstp, path, (int *) &res->fhs_status)) != NULL) {
+               struct mountres3_ok     *ok = &res->mountres3_u.mountinfo;
+
+               ok->fhandle.fhandle3_len = 32;
+               ok->fhandle.fhandle3_val = (char *) fh;
+               ok->auth_flavors.auth_flavors_len = 2;
+               ok->auth_flavors.auth_flavors_val = flavors;
+       }
+       return 1;
+}
+
+static struct knfs_fh *
+get_rootfh(struct svc_req *rqstp, dirpath *path, int *error)
+{
+       struct sockaddr_in *sin = svc_getcaller(rqstp->rq_xprt);
+       struct stat     stb;
+       nfs_export      *exp;
+       char            rpath[MAXPATHLEN+1];
+       char            *p = *path;
+
+       if (*p == '\0')
+               p = "/";
+
+       /* Reload /var/lib/nfs/etab if necessary */
+       auth_reload();
+
+       /* Resolve symlinks */
+       if (realpath(p, rpath) != NULL) {
+               rpath[sizeof (rpath) - 1] = '\0';
+               p = rpath;
+       }
+
+       /* Now authenticate the intruder... */
+       if (!(exp = auth_authenticate("mount", sin, p))) {
+               *error = NFSERR_ACCES;
+       } else if (stat(p, &stb) < 0) {
+               xlog(L_WARNING, "can't stat exported dir %s: %s",
+                               p, strerror(errno));
+               if (errno == ENOENT)
+                       *error = NFSERR_NOENT;
+               else
+                       *error = NFSERR_ACCES;
+       } else if (!S_ISDIR(stb.st_mode) && !S_ISREG(stb.st_mode)) {
+               xlog(L_WARNING, "%s is not a directory or regular file", p);
+               *error = NFSERR_NOTDIR;
+       } else {
+               struct knfs_fh  *fh;
+
+               if (!exp->m_exported)
+                       export_export(exp);
+               if (!exp->m_xtabent)
+                       xtab_append(exp);
+
+               /* We first try the new nfs syscall. */
+               fh = getfh ((struct sockaddr *) sin, p);
+               if (fh == NULL && errno == EINVAL)
+                       /* Let's try the old one. */
+                       fh = getfh_old ((struct sockaddr *) sin,
+                                   stb.st_dev, stb.st_ino);
+               if (fh != NULL) {
+                       mountlist_add(exp, p);
+                       *error = NFS_OK;
+                       export_reset (exp);
+                       return fh;
+               }
+               xlog(L_WARNING, "getfh failed: %s", strerror(errno));
+               *error = NFSERR_ACCES;
+       }
+       export_reset (exp);
+       return NULL;
+}
+
+static exports
+get_exportlist(void)
+{
+       static exports          elist = NULL;
+       struct exportnode       *e, *ne;
+       struct groupnode        *g, *ng, *c, **cp;
+       nfs_export              *exp;
+       int                     i;
+
+       if (!auth_reload() && elist)
+               return elist;
+
+       for (e = elist; e != NULL; e = ne) {
+               ne = e->ex_next;
+               for (g = e->ex_groups; g != NULL; g = ng) {
+                       ng = g->gr_next;
+                       xfree(g->gr_name);
+                       xfree(g);
+               }
+               xfree(e->ex_dir);
+               xfree(e);
+       }
+       elist = NULL;
+
+       for (i = 0; i < MCL_MAXTYPES; i++) {
+               for (exp = exportlist[i]; exp; exp = exp->m_next) {
+                       for (e = elist; e != NULL; e = e->ex_next) {
+                               if (!strcmp(exp->m_export.m_path, e->ex_dir))
+                                       break;
+                       }
+                       if (!e) {
+                               e = (struct exportnode *) xmalloc(sizeof(*e));
+                               e->ex_next = elist;
+                               e->ex_groups = NULL;
+                               e->ex_dir = strdup(exp->m_export.m_path);
+                               elist = e;
+                       }
+
+                       /* We need to check if we should remove
+                          previous ones. */
+                       if (i == MCL_ANONYMOUS && e->ex_groups) {
+                               for (g = e->ex_groups; g; g = ng) {
+                                       ng = g->gr_next;
+                                       xfree(g->gr_name);
+                                       xfree(g);
+                               }
+                               e->ex_groups = NULL;
+                               continue;
+                       }
+
+                       if (i != MCL_FQDN && e->ex_groups) {
+                         struct hostent        *hp;
+
+                         cp = &e->ex_groups;
+                         while ((c = *cp) != NULL) {
+                           if (client_gettype (c->gr_name) == MCL_FQDN
+                               && (hp = gethostbyname(c->gr_name))) {
+                             hp = hostent_dup (hp);
+                             if (client_check(exp->m_client, hp)) {
+                               *cp = c->gr_next;
+                               xfree(c->gr_name);
+                               xfree(c);
+                               xfree (hp);
+                               if ((c = *cp) == NULL)
+                                 break;
+                             }
+                             else
+                               xfree (hp);
+                           }
+                           cp = &(c->gr_next);
+                         }
+                       }
+
+                       if (exp->m_export.e_hostname [0] != '\0') {
+                               for (g = e->ex_groups; g; g = g->gr_next)
+                                       if (strcmp (exp->m_export.e_hostname,
+                                                   g->gr_name) == 0)
+                                               break;
+                               if (g)
+                                       continue;
+                               g = (struct groupnode *) xmalloc(sizeof(*g));
+                               g->gr_name = xstrdup(exp->m_export.e_hostname);
+                               g->gr_next = e->ex_groups;
+                               e->ex_groups = g;
+                       }
+               }
+       }
+
+       return elist;
+}
+
+int
+main(int argc, char **argv)
+{
+       char    *export_file = _PATH_EXPORTS;
+       int     foreground = 0;
+       int     port = 0;
+       int     c;
+       struct sigaction sa;
+
+       /* Parse the command line options and arguments. */
+       opterr = 0;
+       while ((c = getopt_long(argc, argv, "Fd:f:p:P:hN:V:v", longopts, NULL)) != EOF)
+               switch (c) {
+               case 'F':
+                       foreground = 1;
+                       break;
+               case 'd':
+                       xlog_sconfig(optarg, 1);
+                       break;
+               case 'f':
+                       export_file = optarg;
+                       break;
+               case 'h':
+                       usage(argv [0], 0);
+                       break;
+               case 'P':       /* XXX for nfs-server compatibility */
+               case 'p':
+                       port = atoi(optarg);
+                       if (port <= 0 || port > 65535) {
+                               fprintf(stderr, "%s: bad port number: %s\n",
+                                       argv [0], optarg);
+                               usage(argv [0], 1);
+                       }
+                       break;
+               case 'N':
+                       nfs_version &= ~(1 << (atoi (optarg) - 1));
+                       break;
+               case 'V':
+                       nfs_version |= 1 << (atoi (optarg) - 1);
+                       break;
+               case 'v':
+                       printf("kmountd %s\n", VERSION);
+                       exit(0);
+               case 0:
+                       break;
+               case '?':
+               default:
+                       usage(argv [0], 1);
+               }
+
+       /* No more arguments allowed. */
+       if (optind != argc || !(nfs_version & 0x7))
+               usage(argv [0], 1);
+
+       /* Initialize logging. */
+       xlog_open("mountd");
+
+       sa.sa_handler = SIG_IGN;
+       sa.sa_flags = 0;
+       sigemptyset(&sa.sa_mask);
+       sigaction(SIGHUP, &sa, NULL);
+       sigaction(SIGINT, &sa, NULL);
+       sigaction(SIGTERM, &sa, NULL);
+
+       if (nfs_version & 0x1)
+               rpc_init("mountd", MOUNTPROG, MOUNTVERS,
+                        mount_dispatch, port, 0);
+       if (nfs_version & (0x1 << 1))
+               rpc_init("mountd", MOUNTPROG, MOUNTVERS_POSIX,
+                        mount_dispatch, port, 0);
+       if (nfs_version & (0x1 << 2))
+               rpc_init("mountd", MOUNTPROG, MOUNTVERS_NFSV3,
+                        mount_dispatch, port, 0);
+
+       sa.sa_handler = killer;
+       sigaction(SIGHUP, &sa, NULL);
+       sigaction(SIGINT, &sa, NULL);
+       sigaction(SIGTERM, &sa, NULL);
+
+       auth_init(export_file);
+
+       if (!foreground) {
+               /* We first fork off a child. */
+               if ((c = fork()) > 0)
+                       exit(0);
+               if (c < 0) {
+                       xlog(L_FATAL, "mountd: cannot fork: %s\n",
+                                               strerror(errno));
+               }
+               /* Now we remove ourselves from the foreground.
+                  Redirect stdin/stdout/stderr first. */
+               {
+                       int fd = open("/dev/null", O_RDWR);
+                       (void) dup2(fd, 0);
+                       (void) dup2(fd, 1);
+                       (void) dup2(fd, 2);
+                       if (fd > 2) (void) close(fd);
+               }
+               setsid();
+               xlog_background();
+       }
+
+       svc_run();
+
+       xlog(L_ERROR, "Ack! Gack! svc_run returned!\n");
+       exit(1);
+}
+
+static void
+usage(const char *prog, int n)
+{
+       fprintf(stderr,
+"Usage: %s [-Fhnv] [-d kind] [-f exports-file] [-V version]\n"
+"      [-N version] [--debug kind] [-p|--port port] [--help] [--version]\n"
+"      [--exports-file=file] [--nfs-version version]\n"
+"      [--no-nfs-version version]\n", prog);
+       exit(n);
+}
diff --git a/utils/mountd/mountd.h b/utils/mountd/mountd.h
new file mode 100644 (file)
index 0000000..9f9bc1f
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * utils/mountd/mountd.h
+ *
+ * Declarations for mountd.
+ *
+ * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
+ */
+
+#ifndef MOUNTD_H
+#define MOUNTD_H
+
+#include <rpc/rpc.h>
+#include <rpc/svc.h>
+#include "nfslib.h"
+#include "exportfs.h"
+#include "mount.h"
+
+union mountd_arguments {
+       dirpath                 dirpath;
+};
+
+union mountd_results {
+       fhstatus                fstatus;
+       mountlist               mountlist;
+       exports                 exports;
+};
+
+/*
+ * Global Function prototypes.
+ */
+bool_t         mount_null_1_svc(struct svc_req *, void *, void *);
+bool_t         mount_mnt_1_svc(struct svc_req *, dirpath *, fhstatus *);
+bool_t         mount_dump_1_svc(struct svc_req *, void *, mountlist *);
+bool_t         mount_umnt_1_svc(struct svc_req *, dirpath *, void *);
+bool_t         mount_umntall_1_svc(struct svc_req *, void *, void *);
+bool_t         mount_export_1_svc(struct svc_req *, void *, exports *);
+bool_t         mount_exportall_1_svc(struct svc_req *, void *, exports *);
+bool_t         mount_pathconf_2_svc(struct svc_req *, dirpath *, ppathcnf *);
+bool_t         mount_mnt_3_svc(struct svc_req *, dirpath *, mountres3 *);
+
+void           mount_dispatch(struct svc_req *, SVCXPRT *);
+void           auth_init(char *export_file);
+int            auth_reload(void);
+nfs_export *   auth_authenticate(char *what, struct sockaddr_in *sin,
+                                       char *path);
+void           auth_export(nfs_export *exp);
+
+void           mountlist_add(nfs_export *exp, const char *path);
+void           mountlist_del(nfs_export *exp, const char *path);
+void           mountlist_del_all(struct sockaddr_in *sin);
+mountlist      mountlist_list(void);
+
+
+#endif /* MOUNTD_H */
diff --git a/utils/mountd/mountd.man b/utils/mountd/mountd.man
new file mode 100644 (file)
index 0000000..593037b
--- /dev/null
@@ -0,0 +1,92 @@
+.\"
+.\" mountd(8)
+.\"
+.\" Copyright (C) 1999 Olaf Kirch <okir@monad.swb.de>
+.TH rpc.mountd 8 "31 May 1999"
+.SH NAME
+rpc.mountd \- NFS mount daemon
+.SH SYNOPSIS
+.BI "/usr/sbin/rpc.mountd [" options "]"
+.SH DESCRIPTION
+The
+.B rpc.mountd
+program implements the NFS mount protocol. When receiving a MOUNT
+request from an NFS client, it checks the request against the list of
+currently exported file systems. If the client is permitted to mount
+the file system,
+.B rpc.mountd
+obtains a file handle for requested directory and returns it to
+the client.
+.SS Exporting NFS File Systems
+Making file systems available to NFS clients is called
+.IR exporting .
+.P
+Usually, a file system and the hosts it should be made available to
+are listed in the
+.B /etc/exports
+file, and invoking
+.B exportfs -a
+whenever the system is booted. The
+.BR exportfs (8)
+command makes export information available to both the kernel NFS
+server module and the
+.B rpc.mountd
+daemon.
+.P
+Alternatively, you can export individual directories temporarily 
+using
+.BR exportfs 's
+.IB host : /directory
+syntax.
+.SS The rmtab File
+For every mount request received from an NFS client,
+.B rpc.mountd
+adds an entry to the
+.B /var/state/nfs/rmtab
+file. When receiving an unmount request, that entry is removed.
+user level part of the NFS service.
+.P
+However, this file is mostly ornamental. One, the client can continue
+to use the file handle even after calling
+.BR rpc.mountd 's
+UMOUNT procedure. And two, if a client reboots without notifying
+.BR rpc.mountd ,
+a stale entry will remain in
+.BR rmtab .
+.SH OPTIONS
+.TP
+.\" This file isn't touched by mountd at all--even though it
+.\" accepts the option.
+.\" .BR \-f " or " \-\-exports-file
+.\" This option specifies the exports file, listing the clients that this
+.\" server is prepared to serve and parameters to apply to each
+.\" such mount (see
+.\" .BR exports (5)).
+.\" By default, export information is read from
+.\" .IR /etc/exports .
+.TP
+.BR \-N " or " \-\-no-nfs-version
+This option can be used to request that
+.B rpc.mountd
+does not offer certain versions of NFS. The current version of
+.B rpc.mountd
+can support both NFS version 2 and the newer version 3. If the
+NFS kernel module was compiled without support for NFSv3,
+.B rpc.mountd
+must be invoked with the option
+.BR "\-\-no-nfs-version 3" .
+.TP
+.BR \-v " or " \-\-version
+Print the version of
+.B rpc.mountd
+and exit.
+.SH SEE ALSO
+.BR rpc.nfsd (8),
+.BR exportfs (8),
+.BR exports (5),
+.BR rpc.rquotad (8).
+.SH FILES
+.BR /etc/exports ,
+.BR /var/state/nfs/xtab .
+.SH AUTHOR
+Olaf Kirch, H. J. Lu, G. Allan Morris III, and a host of others.
diff --git a/utils/mountd/rmtab.c b/utils/mountd/rmtab.c
new file mode 100644 (file)
index 0000000..289a42e
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ * utils/mountd/rmtab.c
+ *
+ * Manage the rmtab file for mountd.
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include "config.h"
+
+#include <sys/stat.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include "xmalloc.h"
+#include "misc.h"
+#include "exportfs.h"
+#include "xio.h"
+#include "mountd.h"
+
+void
+mountlist_add(nfs_export *exp, const char *path)
+{
+       struct rmtabent xe;
+       struct rmtabent *rep;
+       int             lockid;
+
+       if ((lockid = xflock(_PATH_RMTAB, "a")) < 0)
+               return;
+       setrmtabent("r");
+       while ((rep = getrmtabent(1)) != NULL) {
+               if (strcmp (rep->r_client,
+                           exp->m_client->m_hostname) == 0
+                   && strcmp(rep->r_path, path) == 0) {
+                       endrmtabent();
+                       xfunlock(lockid);
+                       return;
+               }
+       }
+       endrmtabent();
+       strncpy(xe.r_client, exp->m_client->m_hostname,
+               sizeof (xe.r_client) - 1);
+       xe.r_client [sizeof (xe.r_client) - 1] = '\0';
+       strncpy(xe.r_path, path, sizeof (xe.r_path) - 1);
+       xe.r_path [sizeof (xe.r_path) - 1] = '\0';
+       if (setrmtabent("a")) {
+               putrmtabent(&xe);
+               endrmtabent();
+       }
+       xfunlock(lockid);
+}
+
+void
+mountlist_del(nfs_export *exp, const char *path)
+{
+       struct rmtabent *rep;
+       FILE            *fp;
+       char            *hname = exp->m_client->m_hostname;
+       int             lockid;
+
+       if ((lockid = xflock(_PATH_RMTAB, "w")) < 0)
+               return;
+       if (!setrmtabent("r")) {
+               xfunlock(lockid);
+               return;
+       }
+       if (!(fp = fsetrmtabent(_PATH_RMTABTMP, "w"))) {
+               endrmtabent();
+               xfunlock(lockid);
+               return;
+       }
+       while ((rep = getrmtabent(1)) != NULL) {
+               if (strcmp (rep->r_client, hname)
+                   || strcmp(rep->r_path, path))
+                       fputrmtabent(fp, rep);
+       }
+       if (rename(_PATH_RMTABTMP, _PATH_RMTAB) < 0) {
+               xlog(L_ERROR, "couldn't rename %s to %s",
+                               _PATH_RMTABTMP, _PATH_RMTAB);
+       }
+       endrmtabent();  /* close & unlink */
+       fendrmtabent(fp);
+       xfunlock(lockid);
+}
+
+void
+mountlist_del_all(struct sockaddr_in *sin)
+{
+       struct in_addr  addr = sin->sin_addr;
+       struct hostent  *hp;
+       struct rmtabent *rep;
+       nfs_export      *exp;
+       FILE            *fp;
+       int             lockid;
+
+       if ((lockid = xflock(_PATH_RMTAB, "w")) < 0)
+               return;
+       if (!(hp = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET))) {
+               xlog(L_ERROR, "can't get hostname of %s", inet_ntoa(addr));
+               xfunlock(lockid);
+               return;
+       }
+       else
+               hp = hostent_dup (hp);
+
+       if (!setrmtabent("r")) {
+               xfunlock(lockid);
+               free (hp);
+               return;
+       }
+       if (!(fp = fsetrmtabent(_PATH_RMTABTMP, "w"))) {
+               endrmtabent();
+               xfunlock(lockid);
+               free (hp);
+               return;
+       }
+       while ((rep = getrmtabent(1)) != NULL) {
+               if (strcmp(rep->r_client, hp->h_name) == 0 &&
+                   (exp = auth_authenticate("umountall", sin, rep->r_path))) {
+                       export_reset(exp);
+                       continue;
+               }
+               fputrmtabent(fp, rep);
+       }
+       if (rename(_PATH_RMTABTMP, _PATH_RMTAB) < 0) {
+               xlog(L_ERROR, "couldn't rename %s to %s",
+                               _PATH_RMTABTMP, _PATH_RMTAB);
+       }
+       endrmtabent();  /* close & unlink */
+       fendrmtabent(fp);
+       xfunlock(lockid);
+       free (hp);
+}
+
+mountlist
+mountlist_list(void)
+{
+       static mountlist        mlist = NULL;
+       static time_t           last_mtime = 0;
+       mountlist               m;
+       struct rmtabent         *rep;
+       struct stat             stb;
+       int                     lockid;
+
+       if ((lockid = xflock(_PATH_RMTAB, "r")) < 0)
+               return NULL;
+       if (stat(_PATH_RMTAB, &stb) < 0) {
+               xlog(L_ERROR, "can't stat %s", _PATH_RMTAB);
+               return NULL;
+       }
+       if (stb.st_mtime != last_mtime) {
+               while (mlist) {
+                       mlist = (m = mlist)->ml_next;
+                       xfree(m->ml_hostname);
+                       xfree(m->ml_directory);
+                       xfree(m);
+               }
+               last_mtime = stb.st_mtime;
+
+               setrmtabent("r");
+               while ((rep = getrmtabent(1)) != NULL) {
+                       m = (mountlist) xmalloc(sizeof(*m));
+                       m->ml_hostname = xstrdup(rep->r_client);
+                       m->ml_directory = xstrdup(rep->r_path);
+                       m->ml_next = mlist;
+                       mlist = m;
+               }
+               endrmtabent();
+       }
+       xfunlock(lockid);
+
+       return mlist;
+}
diff --git a/utils/nfsd/Makefile b/utils/nfsd/Makefile
new file mode 100644 (file)
index 0000000..4cc6f8a
--- /dev/null
@@ -0,0 +1,35 @@
+#
+# Makefile for knfsd
+#
+
+PROGRAM        = nfsd
+PREFIX = rpc.
+OBJS   = nfsd.o
+DEPLIBS = $(TOP)support/lib/libfs.a
+LIBS   = -lnfs
+MAN8   = nfsd
+
+include $(TOP)rules.mk
+
+# 
+# all::        nfsd
+#      @echo "Done."
+# 
+# nfsd:        $(OBJS)
+#      $(CC) $(LDFLAGS) -o $@ $(OBJS) -lnfs
+# 
+# clean distclean::
+#      rm -f *.o
+# 
+# distclean::
+#      rm -f nfsd .depend
+# 
+# install::
+#      install -o root -g root -m 755 nfsd /usr/sbin/rpc.$knfsd
+# 
+# dep::
+#      $(CC) $(CFLAGS) -M $(OBJS:.o=.c) > .depend
+# 
+# ifeq (.depend,$(wildcard .depend))
+# include .depend
+# endif
diff --git a/utils/nfsd/nfsd.c b/utils/nfsd/nfsd.c
new file mode 100644 (file)
index 0000000..3a22370
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * nfsd
+ *
+ * This is the user level part of nfsd. This is very primitive, because
+ * all the work is now done in the kernel module.
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include "nfslib.h"
+
+static void    usage(const char *);
+
+int
+main(int argc, char **argv)
+{
+       int     count = 1, c, error, port;
+
+       port = 2049;
+
+       /* FIXME: Check for nfs in /etc/services */
+
+       while ((c = getopt(argc, argv, "hp:P:")) != EOF) {
+               switch(c) {
+               case 'P':       /* XXX for nfs-server compatibility */
+               case 'p':
+                       port = atoi(optarg);
+                       if (port <= 0 || port > 65535) {
+                               fprintf(stderr, "%s: bad port number: %s\n",
+                                       argv[0], optarg);
+                               usage(argv [0]);
+                       }
+                       break;
+                       break;
+               case 'h':
+               default:
+                       usage(argv[0]);
+               }
+       }
+
+       if (optind < argc) {
+               if ((count = atoi(argv[optind])) < 0) {
+                       /* insane # of servers */
+                       fprintf(stderr,
+                               "%s: invalid server count (%d), using 1\n",
+                               argv[0], count);
+                       count = 1;
+               }
+       }
+
+       if ((error = nfssvc(port, count)) < 0)
+               perror("nfssvc");
+
+       return (error != 0);
+}
+
+static void
+usage(const char *prog)
+{
+       fprintf(stderr, "usage:\n"
+                       "%s nrservs\n", prog);
+       exit(2);
+}
diff --git a/utils/nfsd/nfsd.man b/utils/nfsd/nfsd.man
new file mode 100644 (file)
index 0000000..f415cfd
--- /dev/null
@@ -0,0 +1,46 @@
+.\"
+.\" nfsd(8)
+.\"
+.\" Copyright (C) 1999 Olaf Kirch <okir@monad.swb.de>
+.TH rpc.nfsd 8 "31 May 1999"
+.SH NAME
+rpc.nfsd \- NFS server process
+.SH SYNOPSIS
+.BI "/usr/sbin/rpc.nfsd [-p " port "] " nproc
+.SH DESCRIPTION
+The
+.B rpc.nfsd
+program implements the user level part of the NFS service. The
+main functionality is handled by the
+.B nfsd.o
+kernel module; the user space program merely starts the specified
+number of kernel threads.
+.P
+The
+.B rpc.mountd
+server provides an ancially service needed to satisfy mount requests
+by NFS clients.
+.SH OPTIONS
+.TP
+.BI \-p " port"
+specify a diferent port to listen on for NFS requests. By default,
+.B rpc.nfsd
+will listen on port 2049.
+.TP
+.I nproc
+specify the number of NFS server threads. By default, just one
+thread is started. However, for optimum performance several threads
+should be used. The actual figure depends on the number of and the work
+load created by the NFS clients, but a useful starting point is
+8 threads. Effects of modifying that number can be checked using
+the
+.BR nfsstat (8)
+program.
+.SH SEE ALSO
+.BR rpc.mountd (8),
+.BR exportfs (8),
+.BR rpc.rquotad (8),
+.BR nfsstat (8).
+.SH AUTHOR
+Olaf Kirch, Bill Hawes, H. J. Lu, G. Allan Morris III,
+and a host of others.
diff --git a/utils/nfsstat/Makefile b/utils/nfsstat/Makefile
new file mode 100644 (file)
index 0000000..e3a9428
--- /dev/null
@@ -0,0 +1,32 @@
+#
+# dummy Makefile
+#
+
+PROGRAM        = nfsstat
+OBJS   = nfsstat.o
+MAN8   = nfsstat
+
+include $(TOP)rules.mk
+
+# 
+# all::        nfsstat
+#      @echo "Done."
+# 
+# nfsstat: $(OBJS)
+#      $(CC) $(LDFLAGS) -o $@ $(OBJS)
+# 
+# clean distclean::
+#      rm -f *.o core
+# 
+# distclean::
+#      rm -f nfsstat .depend
+# 
+# install:
+#      install -o root -g root -m 755 nfsstat /usr/sbin/$knfsstat
+# 
+# dep::
+#      $(CC) $(CFLAGS) -M $(OBJS:.o=.c) > .depend
+# 
+# ifeq (.depend,$(wildcard .depend))
+# include .depend
+# endif
diff --git a/utils/nfsstat/nfsstat.c b/utils/nfsstat/nfsstat.c
new file mode 100644 (file)
index 0000000..55b5cd0
--- /dev/null
@@ -0,0 +1,328 @@
+/*
+ * nfsstat.c           Output NFS statistics
+ *
+ * Copyright (C) 1995, 1996, 1999 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include "config.h"
+
+#define NFSSVCSTAT     "/proc/net/rpc/nfsd"
+#define NFSCLTSTAT     "/proc/net/rpc/nfs"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#define MAXNRVALS      32
+
+static unsigned int    svcv2info[19];  /* NFSv2 call counts ([0] == 18) */
+static unsigned int    cltv2info[19];  /* NFSv2 call counts ([0] == 18) */
+static unsigned int    svcv3info[22];  /* NFSv3 call counts ([0] == 22) */
+static unsigned int    cltv3info[22];  /* NFSv3 call counts ([0] == 22) */
+static unsigned int    svcnetinfo[4];  /* 0  # of received packets
+                                        * 1  UDP packets
+                                        * 2  TCP packets
+                                        * 3  TCP connections
+                                        */
+static unsigned int    cltnetinfo[4];  /* 0  # of received packets
+                                        * 1  UDP packets
+                                        * 2  TCP packets
+                                        * 3  TCP connections
+                                        */
+
+static unsigned int    svcrpcinfo[5];  /* 0  total # of RPC calls
+                                        * 1  total # of bad calls
+                                        * 2  bad format
+                                        * 3  authentication failed
+                                        * 4  unknown client
+                                        */
+static unsigned int    cltrpcinfo[3];  /* 0  total # of RPC calls
+                                        * 1  retransmitted calls
+                                        * 2  cred refreshs
+                                        */
+
+static unsigned int    svcrcinfo[8];   /* 0  repcache hits
+                                        * 1  repcache hits
+                                        * 2  uncached reqs
+                                        *
+                                        * including fh info:
+                                        * 3  cached fh's
+                                        * 4  valid fh's
+                                        * 5  fixup required
+                                        * 6  lookup (?)
+                                        * 7  stale
+                                        */
+
+static const char *    nfsv2name[18] = {
+       "null", "getattr", "setattr", "root",   "lookup",  "readlink",
+       "read", "wrcache", "write",   "create", "remove",  "rename",
+       "link", "symlink", "mkdir",   "rmdir",  "readdir", "fsstat"
+};
+
+static const char *    nfsv3name[22] = {
+       "null",   "getattr", "setattr",  "lookup", "access",  "readlink",
+       "read",   "write",   "create",   "mkdir",  "symlink", "mknod",
+       "remove", "rmdir",   "rename",   "link",   "readdir", "readdirplus",
+       "fsstat", "fsinfo",  "pathconf", "commit"
+};
+
+typedef struct statinfo {
+       char            *tag;
+       int             nrvals;
+       unsigned int *  valptr;
+
+       /* Filled in by parse_statfile */
+       int *           foundp;
+} statinfo;
+
+static statinfo                svcinfo[] = {
+       { "net",        4,      svcnetinfo      },
+       { "rpc",        5,      svcrpcinfo      },
+       { "rc",         8,      svcrcinfo       },      /* including fh_* */
+       { "proc2",      19,     svcv2info       },
+       { "proc3",      23,     svcv3info       },
+       { NULL,         0,      0               }
+};
+
+static statinfo                cltinfo[] = {
+       { "net",        4,      cltnetinfo      },
+       { "rpc",        3,      cltrpcinfo      },
+       { "proc2",      19,     cltv2info       },
+       { "proc3",      23,     cltv3info       },
+       { NULL,         0,      0               }
+};
+
+static void            print_numbers(const char *, unsigned int *,
+                                       unsigned int);
+static void            print_callstats(const char *, const char **,
+                                       unsigned int *, unsigned int);
+static int             parse_statfile(const char *, struct statinfo *);
+
+#define PRNT_CALLS     0x0001
+#define PRNT_RPC       0x0002
+#define PRNT_NET       0x0004
+#define PRNT_FH                0x0008
+#define PRNT_RC                0x0010
+#define PRNT_ALL       0xffff
+
+int
+main(int argc, char **argv)
+{
+       int             opt_all = 0,
+                       opt_srv = 0,
+                       opt_clt = 0,
+                       opt_prt = 0;
+       int             c;
+
+       while ((c = getopt(argc, argv, "acno:rsz")) != -1) {
+               switch (c) {
+               case 'a':
+                       opt_all = 1;
+                       break;
+               case 'c':
+                       opt_clt = 1;
+                       break;
+               case 'n':
+                       opt_prt |= PRNT_CALLS;
+                       break;
+               case 'o':
+                       if (!strcmp(optarg, "nfs"))
+                               opt_prt |= PRNT_CALLS;
+                       else if (!strcmp(optarg, "rpc"))
+                               opt_prt |= PRNT_RPC;
+                       else if (!strcmp(optarg, "net"))
+                               opt_prt |= PRNT_NET;
+                       else if (!strcmp(optarg, "rc"))
+                               opt_prt |= PRNT_RC;
+                       else if (!strcmp(optarg, "fh"))
+                               opt_prt |= PRNT_FH;
+                       else {
+                               fprintf(stderr, "nfsstat: unknown category: "
+                                               "%s\n", optarg);
+                               return 2;
+                       }
+                       break;
+               case 'r':
+                       opt_prt |= PRNT_RPC;
+                       break;
+               case 's':
+                       opt_srv = 1;
+                       break;
+               case 'z':
+                       fprintf(stderr, "nfsstat: zeroing of nfs statistics "
+                                       "not yet supported\n");
+                       return 2;
+               }
+       }
+
+       if (opt_all) {
+               opt_srv = opt_clt = 1;
+               opt_prt = PRNT_ALL;
+       }
+       if (!(opt_srv + opt_clt))
+               opt_srv = opt_clt = 1;
+       if (!opt_prt)
+               opt_prt = PRNT_CALLS + PRNT_RPC;
+       if ((opt_prt & (PRNT_FH|PRNT_RC)) && !opt_srv) {
+               fprintf(stderr,
+                       "You requested file handle or request cache "
+                       "statistics while using the -c option.\n"
+                       "This information is available only for the NFS "
+                       "server.\n");
+       }
+
+       if ((opt_srv && !parse_statfile(NFSSVCSTAT, svcinfo))
+        || (opt_clt && !parse_statfile(NFSCLTSTAT, cltinfo)))
+               return 2;
+
+       if (opt_srv) {
+               if (opt_prt & PRNT_NET) {
+                       print_numbers(
+                       "Server packet stats:\n"
+                       "packets    udp        tcp        tcpconn\n",
+                       svcnetinfo, 4
+                       );
+               }
+               if (opt_prt & PRNT_RPC) {
+                       print_numbers(
+                       "Server rpc stats:\n"
+                       "calls      badcalls   badauth    badclnt    xdrcall\n",
+                       svcrpcinfo, 5
+                       );
+               }
+               if (opt_prt & PRNT_RC) {
+                       print_numbers(
+                       "Server reply cache:\n"
+                       "hits       misses     nocache\n",
+                       svcrcinfo, 3
+                       );
+               }
+               if (opt_prt & PRNT_FH) {
+                       print_numbers(
+                       "Server file handle cache:\n"
+                       "cached     valid      fixup      lookup     stale\n",
+                       svcrcinfo + 3, 5);
+               }
+               if (opt_prt & PRNT_CALLS) {
+                       print_callstats(
+                       "Server nfs v2:\n",
+                       nfsv2name, svcv2info + 1, 18
+                       );
+                       if (svcv3info[0])
+                               print_callstats(
+                               "Server nfs v3:\n",
+                               nfsv3name, svcv3info + 1, 22
+                               );
+               }
+       }
+
+       if (opt_clt) {
+               if (opt_prt & PRNT_NET) {
+                       print_numbers(
+                       "Client packet stats:\n"
+                       "packets    udp        tcp        tcpconn\n",
+                       cltnetinfo, 4
+                       );
+               }
+               if (opt_prt & PRNT_RPC) {
+                       print_numbers(
+                       "Client rpc stats:\n"
+                       "calls      retrans    authrefrsh\n",
+                       cltrpcinfo, 3
+                       );
+               }
+               if (opt_prt & PRNT_CALLS) {
+                       print_callstats(
+                       "Client nfs v2:\n",
+                       nfsv2name, cltv2info + 1, 18
+                       );
+                       if (cltv3info[0])
+                               print_callstats(
+                               "Client nfs v3:\n",
+                               nfsv3name, cltv3info + 1, 22
+                               );
+               }
+       }
+
+       return 0;
+}
+
+static void
+print_numbers(const char *hdr, unsigned int *info, unsigned int nr)
+{
+       unsigned int    i;
+
+       fputs(hdr, stdout);
+       for (i = 0; i < nr; i++)
+               printf("%s%-8d", i? "   " : "", info[i]);
+       printf("\n");
+}
+
+static void
+print_callstats(const char *hdr, const char **names,
+                                unsigned int *info, unsigned int nr)
+{
+       unsigned int    total;
+       int             i, j;
+
+       fputs(hdr, stdout);
+       for (i = 0, total = 0; i < nr; i++)
+               total += info[i];
+       if (!total)
+               total = 1;
+       for (i = 0; i < nr; i += 6) {
+               for (j = 0; j < 6 && i + j < nr; j++)
+                       printf("%-11s", names[i+j]);
+               printf("\n");
+               for (j = 0; j < 6 && i + j < nr; j++)
+                       printf("%-6d %2d%% ",
+                               info[i+j], 100 * info[i+j] / total);
+               printf("\n");
+       }
+       printf("\n");
+}
+
+static int
+parse_statfile(const char *name, struct statinfo *statp)
+{
+       char    buffer[4096], *next;
+       FILE    *fp;
+
+       /* Being unable to read e.g. the nfsd stats file shouldn't
+        * be a fatal error -- it usually means the module isn't loaded.
+        */
+       if ((fp = fopen(name, "r")) == NULL) {
+               fprintf(stderr, "Warning: %s: %m\n", name);
+               return 1;
+       }
+
+       while (fgets(buffer, sizeof(buffer), fp) != NULL) {
+               struct statinfo *ip;
+               char            *sp, *line = buffer;
+               int             i, cnt;
+
+               if ((next = strchr(line, '\n')) != NULL)
+                       *next++ = '\0';
+               if (!(sp = strtok(line, " \t")))
+                       continue;
+               for (ip = statp; ip->tag; ip++) {
+                       if (!strcmp(sp, ip->tag))
+                               break;
+               }
+               if (!ip->tag)
+                       continue;
+               cnt = ip->nrvals;
+
+               for (i = 0; i < cnt; i++) {
+                       if (!(sp = strtok(NULL, " \t")))
+                               break;
+                       ip->valptr[i] = atoi(sp);
+               }
+       }
+
+       fclose(fp);
+       return 1;
+}
diff --git a/utils/nfsstat/nfsstat.man b/utils/nfsstat/nfsstat.man
new file mode 100644 (file)
index 0000000..72c8051
--- /dev/null
@@ -0,0 +1,74 @@
+.\"
+.\" nfsstat(8)
+.\" 
+.\" Copyright (C) 1996 Olaf Kirch <okir@monad.swb.de>
+.TH nfsstat 8 "8 May 1996"
+.SH NAME
+nfsstat \- print NFS statistics
+.SH SYNOPSIS
+.BI "/usr/sbin/nfsstat [-anrcsz] [-o " "facility" "] ...
+.SH DESCRIPTION
+The
+.B nfsstat
+command retrieves and pretty-prints NFS kernel statistics. Currently, only
+server-side statistics are supported, because the NFS client does not yet
+collect any data.
+.SH OPTIONS
+.TP
+.B -s
+Print only server-side statistics. The default is to print both server and
+client statistics.
+.TP
+.B -c
+Print only client-side statistics.
+.TP
+.B -n
+Print only NFS statistics. The default is to print both NFS and RPC
+information.
+.TP
+.B -r
+Print only RPC statistics.
+.TP
+.B -z
+Zero the kernel statistics counters.
+This option is not currently supported.
+.TP
+.BI -o " facility
+Display statistics for the specified facility, which must be one of:
+.RS
+.TP
+.B nfs
+NFS protocol information, split up by RPC call.
+.TP
+.B rpc
+General RPC information.
+.TP
+.B net
+Network layer statistics, such as the number of received packets, number
+of TCP connections, etc.
+.TP
+.B fh
+Usage information on the server's file handle cache, including the
+total number of lookups, and the number of hits and misses.
+.TP
+.B rc
+Usage information on the server's request reply cache, including the
+total number of lookups, and the number of hits and misses.
+.RE
+.SH EXAMPLES
+.\" --------------------- FILES ----------------------------------
+.SH FILES
+.TP
+.B /proc/net/rpc/nfsd
+.BR procfs -based
+interface to kernel NFS server statistics.
+.TP
+.B /proc/net/rpc/nfs
+.BR procfs -based
+interface to kernel NFS client statistics.
+.\" -------------------- SEE ALSO --------------------------------
+.SH SEE ALSO
+.BR rpc.nfsd (8).
+.\" -------------------- AUTHOR ----------------------------------
+.SH AUTHOR
+Olaf Kirch, <okir@monad.swb.de>
diff --git a/utils/nhfsstone/DISCLAIMER b/utils/nhfsstone/DISCLAIMER
new file mode 100644 (file)
index 0000000..afde6a3
--- /dev/null
@@ -0,0 +1,33 @@
+@(#)DISCLAIMER 1.4 89/07/07 Legato Systems, Inc.
+
+       IMPORTANT. READ BEFORE USING. USE OF THE PROGRAM WILL
+       CONSTITUTE ACCEPTANCE OF THE FOLLOWING LICENSE TERMS.
+
+Legato nhfsstone source code is a copyrighted product of Legato
+Systems, Inc. and is provided for unrestricted use and distribution of
+the binary program derived from it.
+
+You may copy Legato nhfsstone source, object code and related
+documentation as necessary, but are not authorized to license it to
+anyone else. Legato nhfsstone may be modified only for the purpose of
+porting. If the basic algorithms are changed the resulting program may
+not be called nhfsstone.
+
+Legato nhfsstone is provided with no support and without any obligation
+on the part of Legato Systems, Inc. to assist in its use, correction,
+modification or enhancement.
+
+LEGATO NHFSSTONE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND
+INCLUDING THE WARRANTIES OF DESIGN, MERCHANTIBILITY, FITNESS FOR A
+PARTICULAR PURPOSE OR NONINFRINGEMENT, OR ARISING FROM A COURSE OF
+DEALING, USAGE OR TRADE PRACTICE.
+
+LEGATO SYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY LEGATO
+NHFSSTONE, ANY PART THEREOF OR THE USE THEREOF.
+
+IN NO EVENT WILL LEGATO SYSTEMS, INC. BE LIABLE UNDER ANY CONTRACT,
+NEGLIGENCE, STRICT LIABILITY OR OTHER THEORY FOR ANY LOST REVENUE OR
+PROFITS OR OTHER SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR COST OF
+PROCUREMENT OF SUBSTITUTE GOODS OR TECHNOLOGY, EVEN IF LEGATO HAS BEEN
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
diff --git a/utils/nhfsstone/Makefile b/utils/nhfsstone/Makefile
new file mode 100644 (file)
index 0000000..d73d85a
--- /dev/null
@@ -0,0 +1,8 @@
+#
+# Makefile for nhfsstone
+#
+
+PROGRAM        = nhfsstone
+OBJS   = nhfsstone.o
+
+include $(TOP)rules.mk
diff --git a/utils/nhfsstone/README b/utils/nhfsstone/README
new file mode 100644 (file)
index 0000000..f13dde5
--- /dev/null
@@ -0,0 +1,111 @@
+@(#)README 1.6 89/07/07 Legato Systems, Inc.
+
+This directory contains the source for the nhfsstone (pronounced
+n-f-s-stone, the "h" is silent) NFS load generating program. This
+version of the program can only be compiled on 4.x BSD based UNIX
+systems.
+
+nhfsstone is used on an NFS client to generate an artificial load
+with a particular mix of NFS operations. It reports the average
+response time of the server in milliseconds per call and the load in
+calls per second.  The program adjusts its calling patterns based on
+the client's kernel NFS statistics and the elapsed time.  Load can be
+generated over a given time or number of NFS calls. See the "nhfsstone.1"
+manual page for more details.
+
+The files in this directory are:
+
+       DISCLAIMER      legal requirements
+       Makefile        Makefile used to build nhfsstone
+       README          This file
+       nhfsstone.c     source file
+       nhfsstone.1     manual page
+       nhfsrun         shell script to run nhfsstone over multiple loads
+       nhfsnums        shell script to convert nhfsrun output to plot(5)
+       nhfsgraph       shell script to create a graph from nhfsnums output
+
+The file "nhfsstone.1" is a manual page that describes how to use the
+nhfsstone program. To look at it type "nroff -man nhfsstone.1".
+
+To build an executable type "make nhfsstone". To install it, become
+super-user and then type "make install". This will strip the
+executable, set the group to "kmem" and set the setgid bit. If your
+site requires different installation of programs that read /dev/kmem
+you may have to use different ownership or permissions. Make install
+will also set the execute bits on the shell scripts nhfsrun, nhfsnums
+and nhfsgraph.
+
+To run an nhfsstone test, create a parent test directory on a filesystem
+that is NFS mounted, cd to that directory and type "nhfsstone". This will
+do a run with the default settings, load = 30 calls/sec, 5000 calls,
+and 7 sub-processes.
+
+If you want to spread the load across several server disks, first
+figure out on the server which disk partitions are exported as which
+filesystems. If you don't already have more than one of these
+filesystems mounted on your test client you can mount them in temporary
+locations, like /mnt. Create test directories on these filesystems so
+that the load will be distributed according to the simulation that you
+want to run (for example, you might put 4 test directories on the
+filesystem where the diskless client's root and swap live, and 2 on the
+home directories filesystem, and one on the executables filesystem).
+Now create a parent test directory cd to it, and make symbolic links
+with the names testdir0, testdir1, ... testdir6, that point to the
+real test directories. Finally, run nhfsstone from the parent test
+directory.
+
+If you are doing the test from a diskless machine, putting half of the
+test directories in /tmp or /usr/tmp and running the test from your
+home directory will simulate real diskless load patterns fairly well.
+
+To do a run over multiple load levels, edit the shell script "nhfsrun" and
+set the shell variables "START", "END", and "INCR" to be the correct
+starting and ending loads, and load increment. The script will iterate
+from START to END with an increment of INCR, run nhfsstone at each
+load level, and put the output in the file "run.out". The output file
+name can be changed by editing the nhfsrun script and changing the
+"OUTFILE" variable or by passing a file name suffix on the command line:
+
+       nhfsrun xysd
+
+This produces the output file "run.xysd".
+
+The script "nhfsnums" takes the output from nhfsrun and converts it
+into plot(5) format so that it can be graphed using graph(1) and other
+tools. It takes its input either from files given on the command line
+or from standard in:
+
+       nhfsnums [numsfile] ...
+
+If file names are given, the suffix of each name (the part after the
+".") is used as the line label for the set of numbers produced (see
+plot(5)).
+
+"nhfsgraph" takes the output from nhfsnums and passes it to graph(1)
+with the right arguments to produce PostScript output for a labeled
+graph. The nhfsgraph script can be used as a filter:
+
+       nhfsnums run.* | nhfsgraph | lpr
+
+
+
+
+This program is provided free of charge to anyone who wants it provided
+certain conditions are met (see DISCLAIMER file for more details).
+
+If you would like to receive regular information and bug fixes please
+send your name, and both your Email and U.S. mail addresses to:
+
+       Legato Systems, Inc.
+       Nhfsstone
+       260 Sheridan Avenue
+       Palo Alto, California  94306
+
+       nhfsstone-request@legato.com or uunet!legato.com!nhfsstone-request
+
+and we will add your name to the nhfsstone mailing list. Comments and bug
+reports should be sent to:
+
+       nhfsstone@legato.com or uunet!legato.com!nhfsstone
+
+
diff --git a/utils/nhfsstone/README.linux b/utils/nhfsstone/README.linux
new file mode 100644 (file)
index 0000000..e9b7899
--- /dev/null
@@ -0,0 +1,11 @@
+
+
+ This is my port of nhfsstone to Linux. As a benchmark, it has been
+ superseded by LADDIS (but unfortunately, LADDIS comes with a 1200 buck
+ price tag), but it's quite good at catching NFS bugs :-)
+
+ Of course, this port does not work with the old NFS client code, as
+ it does not collect RPC stats.
+
+ Olaf
+
diff --git a/utils/nhfsstone/nhfsgraph b/utils/nhfsstone/nhfsgraph
new file mode 100755 (executable)
index 0000000..56e2c77
--- /dev/null
@@ -0,0 +1,23 @@
+#!/bin/sh
+#
+# @(#)nhfsgraph.sh 1.3 89/07/07 Copyright (c) 1989, Legato Systems, Inc.
+#
+# See DISCLAIMER file for restrictions
+#
+
+#
+# Usage: nhfsgraph <graphfile> ...
+#
+# Produce a PostScript graph of nhfsstone numbers.
+# Graphfile is a file with number pairs in plot(5) format derived
+# from runs of nhfsstone at different loads (see "nhfsrun" and "nhfsnums"
+# scripts.
+#
+# If you want something other than PostScript output replace "psplot"
+# with "plot". See plot(1) for more details.
+#
+
+LABEL="x=Load (calls/sec) y=Response (msec/call)"
+
+cat $* \
+    | graph -b -u .1 -h 1.2 -g 2 -l "$LABEL" -x 10 80 10 | psplot
diff --git a/utils/nhfsstone/nhfsnums b/utils/nhfsstone/nhfsnums
new file mode 100755 (executable)
index 0000000..aae625d
--- /dev/null
@@ -0,0 +1,22 @@
+#!/bin/sh
+#
+# @(#)nhfsnums.sh 1.3 89/07/07 Copyright (c) 1989, Legato Systems, Inc.
+#
+# See DISCLAIMER file for restrictions
+#
+
+#
+# Usage: nhfsnums <numsfile> ...
+#
+# Collect raw numbers from nhfsstone output and print in plot(5) format.
+# The nums files should be named "run.xxx" where xxx is a name related
+# to the numbers gathered. Each file will produce one line with a label
+# that is the file suffix (the part following the dot.)
+#
+
+for i in $*; do
+       RUNNAME=`echo $i | sed -e 's/.*\\.//'`
+       awk '{ print $5 "       " $7 }' $i \
+           | sort -n\
+           | sed -e "\$s/\$/   \"$RUNNAME\"/"
+done
diff --git a/utils/nhfsstone/nhfsrun b/utils/nhfsstone/nhfsrun
new file mode 100755 (executable)
index 0000000..dfc24eb
--- /dev/null
@@ -0,0 +1,59 @@
+#!/bin/sh
+#
+# @(#)nhfsrun.sh 1.3 89/07/07 Copyright (c) 1989, Legato Systems, Inc.
+#
+# See DISCLAIMER file for restrictions
+#
+
+#
+# Usage: nhfsrun [suffix]
+#
+# Run nhfsstone with a range of different loads and put
+# results in a file called run.<suffix>
+#
+
+if [ $# -gt 1 ]; then
+       echo "usage: $0 [suffix]"
+       exit 1
+fi
+
+#
+# Output file
+#
+if [ $# -eq 1 ]; then
+       OUTFILE=run.$1
+else
+       OUTFILE=run.out
+fi
+
+#
+# Starting load
+#
+START=10
+
+#
+# Ending load
+#
+END=80
+
+#
+# Load increment
+#
+INCR=10
+
+#
+# Catch SIGUSR1 and ignore it.
+# SIGUSR1 is used by nhfsstone to synchronize child processes.
+#
+nothing() { echo -n ""; }
+trap nothing 30
+
+rm -f $OUTFILE
+
+LOAD=$START
+while [ $LOAD -le $END ]; do
+       echo nhfsstone -l $LOAD
+       nhfsstone -l $LOAD >> $OUTFILE
+       tail -1 $OUTFILE
+       LOAD=`expr $LOAD + $INCR`
+done
diff --git a/utils/nhfsstone/nhfsstone.1 b/utils/nhfsstone/nhfsstone.1
new file mode 100644 (file)
index 0000000..e56eb9e
--- /dev/null
@@ -0,0 +1,381 @@
+.\" @(#)nhfsstone.1 1.13 89/10/05 Copyright (c) 1989, Legato Systems Inc
+.\" See DISCLAIMER file for restrictions
+.TH NHFSSTONE 1 "4 October 1989"
+.SH NAME
+nhfsstone \- Network File System benchmark program
+.SH SYNOPSIS
+.B nhfsstone
+[
+.B \-v
+] [[
+.B \-t secs
+] | [
+.B -c calls
+]] [
+.B \-l load
+] [
+.B \-p nprocs
+] [
+.B \-m mixfile
+] [
+.B dir
+]...
+.SH DESCRIPTION
+.B nhfsstone
+(pronounced n\-f\-s\-stone, the "h" is silent)
+is used on a
+.SM NFS
+client to generate an artificial load with a particular mix of
+.SM NFS
+operations. It reports the average response time of the server in
+milliseconds per call and the load in calls per second.
+The program adjusts its calling patterns based on the client's kernel
+.SM NFS
+statistics and the elapsed time.
+Load can be generated over a given time or number of
+.SM NFS
+calls.
+.LP
+Because it uses the kernel
+.SM NFS
+statistics to monitor its progress,
+.B nhfsstone
+cannot be used to measure the performance of non\-NFS filesystems.
+.LP
+The
+.B nhfsstone
+program uses file and directory manipulation in an attempt to generate
+particular
+.SM NFS
+operations in response to particular system calls.
+To do this it uses several tricks
+that are based on a knowledge of the implementation of the
+.SM NFS
+client side reference port.
+For example, it uses long file names to circumvent the kernel name lookup
+cache so that a
+.BR stat (2)
+system call generates an
+.SM NFS
+lookup operation.
+.LP
+The mix of
+.SM NFS
+operations can be set with a mix file, which is the output of the
+.BR nfsstat (8C)
+command (see the "\-m" option below).
+The percentages taken from
+the mix file are calculated based on the number of
+.SM NFS
+calls, not on the percentages printed by nfsstat. Operations with
+0% in the mix will never get called by
+.BR nhfsstone .
+In a real server load mix, even though the percentage of call for
+a particular
+.SM NFS
+operation may be zero, the number of calls is often nonzero.
+.B Nhfsstone
+makes the assumption that the number of calls to these 0 percent
+operations will have an insignificant effect on server response.
+.LP
+Normally
+.B nhfsstone
+should be given a list of two or more test directories to use
+(default is to use the current directory).
+The test directories used should be located on different disks and
+partitions on the server to realistically simulate typical server loads.
+Each
+.B nhfsstone
+process looks for a directory
+.B <dir>/testdir<n>
+(where <n> is a number from 0 to
+.B nprocs
+\- 1).
+If a process directory name already exists,
+it is checked for the correct set of test files.
+Otherwise the directory is created and populated.
+.SH OPTIONS
+.TP 12
+.B \-v
+Verbose output.
+.TP
+.B \-t secs
+Sets
+.B calls
+based on the given running time (in seconds) and the load.
+.TP
+.B \-c calls
+Total number of
+.SM NFS
+calls to generate (default is 5000).
+.TP
+.B \-l load
+Load to generate in
+.SM NFS
+calls per second (default is 30).
+.TP
+.B \-p nprocs
+Number of load generating sub\-processes to fork (default is 7).
+This can be used to maximize the amount of load a single machine can generate.
+On a small client machine (slow CPU or small amount of memory)
+fewer processes might be used to avoid swapping.
+.TP
+.B \-m mixfile
+Mix of
+.SM NFS
+operations to generate.
+The format of
+.B mixfile
+is the same as the output of the
+.BR nfsstat (8C)
+program.
+A mix file can be created on a server by typing "nfsstat \-s > mixfile".
+The default mix of operations is: null 0%, getattr 13%, setattr 1%,
+root 0%, lookup 34%, readlink 8%, read 22%, wrcache 0%, write 15%, create 2%,
+remove 1%, rename 0%, link 0%, symlink 0%, mkdir 0%, rmdir 0%, readdir 3%,
+fsstat 1%.
+.SH USING NHFSSTONE
+As with all benchmarks,
+.B nhfsstone
+can only provide numbers that are useful if experiments that use it are
+set up carefully.
+Since it is measuring servers, it should be run on a client
+that will not limit the generation of
+.SM NFS
+requests.
+This means it should have a fast CPU,
+a good ethernet interface and the machine
+should not be used for anything else during testing.
+A Sun\-3/50 can generate about 60
+.SM NFS
+calls per second before it runs out of CPU.
+.LP
+.B Nhfsstone
+assumes that all
+.SM NFS
+calls generated on the client are going to a single server, and that
+all of the
+.SM NFS
+load on that server is due to this client.
+To make this assumption hold,
+both the client and server should be as quiescent as possible during tests.
+.LP
+If the network is heavily utilized the delays due to collisions
+may hide any changes in server performance.
+High error rates on either the client or server can also
+cause delays due to retransmissions of lost or damaged packets.
+.BR netstat (8C)
+.B \-i
+can be used to measure the error and collision rates on the client and server.
+.LP
+To best simulate the effects of
+.SM NFS
+clients on the server, the test
+directories should be set up so that they are on at least two of the
+disk partitions that the server exports and the partitions should be
+as far apart as possible. The
+.BR dkinfo (8)
+command can be used to find the physical geometry of disk on BSD based systems.
+.SM NFS
+operations tend to randomize
+access the whole disk so putting all of the
+.B nhfsstone
+test directories on a single partition or on
+two partitions that are close together will not show realistic results.
+.LP
+On all tests it is a good idea to run the tests repeatedly and compare results.
+The number of calls can be increased
+(with the
+.B \-c
+option) until the variance in milliseconds per call is acceptably small.
+If increasing the number of calls does not help there may be something
+wrong with the experimental setup.
+One common problem is too much memory on the client
+test machine. With too much memory,
+.B nhfsstone
+is not able to defeat the client caches and the
+.SM NFS
+operations do not end up going to the server at all. If you suspect that
+there is a caching problem you can use the
+.B -p
+option to increase the number of processes.
+.LP
+The numbers generated by
+.B nhfsstone
+are most useful for comparison if the test setup on the client machine
+is the same between different server configurations. 
+Changing
+.B nhfsstone
+parameters between runs will produce numbers that can not be
+meaningfully compared.
+For example, changing the number of generator processes
+may affect the measured response
+time due to context switching or other delays on the client machine, while
+changing the mix of
+.SM NFS
+operations will change the whole nature of the experiment.
+Other changes to the client configuration may also effect the comparability
+of results.
+While
+.B nhfsstone
+tries to compensate for differences in client configurations
+by sampling the actual
+.SM NFS
+statistics and adjusting both the load and mix of operations, some changes
+are not reflected in either the load or the mix. For example, installing
+a faster CPU or mounting different
+.SM NFS
+filesystems may effect the response time without changing either the
+load or the mix.
+.LP
+To do a comparison of different server configurations, first set up the
+client test directories and do
+.B nhfsstone
+runs at different loads to be sure that the variability is
+reasonably low. Second, run
+.B nhfsstone
+at different loads of interest and
+save the results. Third, change the server configuration (for example,
+add more memory, replace a disk controller, etc.). Finally, run the same
+.B nhfsstone
+loads again and compare the results.
+.SH SEE ALSO
+.LP
+The
+.B nhfsstone.c
+source file has comments that describe in detail the operation of
+of the program.
+.SH ERROR MESSAGES
+.TP
+.B "illegal calls value"
+The 
+.B calls
+argument following the
+.B \-c
+flag on the command line is not a positive number.
+.TP
+.B "illegal load value"
+The
+.B load
+argument following the
+.B \-l
+flag on the command line is not a positive number.
+.TP
+.B "illegal time value"
+The
+.B time
+argument following the
+.B \-t
+flag on the command line is not a positive number.
+.TP
+.B "bad mix file"
+The
+.B mixfile
+file argument following the
+.B \-m
+flag on the command line could not be accessed.
+.TP
+.B "can't find current directory"
+The parent process couldn't find the pathname of the current directory.
+This usually indicates a permission problem.
+.TP
+.B "can't fork"
+The parent couldn't fork the child processes. This usually results from
+lack of resources, such as memory or swap space.
+.TP
+.PD 0
+.B "can't open log file"
+.TP
+.B "can't stat log"
+.TP
+.B "can't truncate log"
+.TP
+.B "can't write sync file"
+.TP
+.B "can't write log"
+.TP
+.B "can't read log"
+.PD
+A problem occurred during the creation, truncation, reading or writing of the
+synchronization log file. The parent process creates the
+log file in /tmp and uses it to synchronize and communicate with its children.
+.TP
+.PD 0
+.B "can't open test directory"
+.TP
+.B "can't create test directory"
+.TP
+.B "can't cd to test directory"
+.TP
+.B "wrong permissions on test dir"
+.TP
+.B "can't stat testfile"
+.TP
+.B "wrong permissions on testfile"
+.TP
+.B "can't create rename file"
+.TP
+.B "can't create subdir"
+.PD
+A child process had problems creating or checking the contents of its
+test directory. This is usually due to a permission problem (for example
+the test directory was created by a different user) or a full filesystem.
+.TP
+.PD 0
+.B "bad mix format: unexpected EOF after 'nfs:'"
+.TP
+.B "bad mix format: can't find 'calls' value"
+.TP
+.B "bad mix format: unexpected EOF after 'calls'"
+.TP
+.B "bad mix format: can't find %d op values"
+.TP
+.B "bad mix format: unexpected EOF"
+.PD
+A problem occurred while parsing the
+.B mix
+file. The expected format of the file is the same as the output of
+the
+.BR nfsstat (8C)
+command when run with the "\-s" option.
+.TP
+.B "op failed: "
+One of the internal pseudo\-NFS operations failed. The name of the operation,
+e.g. read, write, lookup, will be printed along with an indication of the
+nature of the failure.
+.TP
+.B "select failed"
+The select system call returned an unexpected error.
+.SH BUGS
+.LP
+Running
+.B nhfsstone
+on a non\-NFS filesystem can cause the program to run forever because it
+uses the kernel NFS statistics to determine when enough calls have been made.
+.LP
+.B Nhfsstone
+uses many file descriptors. The kernel on the client may
+have to be reconfigured to increase the number of available file table entries.
+.LP
+Shell scripts that used
+.B nhfsstone
+will have to catch and ignore SIGUSR1 (see
+.BR signal (3)).
+This signal is
+used to synchronize the test processes. If the signal is not caught
+the shell that is running the script will be killed.
+.SH FILES
+.PD 0
+.TP 20
+.B /vmunix
+system namelist
+.TP
+.B /dev/kmem
+kernel virtual memory
+.TP
+.B ./testdir*
+per process test directory
+.TP
+.B /tmp/nhfsstone%d
+process synchronization log file
+.PD
diff --git a/utils/nhfsstone/nhfsstone.c b/utils/nhfsstone/nhfsstone.c
new file mode 100644 (file)
index 0000000..034ba79
--- /dev/null
@@ -0,0 +1,1798 @@
+#ifndef lint
+static char sccsid[] = "@(#)nhfsstone.c 1.22 90/05/08 Copyright (c) 1990, Legato Systems Inc";
+#endif
+
+/*
+ * Copyright (c) 1990 Legato Systems Inc.
+ *
+ * See DISCLAIMER file for restrictions
+ *
+ * Ported to Linux by Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/vfs.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#ifdef BSD
+#include <sys/dir.h>
+#define        dirent  direct
+#else
+#include <dirent.h>
+#endif
+#include <signal.h>
+
+#ifndef NULL
+#define NULL   0
+#endif
+
+/*
+ * Usage: nhfsstone [-v] [[-t secs] | [-c calls]] [-l load] [-p nprocs]
+ *                                             [-m mixfile] [dir]...
+ *
+ * Generates an artifical NFS client load based on a given mix of
+ * operations.
+ *
+ * Strategy: loop for some number of NFS calls doing a random sleep
+ * followed by a call to one of the op generator routines. The routines
+ * are called based on a weighting factor determined by the difference
+ * between the current ops percentages (derived from kernel NFS stats)
+ * and a set of default percentages or a mix supplied by the caller.
+ *
+ * The generator routines try very hard to guess how many NFS operations
+ * they are generating so that the calling routine can keep a running
+ * estimate of the number of calls and the mix to avoid having to get
+ * the NFS statistics from the kernel too often.
+ *
+ * The operations are done in a directory that has a set of file names
+ * that are long enough that they won't be cached by the name cache
+ * in the kernel. The "lookup" operation steps through the names and
+ * creates a file if that name does not exist, or closes and reopens it
+ * if it does. This generates a table of open file descriptors. Most of the
+ * other operations are done on random descriptors in the table. The "getattr"
+ * operation tries to avoid the kernel attribute cache by doing "fstat"
+ * system calls on random descriptors in the table. There must be enough
+ * files in the directory so that, on average, the getattr operation hits
+ * any file less often than once each 6 seconds (the default timeout for
+ * the attributes cache).
+ *
+ * The parent process starts children to do the real work of generating load.
+ * The parent coordinates them so that they all start at the same time, and
+ * collects statistics from them when they are done. To coordinate the
+ * start up, the parent waits for each child to write one byte into
+ * a common log file (opened in append mode to avoid overwriting).
+ * After they write a byte the children pause, and the parent send SIGUSR1
+ * when it has heard from all of the kids. The children write their statistics
+ * into the same common log file and the parent reads and accumulates the
+ * statics and prints them out.
+ *
+ * This code will only compile and run on 4.X BSD based systems.
+ */
+
+#define        DEFAULT_LOAD    30              /* default calls per sec */
+#define        DEFAULT_CALLS   5000            /* default number of calls */
+#define        NFILES          40              /* number of test files/dir */
+#define        BUFSIZE         8192            /* block size for read and write */
+#define        MAXFILESIZE     32              /* size, in blocks, of large file */
+#define        SAMPLETIME      5               /* secs between samples of NFS stats */
+#define        NPROCS          7               /* number of children to run */
+
+
+/*
+ * The names of NFS operations
+ */
+char *Opnames[] = {
+       "null", "getattr", "setattr", "root", "lookup", "readlink", "read",
+       "wrcache", "write", "create", "remove", "rename", "link", "symlink",
+       "mkdir", "rmdir", "readdir", "fsstat",
+};
+
+/*
+ * NFS operation numbers
+ *
+ * Used to index the Opnames, Mix and statistics arrays.
+ */
+#define        NOPS            18              /* number of NFS ops */
+#define        NULLCALL        0
+#define        GETATTR         1
+#define        SETATTR         2
+#define        ROOT            3
+#define        LOOKUP          4
+#define        READLINK        5
+#define        READ            6
+#define        WRCACHE         7
+#define        WRITE           8
+#define        CREATE          9
+#define        REMOVE          10
+#define        RENAME          11
+#define        LINK            12
+#define        SYMLINK         13
+#define        MKDIR           14
+#define        RMDIR           15
+#define        READDIR         16
+#define        FSSTAT          17
+
+/*
+ * Operations counts
+ */
+struct count {
+       int             total;
+       int             calls[NOPS];
+};
+
+/*
+ * Software development mix for server with about 50/50 mix of
+ * diskless and diskful clients running SunOS 4.0.
+ */
+int    Mix[NOPS] = {
+       0,              /* null */
+       13,             /* getattr */
+       1,              /* setattr */
+       0,              /* root */
+       34,             /* lookup */
+       8,              /* readlink */
+       22,             /* read */
+       0,              /* wrcache */
+       15,             /* write */
+       2,              /* create */
+       1,              /* remove */
+       0,              /* rename */
+       0,              /* link */
+       0,              /* symlink */
+       0,              /* mkdir */
+       0,              /* rmdir */
+       3,              /* readdir */
+       1,              /* fsstat */
+};
+
+/* Prototype decls */
+int    setmix(FILE *fp);
+void   usage(void);
+void   init_logfile(void);
+void   init_counters(void);
+void   get_delta(struct count *start, struct count *cur);
+void   init_testdir(int dirnum, char *parentdir);
+void   do_op(int rpct);
+void   op(int opnum);
+void   nextfile(void);
+int    createfile(void);
+int    openfile(void);
+int    writefile(void);
+void   collect_counters(void);
+int    check_counters(void);
+void   print(void);
+void   msec_sleep(int msecs);
+void   get_opct(struct count *count);
+int    substr(char *sp, char *subsp);
+int    check_access(struct stat statb);
+void   error(char *str);
+
+/*
+ * NFS operations generator routines
+ */
+int    op_null();
+int    op_getattr();
+int    op_setattr();
+int    op_root();
+int    op_lookup();
+int    op_readlink();
+int    op_read();
+int    op_wrcache();
+int    op_write();
+int    op_create();
+int    op_remove();
+int    op_rename();
+int    op_link();
+int    op_symlink();
+int    op_mkdir();
+int    op_rmdir();
+int    op_readdir();
+int    op_fsstat();
+
+/*
+ * Operations generator vector
+ */
+struct op_vect {
+       int     (*funct)();     /* op */
+} Op_vect[NOPS] = {
+       { op_null },
+       { op_getattr },
+       { op_setattr },
+       { op_root },
+       { op_lookup },
+       { op_readlink },
+       { op_read },
+       { op_wrcache },
+       { op_write },
+       { op_create },
+       { op_remove },
+       { op_rename },
+       { op_link },
+       { op_symlink },
+       { op_mkdir },
+       { op_rmdir },
+       { op_readdir },
+       { op_fsstat },
+};
+
+/*
+ * Name sub-strings
+ */
+#define        DIRSTR  "dir"                   /* directory */
+#define        SYMSTR  "sym"                   /* symbolic link */
+#define        LINSTR  "lin"                   /* hard link */
+
+struct timeval Optime[NOPS+1];         /* cumulative running time for ops */
+struct count Curct;                    /* total number ops called */
+int    Openfd[NFILES];                 /* open file descriptors */
+int    Curnum;                         /* current file number */
+int    Symnum;                         /* current symlink file number */
+int    Linknum;                        /* current link file number */
+int    Dirnum;                         /* current directory number */
+DIR    *Testdir;                       /* my test directory */
+char   Testdirname[MAXNAMLEN*2];       /* my test directory name */
+char   Curname[MAXNAMLEN];             /* current file name */
+char   Dirname[MAXNAMLEN];             /* current directory name */
+char   Symname[MAXNAMLEN];             /* symlink file name */
+char   Linkname[MAXNAMLEN];            /* link file name */
+char   *Otherspec = "%s/%03d";         /* sprintf spec for other names */
+char   *Rename1 = "rename1";           /* first name of rename pair */
+char   *Rename2 = "rename2";           /* second name of rename pair */
+char   *Symlinkpath = "./symlinknamelongstuff";
+                                       /* symlink file data */
+char   *Myname;                        /* name program invoked under */
+char   Namebuf[MAXNAMLEN];             /* unique name for this program */
+int    Log;                            /* synchronization log */
+char   Logname[MAXNAMLEN];             /* synchronization log name */
+int    Kmem;                           /* /dev/kmem file descriptor */
+off_t  Statoffset;                     /* offset to op count in NFS stats */
+int    Nprocs;                         /* sub-processes started */
+int    Verbose;                        /* print more info */
+int    Testop = -1;                    /* operation to test */
+int    Saveerrno;                      /* place to save errno */
+
+#define        subtime(t1, t2) {if ((t1.tv_usec -= t2.tv_usec) >= 1000000) {\
+                               t1.tv_sec += (t1.tv_usec / 1000000); \
+                               t1.tv_usec %= 1000000; \
+                        } else if (t1.tv_usec < 0) { \
+                               t1.tv_usec += 1000000; \
+                               t1.tv_sec--; \
+                        } \
+                        t1.tv_sec -= t2.tv_sec; \
+                       }
+
+#define        addtime(t1, t2) {if ((t1.tv_usec += t2.tv_usec) >= 1000000) {\
+                               t1.tv_sec += (t1.tv_usec / 1000000); \
+                               t1.tv_usec %= 1000000; \
+                        } else if (t1.tv_usec < 0) { \
+                               t1.tv_usec += 1000000; \
+                               t1.tv_sec--; \
+                        } \
+                        t1.tv_sec += t2.tv_sec; \
+                       }
+
+/*
+ * Used to catch the parent's "start" signal
+ */
+void
+startup()
+{
+
+       return;
+}
+
+/*
+ * Clean up and exit
+ */
+void
+cleanup()
+{
+
+       (void) unlink(Logname);
+       exit(1);
+}
+
+int
+main(int argc, char **argv)
+{
+       int runtime;            /* length of run, in seconds */
+       int load;               /* load factor, in client loads */
+       int ncalls;             /* total number of calls to make */
+       int avgmspc;            /* average millisec per call */
+       int mspc;               /* millisec per call */
+       int wantcalls;          /* ncalls that should have happend by now */
+       int pid;                /* process id */
+       int delay;              /* msecs since last checked current time */
+       int randnum;            /* a random number */
+       int oldmask;            /* saved signal mask */
+       int sampletime;         /* secs between reading kernel stats */
+       char *opts;             /* option parsing */
+       int pct;
+       int procnum;
+       FILE *fp;
+       struct timeval curtime;
+       struct timeval starttime;
+       struct count startct;
+       struct stat statb;
+       char workdir[MAXPATHLEN];
+       char *getwd();
+
+       Myname = argv[0];
+
+       argc--;
+       argv++;
+
+       load = DEFAULT_LOAD;
+       ncalls = 0;
+       runtime = 0;
+       Nprocs = NPROCS;
+       pid = 0;
+
+       (void) umask(0);
+
+       /*
+        * Parse options
+        */
+       while (argc && **argv == '-') {
+               opts = &argv[0][1];
+               while (*opts) {
+                       switch (*opts) {
+
+                       case 'c':
+                               /*
+                                * Set number of calls
+                                */
+                               if (!isdigit(argv[1][0])) {
+                                       (void) fprintf(stderr,
+                                           "%s: illegal calls value %s\n",
+                                           Myname, argv[1]);
+                                       exit(1);
+                               }
+                               ncalls = atoi(argv[1]);
+                               argv++;
+                               argc--;
+                               break;
+
+                       case 'l':
+                               /*
+                                * Set load
+                                */
+                               if (!isdigit(argv[1][0])) {
+                                       (void) fprintf(stderr,
+                                           "%s: illegal load value %s\n",
+                                           Myname, argv[1]);
+                                       exit(1);
+                               }
+                               load = atoi(argv[1]);
+                               argv++;
+                               argc--;
+                               break;
+
+                       case 'm':
+                               /*
+                                * Set mix from a file
+                                */
+                               if ((fp = fopen(argv[1], "r")) == NULL) {
+                                       Saveerrno = errno;
+                                       (void) fprintf(stderr,
+                                           "%s: bad mix file", Myname);
+                                       errno = Saveerrno;
+                                       perror("");
+                                       exit(1);
+                               }
+                               if (setmix(fp) < 0) {
+                                       exit(1);
+                               }
+                               (void) fclose(fp);
+                               argv++;
+                               argc--;
+                               break;
+
+                       case 'p':
+                               /*
+                                * Set number of child processes
+                                */
+                               if (!isdigit(argv[1][0])) {
+                                       (void) fprintf(stderr,
+                                           "%s: illegal procs value %s\n",
+                                           Myname, argv[1]);
+                                       exit(1);
+                               }
+                               Nprocs = atoi(argv[1]);
+                               argv++;
+                               argc--;
+                               break;
+
+                       case 'T':
+                               /*
+                                * Set test mode, number following is opnum
+                                */
+                               if (!isdigit(argv[1][0])) {
+                                       (void) fprintf(stderr,
+                                           "%s: illegal test value %s\n",
+                                           Myname, argv[1]);
+                                       exit(1);
+                               }
+                               Testop = atoi(argv[1]);
+                               if (Testop >= NOPS) {
+                                       (void) fprintf(stderr,
+                                           "%s: illegal test value %d\n",
+                                           Myname, Testop);
+                                       exit(1);
+                               }
+                               argv++;
+                               argc--;
+                               break;
+
+                       case 't':
+                               /*
+                                * Set running time
+                                */
+                               if (!isdigit(argv[1][0])) {
+                                       (void) fprintf(stderr,
+                                           "%s: illegal time value %s\n",
+                                           Myname, argv[1]);
+                                       exit(1);
+                               }
+                               runtime = atoi(argv[1]);
+                               argv++;
+                               argc--;
+                               break;
+
+                       case 'v':
+                               /*
+                                * Set verbose mode
+                                */
+                               Verbose++;
+                               break;
+
+                       default:
+                               usage();
+                               exit(1);
+
+                       }
+                       opts++;
+               }
+               argv++;
+               argc--;
+       }
+
+       init_logfile();         /* Set up synchronizatin log file */
+
+       if (getcwd(workdir, sizeof(workdir)) == (char *) 0) {
+               Saveerrno = errno;
+               (void) fprintf(stderr,
+                   "%s: can't find current directory ", Myname);
+               errno = Saveerrno;
+               perror("");
+               exit(1);
+       }
+
+       (void) signal(SIGINT, cleanup);
+       (void) signal(SIGUSR1, startup);
+       oldmask = sigblock(sigmask(SIGUSR1));
+
+       if (ncalls == 0) {
+               if (runtime == 0) {
+                       ncalls = DEFAULT_CALLS;
+               } else {
+                       ncalls = runtime * load;
+               }
+       }
+       avgmspc = Nprocs * 1000 / load;
+
+       /*
+        * Fork kids
+        */
+       for (procnum = 0; procnum < Nprocs; procnum++) {
+               if ((pid = fork()) == -1) {
+                       Saveerrno = errno;
+                       (void) fprintf(stderr, "%s: can't fork ", Myname);
+                       errno = Saveerrno;
+                       perror("");
+                       (void) kill(0, SIGINT);
+                       exit(1);
+               }
+               /*
+                * Kids go initialize
+                */
+               if (pid == 0) {
+                       break;
+               }
+       }
+
+       /*
+        * Parent: wait for kids to get ready, start them, wait for them to
+        * finish, read and accumulate results.
+        */
+       if (pid != 0) {
+               /*
+                * wait for kids to initialize
+                */
+               do {
+                       sleep(1);
+                       if (fstat(Log, &statb) == -1) {
+                               (void) fprintf(stderr, "%s: can't stat log %s",
+                                   Myname, Logname);
+                               (void) kill(0, SIGINT);
+                               exit(1);
+                       }
+               } while (statb.st_size != Nprocs);
+
+               if (ftruncate(Log, 0L) == -1) {
+                       (void) fprintf(stderr, "%s: can't truncate log %s",
+                           Myname, Logname);
+                       (void) kill(0, SIGINT);
+                       exit(1);
+               }
+
+               sync();
+               sleep(3);
+
+               /*
+                * Be sure there isn't something else going on
+                */
+               get_opct(&startct);
+               msec_sleep(2000);
+               get_delta(&startct, &Curct);
+               if (Curct.total > 20) {
+                       (void) fprintf(stderr,
+                           "%s: too much background activity (%d calls/sec)\n",
+                           Myname, Curct.total);
+                       (void) kill(0, SIGINT);
+                       exit(1);
+               }
+
+               /*
+                * get starting stats
+                */
+               get_opct(&startct);
+
+               /*
+                * Start kids
+                */
+               (void) kill(0, SIGUSR1);
+
+               /*
+                * Kids started, wait for first one to finish, signal the
+                * rest and wait for them to finish.
+                */
+               if (wait((union wait *) 0) != -1) {
+                       (void) kill(0, SIGUSR1);
+                       while (wait((union wait *) 0) != -1)
+                               /* nothing */;
+               }
+
+               /*
+                * Initialize and sum up counters
+                */
+               init_counters();
+               get_delta(&startct, &Curct);
+               collect_counters();
+               if (check_counters() == -1) {
+                       Verbose = 1;
+               }
+               print();
+
+               (void) close(Log);
+               (void) unlink(Logname);
+
+               exit(0);
+       }
+
+       /*
+        * Children: initialize, then notify parent through log file,
+        * wait to get signal, beat the snot out of the server, write
+        * stats to the log file, and exit.
+        */
+
+       /*
+        * Change my name for error logging
+        */
+       (void) sprintf(Namebuf, "%s%d", Myname, procnum);
+       Myname = Namebuf;
+
+       /*
+        * Initialize and cd to test directory
+        */
+       if (argc != 0) {
+               init_testdir(procnum, argv[procnum % argc]);
+       } else {
+               init_testdir(procnum, ".");
+       }
+       if ((Testdir = opendir(".")) == NULL) {
+               Saveerrno = errno;
+               (void) fprintf(stderr,
+                   "%s: can't open test directory ", Myname);
+               errno = Saveerrno;
+               perror(Testdirname);
+               exit(1);
+       }
+
+       init_counters();
+       srandom(procnum+1);
+
+       /*
+        * Tell parent I'm ready then wait for go ahead
+        */
+       if (write(Log, " ", 1) != 1) {
+               (void) fprintf(stderr, "%s: can't write sync file %s",
+                   Myname, Logname);
+               (void) kill(0, SIGINT);
+               exit(1);
+       }
+
+       sigpause(oldmask);
+
+       /*
+        * Initialize counters
+        */
+       get_opct(&startct);
+       (void) gettimeofday(&starttime, (struct timezone *)NULL);
+       sampletime = starttime.tv_sec + ((int) random()) % (2 * SAMPLETIME);
+       curtime = starttime;
+
+       /*
+        * Do pseudo NFS operations and adapt to dynamic changes in load
+        * by adjusting the sleep time between operations based on the
+        * number of calls that should have occured since starttime and
+        * the number that have actually occured.  A delay is used to avoid
+        * doing gettimeofday calls too often, and a sampletime is
+        * used to avoid reading kernel NFS stats too often.
+        * If parent interrupts, get out and clean up.
+        */
+       delay = 0;
+       mspc = avgmspc;
+       for (;;) {
+               randnum = (int) random();
+               if (mspc > 0) {
+                       msec_sleep(randnum % (mspc << 1));
+               }
+
+               /*
+                * Do the NFS operation
+                * We use a random number from 0-199 to avoid starvation
+                * of the operations at the end of the mix.
+                */
+               do_op(randnum % 200);
+
+               /*
+                * Do a gettimeofday call only once per second
+                */
+               delay += mspc;
+               if (delay > 1000 || Curct.total >= ncalls) {
+                       delay = 0;
+                       (void) gettimeofday(&curtime, (struct timezone *)NULL);
+
+                       /*
+                        * If sample time is up, check the kernel stats
+                        * and adjust our parameters to either catch up or
+                        * slow down.
+                        */
+                       if (curtime.tv_sec > sampletime ||
+                           Curct.total >= ncalls) {
+                               sampletime = curtime.tv_sec + SAMPLETIME;
+                               get_delta(&startct, &Curct);
+                               if (Curct.total >= ncalls) {
+                                       break;
+                               }
+                               wantcalls =
+                                   ((curtime.tv_sec - starttime.tv_sec) * 1000
+                                   +(curtime.tv_usec-starttime.tv_usec) / 1000)
+                                   * Nprocs / avgmspc;
+                               pct = 1000 * (Curct.total - wantcalls) / ncalls;
+                               mspc = avgmspc + avgmspc * pct / 20;
+                               if (mspc <= 0) {
+                                       /*
+                                        * mspc must be positive or we will
+                                        * never advance time.
+                                        */
+                                       mspc = 10;
+                               }
+                       }
+               }
+       }
+
+       /*
+        * Store total time in last slot of counts array
+        */
+       Optime[NOPS].tv_sec = curtime.tv_sec - starttime.tv_sec;
+       Optime[NOPS].tv_usec = curtime.tv_usec - starttime.tv_usec;
+
+       /*
+        * write stats to log file (append mode)
+        */
+       if (write(Log, (char *)Optime, sizeof (Optime)) == -1) {
+               Saveerrno = errno;
+               (void) fprintf(stderr, "%s: can't write log ", Myname);
+               errno = Saveerrno;
+               perror("");
+               (void) kill(0, SIGINT);
+               exit(1);
+       }
+       (void) close(Log);
+
+       exit(0);
+}
+
+/*
+ * Initialize test directory
+ *
+ * If the directory already exists, check to see that all of the
+ * files exist and we can write them.  If directory doesn't exist
+ * create it and fill it using the LOOKUP and WRITE ops.
+ * Chdir to the directory.
+ */
+void
+init_testdir(int dirnum, char *parentdir)
+{
+       int i;
+       int fd;
+       char cmd[256];
+       struct stat statb;
+
+       (void) sprintf(Testdirname, "%s/testdir%d", parentdir, dirnum);
+       if (stat(Testdirname, &statb) == -1) {
+               if (mkdir(Testdirname, 0777) == -1) {
+                       Saveerrno = errno;
+                       (void) fprintf(stderr,
+                           "%s: can't create test directory ", Myname);
+                       errno = Saveerrno;
+                       perror(Testdirname);
+                       (void) kill(0, SIGINT);
+                       exit(1);
+               }
+               if (chdir(Testdirname) == -1) {
+                       Saveerrno = errno;
+                       (void) fprintf(stderr,
+                           "%s: can't cd to test directory ", Myname);
+                       errno = Saveerrno;
+                       perror(Testdirname);
+                       (void) kill(0, SIGINT);
+                       exit(1);
+               }
+
+               /*
+                * create some files with long names and average size
+                */
+               for (i = 0; i < NFILES; i++) {
+                       nextfile();
+                       (void) createfile();
+                       if (Openfd[Curnum] == 0 || writefile() == 0) {
+                               Saveerrno = errno;
+                               (void) fprintf(stderr,
+                                   "%s: can't create test file '%s'\n",
+                                   Myname, Curname);
+                               errno = Saveerrno;
+                               perror(Testdirname);
+                               (void) kill(0, SIGINT);
+                               exit(1);
+                       }
+               }
+       } else {
+               if (chdir(Testdirname) == -1) {
+                       Saveerrno = errno;
+                       (void) fprintf(stderr,
+                           "%s: can't cd to test directory ", Myname);
+                       errno = Saveerrno;
+                       perror(Testdirname);
+                       (void) kill(0, SIGINT);
+                       exit(1);
+               }
+
+               /*
+                * Verify that we can read and write the test dir
+                */
+               if (check_access(statb) == -1) {
+                       (void) fprintf(stderr,
+                           "%s: wrong permissions on test dir %s\n",
+                           Myname, Testdirname);
+                       (void) kill(0, SIGINT);
+                       exit(1);
+               }
+
+               /*
+                * Verify that we can read and write all the files
+                */
+               for (i = 0; i < NFILES; i++) {
+                       nextfile();
+                       if (stat(Curname, &statb) == -1 || statb.st_size == 0) {
+                               /*
+                                * File doesn't exist or is 0 size
+                                */
+                               (void) createfile();
+                               if (Openfd[Curnum] == 0 || writefile() == 0) {
+                                       (void) kill(0, SIGINT);
+                                       exit(1);
+                               }
+                       } else if (check_access(statb) == -1) {
+                               /*
+                                * should try to remove and recreate it
+                                */
+                               (void) fprintf(stderr,
+                                   "%s: wrong permissions on testfile %s\n",
+                                   Myname, Curname);
+                               (void) kill(0, SIGINT);
+                               exit(1);
+                       } else if (Openfd[Curnum] == 0) {
+                               (void) openfile();
+                               if (Openfd[Curnum] == 0) {
+                                       (void) kill(0, SIGINT);
+                                       exit(1);
+                               }
+                       }
+               }
+       }
+
+       /*
+        * Start with Rename1 and no Rename2 so the
+        * rename op can ping pong back and forth.
+        */
+       (void) unlink(Rename2);
+       if ((fd = open(Rename1, O_CREAT|O_TRUNC|O_RDWR, 0666)) == -1) {
+               Saveerrno = errno;
+               (void) fprintf(stderr, "%s: can't create rename file ", Myname);
+               errno = Saveerrno;
+               perror(Rename1);
+               (void) kill(0, SIGINT);
+               exit(1);
+       }
+
+       /*
+        * Remove and recreate the test sub-directories
+        * for mkdir symlink and hard link.
+        */
+       (void) sprintf(cmd, "rm -rf %s %s %s", DIRSTR, SYMSTR, LINSTR);
+       if (system(cmd) != 0) {
+               (void) fprintf(stderr, "%s: can't %s\n", Myname, cmd);
+               (void) kill(0, SIGINT);
+               exit(1);
+       }
+
+       if (mkdir(DIRSTR, 0777) == -1) {
+               (void) fprintf(stderr,
+                   "%s: can't create subdir %s\n", Myname, DIRSTR);
+               (void) kill(0, SIGINT);
+               exit(1);
+       }
+
+       if (mkdir(SYMSTR, 0777) == -1) {
+               (void) fprintf(stderr,
+                   "%s: can't create subdir %s\n", Myname, SYMSTR);
+               (void) kill(0, SIGINT);
+               exit(1);
+       }
+       op(SYMLINK);
+
+       if (mkdir(LINSTR, 0777) == -1) {
+               (void) fprintf(stderr, "%s: can't create subdir %s\n", Myname,
+                   LINSTR);
+               (void) kill(0, SIGINT);
+               exit(1);
+       }
+
+       (void) close(fd);
+}
+
+/*
+ * The routines below attempt to do over-the-wire operations.
+ * Each op tries to cause one or more of a particular
+ * NFS operation to go over the wire.  OPs return the number
+ * of OTW calls they think they have generated.
+ *
+ * An array of open file descriptors is kept for the files in each
+ * test directory. The open fd's are used to get access to the files
+ * without generating lookups. An fd value of 0 mean the corresponding
+ * file name is closed.  Ops that need a name use Curname.
+ */
+
+/*
+ * Call an op based on a random number and the current
+ * op calling weights. Op weights are derived from the
+ * mix percentage and the current NFS stats mix percentage.
+ */
+void
+do_op(int rpct)
+{
+       int opnum;
+       int weight;
+       int oppct;
+
+       if (Testop != -1) {
+               nextfile();
+               op(Testop);
+               return;
+       }
+       for (opnum = rpct % NOPS; rpct >= 0; opnum = (opnum + 1) % NOPS) {
+               if (Curct.total) {
+                       oppct = (Curct.calls[opnum] * 100) / Curct.total;
+               } else {
+                       oppct = 0;
+               }
+               /*
+                * Weight is mix percent - (how far off we are * fudge)
+                * fudge factor is required because some ops (read, write)
+                * generate many NFS calls for a single op call
+                */
+               weight = Mix[opnum] - ((oppct - Mix[opnum]) << 4);
+               if (weight <= 0) {
+                       continue;
+               }
+               rpct -= weight;
+               if (rpct < 0) {
+                       if (opnum == RMDIR && Dirnum == 0) {
+                               op(MKDIR);
+                       } else if (opnum != CREATE && opnum != LOOKUP &&
+                           opnum != REMOVE) {
+                               nextfile();
+                       }
+                       op(opnum);
+                       if (Openfd[Curnum] == 0) {
+                               op(CREATE);
+#ifdef XXX
+                               op(WRITE);
+#endif /* XXX */
+                       }
+                       return;
+               }
+       }
+}
+
+/*
+ * Call an op generator and keep track of its running time
+ */
+void
+op(int opnum)
+{
+       struct timeval start;
+       struct timeval stop;
+       int nops;
+
+       (void) gettimeofday(&start, (struct timezone *)NULL);
+       nops = (*Op_vect[opnum].funct)();
+       (void) gettimeofday(&stop, (struct timezone *)NULL);
+       stop.tv_sec -= start.tv_sec;
+       stop.tv_usec -= start.tv_usec;
+
+#ifdef SUNOS4
+       /*
+        * SunOS 4.0 does a lookup and a getattr on each open
+        * so we have to account for that in the getattr op
+        */
+       if (opnum == GETATTR && nops == 2) {
+               nops = 1;
+               stop.tv_sec /= 2;
+               stop.tv_usec /= 2;
+               Curct.total += Nprocs;
+               Curct.calls[LOOKUP] += Nprocs;
+               addtime(Optime[LOOKUP], stop);
+       }
+#endif
+
+       nops *= Nprocs;
+       Curct.total += nops;
+       Curct.calls[opnum] += nops;
+       addtime(Optime[opnum], stop);
+}
+
+/*
+ * Advance file number (Curnum) and name (Curname)
+ */
+void
+nextfile(void)
+{
+       static char *numpart = NULL;
+       int num;
+
+       Curnum = (Curnum + 1) % NFILES;
+       if (numpart == NULL) {
+               (void) sprintf(Curname, "%03dabcdefghijklmn", Curnum);
+               numpart = Curname;
+       } else {
+               num = Curnum;
+               numpart[0] = '0' + num / 100;
+               num %= 100;
+               numpart[1] = '0' + num / 10;
+               num %= 10;
+               numpart[2] = '0' + num;
+       }
+}
+
+int
+createfile(void)
+{
+       int ret;
+       int fd;
+
+       ret = 0;
+       fd = Openfd[Curnum];
+
+       if ((fd && close(fd) == -1) ||
+           (fd = open(Curname, O_CREAT|O_RDWR|O_TRUNC, 0666)) == -1) {
+               fd = 0;
+               ret = -1;
+               error("create");
+       }
+       Openfd[Curnum] = fd;
+       return (ret);
+}
+
+int
+openfile(void)
+{
+       int ret;
+       int fd;
+
+       ret = 0;
+       fd = Openfd[Curnum];
+       if (fd == 0 && (fd = open(Curname, O_RDWR, 0666)) == -1) {
+               fd = 0;
+               ret = -1;
+               error("open");
+       }
+       Openfd[Curnum] = fd;
+       return (ret);
+}
+
+int
+writefile(void)
+{
+       int fd;
+       int wrote;
+       int bufs;
+       int size;
+       int randnum;
+       char buf[BUFSIZE];
+
+       fd = Openfd[Curnum];
+
+       if (lseek(fd, 0L, 0) == (off_t) -1) {
+               error("write: lseek");
+               return (-1);
+       }
+
+       randnum = (int) random();
+       bufs = randnum % 100;   /* using this for distribution desired */
+       /*
+        * Attempt to create a distribution of file sizes
+        * to reflect reality.  Most files are small,
+        * but there are a few files that are very large.
+        *
+        * The sprite paper (USENIX 198?) claims :
+        *      50% of all files are < 2.5K
+        *      80% of all file accesses are to files < 10K
+        *      40% of all file I/O is to files > 25K
+        *
+        * static examination of the files in our file system
+        * seems to support the claim that 50% of all files are
+        * smaller than 2.5K
+        */
+       if (bufs < 50)  {
+               bufs = (randnum % 3) + 1;
+               size = 1024;
+       } else if (bufs < 97) {
+               bufs = (randnum % 6) + 1;
+               size = BUFSIZE;
+       } else {
+               bufs = MAXFILESIZE;
+               size = BUFSIZE;
+       }
+
+       for (wrote = 0; wrote < bufs; wrote++) {
+               if (write(fd, buf, size) == -1) {
+                       error("write");
+                       break;
+               }
+       }
+
+       return (wrote);
+}
+
+int
+op_null(void)
+{
+
+       return (1);
+}
+
+
+/*
+ * Generate a getattr call by fstat'ing the current file
+ * or by closing and re-opening it. This helps to keep the
+ * attribute cache cold.
+ */
+int
+op_getattr(void)
+{
+       struct stat statb;
+
+       if ((random() % 2) == 0) {
+               (void) close(Openfd[Curnum]);
+               Openfd[Curnum] = 0;
+               if (openfile() == -1) {
+                       return (0);
+               }
+               return (2);
+       }
+       if (fstat(Openfd[Curnum], &statb) == -1) {
+               error("getattr");
+       }
+       return (1);
+}
+
+
+int op_setattr(void)
+{
+
+       if (fchmod(Openfd[Curnum], 0666) == -1) {
+               error("setattr");
+       }
+       return (1);
+}
+
+
+int op_root(void)
+{
+
+       error("root");
+       return (0);
+}
+
+
+/*
+ * Generate a lookup by stat'ing the current name.
+ */
+int op_lookup(void)
+{
+       struct stat statb;
+
+       if (stat(Curname, &statb) == -1) {
+               error("lookup");
+       }
+       return (1);
+}
+
+
+int op_read(void)
+{
+       int got;
+       int bufs;
+       int fd;
+       char buf[BUFSIZE];
+
+       bufs = 0;
+       fd = Openfd[Curnum];
+
+       if (lseek(fd, 0L, 0) == (off_t) -1) {
+               error("read: lseek");
+               return (0);
+       }
+
+       while ((got = read(fd, buf, sizeof (buf))) > 0) {
+               bufs++;
+       }
+
+       if (got == -1) {
+               error("read");
+       } else {
+               bufs++;         /* did one extra read to find EOF */
+       }
+       return (bufs);
+}
+
+
+int op_wrcache(void)
+{
+       error("wrcache");
+       return 0;
+}
+
+
+int op_write(void)
+{
+       int bufs;
+
+       bufs = writefile();
+       if (bufs == 0) {
+               return (0);
+       }
+       (void) fsync(Openfd[Curnum]);
+
+       return (bufs + 2);
+}
+
+
+int op_create(void)
+{
+
+       if (createfile() == -1) {
+               return (0);
+       }
+       return (1);
+}
+
+
+int op_remove(void)
+{
+       int fd;
+       int got;
+
+       if (Linknum > 0) {
+               got = unlink(Linkname);
+               Linknum--;
+               (void) sprintf(Linkname, Otherspec, LINSTR, Linknum);
+       } else if (Symnum > 1) {
+               got = unlink(Symname);
+               Symnum--;
+               (void) sprintf(Symname, Otherspec, SYMSTR, Symnum);
+       } else {
+               fd = Openfd[Curnum];
+
+               if (fd && (close(fd) == -1)) {
+                       error("remove: close");
+               }
+               Openfd[Curnum] = 0;
+               got = unlink(Curname);
+       }
+       if (got == -1) {
+               error("remove");
+       }
+       return (1);
+}
+
+
+int toggle = 0;
+
+int op_rename(void)
+{
+       int got;
+
+       if (toggle++ & 01) {
+               got = rename(Rename2, Rename1);
+       } else {
+               got = rename(Rename1, Rename2);
+       }
+       if (got == -1) {
+               error("rename");
+       }
+       return (1);
+}
+
+
+int op_link(void)
+{
+
+       Linknum++;
+       (void) sprintf(Linkname, Otherspec, LINSTR, Linknum);
+       if (link(Curname, Linkname) == -1) {
+               error("link");
+       }
+       return (1);
+}
+
+
+int op_readlink(void)
+{
+       char    buf[MAXPATHLEN];
+
+       if (Symnum == 0) {
+               error("readlink");
+               return (0);
+       }
+       if (readlink(Symname, buf, sizeof (buf)) == -1) {
+               error("readlink");
+       }
+       return (1);
+}
+
+
+int op_symlink(void)
+{
+
+       Symnum++;
+       (void) sprintf(Symname, Otherspec, SYMSTR, Symnum);
+       if (symlink(Symlinkpath, Symname) == -1) {
+               error("symlink");
+       }
+       return (1);
+}
+
+
+int op_mkdir(void)
+{
+
+       Dirnum++;
+       (void) sprintf(Dirname, Otherspec, DIRSTR, Dirnum);
+       if (mkdir(Dirname, 0777) == -1) {
+               error("mkdir");
+       }
+       return (1);
+}
+
+
+int op_rmdir(void)
+{
+
+       if (Dirnum == 0) {
+               error("rmdir");
+               return (0);
+       }
+       if (rmdir(Dirname) == -1) {
+               error("rmdir");
+       }
+       Dirnum--;
+       (void) sprintf(Dirname, Otherspec, DIRSTR, Dirnum);
+       return (1);
+}
+
+
+int op_readdir(void)
+{
+
+       rewinddir(Testdir);
+       while (readdir(Testdir) != (struct dirent *)NULL)
+               /* nothing */;
+       return (1);
+}
+
+
+int op_fsstat(void)
+{
+       struct statfs statfsb;
+
+       if (statfs(".", &statfsb) == -1) {
+               error("statfs");
+       }
+       return (1);
+}
+
+
+/*
+ * Utility routines
+ */
+
+/*
+ * Read counter arrays out of log file and accumulate them in "Optime"
+ */
+void
+collect_counters(void)
+{
+       int i;
+       int j;
+
+       (void) lseek(Log, 0L, 0);
+
+       for (i = 0; i < Nprocs; i++) {
+               struct timeval buf[NOPS+1];
+
+               if (read(Log, (char *)buf, sizeof (buf)) == -1) {
+                       Saveerrno = errno;
+                       (void) fprintf(stderr, "%s: can't read log ", Myname);
+                       errno = Saveerrno;
+                       perror("");
+                       (void) kill(0, SIGINT);
+                       exit(1);
+               }
+
+               for (j = 0; j < NOPS+1; j++) {
+                       addtime(Optime[j], buf[j]);
+               }
+       }
+}
+
+/*
+ * Check consistance of results
+ */
+int
+check_counters(void)
+{
+       int i;
+       int mixdiff;
+       int got;
+       int want;
+
+       mixdiff = 0;
+       for (i = 0; i < NOPS; i++) {
+               got = Curct.calls[i] * 10000 / Curct.total;
+               want = Mix[i] * 100;
+               if (got > want) {
+                       mixdiff += got - want;
+               } else {
+                       mixdiff += want - got;
+               }
+       }
+       if (mixdiff > 1000) {
+               (void) fprintf(stdout,
+                   "%s: INVALID RUN, mix generated is off by %d.%02d%%\n",
+                   Myname, mixdiff / 100, mixdiff % 100);
+               return (-1);
+       }
+       return (0);
+}
+
+/*
+ * Print results
+ */
+void
+print(void)
+{
+       int totalmsec;
+       int runtime;
+       int msec;
+       int i;
+
+       totalmsec = 0;
+       for (i = 0; i < NOPS; i++) {
+               totalmsec += Optime[i].tv_sec * 1000;
+               totalmsec += Optime[i].tv_usec / 1000;
+       }
+
+       if (Verbose) {
+          const char *format = sizeof (Optime[0].tv_sec) == sizeof (long)
+            ? "%-10s%3d%%    %2d.%02d%%   %6d   %4ld.%02ld    %4d.%02d    %2d.%02d%%\n"
+            : "%-10s%3d%%    %2d.%02d%%   %6d   %4d.%02d    %4d.%02d    %2d.%02d%%\n";
+               (void) fprintf(stdout,
+"op        want       got    calls      secs  msec/call    time %%\n");
+               for (i = 0; i < NOPS; i++) {
+                       msec = Optime[i].tv_sec * 1000
+                           + Optime[i].tv_usec / 1000;
+                       (void) fprintf(stdout, format,
+                           Opnames[i], Mix[i],
+                           Curct.calls[i] * 100 / Curct.total,
+                           (Curct.calls[i] * 100 % Curct.total)
+                               * 100 / Curct.total,
+                           Curct.calls[i],
+                           Optime[i].tv_sec, Optime[i].tv_usec / 10000,
+                           Curct.calls[i]
+                               ? msec / Curct.calls[i]
+                               : 0,
+                           Curct.calls[i]
+                               ? (msec % Curct.calls[i]) * 100 / Curct.calls[i]
+                               : 0,
+                           msec * 100 / totalmsec,
+                           (msec * 100 % totalmsec) * 100 / totalmsec);
+               }
+       }
+
+       runtime = Optime[NOPS].tv_sec / Nprocs;
+       (void) fprintf(stdout,
+           "%d sec %d calls %d.%02d calls/sec %d.%02d msec/call\n",
+           runtime, Curct.total,
+           Curct.total / runtime,
+           ((Curct.total % runtime) * 100) / runtime,
+           totalmsec / Curct.total,
+           ((totalmsec % Curct.total) * 100) / Curct.total);
+}
+
+/*
+ * Use select to sleep for some number of milliseconds
+ * granularity is 20 msec
+ */
+void
+msec_sleep(int msecs)
+{
+       struct timeval sleeptime;
+
+       if (msecs < 20) {
+               return;
+       }
+       sleeptime.tv_sec = msecs / 1000;
+       sleeptime.tv_usec = (msecs % 1000) * 1000;
+
+       if (select(0, (fd_set *)0, (fd_set *)0, (fd_set *)0, &sleeptime) == -1){
+               Saveerrno = errno;
+               (void) fprintf(stderr, "%s: select failed ", Myname);
+               errno = Saveerrno;
+               perror("");
+               (void) kill(0, SIGINT);
+               exit(1);
+       }
+}
+
+/*
+ * Open the synchronization file with append mode
+ */
+void
+init_logfile(void)
+{
+
+       (void) sprintf(Logname, "/tmp/nhfsstone%d", getpid());
+       if ((Log = open(Logname, O_RDWR|O_CREAT|O_TRUNC|O_APPEND, 0666)) == -1){
+               Saveerrno = errno;
+               (void) fprintf(stderr,
+                   "%s: can't open log file %s ", Myname, Logname);
+               errno = Saveerrno;
+               perror("");
+               exit(1);
+       }
+}
+
+/*
+ * Zero counters
+ */
+void
+init_counters(void)
+{
+       int i;
+
+       Curct.total = 0;
+       for (i = 0; i < NOPS; i++) {
+               Curct.calls[i] = 0;
+               Optime[i].tv_sec = 0;
+               Optime[i].tv_usec = 0;
+       }
+       Optime[NOPS].tv_sec = 0;
+       Optime[NOPS].tv_usec = 0;
+}
+
+/*
+ * Set cur = cur - start
+ */
+void
+get_delta(struct count *start, struct count *cur)
+{
+       int i;
+
+       get_opct(cur);
+       cur->total -= start->total;
+       for (i = 0; i < NOPS; i++) {
+               cur->calls[i] -= start->calls[i];
+       }
+}
+
+/*
+ * Read kernel stats
+ */
+void
+get_opct(struct count *count)
+{
+       static FILE     *fp = NULL;
+       char            buffer[256];
+       int i;
+
+       if (fp == NULL && !(fp = fopen("/proc/net/rpc/nfs", "r"))) {
+               perror("/proc/net/rpc/nfs");
+               (void) kill(0, SIGINT);
+               exit(1);
+       } else {
+               fflush(fp);
+               rewind(fp);
+       }
+
+       while (fgets(buffer, sizeof(buffer), fp) != NULL) {
+               char    *sp, *line = buffer;
+
+               if ((sp = strchr(line, '\n')) != NULL)
+                       *sp = '\0';
+               if (!(sp = strtok(line, " \t")) || strcmp(line, "proc2"))
+                       continue;
+               if (!(sp = strtok(NULL, " \t")))
+                       goto bummer;
+               count->total = 0;
+               for (i = 0; i < 18; i++) {
+                       if (!(sp = strtok(NULL, " \t")))
+                               goto bummer;
+                       /* printf("call %d -> %s\n", i, sp); */
+                       count->calls[i] = atoi(sp);
+                       count->total += count->calls[i];
+               }
+               /* printf("total calls %d\n", count->total); */
+               break;
+       }
+
+       return;
+
+bummer:
+       fprintf(stderr, "parse error in /proc/net/rpc/nfs!\n");
+       kill(0, SIGINT);
+       exit(1);
+}
+
+#define        LINELEN         128             /* max bytes/line in mix file */
+#define        MIX_START       0
+#define        MIX_DATALINE    1
+#define        MIX_DONE        2
+#define        MIX_FIRSTLINE   3
+
+/*
+ * Mix file parser.
+ * Assumes that the input file is in the same format as
+ * the output of the nfsstat(8) command.
+ *
+ * Uses a simple state transition to keep track of what to expect.
+ * Parsing is done a line at a time.
+ *
+ * State          Input                action          New state
+ * MIX_START      ".*nfs:.*"           skip one line   MIX_FIRSTLINE
+ * MIX_FIRSTLINE   ".*[0-9]*.*"                get ncalls      MIX_DATALINE
+ * MIX_DATALINE    "[0-9]* [0-9]*%"X6  get op counts   MIX_DATALINE
+ * MIX_DATALINE    "[0-9]* [0-9]*%"X4  get op counts   MIX_DONE
+ * MIX_DONE       EOF                  return
+ */
+int
+setmix(FILE *fp)
+{
+       int state;
+       int got;
+       int opnum;
+       int calls;
+       int len;
+       char line[LINELEN];
+
+       state = MIX_START;
+       opnum = 0;
+
+       while (state != MIX_DONE && fgets(line, LINELEN, fp)) {
+
+               switch (state) {
+
+               case MIX_START:
+                       len = strlen(line);
+                       if (len >= 4 && substr(line, "nfs:")) {
+                               if (fgets(line, LINELEN, fp) == NULL) {
+                                       (void) fprintf(stderr,
+"%s: bad mix format: unexpected EOF after 'nfs:'\n", Myname);
+                                       return (-1);
+                               }
+                               state = MIX_FIRSTLINE;
+                       }
+                       break;
+
+               case MIX_FIRSTLINE:
+                       got = sscanf(line, "%d", &calls);
+                       if (got != 1) {
+                               (void) fprintf(stderr,
+"%s: bad mix format: can't find 'calls' value %d\n", Myname, got);
+                               return (-1);
+                       }
+                       if (fgets(line, LINELEN, fp) == NULL) {
+                               (void) fprintf(stderr,
+"%s: bad mix format: unexpected EOF after 'calls'\n", Myname);
+                               return (-1);
+                       }
+                       state = MIX_DATALINE;
+                       break;
+
+               case MIX_DATALINE:
+                       got = sscanf(line,
+       "%d %*d%% %d %*d%% %d %*d%% %d %*d%% %d %*d%% %d %*d%% %d %*d%%",
+       &Mix[opnum], &Mix[opnum+1], &Mix[opnum+2], &Mix[opnum+3],
+       &Mix[opnum+4], &Mix[opnum+5], &Mix[opnum+6]);
+                       if (got == 4 && opnum == 14) {
+                               /*
+                                * looks like the last line
+                                */
+                               state = MIX_DONE;
+                       } else if (got == 7) {
+                               opnum += 7;
+                               if (fgets(line, LINELEN, fp) == NULL) {
+                                       (void) fprintf(stderr,
+"%s: bad mix format: unexpected EOF after 'calls'\n", Myname);
+                                       return (-1);
+                               }
+                       } else {
+                               (void) fprintf(stderr,
+"%s: bad mix format: can't find %d op values\n", Myname, got);
+                               return (-1);
+                       }
+                       break;
+               default:
+                       (void) fprintf(stderr,
+                           "%s: unknown state %d\n", Myname, state);
+                       return (-1);
+               }
+       }
+       if (state != MIX_DONE) {
+               (void) fprintf(stderr,
+                   "%s: bad mix format: unexpected EOF\n", Myname);
+               return (-1);
+       }
+       for (opnum = 0; opnum < NOPS; opnum++) {
+               Mix[opnum] = Mix[opnum] * 100 / calls
+                   + ((Mix[opnum] * 1000 / calls % 10) >= 5);
+       }
+       return (0);
+}
+
+/*
+ * return true if sp contains the substring subsp, false otherwise
+ */
+int
+substr(char *sp, char *subsp)
+{
+       int found;
+       int want;
+       char *s2;
+
+       if (sp == NULL || subsp == NULL) {
+               return (0);
+       }
+
+       want = strlen(subsp);
+
+       while (*sp != '\0') {
+               while (*sp != *subsp && *sp != '\0') {
+                       sp++;
+               }
+               found = 0;
+               s2 = subsp;
+               while (*sp == *s2) {
+                       sp++;
+                       s2++;
+                       found++;
+               }
+               if (found == want) {
+                       return (1);
+               }
+       }
+       return (0);
+}
+
+/*
+ * check to make sure that we have
+ * both read and write permissions
+ * for this file or directory.
+ */
+int
+check_access(struct stat statb)
+{
+       int gidsetlen;
+       gid_t gidset[NGROUPS];
+       int i;
+
+       if (statb.st_uid == getuid()) {
+               if ((statb.st_mode & 0200) && (statb.st_mode & 0400)) {
+                       return 1;
+               } else {
+                       return -1;
+               }
+       }
+
+       gidsetlen = NGROUPS;
+
+       if (getgroups(gidsetlen, gidset) == -1) {
+               perror("getgroups");
+               return -1;
+       }
+
+       for (i = 0; i < NGROUPS; i++) {
+               if (statb.st_gid == gidset[i]) {
+                       if ((statb.st_mode & 020) && (statb.st_mode & 040)) {
+                               return 1;
+                       } else {
+                               return -1;
+                       }
+               }
+       }
+
+       if ((statb.st_mode & 02) && (statb.st_mode & 04)) {
+               return 1;
+       } else {
+               return -1;
+       }
+}
+
+void
+usage(void)
+{
+
+       (void) fprintf(stderr, "usage: %s [-v] [[-t secs] | [-c calls]] [-l load] [-p nprocs] [-m mixfile] [dir]...\n", Myname);
+}
+
+void
+error(char *str)
+{
+
+       Saveerrno = errno;
+       (void) fprintf(stderr, "%s: op failed: %s ", Myname, str);
+       errno = Saveerrno;
+       perror("");
+}
diff --git a/utils/rquotad/Makefile b/utils/rquotad/Makefile
new file mode 100644 (file)
index 0000000..1572655
--- /dev/null
@@ -0,0 +1,13 @@
+#
+# Makefile for rpc.mountd
+#
+
+PROGRAM        = rquotad
+PREFIX = rpc.
+OBJS   = rquota_server.o rquota_svc.o rquota_xdr.o quotactl.o hasquota.o
+DEPLIBS        = 
+MAN8   = rquotad
+
+LIBS   += -lnfs $(LIBBSD)
+
+include $(TOP)rules.mk
diff --git a/utils/rquotad/NEW b/utils/rquotad/NEW
new file mode 100644 (file)
index 0000000..40c6fd2
--- /dev/null
@@ -0,0 +1,3 @@
+This is Marco van Wieringen's rpc.rquotad in quotas-1.70 from
+
+ftp://ftp.cistron.nl/pub/people/mvw/quota
diff --git a/utils/rquotad/README.okir b/utils/rquotad/README.okir
new file mode 100644 (file)
index 0000000..08938b9
--- /dev/null
@@ -0,0 +1,3 @@
+
+This is Marco van Wieringen's rpc.rquotad from quotas-1.55.
+
diff --git a/utils/rquotad/hasquota.c b/utils/rquotad/hasquota.c
new file mode 100644 (file)
index 0000000..008a93f
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * QUOTA    An implementation of the diskquota system for the LINUX
+ *          operating system. QUOTA is implemented using the BSD systemcall
+ *          interface as the means of communication with the user level.
+ *          Should work for all filesystems because of integration into the
+ *          VFS layer of the operating system.
+ *          This is based on the Melbourne quota system wich uses both user and
+ *          group quota files.
+ *
+ *          Determines if a filesystem has quota enabled and how the quotafile
+ *          is named.
+ *
+ * Version: $Id: hasquota.c,v 2.6 1996/11/17 16:59:46 mvw Exp mvw $
+ *
+ * Author:  Marco van Wieringen <mvw@planets.elm.net>
+ *
+ *          This program 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 of the License, or (at your option) any later version.
+ */
+#include "config.h"
+
+#include <sys/types.h>
+#include <sys/quota.h>
+#include <limits.h>
+#include <string.h>
+#include "mntent.h"
+#include "xmalloc.h"
+
+#undef min
+#define min(x,y) ((x) < (y)) ? (x) : (y)
+
+#define CORRECT_FSTYPE(type) \
+(!strcmp(type,MNTTYPE_EXT2))
+
+char *qfextension[] = INITQFNAMES;
+
+/*
+ * Check to see if a particular quota is to be enabled.
+ */
+int
+hasquota(struct mntent *mnt, int type, char **qfnamep)
+{
+   char *qfname = QUOTAFILENAME;
+   char *option, *pathname;
+
+   if (!CORRECT_FSTYPE(mnt->mnt_type))
+      return (0);
+
+   if (((type == USRQUOTA) && (option = hasmntopt(mnt, MNTOPT_USRQUOTA)) != (char *)0) ||
+       ((type == GRPQUOTA) && (option = hasmntopt(mnt, MNTOPT_GRPQUOTA)) != (char *)0)) {
+      if ((pathname = strchr(option, '=')) == (char *)0) {
+         *qfnamep=xmalloc(strlen(mnt->mnt_dir)+strlen(qfname)+strlen(qfextension[type])+2);
+         (void) sprintf(*qfnamep, "%s%s%s.%s", mnt->mnt_dir,
+                       (mnt->mnt_dir[strlen(mnt->mnt_dir) - 1] == '/') ? "" : "/",
+                       qfname, qfextension[type]);
+      } else {
+         if ((option = strchr(++pathname, ',')) != (char *)NULL) {
+           int len=option-pathname;
+           *qfnamep=xmalloc(len);
+            memcpy(*qfnamep, pathname, len-1);
+            (*qfnamep) [len-1] = '\0';
+        }
+         else {
+           *qfnamep=xstrdup(pathname);
+        }
+      }
+      return (1);
+   } else
+      return (0);
+}
diff --git a/utils/rquotad/mntent.h b/utils/rquotad/mntent.h
new file mode 100644 (file)
index 0000000..6c58451
--- /dev/null
@@ -0,0 +1,112 @@
+#ifndef _MNTENT_H
+#define _MNTENT_H
+
+#include <features.h>
+
+#define MNTTAB "/etc/fstab"
+#define MOUNTED "/etc/mtab"
+
+#define MNTMAXSTR      512
+
+#define MNTTYPE_COHERENT       "coherent"      /* Coherent file system */
+#define MNTTYPE_EXT            "ext"           /* Extended file system */
+#define MNTTYPE_EXT2           "ext2"          /* Second Extended file system */
+#define MNTTYPE_HPFS           "hpfs"          /* OS/2's high performance file system */
+#define MNTTYPE_ISO9660                "iso9660"       /* ISO CDROM file system */
+#define MNTTYPE_MINIX          "minix"         /* MINIX file system */
+#define MNTTYPE_MSDOS          "msdos"         /* MS-DOS file system */
+#define MNTTYPE_SYSV           "sysv"          /* System V file system */
+#define MNTTYPE_UMSDOS         "umsdos"        /* U MS-DOS file system */
+#define MNTTYPE_XENIX          "xenix"         /* Xenix file system */
+#define MNTTYPE_XIAFS          "xiafs"         /* Frank Xia's file system */
+#define MNTTYPE_NFS            "nfs"           /* Network file system */
+#define MNTTYPE_PROC           "proc"          /* Linux process file system */
+#define MNTTYPE_IGNORE         "ignore"        /* Ignore this entry */
+#define MNTTYPE_SWAP           "swap"          /* Swap device */
+
+/* generic mount options */
+#define MNTOPT_DEFAULTS                "defaults"      /* use all default opts */
+#define MNTOPT_RO              "ro"            /* read only */
+#define MNTOPT_RW              "rw"            /* read/write */
+#define MNTOPT_SUID            "suid"          /* set uid allowed */
+#define MNTOPT_NOSUID          "nosuid"        /* no set uid allowed */
+#define MNTOPT_NOAUTO          "noauto"        /* don't auto mount */
+
+/* ext2 and msdos options */
+#define        MNTOPT_CHECK            "check"         /* filesystem check level */
+
+/* ext2 specific options */
+#define        MNTOPT_BSDDF            "bsddf"         /* disable MINIX compatibility disk free counting */
+#define        MNTOPT_BSDGROUPS        "bsdgroups"     /* set BSD group usage */
+#define        MNTOPT_ERRORS           "errors"        /* set behaviour on error */
+#define        MNTOPT_GRPID            "grpid"         /* set BSD group usage */
+#define        MNTOPT_MINIXDF          "minixdf"       /* enable MINIX compatibility disk free counting */
+#define        MNTOPT_NOCHECK          "nocheck"       /* reset filesystem checks */
+#define        MNTOPT_NOGRPID          "nogrpid"       /* set SYSV group usage */
+#define        MNTOPT_RESGID           "resgid"        /* group to consider like root for reserved blocks */
+#define        MNTOPT_RESUID           "resuid"        /* user to consider like root for reserved blocks */
+#define        MNTOPT_SB               "sb"            /* set used super block */
+#define        MNTOPT_SYSVGROUPS       "sysvgroups"    /* set SYSV group usage */
+
+/* options common to hpfs, isofs, and msdos */
+#define        MNTOPT_CONV             "conv"          /* convert specified types of data */
+#define        MNTOPT_GID              "gid"           /* use given gid */
+#define        MNTOPT_UID              "uid"           /* use given uid */
+#define        MNTOPT_UMASK            "umask"         /* use given umask, not isofs */
+
+/* hpfs specific options */
+#define        MNTOPT_CASE             "case"          /* case conversation */
+
+/* isofs specific options */
+#define        MNTOPT_BLOCK            "block"         /* use given block size */
+#define        MNTOPT_CRUFT            "cruft"         /* ??? */
+#define        MNTOPT_MAP              "map"           /* ??? */
+#define        MNTOPT_NOROCK           "norock"        /* not rockwell format ??? */
+
+/* msdos specific options */
+#define        MNTOPT_FAT              "fat"           /* set FAT size */
+#define        MNTOPT_QUIET            "quiet"         /* ??? */
+
+/* swap specific options */
+
+/* options common to ext, ext2, minix, xiafs, sysv, xenix, coherent */
+#define MNTOPT_NOQUOTA         "noquota"       /* don't use any quota on this partition */
+#define MNTOPT_USRQUOTA                "usrquota"      /* use userquota on this partition */
+#define MNTOPT_GRPQUOTA                "grpquota"      /* use groupquota on this partition */
+#define MNTOPT_RSQUASH         "rsquash"       /* threat root as an ordinary user */
+
+/* none defined yet */
+
+__BEGIN_DECLS
+
+struct mntent{
+       char *mnt_fsname;
+       char *mnt_dir;
+       char *mnt_type;
+       char *mnt_opts;
+       int  mnt_freq;
+       int  mnt_passno;
+};
+
+__END_DECLS
+
+#define __need_file
+#include <stdio.h>
+
+__BEGIN_DECLS
+
+extern FILE    *setmntent __P ((__const char *__filep,
+                       __const char *__type));
+extern struct mntent
+               *getmntent __P ((FILE *__filep));
+extern int     addmntent __P ((FILE *__filep,
+                       __const struct mntent *__mnt));
+extern char    *hasmntopt __P ((__const struct mntent *__mnt,
+                       __const char *__opt));
+extern int     endmntent __P ((FILE *__filep));
+
+extern int     hasquota __P ((struct mntent *, int, char **));
+
+__END_DECLS
+
+#endif /* _MNTENT_H */
diff --git a/utils/rquotad/pathnames.h b/utils/rquotad/pathnames.h
new file mode 100644 (file)
index 0000000..6604a18
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ *
+ *   @@(#)pathnames.h   5.3 (Berkeley) 6/1/90
+ */
+
+#include <paths.h>
+
+#undef _PATH_TMP
+#define   _PATH_TMP   "/tmp/EdP.aXXXXXX"
diff --git a/utils/rquotad/quotactl.c b/utils/rquotad/quotactl.c
new file mode 100644 (file)
index 0000000..30e68a4
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * QUOTA    An implementation of the diskquota system for the LINUX
+ *          operating system. QUOTA is implemented using the BSD systemcall
+ *          interface as the means of communication with the user level.
+ *          Should work for all filesystems because of integration into the
+ *          VFS layer of the operating system.
+ *          This is based on the Melbourne quota system wich uses both user and
+ *          group quota files.
+ *
+ *          System call interface.
+ *
+ * Version: $Id: quotactl.c,v 2.3 1995/07/23 09:58:06 mvw Exp mvw $
+ *
+ * Author:  Marco van Wieringen <mvw@planets.ow.nl> <mvw@tnix.net>
+ *
+ *          This program 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 of the License, or (at your option) any later version.
+ */
+#include "config.h"
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/syscall.h>
+
+int quotactl(int cmd, const char * special, int id, caddr_t addr)
+{
+       return syscall(SYS_quotactl, cmd, special, id, addr);
+}
diff --git a/utils/rquotad/rquota.h b/utils/rquotad/rquota.h
new file mode 100644 (file)
index 0000000..f81e732
--- /dev/null
@@ -0,0 +1,64 @@
+#define RQ_PATHLEN 1024
+
+struct getquota_args {
+       char *gqa_pathp;
+       int gqa_uid;
+};
+typedef struct getquota_args getquota_args;
+bool_t xdr_getquota_args();
+
+
+struct ext_getquota_args {
+       char *gqa_pathp;
+       int gqa_type;
+       int gqa_id;
+};
+typedef struct ext_getquota_args ext_getquota_args;
+bool_t xdr_ext_getquota_args();
+
+
+struct rquota {
+       int rq_bsize;
+       bool_t rq_active;
+       u_int rq_bhardlimit;
+       u_int rq_bsoftlimit;
+       u_int rq_curblocks;
+       u_int rq_fhardlimit;
+       u_int rq_fsoftlimit;
+       u_int rq_curfiles;
+       u_int rq_btimeleft;
+       u_int rq_ftimeleft;
+};
+typedef struct rquota rquota;
+bool_t xdr_rquota();
+
+
+enum gqr_status {
+       Q_OK = 1,
+       Q_NOQUOTA = 2,
+       Q_EPERM = 3,
+};
+typedef enum gqr_status gqr_status;
+bool_t xdr_gqr_status();
+
+
+struct getquota_rslt {
+       gqr_status status;
+       union {
+               rquota gqr_rquota;
+       } getquota_rslt_u;
+};
+typedef struct getquota_rslt getquota_rslt;
+bool_t xdr_getquota_rslt();
+
+
+#define RQUOTAPROG ((u_long)100011)
+#define RQUOTAVERS ((u_long)1)
+#define RQUOTAPROC_GETQUOTA ((u_long)1)
+extern getquota_rslt *rquotaproc_getquota_1();
+#define RQUOTAPROC_GETACTIVEQUOTA ((u_long)2)
+extern getquota_rslt *rquotaproc_getactivequota_1();
+#define EXT_RQUOTAVERS ((u_long)2)
+extern getquota_rslt *rquotaproc_getquota_2();
+extern getquota_rslt *rquotaproc_getactivequota_2();
+
diff --git a/utils/rquotad/rquota.x b/utils/rquotad/rquota.x
new file mode 100644 (file)
index 0000000..120abe5
--- /dev/null
@@ -0,0 +1,84 @@
+/* @(#)rquota.x        2.1 88/08/01 4.0 RPCSRC */
+/* @(#)rquota.x 1.2 87/09/20 Copyr 1987 Sun Micro */
+
+/*
+ * Remote quota protocol
+ * Requires unix authentication
+ */
+
+#ifdef RPC_CLNT
+%#include <string.h>
+#endif
+
+const RQ_PATHLEN = 1024;
+
+struct getquota_args {
+       string gqa_pathp<RQ_PATHLEN>;   /* path to filesystem of interest */
+       int gqa_uid;                    /* Inquire about quota for uid */
+};
+
+struct ext_getquota_args {
+       string gqa_pathp<RQ_PATHLEN>;   /* path to filesystem of interest */
+       int gqa_type;                   /* Type of quota info is needed about */
+       int gqa_id;                     /* Inquire about quota for id */
+};
+
+/*
+ * remote quota structure
+ */
+struct rquota {
+       int rq_bsize;                   /* block size for block counts */
+       bool rq_active;                 /* indicates whether quota is active */
+       unsigned int rq_bhardlimit;     /* absolute limit on disk blks alloc */
+       unsigned int rq_bsoftlimit;     /* preferred limit on disk blks */
+       unsigned int rq_curblocks;      /* current block count */
+       unsigned int rq_fhardlimit;     /* absolute limit on allocated files */
+       unsigned int rq_fsoftlimit;     /* preferred file limit */
+       unsigned int rq_curfiles;       /* current # allocated files */
+       unsigned int rq_btimeleft;      /* time left for excessive disk use */
+       unsigned int rq_ftimeleft;      /* time left for excessive files */
+};     
+
+enum gqr_status {
+       Q_OK = 1,       /* quota returned */
+       Q_NOQUOTA = 2,  /* noquota for uid */
+       Q_EPERM = 3     /* no permission to access quota */
+};
+
+union getquota_rslt switch (gqr_status status) {
+case Q_OK:
+       rquota gqr_rquota;      /* valid if status == Q_OK */
+case Q_NOQUOTA:
+       void;
+case Q_EPERM:
+       void;
+};
+
+program RQUOTAPROG {
+       version RQUOTAVERS {
+               /*
+                * Get all quotas
+                */
+               getquota_rslt
+               RQUOTAPROC_GETQUOTA(getquota_args) = 1;
+
+               /*
+                * Get active quotas only
+                */
+               getquota_rslt
+               RQUOTAPROC_GETACTIVEQUOTA(getquota_args) = 2;
+       } = 1;
+       version EXT_RQUOTAVERS {
+               /*
+                * Get all quotas
+                */
+               getquota_rslt
+               RQUOTAPROC_GETQUOTA(ext_getquota_args) = 1;
+
+               /*
+                * Get active quotas only
+                */
+               getquota_rslt
+               RQUOTAPROC_GETACTIVEQUOTA(ext_getquota_args) = 2;
+       } = 2;
+} = 100011;
diff --git a/utils/rquotad/rquota_server.c b/utils/rquotad/rquota_server.c
new file mode 100644 (file)
index 0000000..08c4f8c
--- /dev/null
@@ -0,0 +1,246 @@
+/*
+ * QUOTA    An implementation of the diskquota system for the LINUX
+ *          operating system. QUOTA is implemented using the BSD systemcall
+ *          interface as the means of communication with the user level.
+ *          Should work for all filesystems because of integration into the
+ *          VFS layer of the operating system.
+ *          This is based on the Melbourne quota system wich uses both user and
+ *          group quota files.
+ *
+ *          This part does the lookup of the info.
+ *
+ * Version: $Id: rquota_server.c,v 2.9 1996/11/17 16:59:46 mvw Exp mvw $
+ *
+ * Author:  Marco van Wieringen <mvw@planets.elm.net>
+ *
+ *          This program 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 of the License, or (at your option) any later version.
+ */
+#include "config.h"
+
+#include <rpc/rpc.h>
+#include "rquota.h"
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <sys/quota.h>
+#include <dirent.h>
+#include <paths.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include "mntent.h"
+#include "xmalloc.h"
+
+#define TYPE_EXTENDED  0x01
+#define ACTIVE         0x02
+
+#ifdef ELM
+#define _PATH_DEV_DSK   "/dev/dsk/"
+#else
+#define _PATH_DEV_DSK   "/dev/"
+#endif
+
+/*
+ * Global unix authentication credentials.
+ */
+extern struct authunix_parms *unix_cred;
+
+char *nfsmount_to_devname(char *pathname, int *blksize)
+{
+   DIR *dp;
+   dev_t device;
+   struct stat st;
+   struct dirent *de;
+   static char *devicename = NULL;
+   static int devicelen = 0;
+
+   if (stat(pathname, &st) == -1)
+      return (char *)0;
+
+   device = st.st_dev;
+   *blksize = st.st_blksize;
+
+   /*
+    * search for devicename in _PATH_DEV_DSK dir.
+    */
+   if ((dp = opendir(_PATH_DEV_DSK)) == (DIR *)0)
+      return (char *)0;
+
+   while ((de = readdir(dp)) != (struct dirent *)0) {
+      if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
+         continue;
+
+      if (devicelen == 0) {
+        devicelen = sizeof (_PATH_DEV_DSK) + strlen (de->d_name) + 1;
+        devicename = (char *) xmalloc (devicelen);
+      }
+      else {
+         int newlen = sizeof (_PATH_DEV_DSK) + strlen (de->d_name) + 1;
+         if (newlen > devicelen) {
+             devicelen = newlen;
+             devicename = (char *) xrealloc (devicename, devicelen);
+         }
+      }
+
+      strcpy(devicename, _PATH_DEV_DSK);
+      strcat(devicename, de->d_name);
+      if (stat(devicename, &st) == -1)
+         continue;
+
+      if (!S_ISBLK(st.st_mode))
+         continue;
+
+      if ((device == st.st_rdev) && S_ISBLK(st.st_mode))
+         break;
+   }
+   closedir(dp);
+
+   if (de != (struct dirent *)0) {
+      return devicename;
+   } else
+      return (char *)0;
+}
+
+int in_group (gid_t *gids, u_int len, gid_t gid)
+{
+   int cnt = 0;
+
+   while (cnt < len) {
+      if (gids[cnt] == gid)
+         return 1;
+      cnt++;
+   }
+   return 0;
+}
+
+getquota_rslt *getquotainfo(int flags, caddr_t *argp, struct svc_req *rqstp)
+{
+   static getquota_rslt result;
+   union {
+      getquota_args *args;
+      ext_getquota_args *ext_args;
+   } arguments;
+   FILE *fp;
+   struct dqblk dq_dqb;
+   struct mntent *mnt;
+   char *pathname, *devicename, *qfpathname;
+   int fd, err, id, type;
+
+   /*
+    * First check authentication.
+    */
+   if (flags & TYPE_EXTENDED) {
+      arguments.ext_args = (ext_getquota_args *)argp;
+      id = arguments.ext_args->gqa_id;
+      type = arguments.ext_args->gqa_type;
+      pathname = arguments.ext_args->gqa_pathp;
+
+      if (type == USRQUOTA && unix_cred->aup_uid && unix_cred->aup_uid != id) {
+         result.status = Q_EPERM;
+         return(&result);
+      }
+
+      if (type == GRPQUOTA && unix_cred->aup_uid && unix_cred->aup_gid != id &&
+          !in_group((gid_t *)unix_cred->aup_gids, unix_cred->aup_len, id)) {
+         result.status = Q_EPERM;
+         return(&result);
+      }
+   } else {
+      arguments.args = (getquota_args *)argp;
+      id = arguments.args->gqa_uid;
+      type = USRQUOTA;
+      pathname = arguments.args->gqa_pathp;
+
+      if (unix_cred->aup_uid && unix_cred->aup_uid != id) {
+         result.status = Q_EPERM;
+         return(&result);
+      }
+   }
+
+   /*
+    * Convert a nfs_mountpoint to a local devicename.
+    */
+   if ((devicename = nfsmount_to_devname(pathname,
+        &result.getquota_rslt_u.gqr_rquota.rq_bsize)) == (char *)0) {
+      result.status = Q_NOQUOTA;   
+      return(&result);
+   }
+
+   fp = setmntent(MNTTAB, "r");
+   while ((mnt = getmntent(fp)) != (struct mntent *)0) {
+      if (strcmp(devicename, mnt->mnt_fsname))
+         continue;
+
+      if (hasquota(mnt, type, &qfpathname)) {
+         if ((err = quotactl(QCMD(Q_GETQUOTA, type), devicename, id,
+            (caddr_t)&dq_dqb)) == -1 && !(flags & ACTIVE)) {
+            if ((fd = open(qfpathname, O_RDONLY)) < 0)
+           {
+              free(qfpathname);
+               continue;
+           }
+            free(qfpathname);
+            lseek(fd, (long) dqoff(id), L_SET);
+            switch (read(fd, &dq_dqb, sizeof(struct dqblk))) {
+               case 0:/* EOF */
+                  /*
+                   * Convert implicit 0 quota (EOF) into an
+                   * explicit one (zero'ed dqblk)
+                   */
+                  memset((caddr_t)&dq_dqb, 0, sizeof(struct dqblk));
+                  break;
+               case sizeof(struct dqblk):   /* OK */
+                  break;
+               default:   /* ERROR */
+                  close(fd);
+                  continue;
+            }
+            close(fd);
+         }
+         endmntent(fp);
+
+         if (err && (flags & ACTIVE)) {
+            result.status = Q_NOQUOTA;   
+            return(&result);
+         }
+
+         result.status = Q_OK;   
+         result.getquota_rslt_u.gqr_rquota.rq_active = (err == 0) ? TRUE : FALSE;
+         /*
+          * Make a copy of the info into the last part of the remote quota
+          * struct which is exactly the same.
+          */
+         memcpy((caddr_t *)&result.getquota_rslt_u.gqr_rquota.rq_bhardlimit,
+                (caddr_t *)&dq_dqb, sizeof(struct dqblk));
+
+         return(&result);
+      }
+   }
+   endmntent(fp);
+
+   result.status = Q_NOQUOTA;   
+   return(&result);
+}
+
+getquota_rslt *rquotaproc_getquota_1(getquota_args *argp, struct svc_req *rqstp)
+{
+   return(getquotainfo(0, (caddr_t *)argp, rqstp));
+}
+
+getquota_rslt *rquotaproc_getactivequota_1(getquota_args *argp, struct svc_req *rqstp)
+{
+   return(getquotainfo(ACTIVE, (caddr_t *)argp, rqstp));
+}
+
+getquota_rslt *rquotaproc_getquota_2(ext_getquota_args *argp, struct svc_req *rqstp)
+{
+   return(getquotainfo(TYPE_EXTENDED, (caddr_t *)argp, rqstp));
+}
+
+getquota_rslt *rquotaproc_getactivequota_2(ext_getquota_args *argp, struct svc_req *rqstp)
+{
+   return(getquotainfo(TYPE_EXTENDED | ACTIVE, (caddr_t *)argp, rqstp));
+}
diff --git a/utils/rquotad/rquota_svc.c b/utils/rquotad/rquota_svc.c
new file mode 100644 (file)
index 0000000..d402f0b
--- /dev/null
@@ -0,0 +1,213 @@
+/*
+ * QUOTA    An implementation of the diskquota system for the LINUX
+ *          operating system. QUOTA is implemented using the BSD systemcall
+ *          interface as the means of communication with the user level.
+ *          Should work for all filesystems because of integration into the
+ *          VFS layer of the operating system.
+ *          This is based on the Melbourne quota system wich uses both user and
+ *          group quota files.
+ *
+ *          This part accepts the rquota rpc-requests.
+ *
+ * Version: $Id: rquota_svc.c,v 2.6 1996/11/17 16:59:46 mvw Exp mvw $
+ *
+ * Author:  Marco van Wieringen <mvw@planets.elm.net>
+ *
+ *          This program 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 of the License, or (at your option) any later version.
+ */
+#include "config.h"
+
+#include <unistd.h>
+#include <rpc/rpc.h>
+#include "rquota.h"
+#include <stdlib.h>
+#include <rpc/pmap_clnt.h>
+#include <string.h>
+#include <memory.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <syslog.h>
+
+#ifdef __STDC__
+#define SIG_PF void(*)(int)
+#endif
+
+extern getquota_rslt *rquotaproc_getquota_1(getquota_args *argp,
+                                           struct svc_req *rqstp);
+extern getquota_rslt *rquotaproc_getactivequota_1(getquota_args *argp,
+                                                 struct svc_req *rqstp);
+extern getquota_rslt *rquotaproc_getquota_2(ext_getquota_args *argp,
+                                           struct svc_req *rqstp);
+extern getquota_rslt *rquotaproc_getactivequota_2(ext_getquota_args *argp,
+                                                 struct svc_req *rqstp);
+
+/*
+ * Global authentication credentials.
+ */
+struct authunix_parms *unix_cred;
+
+static void rquotaprog_1(struct svc_req *rqstp, register SVCXPRT *transp)
+{
+   union {
+      getquota_args rquotaproc_getquota_1_arg;
+      getquota_args rquotaproc_getactivequota_1_arg;
+   } argument;
+   char *result;
+   xdrproc_t xdr_argument, xdr_result;
+   char *(*local)(char *, struct svc_req *);
+
+   /*
+    * Don't bother authentication for NULLPROC.
+    */
+   if (rqstp->rq_proc == NULLPROC) {
+      (void) svc_sendreply(transp, (xdrproc_t) xdr_void, (char *)NULL);
+      return;
+   }
+
+   /*
+    * First get authentication.
+    */
+   switch (rqstp->rq_cred.oa_flavor) {
+      case AUTH_UNIX:
+         unix_cred = (struct authunix_parms *)rqstp->rq_clntcred;
+         break;
+      case AUTH_NULL:
+      default:
+         svcerr_weakauth(transp);
+         return;
+   }
+
+   switch (rqstp->rq_proc) {
+      case RQUOTAPROC_GETQUOTA:
+         xdr_argument = (xdrproc_t) xdr_getquota_args;
+         xdr_result = (xdrproc_t) xdr_getquota_rslt;
+         local = (char *(*)(char *, struct svc_req *)) rquotaproc_getquota_1;
+         break;
+
+      case RQUOTAPROC_GETACTIVEQUOTA:
+         xdr_argument = (xdrproc_t) xdr_getquota_args;
+         xdr_result = (xdrproc_t) xdr_getquota_rslt;
+         local = (char *(*)(char *, struct svc_req *)) rquotaproc_getactivequota_1;
+         break;
+
+      default:
+         svcerr_noproc(transp);
+         return;
+   }
+
+   (void) memset((char *)&argument, 0, sizeof (argument));
+   if (!svc_getargs(transp, xdr_argument, (caddr_t) &argument)) {
+      svcerr_decode(transp);
+      return;
+   }
+   result = (*local)((char *)&argument, rqstp);
+   if (result != NULL && !svc_sendreply(transp, xdr_result, result)) {
+      svcerr_systemerr(transp);
+   }
+
+   if (!svc_freeargs(transp, xdr_argument, (caddr_t) &argument)) {
+      syslog(LOG_ERR, "unable to free arguments");
+      exit(1);
+   }
+   return;
+}
+
+static void rquotaprog_2(struct svc_req *rqstp, register SVCXPRT *transp)
+{
+   union {
+      ext_getquota_args rquotaproc_getquota_2_arg;
+      ext_getquota_args rquotaproc_getactivequota_2_arg;
+   } argument;
+   char *result;
+   xdrproc_t xdr_argument, xdr_result;
+   char *(*local)(char *, struct svc_req *);
+
+   /*
+    * Don't bother authentication for NULLPROC.
+    */
+   if (rqstp->rq_proc == NULLPROC) {
+      (void) svc_sendreply(transp, (xdrproc_t) xdr_void, (char *)NULL);
+      return;
+   }
+
+   /*
+    * First get authentication.
+    */
+   switch (rqstp->rq_cred.oa_flavor) {
+      case AUTH_UNIX:
+         unix_cred = (struct authunix_parms *)rqstp->rq_clntcred;
+         break;
+      case AUTH_NULL:
+      default:
+         svcerr_weakauth(transp);
+         return;
+   }
+
+   switch (rqstp->rq_proc) {
+      case RQUOTAPROC_GETQUOTA:
+         xdr_argument = (xdrproc_t) xdr_ext_getquota_args;
+         xdr_result = (xdrproc_t) xdr_getquota_rslt;
+         local = (char *(*)(char *, struct svc_req *)) rquotaproc_getquota_2;
+         break;
+
+      case RQUOTAPROC_GETACTIVEQUOTA:
+         xdr_argument = (xdrproc_t) xdr_ext_getquota_args;
+         xdr_result = (xdrproc_t) xdr_getquota_rslt;
+         local = (char *(*)(char *, struct svc_req *)) rquotaproc_getactivequota_2;
+         break;
+
+      default:
+         svcerr_noproc(transp);
+         return;
+   }
+
+   (void) memset((char *)&argument, 0, sizeof (argument));
+   if (!svc_getargs(transp, xdr_argument, (caddr_t) &argument)) {
+      svcerr_decode(transp);
+      return;
+   }
+   result = (*local)((char *)&argument, rqstp);
+   if (result != NULL && !svc_sendreply(transp, xdr_result, result)) {
+      svcerr_systemerr(transp);
+   }
+
+   if (!svc_freeargs(transp, xdr_argument, (caddr_t) &argument)) {
+      syslog(LOG_ERR, "unable to free arguments");
+      exit(1);
+   }
+   return;
+}
+
+int main(int argc, char **argv)
+{
+   register SVCXPRT *transp;
+
+   (void) pmap_unset(RQUOTAPROG, RQUOTAVERS);
+   (void) pmap_unset(RQUOTAPROG, EXT_RQUOTAVERS);
+
+   openlog("rquota", LOG_PID, LOG_DAEMON);
+
+   transp = svcudp_create(RPC_ANYSOCK);
+   if (transp == NULL) {
+      syslog(LOG_ERR, "cannot create udp service.");
+      exit(1);
+   }
+   if (!svc_register(transp, RQUOTAPROG, RQUOTAVERS, rquotaprog_1, IPPROTO_UDP)) {
+      syslog(LOG_ERR, "unable to register (RQUOTAPROG, RQUOTAVERS, udp).");
+      exit(1);
+   }
+   if (!svc_register(transp, RQUOTAPROG, EXT_RQUOTAVERS, rquotaprog_2, IPPROTO_UDP)) {
+      syslog(LOG_ERR, "unable to register (RQUOTAPROG, EXT_RQUOTAVERS, udp).");
+      exit(1);
+   }
+
+   daemon(1,1);
+   svc_run();
+
+   syslog(LOG_ERR, "svc_run returned");
+   exit(1);
+   /* NOTREACHED */
+}
diff --git a/utils/rquotad/rquota_xdr.c b/utils/rquotad/rquota_xdr.c
new file mode 100644 (file)
index 0000000..6e68bd4
--- /dev/null
@@ -0,0 +1,123 @@
+#include "config.h"
+
+#include <rpc/rpc.h>
+#include "rquota.h"
+
+
+bool_t
+xdr_getquota_args(xdrs, objp)
+       XDR *xdrs;
+       getquota_args *objp;
+{
+       if (!xdr_string(xdrs, &objp->gqa_pathp, RQ_PATHLEN)) {
+               return (FALSE);
+       }
+       if (!xdr_int(xdrs, &objp->gqa_uid)) {
+               return (FALSE);
+       }
+       return (TRUE);
+}
+
+
+
+
+bool_t
+xdr_ext_getquota_args(xdrs, objp)
+       XDR *xdrs;
+       ext_getquota_args *objp;
+{
+       if (!xdr_string(xdrs, &objp->gqa_pathp, RQ_PATHLEN)) {
+               return (FALSE);
+       }
+       if (!xdr_int(xdrs, &objp->gqa_type)) {
+               return (FALSE);
+       }
+       if (!xdr_int(xdrs, &objp->gqa_id)) {
+               return (FALSE);
+       }
+       return (TRUE);
+}
+
+
+
+
+bool_t
+xdr_rquota(xdrs, objp)
+       XDR *xdrs;
+       rquota *objp;
+{
+       if (!xdr_int(xdrs, &objp->rq_bsize)) {
+               return (FALSE);
+       }
+       if (!xdr_bool(xdrs, &objp->rq_active)) {
+               return (FALSE);
+       }
+       if (!xdr_u_int(xdrs, &objp->rq_bhardlimit)) {
+               return (FALSE);
+       }
+       if (!xdr_u_int(xdrs, &objp->rq_bsoftlimit)) {
+               return (FALSE);
+       }
+       if (!xdr_u_int(xdrs, &objp->rq_curblocks)) {
+               return (FALSE);
+       }
+       if (!xdr_u_int(xdrs, &objp->rq_fhardlimit)) {
+               return (FALSE);
+       }
+       if (!xdr_u_int(xdrs, &objp->rq_fsoftlimit)) {
+               return (FALSE);
+       }
+       if (!xdr_u_int(xdrs, &objp->rq_curfiles)) {
+               return (FALSE);
+       }
+       if (!xdr_u_int(xdrs, &objp->rq_btimeleft)) {
+               return (FALSE);
+       }
+       if (!xdr_u_int(xdrs, &objp->rq_ftimeleft)) {
+               return (FALSE);
+       }
+       return (TRUE);
+}
+
+
+
+
+bool_t
+xdr_gqr_status(xdrs, objp)
+       XDR *xdrs;
+       gqr_status *objp;
+{
+       if (!xdr_enum(xdrs, (enum_t *)objp)) {
+               return (FALSE);
+       }
+       return (TRUE);
+}
+
+
+
+
+bool_t
+xdr_getquota_rslt(xdrs, objp)
+       XDR *xdrs;
+       getquota_rslt *objp;
+{
+       if (!xdr_gqr_status(xdrs, &objp->status)) {
+               return (FALSE);
+       }
+       switch (objp->status) {
+       case Q_OK:
+               if (!xdr_rquota(xdrs, &objp->getquota_rslt_u.gqr_rquota)) {
+                       return (FALSE);
+               }
+               break;
+       case Q_NOQUOTA:
+               break;
+       case Q_EPERM:
+               break;
+       default:
+               return (FALSE);
+       }
+       return (TRUE);
+}
+
+
diff --git a/utils/rquotad/rquotad.man b/utils/rquotad/rquotad.man
new file mode 100644 (file)
index 0000000..da8fa8c
--- /dev/null
@@ -0,0 +1,41 @@
+.\"@(#)rquotad.8c"
+.TH RQUOTAD 8C"
+.SH NAME
+rquotad, rpc.rquotad \- remote quota server
+.SH SYNOPSIS
+.B /usr/etc/rpc.rquotad
+.SH DESCRIPTION
+.LP
+.IX  "rquotad daemon"  ""  "\fLrquotad\fP \(em remote quota server"
+.IX  daemons  "rquotad daemon"  ""  "\fLrquotad\fP \(em remote quota server"
+.IX  "user quotas"  "rquotad daemon"  ""  "\fLrquotad\fP \(em remote quota server"
+.IX  "disk quotas"  "rquotad daemon"  ""  "\fLrquotad\fP \(em remote quota server"
+.IX  "quotas"  "rquotad daemon"  ""  "\fLrquotad\fP \(em remote quota server"
+.IX  "file system"  "rquotad daemon"  ""  "\fLrquotad\fP \(em remote quota server"
+.IX  "remote procedure call services"  "rquotad"  ""  "\fLrquotad\fP \(em remote quota server"
+.B rquotad
+is an
+.BR rpc (3N)
+server which returns quotas for a user of a local file system
+which is mounted by a remote machine over the
+.SM NFS\s0.
+The results are used by
+.BR quota (1)
+to display user quotas for remote file systems.
+The
+.B rquotad
+daemon is normally started at boottime from the
+.BR rc.net
+script
+.SH FILES
+.PD 0
+.TP 20
+.B quotas
+quota file at the file system root
+.PD
+.SH "SEE ALSO"
+.BR quota (1),
+.BR rpc (3N),
+.BR nfs (4P),
+.BR services (5)
+.BR inetd (8C),
diff --git a/utils/showmount/Makefile b/utils/showmount/Makefile
new file mode 100644 (file)
index 0000000..c8aa34d
--- /dev/null
@@ -0,0 +1,11 @@
+#
+# dummy Makefile
+#
+
+PROGRAM        = showmount
+OBJS   = showmount.o
+LIBDEPS        = $(TOP)support/lib/libexport.a
+LIBS   = -lexport
+MAN8   = showmount
+
+include $(TOP)rules.mk
diff --git a/utils/showmount/showmount.c b/utils/showmount/showmount.c
new file mode 100644 (file)
index 0000000..47b5825
--- /dev/null
@@ -0,0 +1,287 @@
+/*
+ * showmount.c -- show mount information for an NFS server
+ * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
+ *
+ * This program 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 program 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.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <rpc/rpc.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/time.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <memory.h>
+#include <stdlib.h>
+
+#include <netdb.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <getopt.h>
+#include <mount.h>
+#include <unistd.h>
+
+static char *  version = "showmount for " VERSION;
+static char *  program_name;
+static int     headers = 1;
+static int     hflag = 0;
+static int     aflag = 0;
+static int     dflag = 0;
+static int     eflag = 0;
+
+static struct option longopts[] =
+{
+       { "all", 0, 0, 'a' },
+       { "directories", 0, 0, 'd' },
+       { "exports", 0, 0, 'e' },
+       { "no-headers", 0, &headers, 0 },
+       { "version", 0, 0, 'v' },
+       { "help", 0, 0, 'h' },
+       { NULL, 0, 0, 0 }
+};
+
+#define MAXHOSTLEN 256
+
+int dump_cmp(p, q)
+char **p;
+char **q;
+{
+       return strcmp(*p, *q);
+}
+
+static void usage(fp, n)
+FILE *fp;
+int n;
+{
+       fprintf(fp, "Usage: %s [-adehv]\n", program_name);
+       fprintf(fp, "       [--all] [--directories] [--exports]\n");
+       fprintf(fp, "       [--no-headers] [--help] [--version] [host]\n");
+       exit(n);
+}
+
+int main(argc, argv)
+int argc;
+char **argv;
+{
+       char hostname_buf[MAXHOSTLEN];
+       char *hostname;
+       enum clnt_stat clnt_stat;
+       struct hostent *hp;
+       struct sockaddr_in server_addr;
+       int msock;
+       struct timeval total_timeout;
+       struct timeval pertry_timeout;
+       int c;
+       CLIENT *mclient;
+       groups grouplist;
+       exports exportlist, exl;
+       mountlist dumplist;
+       mountlist list;
+       int i;
+       int n;
+       int maxlen;
+       char **dumpv;
+
+       program_name = argv[0];
+       while ((c = getopt_long(argc, argv, "adehv", longopts, NULL)) != EOF) {
+               switch (c) {
+               case 'a':
+                       aflag = 1;
+                       break;
+               case 'd':
+                       dflag = 1;
+                       break;
+               case 'e':
+                       eflag = 1;
+                       break;
+               case 'h':
+                       usage(stdout, 0);
+                       break;
+               case 'v':
+                       printf("%s\n", version);
+                       exit(0);
+               case 0:
+                       break;
+               case '?':
+               default:
+                       usage(stderr, 1);
+                       break;
+               }
+       }
+       argc -= optind;
+       argv += optind;
+
+       switch (aflag + dflag + eflag) {
+       case 0:
+               hflag = 1;
+               break;
+       case 1:
+               break;
+       default:
+               fprintf(stderr, "%s: only one of -a, -d or -e is allowed\n",
+                       program_name);
+               exit(1);
+               break;
+       }
+
+       switch (argc) {
+       case 0:
+               if (gethostname(hostname_buf, MAXHOSTLEN) < 0) {
+                       perror("getting hostname");
+                       exit(1);
+               }
+               hostname = hostname_buf;
+               break;
+       case 1:
+               hostname = argv[0];
+               break;
+       default:
+               fprintf(stderr, "%s: only one hostname is allowed\n",
+                       program_name);
+               exit(1);
+               break;
+       }
+
+       if (hostname[0] >= '0' && hostname[0] <= '9') {
+               server_addr.sin_family = AF_INET;
+               server_addr.sin_addr.s_addr = inet_addr(hostname);
+       }
+       else {
+               if ((hp = gethostbyname(hostname)) == NULL) {
+                       fprintf(stderr, "%s: can't get address for %s\n",
+                               program_name, hostname);
+                       exit(1);
+               }
+               server_addr.sin_family = AF_INET;
+               memcpy(&server_addr.sin_addr, hp->h_addr, hp->h_length);
+       }
+
+       /* create mount deamon client */
+
+       server_addr.sin_port = 0;
+       msock = RPC_ANYSOCK;
+       if ((mclient = clnttcp_create(&server_addr,
+           MOUNTPROG, MOUNTVERS, &msock, 0, 0)) == NULL) {
+               server_addr.sin_port = 0;
+               msock = RPC_ANYSOCK;
+               pertry_timeout.tv_sec = 3;
+               pertry_timeout.tv_usec = 0;
+               if ((mclient = clntudp_create(&server_addr,
+                   MOUNTPROG, MOUNTVERS, pertry_timeout, &msock)) == NULL) {
+                       clnt_pcreateerror("mount clntudp_create");
+                       exit(1);
+               }
+       }
+       mclient->cl_auth = authunix_create_default();
+       total_timeout.tv_sec = 20;
+       total_timeout.tv_usec = 0;
+
+       if (eflag) {
+               memset(&exportlist, '\0', sizeof(exportlist));
+               clnt_stat = clnt_call(mclient, MOUNTPROC_EXPORT,
+                       (xdrproc_t) xdr_void, NULL,
+                       (xdrproc_t) xdr_exports, (caddr_t) &exportlist,
+                       total_timeout);
+               if (clnt_stat != RPC_SUCCESS) {
+                       clnt_perror(mclient, "rpc mount export");
+                       exit(1);
+               }
+               if (headers)
+                       printf("Export list for %s:\n", hostname);
+               maxlen = 0;
+               for (exl = exportlist; exl; exl = exl->ex_next) {
+                       if ((n = strlen(exl->ex_dir)) > maxlen)
+                               maxlen = n;
+               }
+               while (exportlist) {
+                       printf("%-*s ", maxlen, exportlist->ex_dir);
+                       grouplist = exportlist->ex_groups;
+                       if (grouplist)
+                               while (grouplist) {
+                                       printf("%s%s", grouplist->gr_name,
+                                               grouplist->gr_next ? "," : "");
+                                       grouplist = grouplist->gr_next;
+                               }
+                       else
+                               printf("(everyone)");
+                       printf("\n");
+                       exportlist = exportlist->ex_next;
+               }
+               exit(0);
+       }
+
+       memset(&dumplist, '\0', sizeof(dumplist));
+       clnt_stat = clnt_call(mclient, MOUNTPROC_DUMP,
+               (xdrproc_t) xdr_void, NULL,
+               (xdrproc_t) xdr_mountlist, (caddr_t) &dumplist,
+               total_timeout);
+       if (clnt_stat != RPC_SUCCESS) {
+               clnt_perror(mclient, "rpc mount dump");
+               exit(1);
+       }
+
+       n = 0;
+       for (list = dumplist; list; list = list->ml_next)
+               n++;
+       dumpv = (char **) calloc(n, sizeof (char *));
+       if (n && !dumpv) {
+               fprintf(stderr, "%s: out of memory\n", program_name);
+               exit(1);
+       }
+       i = 0;
+
+       if (hflag) {
+               if (headers)
+                       printf("Hosts on %s:\n", hostname);
+               while (dumplist) {
+                       dumpv[i++] = dumplist->ml_hostname;
+                       dumplist = dumplist->ml_next;
+               }
+       }
+       else if (aflag) {
+               if (headers)
+                       printf("All mount points on %s:\n", hostname);
+               while (dumplist) {
+                       char *t;
+
+                       t=malloc(strlen(dumplist->ml_hostname)+strlen(dumplist->ml_directory)+2);
+                       if (!t)
+                       {
+                               fprintf(stderr, "%s: out of memory\n", program_name);
+                               exit(1);
+                       }
+                       sprintf(t, "%s:%s", dumplist->ml_hostname, dumplist->ml_directory);
+                       dumpv[i++] = t;
+                       dumplist = dumplist->ml_next;
+               }
+       }
+       else if (dflag) {
+               if (headers)
+                       printf("Directories on %s:\n", hostname);
+               while (dumplist) {
+                       dumpv[i++] = dumplist->ml_directory;
+                       dumplist = dumplist->ml_next;
+               }
+       }
+
+       qsort(dumpv, n, sizeof (char *), dump_cmp);
+       
+       for (i = 0; i < n; i++) {
+               if (i == 0 || strcmp(dumpv[i], dumpv[i - 1]) != 0)
+                       printf("%s\n", dumpv[i]);
+       }
+       exit(0);
+}
+
diff --git a/utils/showmount/showmount.man b/utils/showmount/showmount.man
new file mode 100644 (file)
index 0000000..63342c7
--- /dev/null
@@ -0,0 +1,58 @@
+.\" Copyright 1993 Rick Sladkey <jrs@world.std.com>
+.\" May be distributed under the GNU General Public License
+.TH SHOWMOUNT 8 "6 October 1993"
+.SH NAME
+showmount \- show mount information for an NFS server
+.SH SYNOPSIS
+.B /usr/sbin/showmount
+.B "[\ \-adehv\ ]"
+.B "[\ \-\-all\ ]"
+.B "[\ \-\-directories\ ]"
+.B "[\ \-\-exports\ ]"
+.B "[\ \-\-help\ ]"
+.B "[\ \-\-version\ ]"
+.B "[\ host\ ]"
+.SH DESCRIPTION
+.B showmount
+queries the mount daemon on a remote host for information about
+the state of the NFS server on that machine.  With no options
+.B showmount
+lists the set of clients who are mounting from that host.
+The output from
+.B showmount
+is designed to
+appear as though it were processesed through ``sort -u''.
+.SH OPTIONS
+.TP
+.BR \-a " or " \-\-all
+List both the client hostname and mounted directory in
+host:dir format.
+.TP
+.BR \-d " or " \-\-directories
+List only the directories mounted by some client.
+.TP
+.BR \-e " or " \-\-exports
+Show the NFS server's export list.
+.TP
+.BR \-h " or " \-\-help
+Provide a short help summary.
+.TP
+.BR \-v " or " \-\-version
+Report the current version number of the program.
+.TP
+.B \-\-no\-headers
+Suppress the descriptive headings from the output.
+.SH "SEE ALSO"
+.BR rpc.mountd (8),
+.BR rpc.nfsd (8)
+.SH BUGS
+The completeness and accurary of the information that
+.B showmount
+displays varies according to the NFS server's implementation.
+.P
+Because
+.B showmount
+sorts and uniqs the output, it is impossible to determine from
+the output whether a client is mounting the same directory more than once.
+.SH AUTHOR
+Rick Sladkey <jrs@world.std.com>
diff --git a/utils/statd/COPYING b/utils/statd/COPYING
new file mode 100644 (file)
index 0000000..60549be
--- /dev/null
@@ -0,0 +1,340 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+           How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) 19yy  <name of author>
+
+    This program 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 of the License, or
+    (at your option) any later version.
+
+    This program 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 program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) 19yy name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/utils/statd/COPYRIGHT b/utils/statd/COPYRIGHT
new file mode 100644 (file)
index 0000000..7b91031
--- /dev/null
@@ -0,0 +1,25 @@
+rpc.statd -- Network Status Monitor (NSM) protocol daemon for Linux.
+Copyright (C) 1995-1999 Jeffrey A. Uphoff
+
+This program 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 of the License, or
+(at your option) any later version.
+
+This program 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 program; if not, write to the Free Software
+Foundation, Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA.
+
+Jeffrey A. Uphoff
+Transmeta Corporation
+2540 Mission College Blvd.
+Santa Clara, CA, 95054
+USA
+
+Phone:     +1-408-919-6458
+Internet:  juphoff@transmeta.com
diff --git a/utils/statd/Makefile b/utils/statd/Makefile
new file mode 100644 (file)
index 0000000..3a3a794
--- /dev/null
@@ -0,0 +1,58 @@
+# Copyright (C) 1995-1999 Jeffrey A. Uphoff
+# Adapted for linux-nfs build tree by Olaf Kirch, 1996.
+#
+# NSM for Linux.
+
+# Uncomment for embedded client-side simulation functions.
+#SIMUL = -DSIMULATIONS
+
+# Undefined is normal, defined provides debug logging.
+#DEBUG = -DDEBUG
+
+##################################################################
+#      no user-serviceable parts below this line
+##################################################################
+PROGRAM        = statd
+PREFIX = rpc.
+OBJS   = $(SRCS:.c=.o)
+CCOPTS = $(DEBUG) $(SIMUL)
+LIBS   = -lexport
+
+SRCS   = $(RPCSRCS) $(SIMSRCS) \
+         callback.c notlist.c log.c misc.c monitor.c notify.c simu.c \
+         stat.c statd.c state.c svc_run.c rmtcall.c
+HDRS   = $(RPCHDRS) $(SIMHDRS)
+
+RPCSRCS        = sm_inter_clnt.c sm_inter_svc.c sm_inter_xdr.c
+RPCHDRS        = sm_inter.h
+
+ifdef SIMUL
+SIMSRCS        = sim_sm_inter_clnt.c sim_sm_inter_svc.c
+SIMHDRS        = sim_sm_inter.h
+SRCS   += simulate.c
+endif
+
+MAN8   = statd
+
+include $(TOP)rules.mk
+
+AFLAGS += -Wno-unused
+
+$(RPCHDRS) $(RPCSRCS): sm_inter.x
+       $(RM) $(RPCHDRS) $(RPCSRCS)
+       $(RPCGEN) -h -o sm_inter.h $<
+       $(RPCGEN) -l -o sm_inter_clnt.c $<
+       $(RPCGEN) -m -o sm_inter_svc.c $<
+       $(RPCGEN) -c -o sm_inter_xdr.c $<
+
+$(SIMHDRS) $(SIMSRCS): sim_sm_inter.x
+       $(RM) $(SIMHDRS) $(SIMSRCS)
+       $(RPCGEN) -h -o sim_sm_inter.h $<
+       $(RPCGEN) -l -o sim_sm_inter_clnt.c $<
+       $(RPCGEN) -m -o sim_sm_inter_svc.c $<
+
+clean::
+       $(RM) $(PROGRAM)
+
+distclean::
+       $(RM) $(RPCHDRS) $(RPCSRCS) $(SIMHDRS) $(SIMSRCS)
diff --git a/utils/statd/TODO b/utils/statd/TODO
new file mode 100644 (file)
index 0000000..0ee050a
--- /dev/null
@@ -0,0 +1,13 @@
+Some things still left to do (not a comprehensive list):
+
+* Go through Olaf's extensive changes (especially the list and callback
+  handling, which is the meat of the server) and understand everything
+  that he's done.
+
+* Continue checking for security holes.
+
+* Handle multiple SM_MON requests that are identical save for the "priv"
+  information.  How should I do this?  No spec's...(it's not really
+  supposed to happen).  [Did Olaf already address this?]
+
+* BETTER CODE COMMENTS!
diff --git a/utils/statd/callback.c b/utils/statd/callback.c
new file mode 100644 (file)
index 0000000..e3fad6a
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 1995, 1997-1999 Jeffrey A. Uphoff
+ * Modified by Olaf Kirch, Oct. 1996.
+ *
+ * NSM for Linux.
+ */
+
+#include "config.h"
+#include "statd.h"
+#include "notlist.h"
+
+/* Callback notify list. */
+notify_list *cbnl = NULL;
+
+
+/* 
+ * Services SM_NOTIFY requests.
+ * Any clients that have asked us to monitor that host are put on
+ * the global callback list, which is processed as soon as statd
+ * returns to svc_run.
+ */
+void *
+sm_notify_1_svc(struct stat_chge *argp, struct svc_req *rqstp)
+{
+       notify_list    *lp, *call;
+       static char    *result = NULL;
+
+       dprintf(L_DEBUG, "Received SM_NOTIFY from %s, state: %d",
+                               argp->mon_name, argp->state);
+
+       if ((lp = rtnl) != NULL) {
+               log(L_WARNING, "SM_NOTIFY from %s--nobody looking!",
+                               argp->mon_name, argp->state);
+               return ((void *) &result);
+       }
+
+       /* okir change: statd doesn't remove the remote host from its
+        * internal monitor list when receiving an SM_NOTIFY call from
+        * it. Lockd will want to continue monitoring the remote host
+        * until it issues an SM_UNMON call.
+        */
+       while ((lp = nlist_gethost(lp, argp->mon_name, 0)) != NULL) {
+               if (NL_STATE(lp) != argp->state) {
+                       NL_STATE(lp) = argp->state;
+                       call = nlist_clone(lp);
+                       NL_TYPE(call) = NOTIFY_CALLBACK;
+                       nlist_insert(&notify, call);
+               }
+               lp = NL_NEXT(lp);
+       }
+
+       return ((void *) &result);
+}
diff --git a/utils/statd/log.c b/utils/statd/log.c
new file mode 100644 (file)
index 0000000..38f7d3a
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 1995 Olaf Kirch
+ * Modified by Jeffrey A. Uphoff, 1995, 1997, 1999.
+ * Modified by H.J. Lu, 1998.
+ *
+ * NSM for Linux.
+ */
+
+/* 
+ *     log.c - logging functions for lockd/statd
+ *     260295   okir   started with simply syslog logging.
+ */
+
+#include "config.h"
+
+#include <syslog.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <time.h>
+#include <sys/types.h>
+#include "log.h"
+
+static char    progname[256];
+static pid_t   mypid;
+                                /* Turns on logging to console/stderr. */
+static int     opt_debug = 0;  /* Will be command-line option, eventually */
+
+void
+log_init(char *name)
+{
+    char       *sp;
+
+    openlog(name, LOG_PID, LOG_LOCAL5);
+    if ((sp = strrchr(name, '/')) != NULL)
+       name = ++sp;
+    strncpy(progname, name, sizeof (progname) - 1);
+    progname[sizeof (progname) - 1] = '\0';
+    mypid = getpid();
+}
+
+void
+log_background(void)
+{
+    /* NOP */
+}
+
+void
+log_enable(int level)
+{
+    opt_debug = 1;
+}
+
+int
+log_enabled(int level)
+{
+    return opt_debug;
+}
+
+void
+die(char *fmt, ...)
+{
+    char       buffer[1024];
+    va_list    ap;
+
+    va_start(ap, fmt);
+    vsnprintf (buffer, 1024, fmt, ap);
+    va_end(ap);
+    buffer[1023]=0;
+
+    log(L_FATAL, "%s", buffer);
+
+#ifndef DEBUG
+    exit (2);
+#else
+    abort();   /* make a core */
+#endif
+}
+
+void
+log(int level, char *fmt, ...)
+{
+    char       buffer[1024];
+    va_list    ap;
+
+    va_start(ap, fmt);
+    vsnprintf (buffer, 1024, fmt, ap);
+    va_end(ap);
+    buffer[1023]=0;
+
+    if (level < L_DEBUG) {
+       syslog(level, buffer);
+    }
+
+    if (opt_debug) {
+        time_t         now;
+        struct tm *    tm;
+
+        time(&now);
+        tm = localtime(&now);
+        fprintf (stderr, "%02d.%02d.%02d %02d:%02d:%02d %s[%d]: %s\n",
+                       tm->tm_mday, tm->tm_mon, tm->tm_year,
+                       tm->tm_hour, tm->tm_min, tm->tm_sec,
+                       progname, mypid,
+                       buffer);
+    }
+}
diff --git a/utils/statd/log.h b/utils/statd/log.h
new file mode 100644 (file)
index 0000000..f00bb63
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 1995 Olaf Kirch
+ * Modified by Jeffrey A. Uphoff, 1996, 1997, 1999.
+ *
+ * NSM for Linux.
+ */
+
+/*
+ *     logging functionality
+ *     260295  okir
+ */
+
+#ifndef _LOCKD_LOG_H_
+#define _LOCKD_LOG_H_
+
+#include <syslog.h>
+
+void   log_init(char *name);
+void   log_background(void);
+void   log_enable(int facility);
+int    log_enabled(int facility);
+void   log(int level, char *fmt, ...);
+void   die(char *fmt, ...);
+
+/*
+ * Map per-application severity to system severity. What's fatal for
+ * lockd is merely an itching spot from the universe's point of view.
+ */
+#define L_CRIT         LOG_CRIT
+#define L_FATAL                LOG_ERR
+#define L_ERROR                LOG_WARNING
+#define L_WARNING      LOG_NOTICE
+#define L_DEBUG                LOG_DEBUG
+
+#ifdef DEBUG
+#define dprintf                log
+#else
+#define dprintf                if (0) log
+#endif
+
+#endif /* _LOCKD_LOG_H_ */
diff --git a/utils/statd/misc.c b/utils/statd/misc.c
new file mode 100644 (file)
index 0000000..42f6e57
--- /dev/null
@@ -0,0 +1,72 @@
+/* 
+ * Copyright (C) 1995-1999 Jeffrey A. Uphoff
+ * Modified by Olaf Kirch, 1996.
+ * Modified by H.J. Lu, 1998.
+ *
+ * NSM for Linux.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <limits.h>
+#include <string.h>
+#include <unistd.h>
+#include "statd.h"
+#include "notlist.h"
+
+/*
+ * Error-checking malloc() wrapper.
+ */
+void *
+xmalloc (size_t size)
+{
+  void *ptr;
+
+  if (size == 0)
+    return ((void *)NULL);
+
+  if (!(ptr = malloc (size)))
+    /* SHIT!  SHIT!  SHIT! */
+    die ("malloc failed");
+
+  return (ptr);
+}
+
+
+/* 
+ * Error-checking strdup() wrapper.
+ */
+char *
+xstrdup (const char *string)
+{
+  char *result;
+
+  /* Will only fail if underlying malloc() fails (ENOMEM). */
+  if (!(result = strdup (string)))
+    die ("strdup failed");
+
+  return (result);
+}
+
+
+/*
+ * Call with check=1 to verify that this host is not still on the rtnl
+ * before unlinking file.
+ */
+void
+xunlink (char *path, char *host, short int check)
+{
+  char *tozap;
+
+  tozap=alloca (strlen(path)+strlen(host)+2);
+  sprintf (tozap, "%s/%s", path, host);
+
+  if (!check || !nlist_gethost(rtnl, host, 0))
+    if (unlink (tozap) == -1)
+      log (L_ERROR, "unlink (%s): %s", tozap, strerror (errno));
+    else
+      dprintf (L_DEBUG, "Unlinked %s", tozap);
+  else
+    dprintf (L_DEBUG, "Not unlinking %s--host still monitored.", tozap);
+}
diff --git a/utils/statd/monitor.c b/utils/statd/monitor.c
new file mode 100644 (file)
index 0000000..5a782dc
--- /dev/null
@@ -0,0 +1,287 @@
+/*
+ * Copyright (C) 1995-1999 Jeffrey A. Uphoff
+ * Major rewrite by Olaf Kirch, Dec. 1996.
+ * Modified by H.J. Lu, 1998.
+ * Tighter access control, Olaf Kirch June 1999.
+ *
+ * NSM for Linux.
+ */
+
+#include "config.h"
+
+#include <fcntl.h>
+#include <limits.h>
+#include <netdb.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <arpa/inet.h>
+#include "misc.h"
+#include "statd.h"
+#include "notlist.h"
+
+notify_list *          rtnl = NULL;    /* Run-time notify list. */
+
+
+/*
+ * Services SM_MON requests.
+ */
+struct sm_stat_res *
+sm_mon_1_svc(struct mon *argp, struct svc_req *rqstp)
+{
+       static sm_stat_res result;
+       char            *mon_name = argp->mon_id.mon_name,
+                       *my_name  = argp->mon_id.my_id.my_name;
+       struct my_id    *id = &argp->mon_id.my_id;
+       char            *path;
+       int             fd;
+       notify_list     *clnt;
+       struct in_addr  my_addr;
+#ifdef RESTRICTED_STATD
+       struct in_addr  mon_addr, caller;
+#else
+       struct hostent  *hostinfo = NULL;
+#endif
+
+       /* Assume that we'll fail. */
+       result.res_stat = STAT_FAIL;
+       result.state = -1;      /* State is undefined for STAT_FAIL. */
+
+       /* Restrict access to statd.
+        * In the light of CERT CA-99.05, we tighten access to
+        * statd.                       --okir
+        */
+#ifdef RESTRICTED_STATD
+       /* 1.   Reject anyone not calling from 127.0.0.1.
+        *      Ignore the my_name specified by the caller, and
+        *      use "127.0.0.1" instead.
+        */
+       caller = svc_getcaller(rqstp->rq_xprt)->sin_addr;
+       if (caller.s_addr != htonl(INADDR_LOOPBACK)) {
+               log(L_WARNING,
+                       "Call to statd from non-local host %s",
+                       inet_ntoa(caller));
+               goto failure;
+       }
+       my_addr.s_addr = htonl(INADDR_LOOPBACK);
+       my_name = "127.0.0.1";
+
+       /* 2.   Reject any registrations for non-lockd services.
+        *      This is specific to the linux kernel lockd, which
+        *      makes the callback procedure part of the lockd interface.
+        */
+       if (id->my_proc != 100021) {
+               log(L_WARNING,
+                       "Attempt to register callback to service %d",
+                       id->my_proc);
+               goto failure;
+       }
+
+       /* 3.   mon_name must be an address in dotted quad.
+        *      Again, specific to the linux kernel lockd.
+        */
+       if (!inet_aton(mon_name, &mon_addr)) {
+               log(L_WARNING,
+                       "Attempt to register host %s (not a dotted quad)",
+                       mon_name);
+               goto failure;
+       }
+#else
+       /*
+        * Check hostnames.  If I can't look them up, I won't monitor.  This
+        * might not be legal, but it adds a little bit of safety and sanity.
+        */
+
+       /* must check for /'s in hostname!  See CERT's CA-96.09 for details. */
+       if (strchr(mon_name, '/')) {
+               log(L_CRIT, "SM_MON request for hostname containing '/': %s",
+                       mon_name);
+               log(L_CRIT, "POSSIBLE SPOOF/ATTACK ATTEMPT!");
+               goto failure;
+       } else if (gethostbyname(mon_name) == NULL) {
+               log(L_WARNING, "gethostbyname error for %s", mon_name);
+               goto failure;
+       } else if (!(hostinfo = gethostbyname(my_name))) {
+               log(L_WARNING, "gethostbyname error for %s", my_name);
+               goto failure;
+       } else
+               my_addr = *(struct in_addr *) hostinfo->h_addr;
+#endif
+
+       /*
+        * Hostnames checked OK.
+        * Now check to see if this is a duplicate, and warn if so.
+        * I will also return STAT_FAIL. (I *think* this is how I should
+        * handle it.)
+        *
+        * Olaf requests that I allow duplicate SM_MON requests for
+        * hosts due to the way he is coding lockd. No problem,
+        * I'll just do a quickie success return and things should
+        * be happy.
+        */
+       if (rtnl) {
+               notify_list    *temp = rtnl;
+
+               while ((temp = nlist_gethost(temp, mon_name, 0))) {
+                       if (matchhostname(NL_MY_NAME(temp), my_name) &&
+                               NL_MY_PROC(temp) == id->my_proc &&
+                               NL_MY_PROG(temp) == id->my_prog &&
+                               NL_MY_VERS(temp) == id->my_vers) {
+                               /* Hey!  We already know you guys! */
+                               dprintf(L_DEBUG,
+                                       "Duplicate SM_MON request for %s "
+                                       "from procedure on %s",
+                                       mon_name, my_name);
+
+                               /* But we'll let you pass anyway. */
+                               result.res_stat = STAT_SUCC;
+                               result.state = MY_STATE;
+                               return (&result);
+                       }
+                       temp = NL_NEXT(temp);
+               }
+       }
+
+       /*
+        * We're committed...ignoring errors.  Let's hope that a malloc()
+        * doesn't fail.  (I should probably fix this assumption.)
+        */
+       if (!(clnt = nlist_new(my_name, mon_name, 0))) {
+               log(L_WARNING, "out of memory");
+               goto failure;
+       }
+
+       NL_ADDR(clnt) = my_addr;
+       NL_MY_PROG(clnt) = id->my_prog;
+       NL_MY_VERS(clnt) = id->my_vers;
+       NL_MY_PROC(clnt) = id->my_proc;
+       memcpy(NL_PRIV(clnt), argp->priv, SM_PRIV_SIZE);
+
+       /*
+        * Now, Create file on stable storage for host.
+        */
+
+       path=xmalloc(strlen(SM_DIR)+strlen(mon_name)+2);
+       sprintf(path, SM_DIR "/%s", mon_name);
+       if ((fd = open(path, O_WRONLY|O_SYNC|O_CREAT, S_IRUSR|S_IWUSR)) < 0) {
+               /* Didn't fly.  We won't monitor. */
+               log(L_ERROR, "creat(%s) failed: %m", path);
+               nlist_free(NULL, clnt);
+               free(path);
+               goto failure;
+       }
+       free(path);
+       nlist_insert(&rtnl, clnt);
+       close(fd);
+
+       result.res_stat = STAT_SUCC;
+       result.state = MY_STATE;
+       dprintf(L_DEBUG, "MONITORING %s for %s", mon_name, my_name);
+       return (&result);
+
+failure:
+       log(L_WARNING, "STAT_FAIL to %s for SM_MON of %s", my_name, mon_name);
+       return (&result);
+}
+
+
+/*
+ * Services SM_UNMON requests.
+ *
+ * There is no statement in the X/Open spec's about returning an error
+ * for requests to unmonitor a host that we're *not* monitoring.  I just
+ * return the state of the NSM when I get such foolish requests for lack
+ * of any better ideas.  (I also log the "offense.")
+ */
+struct sm_stat *
+sm_unmon_1_svc(struct mon_id *argp, struct svc_req *rqstp)
+{
+       static sm_stat  result;
+       notify_list     *clnt;
+       char            *mon_name = argp->mon_name,
+                       *my_name  = argp->my_id.my_name;
+       struct my_id    *id = &argp->my_id;
+
+       result.state = MY_STATE;
+
+       /* Check if we're monitoring anyone. */
+       if (!(clnt = rtnl)) {
+               log(L_WARNING,
+                       "Received SM_UNMON request from %s for %s while not "
+                       "monitoring any hosts.", my_name, argp->mon_name);
+               return (&result);
+       }
+
+       /*
+        * OK, we are.  Now look for appropriate entry in run-time list.
+        * There should only be *one* match on this, since I block "duplicate"
+        * SM_MON calls.  (Actually, duplicate calls are allowed, but only one
+        * entry winds up in the list the way I'm currently handling them.)
+        */
+       while ((clnt = nlist_gethost(clnt, mon_name, 0))) {
+               if (matchhostname(NL_MY_NAME(clnt), my_name) &&
+                       NL_MY_PROC(clnt) == id->my_proc &&
+                       NL_MY_PROG(clnt) == id->my_prog &&
+                       NL_MY_VERS(clnt) == id->my_vers) {
+                       /* Match! */
+                       dprintf(L_DEBUG, "UNMONITORING %s for %s",
+                                       mon_name, my_name);
+                       nlist_free(&rtnl, clnt);
+                       xunlink(SM_DIR, mon_name, 1);
+
+                       return (&result);
+               } else
+                       clnt = NL_NEXT(clnt);
+       }
+
+       log(L_WARNING, "Received erroneous SM_UNMON request from %s for %s",
+               my_name, mon_name);
+       return (&result);
+}
+
+
+struct sm_stat *
+sm_unmon_all_1_svc(struct my_id *argp, struct svc_req *rqstp)
+{
+       short int       count = 0;
+       static sm_stat  result;
+       notify_list     *clnt;
+
+       result.state = MY_STATE;
+
+       if (!(clnt = rtnl)) {
+               log(L_WARNING, "Received SM_UNMON_ALL request from %s "
+                       "while not monitoring any hosts", argp->my_name);
+               return (&result);
+       }
+
+       while ((clnt = nlist_gethost(clnt, argp->my_name, 1))) {
+               if (NL_MY_PROC(clnt) == argp->my_proc &&
+                       NL_MY_PROG(clnt) == argp->my_prog &&
+                       NL_MY_VERS(clnt) == argp->my_vers) {
+                       /* Watch stack! */
+                       char            mon_name[SM_MAXSTRLEN + 1];
+                       notify_list     *temp;
+
+                       dprintf(L_DEBUG,
+                               "UNMONITORING (SM_UNMON_ALL) %s for %s",
+                               NL_MON_NAME(clnt), NL_MY_NAME(clnt));
+                       strncpy(mon_name, NL_MON_NAME(clnt),
+                               sizeof (mon_name) - 1);
+                       mon_name[sizeof (mon_name) - 1] = '\0';
+                       temp = NL_NEXT(clnt);
+                       nlist_free(&rtnl, clnt);
+                       xunlink(SM_DIR, mon_name, 1);
+                       ++count;
+                       clnt = temp;
+               } else
+                       clnt = NL_NEXT(clnt);
+       }
+
+       if (!count) {
+               dprintf(L_DEBUG, "SM_UNMON_ALL request from %s with no "
+                       "SM_MON requests from it.", argp->my_name);
+       }
+
+       return (&result);
+}
diff --git a/utils/statd/notify.c b/utils/statd/notify.c
new file mode 100644 (file)
index 0000000..89d2946
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 1995, 1997-1999 Jeffrey A. Uphoff
+ * Modified by Olaf Kirch, Oct. 1996.
+ * Modified by H.J. Lu, 1998.
+ *
+ * NSM for Linux.
+ */
+
+/*
+ * NSM notify list handling.
+ */
+
+#include "config.h"
+
+#include <dirent.h>
+#include <errno.h>
+#include <netdb.h>
+#include <string.h>
+#include <unistd.h>
+#include "misc.h"
+#include "statd.h"
+#include "notlist.h"
+
+/*
+ * Initial (startup) notify list.
+ */
+notify_list            *inl = NULL;
+
+
+/* 
+ * Get list of hosts from stable storage, build list of hosts to
+ * contact. These hosts are added to the global RPC notify list
+ * which is processed as soon as statd enters svc_run.
+ */
+void
+notify_hosts(void)
+{
+       DIR            *nld;
+       struct dirent  *de;
+       notify_list    *call;
+
+       if (!(nld = opendir(SM_BAK_DIR))) {
+               perror("opendir");
+               exit(errno);
+       }
+
+       while ((de = readdir(nld))) {
+               if (de->d_name[0] == '.')
+                       continue;
+
+               /* The following can happen for loopback NFS mounts
+                * (e.g. with cfsd) */
+               if (matchhostname(de->d_name, MY_NAME)
+                || matchhostname(de->d_name, "localhost")) {
+                       char *fname;
+                       fname=xmalloc(strlen(SM_BAK_DIR)+sizeof(de->d_name)+2);
+                       dprintf(L_DEBUG, "We're on our own notify list?!?");
+                       sprintf(fname, SM_BAK_DIR "/%s", de->d_name);
+                       if (unlink(fname)) 
+                               log(L_ERROR, "unlink(%s): %s", 
+                                       fname, strerror(errno));
+                       free(fname);
+                       continue;
+               }
+
+               call = nlist_new(MY_NAME, de->d_name, -1);
+               NL_TYPE(call) = NOTIFY_REBOOT;
+               nlist_insert(&notify, call);
+       }
+
+       if (closedir(nld) == -1) {
+               perror("closedir");
+               exit(1);
+       }
+}
diff --git a/utils/statd/notlist.c b/utils/statd/notlist.c
new file mode 100644 (file)
index 0000000..bc0c294
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 1995, 1997-1999 Jeffrey A. Uphoff
+ * Modified by Olaf Kirch, 1996.
+ * Modified by H.J. Lu, 1998.
+ *
+ * NSM for Linux.
+ */
+
+/*
+ * Simple list management for notify list
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include "misc.h"
+#include "statd.h"
+#include "notlist.h"
+
+notify_list *
+nlist_new(char *my_name, char *mon_name, int state)
+{
+       notify_list     *new;
+
+       if (!(new = (notify_list *) xmalloc(sizeof(notify_list))))
+               return NULL;
+       memset(new, 0, sizeof(*new));
+
+       NL_TIMES(new) = MAX_TRIES;
+       NL_STATE(new) = state;
+       if (!(NL_MY_NAME(new) = xstrdup(my_name))
+        || !(NL_MON_NAME(new) = xstrdup(mon_name)))
+               return NULL;
+
+       return new;
+}
+
+void
+nlist_insert(notify_list **head, notify_list *entry)
+{
+       notify_list     *next = *head, *tail = entry;
+
+       /* Find end of list to be inserted */
+       while (tail->next)
+               tail = tail->next;
+
+       if (next)
+               next->prev = tail;
+       tail->next = next;
+       *head = entry;
+}
+
+void
+nlist_insert_timer(notify_list **head, notify_list *entry)
+{
+       /* Find first entry with higher timeout value */
+       while (*head && NL_WHEN(*head) <= NL_WHEN(entry))
+               head = &(*head)->next;
+       nlist_insert(head, entry);
+}
+
+void
+nlist_remove(notify_list **head, notify_list *entry)
+{
+       notify_list     *prev = entry->prev,
+                       *next = entry->next;
+
+       if (next)
+               next->prev = prev;
+       if (prev)
+               prev->next = next;
+       else
+               *head = next;
+       entry->next = entry->prev = NULL;
+}
+
+notify_list *
+nlist_clone(notify_list *entry)
+{
+       notify_list     *new;
+
+       new = nlist_new(NL_MY_NAME(entry), NL_MON_NAME(entry), NL_STATE(entry));
+       NL_MY_PROG(new) = NL_MY_PROG(entry);
+       NL_MY_VERS(new) = NL_MY_VERS(entry);
+       NL_MY_PROC(new) = NL_MY_PROC(entry);
+       NL_ADDR(new)    = NL_ADDR(entry);
+       memcpy(NL_PRIV(new), NL_PRIV(entry), SM_PRIV_SIZE);
+
+       return new;
+}
+
+void
+nlist_free(notify_list **head, notify_list *entry)
+{
+       if (head)
+               nlist_remove(head, entry);
+       if (NL_MY_NAME(entry))
+               free(NL_MY_NAME(entry));
+       if (NL_MON_NAME(entry))
+               free(NL_MON_NAME(entry));
+       free(entry);
+}
+
+void
+nlist_kill(notify_list **head)
+{
+       while (*head)
+               nlist_free(head, *head);
+}
+
+/*
+ * Walk a list looking for a matching name in the NL_MON_NAME field.
+ */
+notify_list *
+nlist_gethost(notify_list *list, char *host, int myname)
+{
+       notify_list     *lp;
+
+       for (lp = list; lp; lp = lp->next) {
+               if (matchhostname(host, myname? NL_MY_NAME(lp) : NL_MON_NAME(lp)))
+                       return lp;
+       }
+
+       return (notify_list *) NULL;
+}
diff --git a/utils/statd/notlist.h b/utils/statd/notlist.h
new file mode 100644 (file)
index 0000000..0c6709c
--- /dev/null
@@ -0,0 +1,111 @@
+/* 
+ * Copyright (C) 1995, 1997-1999 Jeffrey A. Uphoff
+ * Major rewrite by Olaf Kirch, Dec. 1996.
+ *
+ * NSM for Linux.
+ */
+
+#include <netinet/in.h>
+
+/*
+ * Primary information structure.
+ */
+struct notify_list {
+  mon                  mon;    /* Big honkin' NSM structure. */
+  struct in_addr       addr;   /* IP address for callback. */
+  unsigned short       port;   /* port number for callback */
+  short int            times;  /* Counter used for various things. */
+  int                  state;  /* For storing notified state for callbacks. */
+  struct notify_list   *next;  /* Linked list forward pointer. */
+  struct notify_list   *prev;  /* Linked list backward pointer. */
+  u_int32_t            xid;    /* XID of MS_NOTIFY RPC call */
+  time_t               when;   /* notify: timeout for re-xmit */
+  int                  type;   /* type of notify (REBOOT/CALLBACK) */
+};
+
+typedef struct notify_list notify_list;
+
+#define NOTIFY_REBOOT  0       /* notify remote of our reboot */
+#define NOTIFY_CALLBACK        1       /* notify client of remote reboot */
+
+/*
+ * Global Variables
+ */
+extern notify_list *   rtnl;   /* Run-time notify list */
+extern notify_list *   notify; /* Pending RPC calls */
+
+/*
+ * List-handling functions
+ */
+extern notify_list *   nlist_new(char *, char *, int);
+extern void            nlist_insert(notify_list **, notify_list *);
+extern void            nlist_remove(notify_list **, notify_list *);
+extern void            nlist_insert_timer(notify_list **, notify_list *);
+extern notify_list *   nlist_clone(notify_list *);
+extern void            nlist_free(notify_list **, notify_list *);
+extern void            nlist_kill(notify_list **);
+extern notify_list *   nlist_gethost(notify_list *, char *, int);
+
+/* 
+ * List-handling macros.
+ * THESE INHERIT INFORMATION FROM PREVIOUSLY-DEFINED MACROS.
+ * (So don't change their order unless you study them first!)
+ */
+#define NL_NEXT(L)     ((L)->next)
+#define NL_FIRST       NL_NEXT
+#define NL_PREV(L)     ((L)->prev)
+#define NL_DATA(L)     ((L)->mon)
+#define NL_ADDR(L)     ((L)->addr)
+#define NL_STATE(L)    ((L)->state)
+#define NL_TIMES(L)    ((L)->times)
+#define NL_MON_ID(L)   (NL_DATA((L)).mon_id)
+#define NL_PRIV(L)     (NL_DATA((L)).priv)
+#define NL_MON_NAME(L) (NL_MON_ID((L)).mon_name)
+#define NL_MY_ID(L)    (NL_MON_ID((L)).my_id)
+#define NL_MY_NAME(L)  (NL_MY_ID((L)).my_name)
+#define NL_MY_PROC(L)  (NL_MY_ID((L)).my_proc)
+#define NL_MY_PROG(L)  (NL_MY_ID((L)).my_prog)
+#define NL_MY_VERS(L)  (NL_MY_ID((L)).my_vers)
+#define NL_WHEN(L)     ((L)->when)
+#define NL_TYPE(L)     ((L)->type)
+
+#if 0
+#define NL_ADD_NO_ZERO(LIST, ITEM)\
+  NL_PREV(NL_FIRST((LIST))) = (ITEM);\
+  NL_NEXT((ITEM)) = NL_FIRST((LIST));\
+  NL_FIRST((LIST)) = (ITEM);\
+  NL_PREV((ITEM)) = (LIST);\
+  NL_TIMES((ITEM)) = 0;
+
+#define NL_ADD(LIST, ITEM)\
+  NL_ADD_NO_ZERO((LIST), (ITEM));\
+  NL_ADDR((ITEM)) = 0;\
+  NL_STATE((ITEM)) = 0;
+
+#define NL_DEL(ITEM)\
+  NL_NEXT(NL_PREV((ITEM))) = NL_NEXT((ITEM));\
+  NL_PREV(NL_NEXT((ITEM))) = NL_PREV((ITEM));
+
+#define NL_FREE(ITEM)\
+  if (NL_MY_NAME ((ITEM)))\
+    free (NL_MY_NAME ((ITEM)));\
+  if (NL_MON_NAME ((ITEM)))\
+    free (NL_MON_NAME((ITEM)));\
+  free ((ITEM));
+
+#define NL_DEL_FREE(ITEM)\
+  NL_DEL((ITEM))\
+  NL_FREE((ITEM))
+
+/* Yuck.  Kludge. */
+#define NL_COPY(SRC, DEST)\
+  NL_TIMES((DEST)) = NL_TIMES((SRC));\
+  NL_STATE((DEST)) = NL_TIMES((SRC));\
+  NL_MY_PROC((DEST)) = NL_MY_PROC((SRC));\
+  NL_MY_PROG((DEST)) = NL_MY_PROG((SRC));\
+  NL_MY_VERS((DEST)) = NL_MY_VERS((SRC));\
+  NL_MON_NAME((DEST)) = xstrdup (NL_MON_NAME((SRC)));\
+  NL_MY_NAME((DEST)) = xstrdup (NL_MY_NAME((SRC)));\
+  memcpy (&NL_ADDR((DEST)), &NL_ADDR((SRC)), sizeof (u_long));\
+  memcpy (NL_PRIV((DEST)), NL_PRIV((SRC)), SM_PRIV_SIZE);
+#endif
diff --git a/utils/statd/rmtcall.c b/utils/statd/rmtcall.c
new file mode 100644 (file)
index 0000000..a08c4b1
--- /dev/null
@@ -0,0 +1,406 @@
+/*
+ * Copyright (C) 1996, 1999 Olaf Kirch
+ * Modified by Jeffrey A. Uphoff, 1997-1999.
+ * Modified by H.J. Lu, 1998.
+ *
+ * NSM for Linux.
+ */
+
+/*
+ * After reboot, notify all hosts on our notify list. In order not to
+ * hang statd with delivery to dead hosts, we perform all RPC calls in
+ * parallel.
+ *
+ * It would have been nice to use the portmapper's rmtcall feature,
+ * but that's not possible for security reasons (the portmapper would
+ * have to forward the call with root privs for most statd's, which
+ * it won't if it's worth its money).
+ */
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <rpc/rpc.h>
+#include <rpc/pmap_prot.h>
+#include <rpc/pmap_rmt.h>
+#include <netdb.h>
+#include <string.h>
+#include <unistd.h>
+#include "sm_inter.h"
+#include "statd.h"
+#include "notlist.h"
+#include "log.h"
+
+#define MAXMSGSIZE     (2048 / sizeof(unsigned int))
+
+static unsigned long   xid = 0;        /* RPC XID counter */
+static int             sockfd = -1;    /* notify socket */
+
+/*
+ * Initialize callback socket
+ */
+static int
+get_socket(void)
+{
+       struct sockaddr_in      sin;
+
+       if (sockfd >= 0)
+               return sockfd;
+
+       if ((sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
+               log(L_CRIT, "Can't create socket: %m");
+               return -1;
+       }
+
+       FD_SET(sockfd, &SVC_FDSET);
+
+       memset(&sin, 0, sizeof(sin));
+       sin.sin_family = AF_INET;
+       if (bindresvport(sockfd, &sin) < 0) {
+               dprintf(L_WARNING,
+                       "process_hosts: can't bind to reserved port\n");
+       }
+
+       return sockfd;
+}
+
+/*
+ * Try to resolve host name for notify/callback request
+ *
+ * When compiled with RESTRICTED_STATD defined, we expect all
+ * host names to be dotted quads. See monitor.c for details. --okir
+ */
+#ifdef RESTRICTED_STATD
+static int
+try_to_resolve(notify_list *lp)
+{
+       char            *hname;
+
+       if (NL_TYPE(lp) == NOTIFY_REBOOT)
+               hname = NL_MON_NAME(lp);
+       else
+               hname = NL_MY_NAME(lp);
+       if (!inet_aton(hname, &(NL_ADDR(lp)))) {
+               log(L_ERROR, "%s is not an dotted-quad address", hname);
+               NL_TIMES(lp) = 0;
+               return 0;
+       }
+
+       /* XXX: In order to handle multi-homed hosts, we could do
+        * a reverse lookup, a forward lookup, and cycle through
+        * all the addresses.
+        */
+       return 1;
+}
+#else
+static int
+try_to_resolve(notify_list *lp)
+{
+       struct hostent  *hp;
+       char            *hname;
+
+       if (NL_TYPE(lp) == NOTIFY_REBOOT)
+               hname = NL_MON_NAME(lp);
+       else
+               hname = NL_MY_NAME(lp);
+
+       dprintf(L_DEBUG, "Trying to resolve %s.", hname);
+       if (!(hp = gethostbyname(hname))) {
+               herror("gethostbyname");
+               NL_TIMES(lp) -= 1;
+               return 0;
+       }
+
+       if (hp->h_addrtype != AF_INET) {
+               log(L_ERROR, "%s is not an AF_INET address", hname);
+               NL_TIMES(lp) = 0;
+               return 0;
+       }
+
+       /* FIXME: should try all addresses for multi-homed hosts in
+        * alternation because one interface might be down/unreachable. */
+       NL_ADDR(lp) = *(struct in_addr *) hp->h_addr;
+
+       dprintf(L_DEBUG, "address of %s is %s", hname, inet_ntoa(NL_ADDR(lp)));
+       return 1;
+}
+#endif
+
+static unsigned long
+xmit_call(int sockfd, struct sockaddr_in *sin,
+         u_int32_t prog, u_int32_t vers, u_int32_t proc,
+         xdrproc_t func, void *obj)
+/*             __u32 prog, __u32 vers, __u32 proc, xdrproc_t func, void *obj) */
+{
+       unsigned int            msgbuf[MAXMSGSIZE], msglen;
+       struct rpc_msg          mesg;
+       struct pmap             pmap;
+       XDR                     xdr, *xdrs = &xdr;
+       int                     err;
+
+       if (!xid)
+               xid = getpid() + time(NULL);
+
+       mesg.rm_xid = ++xid;
+       mesg.rm_direction = CALL;
+       mesg.rm_call.cb_rpcvers = 2;
+       if (sin->sin_port == 0) {
+               sin->sin_port = htons(PMAPPORT);
+               mesg.rm_call.cb_prog = PMAPPROG;
+               mesg.rm_call.cb_vers = PMAPVERS;
+               mesg.rm_call.cb_proc = PMAPPROC_GETPORT;
+               pmap.pm_prog = prog;
+               pmap.pm_vers = vers;
+               pmap.pm_prot = IPPROTO_UDP;
+               pmap.pm_port = 0;
+               func = (xdrproc_t) xdr_pmap;
+               obj  = &pmap;
+       } else {
+               mesg.rm_call.cb_prog = prog;
+               mesg.rm_call.cb_vers = vers;
+               mesg.rm_call.cb_proc = proc;
+       }
+       mesg.rm_call.cb_cred.oa_flavor = AUTH_NULL;
+       mesg.rm_call.cb_cred.oa_base = (caddr_t) NULL;
+       mesg.rm_call.cb_cred.oa_length = 0;
+       mesg.rm_call.cb_verf.oa_flavor = AUTH_NULL;
+       mesg.rm_call.cb_verf.oa_base = (caddr_t) NULL;
+       mesg.rm_call.cb_verf.oa_length = 0;
+
+       /* Create XDR memory object for encoding */
+       xdrmem_create(xdrs, (caddr_t) msgbuf, sizeof(msgbuf), XDR_ENCODE);
+
+       /* Encode the RPC header part and payload */
+       if (!xdr_callmsg(xdrs, &mesg) || !func(xdrs, obj)) {
+               dprintf(L_WARNING, "xmit_mesg: can't encode RPC message!\n");
+               xdr_destroy(xdrs);
+               return 0;
+       }
+
+       /* Get overall length of datagram */
+       msglen = xdr_getpos(xdrs);
+
+       if ((err = sendto(sockfd, msgbuf, msglen, 0,
+                       (struct sockaddr *) sin, sizeof(*sin))) < 0) {
+               dprintf(L_WARNING, "xmit_mesg: sendto failed: %m");
+       } else if (err != msglen) {
+               dprintf(L_WARNING, "xmit_mesg: short write: %m\n");
+       }
+
+       xdr_destroy(xdrs);
+
+       return err == msglen? xid : 0;
+}
+
+static notify_list *
+recv_rply(int sockfd, struct sockaddr_in *sin, u_long *portp)
+{
+       unsigned int            msgbuf[MAXMSGSIZE], msglen;
+       struct rpc_msg          mesg;
+       notify_list             *lp = NULL;
+       XDR                     xdr, *xdrs = &xdr;
+       int                     alen = sizeof(*sin);
+
+       /* Receive message */
+       if ((msglen = recvfrom(sockfd, msgbuf, sizeof(msgbuf), 0,
+                       (struct sockaddr *) sin, &alen)) < 0) {
+               dprintf(L_WARNING, "recv_rply: recvfrom failed: %m");
+               return NULL;
+       }
+
+       /* Create XDR object for decoding buffer */
+       xdrmem_create(xdrs, (caddr_t) msgbuf, msglen, XDR_DECODE);
+
+       memset(&mesg, 0, sizeof(mesg));
+       mesg.rm_reply.rp_acpt.ar_results.where = NULL;
+       mesg.rm_reply.rp_acpt.ar_results.proc = (xdrproc_t) xdr_void;
+
+       if (!xdr_replymsg(xdrs, &mesg)) {
+               log(L_WARNING, "recv_rply: can't decode RPC message!\n");
+               goto done;
+       }
+
+       if (mesg.rm_reply.rp_stat != 0) {
+               log(L_WARNING, "recv_rply: [%s] RPC status %d\n", 
+                               inet_ntoa(sin->sin_addr),
+                               mesg.rm_reply.rp_stat);
+               goto done;
+       }
+       if (mesg.rm_reply.rp_acpt.ar_stat != 0) {
+               log(L_WARNING, "recv_rply: [%s] RPC status %d\n",
+                               inet_ntoa(sin->sin_addr),
+                               mesg.rm_reply.rp_acpt.ar_stat);
+               goto done;
+       }
+
+       for (lp = notify; lp != NULL; lp = lp->next) {
+               if (lp->xid != xid)
+                       continue;
+               if (lp->addr.s_addr != sin->sin_addr.s_addr) {
+                       char addr [18];
+                       strncpy (addr, inet_ntoa(lp->addr),
+                                sizeof (addr) - 1);
+                       addr [sizeof (addr) - 1] = '\0';
+                       dprintf(L_WARNING, "address mismatch: "
+                               "expected %s, got %s\n",
+                               addr, inet_ntoa(sin->sin_addr));
+               }
+               if (lp->port == 0) {
+                       if (!xdr_u_long(xdrs, portp)) {
+                               log(L_WARNING, "recv_rply: [%s] "
+                                       "can't decode reply body!\n",
+                                       inet_ntoa(sin->sin_addr));
+                               lp = NULL;
+                               goto done;
+                       }
+               }
+               break;
+       }
+
+done:
+       xdr_destroy(xdrs);
+       return lp;
+}
+
+/*
+ * Notify operation for a single list entry
+ */
+static int
+process_entry(int sockfd, notify_list *lp)
+{
+       struct sockaddr_in      sin;
+       struct status           new_status;
+       xdrproc_t               func;
+       void                    *objp;
+       u_int32_t               proc, vers, prog;
+/*     __u32                   proc, vers, prog; */
+
+       if (lp->addr.s_addr == INADDR_ANY && !try_to_resolve(lp))
+               return NL_TIMES(lp);
+       if (NL_TIMES(lp) == 0) {
+               log(L_DEBUG, "Cannot notify %s, giving up.\n",
+                                       inet_ntoa(NL_ADDR(lp)));
+               return 0;
+       }
+
+       memset(&sin, 0, sizeof(sin));
+       sin.sin_family = AF_INET;
+       sin.sin_port   = lp->port;
+       sin.sin_addr   = lp->addr;
+
+       switch (NL_TYPE(lp)) {
+       case NOTIFY_REBOOT:
+               prog = SM_PROG;
+               vers = SM_VERS;
+               proc = SM_NOTIFY;
+               func = (xdrproc_t) xdr_stat_chge;
+               objp = &SM_stat_chge;
+               break;
+       case NOTIFY_CALLBACK:
+               prog = NL_MY_PROG(lp);
+               vers = NL_MY_VERS(lp);
+               proc = NL_MY_PROC(lp);
+               func = (xdrproc_t) xdr_status;
+               objp = &new_status;
+               new_status.mon_name = NL_MON_NAME(lp);
+               new_status.state    = NL_STATE(lp);
+               memcpy(new_status.priv, NL_PRIV(lp), SM_PRIV_SIZE);
+               break;
+       default:
+               log(L_ERROR, "notify_host: unknown notify type %d",
+                               NL_TYPE(lp));
+               return 0;
+       }
+
+       lp->xid = xmit_call(sockfd, &sin, prog, vers, proc, func, objp);
+       if (!lp->xid) {
+               log(L_WARNING, "notify_host: failed to notify %s\n",
+                               inet_ntoa(lp->addr));
+       }
+       NL_TIMES(lp) -= 1;
+
+       return 1;
+}
+
+/*
+ * Process a datagram received on the notify socket
+ */
+int
+process_reply(FD_SET_TYPE *rfds)
+{
+       struct sockaddr_in      sin;
+       notify_list             *lp;
+       u_long                  port;
+
+       if (sockfd == -1 || !FD_ISSET(sockfd, rfds))
+               return 0;
+
+       if (!(lp = recv_rply(sockfd, &sin, &port)))
+               return 1;
+
+       if (lp->port == 0) {
+               if (port != 0) {
+                       lp->port = htons((unsigned short) port);
+                       process_entry(sockfd, lp);
+                       NL_WHEN(lp) = time(NULL) + NOTIFY_TIMEOUT;
+                       nlist_remove(&notify, lp);
+                       nlist_insert_timer(&notify, lp);
+                       return 1;
+               }
+               log(L_WARNING, "recv_rply: [%s] service %d not registered",
+                       inet_ntoa(lp->addr),
+                       NL_TYPE(lp) == NOTIFY_REBOOT? SM_PROG : NL_MY_PROG(lp));
+       } else if (NL_TYPE(lp) == NOTIFY_REBOOT) {
+               dprintf(L_DEBUG, "Notification of %s succeeded.",
+                       NL_MON_NAME(lp));
+               xunlink(SM_BAK_DIR, NL_MON_NAME(lp), 0);
+       } else {
+               dprintf(L_DEBUG, "Callback to %s (for %d) succeeded.",
+                       NL_MY_NAME(lp), NL_MON_NAME(lp));
+       }
+       nlist_free(&notify, lp);
+       return 1;
+}
+
+/*
+ * Process a notify list, either for notifying remote hosts after reboot
+ * or for calling back (local) statd clients when the remote has notified
+ * us of a crash. 
+ */
+int
+process_notify_list(void)
+{
+       notify_list     *entry;
+       time_t          now;
+       int             fd;
+
+       if ((fd = get_socket()) < 0)
+               return 0;
+
+       while ((entry = notify) != NULL && NL_WHEN(entry) < time(&now)) {
+               if (process_entry(fd, entry)) {
+                       NL_WHEN(entry) = time(NULL) + NOTIFY_TIMEOUT;
+                       nlist_remove(&notify, entry);
+                       nlist_insert_timer(&notify, entry);
+               } else if (NL_TYPE(entry) == NOTIFY_CALLBACK) {
+                       log(L_ERROR,
+                               "Can't callback %s (%d,%d), giving up.",
+                                       NL_MY_NAME(entry),
+                                       NL_MY_PROG(entry),
+                                       NL_MY_VERS(entry));
+                       nlist_free(&notify, entry);
+               } else {
+                       log(L_ERROR,
+                               "Can't notify %s, giving up.",
+                                       NL_MON_NAME(entry));
+                       xunlink(SM_BAK_DIR, NL_MON_NAME(entry), 0);
+                       nlist_free(&notify, entry);
+               }
+       }
+
+       return 1;
+}
diff --git a/utils/statd/sim_sm_inter.x b/utils/statd/sim_sm_inter.x
new file mode 100644 (file)
index 0000000..4346199
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 1995, 1997-1999 Jeffrey A. Uphoff
+ * Modified by Olaf Kirch, 1996.
+ * Modified by H.J. Lu, 1998.
+ *
+ * NSM for Linux.
+ */
+
+#ifdef RPC_CLNT
+%#include <string.h>
+#endif
+
+program SIM_SM_PROG { 
+       version SIM_SM_VERS  {
+               void                     SIM_SM_MON(struct status) = 1;
+       } = 1;
+} = 200048;
+
+const  SM_MAXSTRLEN = 1024;
+const  SM_PRIV_SIZE = 16;
+
+/* 
+ * structure of the status message sent back by the status monitor
+ * when monitor site status changes
+ */
+%#ifndef SM_INTER_X
+struct status {
+       string mon_name<SM_MAXSTRLEN>;
+       int state;
+       opaque priv[SM_PRIV_SIZE]; /* stored private information */
+};
+%#endif /* SM_INTER_X */
diff --git a/utils/statd/simu.c b/utils/statd/simu.c
new file mode 100644 (file)
index 0000000..fa4e3a6
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 1995, 1997-1999 Jeffrey A. Uphoff
+ *
+ * NSM for Linux.
+ */
+
+#include "config.h"
+#include "statd.h"
+#include "notlist.h"
+
+extern void my_svc_exit (void);
+
+
+/*
+ * Services SM_SIMU_CRASH requests.
+ */
+void *
+sm_simu_crash_1_svc (void *argp, struct svc_req *rqstp)
+{
+  static char *result = NULL;
+
+  log (L_WARNING, "*** SIMULATING CRASH! ***");
+  my_svc_exit ();
+
+  if (rtnl)
+    nlist_kill (&rtnl);
+
+  return ((void *)&result);
+}
diff --git a/utils/statd/simulate.c b/utils/statd/simulate.c
new file mode 100644 (file)
index 0000000..4b8d59c
--- /dev/null
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 1995-1997, 1999 Jeffrey A. Uphoff
+ *
+ * NSM for Linux.
+ */
+
+#include "config.h"
+#ifndef SIMULATIONS
+# error How the hell did we get here?
+#endif
+
+/* If we're running the simulator, we're debugging.  Pretty simple. */
+#ifndef DEBUG
+# define DEBUG
+#endif
+
+#include <signal.h>
+#include <string.h>
+#include <rpc/rpc.h>
+#include <rpc/pmap_clnt.h>
+#include "statd.h"
+#include "sim_sm_inter.h"
+
+static void daemon_simulator (void);
+static void sim_killer (int sig);
+static void simulate_crash (char *);
+static void simulate_mon (char *, char *, char *, char *, char *);
+static void simulate_stat (char *, char *);
+static void simulate_unmon (char *, char *, char *, char *);
+static void simulate_unmon_all (char *, char *, char *);
+
+static int sim_port = 0;
+
+extern void sim_sm_prog_1 (struct svc_req *, register SVCXPRT);
+extern void svc_exit (void);
+
+void
+simulator (int argc, char **argv)
+{
+  log_enable (1);
+
+  if (argc == 2)
+    if (!strcasecmp (*argv, "crash"))
+      simulate_crash (*(&argv[1]));
+
+  if (argc == 3) {
+    if (!strcasecmp (*argv, "stat"))
+      simulate_stat (*(&argv[1]), *(&argv[2]));
+  }
+  if (argc == 4) {
+    if (!strcasecmp (*argv, "unmon_all"))
+      simulate_unmon_all (*(&argv[1]), *(&argv[2]), *(&argv[3]));
+  }
+  if (argc == 5) {
+    if (!strcasecmp (*argv, "unmon"))
+      simulate_unmon (*(&argv[1]), *(&argv[2]), *(&argv[3]), *(&argv[4]));
+  }
+  if (argc == 6) {
+    if (!strcasecmp (*argv, "mon"))
+      simulate_mon (*(&argv[1]), *(&argv[2]), *(&argv[3]), *(&argv[4]),
+                   *(&argv[5]));
+  }
+  die ("WTF?  Give me something I can use!");
+}
+
+static void
+simulate_mon (char *calling, char *monitoring, char *as, char *proggy,
+             char *fool)
+{
+  CLIENT *client;
+  sm_stat_res *result;
+  mon mon;
+
+  dprintf (L_DEBUG, "Calling %s (as %s) to monitor %s", calling, as,
+          monitoring);
+
+  if ((client = clnt_create (calling, SM_PROG, SM_VERS, "udp")) == NULL)
+    die ("%s", clnt_spcreateerror ("clnt_create"));
+
+  memcpy (mon.priv, fool, SM_PRIV_SIZE);
+  mon.mon_id.my_id.my_name = xstrdup (as);
+  sim_port = atoi (proggy) * SIM_SM_PROG;
+  mon.mon_id.my_id.my_prog = sim_port; /* Pseudo-dummy */
+  mon.mon_id.my_id.my_vers = SIM_SM_VERS;
+  mon.mon_id.my_id.my_proc = SIM_SM_MON;
+  mon.mon_id.mon_name = monitoring;
+
+  if (!(result = sm_mon_1 (&mon, client)))
+    die ("%s", clnt_sperror (client, "sm_mon_1"));
+
+  free (mon.mon_id.my_id.my_name);
+
+  if (result->res_stat != STAT_SUCC) {
+    log (L_FATAL, "SM_MON request failed, state: %d", result->state);
+    exit (0);
+  } else {
+    dprintf (L_DEBUG, "SM_MON result successful, state: %d\n", result->state);
+    dprintf (L_DEBUG, "Waiting for callback.");
+    daemon_simulator ();
+    exit (0);
+  }
+}
+
+static void
+simulate_unmon (char *calling, char *unmonitoring, char *as, char *proggy)
+{
+  CLIENT *client;
+  sm_stat *result;
+  mon_id mon_id;
+
+  dprintf (L_DEBUG, "Calling %s (as %s) to unmonitor %s", calling, as,
+          unmonitoring);
+
+  if ((client = clnt_create (calling, SM_PROG, SM_VERS, "udp")) == NULL)
+    die ("%s", clnt_spcreateerror ("clnt_create"));
+
+  mon_id.my_id.my_name = xstrdup (as);
+  mon_id.my_id.my_prog = atoi (proggy) * SIM_SM_PROG;
+  mon_id.my_id.my_vers = SIM_SM_VERS;
+  mon_id.my_id.my_proc = SIM_SM_MON;
+  mon_id.mon_name = unmonitoring;
+
+  if (!(result = sm_unmon_1 (&mon_id, client)))
+    die ("%s", clnt_sperror (client, "sm_unmon_1"));
+
+  free (mon_id.my_id.my_name);
+  dprintf (L_DEBUG, "SM_UNMON request returned state: %d\n", result->state);
+  exit (0);
+}
+
+static void
+simulate_unmon_all (char *calling, char *as, char *proggy)
+{
+  CLIENT *client;
+  sm_stat *result;
+  my_id my_id;
+
+  dprintf (L_DEBUG, "Calling %s (as %s) to unmonitor all hosts", calling, as);
+
+  if ((client = clnt_create (calling, SM_PROG, SM_VERS, "udp")) == NULL)
+    die ("%s", clnt_spcreateerror ("clnt_create"));
+
+  my_id.my_name = xstrdup (as);
+  my_id.my_prog = atoi (proggy) * SIM_SM_PROG;
+  my_id.my_vers = SIM_SM_VERS;
+  my_id.my_proc = SIM_SM_MON;
+
+  if (!(result = sm_unmon_all_1 (&my_id, client)))
+    die ("%s", clnt_sperror (client, "sm_unmon_all_1"));
+
+  free (my_id.my_name);
+  dprintf (L_DEBUG, "SM_UNMON_ALL request returned state: %d\n", result->state);
+  exit (0);
+}
+
+static void
+simulate_crash (char *host)
+{
+  CLIENT *client;
+
+  if ((client = clnt_create (host, SM_PROG, SM_VERS, "udp")) == NULL)
+    die ("%s", clnt_spcreateerror ("clnt_create"));
+
+  if (!sm_simu_crash_1 (NULL, client))
+    die ("%s", clnt_sperror (client, "sm_simu_crash_1"));
+
+  exit (0);
+}
+
+static void
+simulate_stat (char *calling, char *monitoring)
+{
+  CLIENT *client;
+  sm_name checking;
+  sm_stat_res *result;
+  
+  if ((client = clnt_create (calling, SM_PROG, SM_VERS, "udp")) == NULL)
+    die ("%s", clnt_spcreateerror ("clnt_create"));
+
+  checking.mon_name = monitoring;
+
+  if (!(result = sm_stat_1 (&checking, client)))
+    die ("%s", clnt_sperror (client, "sm_stat_1"));
+
+  if (result->res_stat == STAT_SUCC)
+    dprintf (L_DEBUG, "STAT_SUCC from %s for %s, state: %d", calling,
+            monitoring, result->state);
+  else
+    dprintf (L_DEBUG, "STAT_FAIL from %s for %s, state: %d", calling,
+            monitoring, result->state);
+
+  exit (0);
+}
+
+static void
+sim_killer (int sig)
+{
+  log (L_FATAL, "Simulator caught signal %d, un-registering and exiting.", sig);
+  pmap_unset (sim_port, SIM_SM_VERS);
+  exit (0);
+}
+
+static void
+daemon_simulator (void)
+{
+  signal (SIGHUP, sim_killer);
+  signal (SIGINT, sim_killer);
+  signal (SIGTERM, sim_killer);
+  pmap_unset (sim_port, SIM_SM_VERS);
+  do_regist (sim_port, sim_sm_prog_1);
+/*   do_regist (sim_port, (__dispatch_fn_t)sim_sm_prog_1); */
+  svc_run ();
+  pmap_unset (sim_port, SIM_SM_VERS);
+}
+
+void *
+sim_sm_mon_1_svc (struct status *argp, struct svc_req *rqstp)
+{
+  static char *result;
+
+  dprintf (L_DEBUG, "Recieved state %d for mon_name %s (opaque \"%s\")",
+          argp->state, argp->mon_name, argp->priv);
+  svc_exit ();
+  return ((void *)&result);
+}
diff --git a/utils/statd/sm_inter.x b/utils/statd/sm_inter.x
new file mode 100644 (file)
index 0000000..5232a28
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 1986 Sun Microsystems, Inc.
+ * Modified by Jeffrey A. Uphoff, 1995, 1997-1999.
+ * Modified by Olaf Kirch, 1996.
+ * Modified by H.J. Lu, 1998.
+ *
+ * NSM for Linux.
+ */
+
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part.  Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ * 
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ * 
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ * 
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ * 
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ * 
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California  94043
+ */
+
+/*
+ * Status monitor protocol specification
+ */
+
+#ifdef RPC_CLNT
+%#include <string.h>
+#endif
+
+program SM_PROG { 
+       version SM_VERS  {
+               /* res_stat = stat_succ if status monitor agrees to monitor */
+               /* res_stat = stat_fail if status monitor cannot monitor */
+               /* if res_stat == stat_succ, state = state number of site sm_name */
+               struct sm_stat_res                       SM_STAT(struct sm_name) = 1;
+
+               /* res_stat = stat_succ if status monitor agrees to monitor */
+               /* res_stat = stat_fail if status monitor cannot monitor */
+               /* stat consists of state number of local site */
+               struct sm_stat_res                       SM_MON(struct mon) = 2;
+
+               /* stat consists of state number of local site */
+               struct sm_stat                           SM_UNMON(struct mon_id) = 3;
+
+               /* stat consists of state number of local site */
+               struct sm_stat                           SM_UNMON_ALL(struct my_id) = 4;
+
+               void                                     SM_SIMU_CRASH(void) = 5;
+
+               void                                     SM_NOTIFY(struct stat_chge) = 6;
+
+       } = 1;
+} = 100024;
+
+const  SM_MAXSTRLEN = 1024;
+const  SM_PRIV_SIZE = 16;
+
+struct sm_name {
+       string mon_name<SM_MAXSTRLEN>;
+};
+
+struct my_id {
+       string   my_name<SM_MAXSTRLEN>;         /* name of the site iniates the monitoring request*/
+       int     my_prog;                        /* rpc program # of the requesting process */
+       int     my_vers;                        /* rpc version # of the requesting process */
+       int     my_proc;                        /* rpc procedure # of the requesting process */
+};
+
+struct mon_id {
+       string  mon_name<SM_MAXSTRLEN>;         /* name of the site to be monitored */
+       struct my_id my_id;
+};
+
+
+struct mon {
+       struct mon_id mon_id;
+       opaque priv[SM_PRIV_SIZE];              /* private information to store at monitor for requesting process */
+};
+
+struct stat_chge {
+       string  mon_name<SM_MAXSTRLEN>;         /* name of the site that had the state change */
+       int     state;
+};
+
+/*
+ * state # of status monitor monitonically increases each time
+ * status of the site changes:
+ * an even number (>= 0) indicates the site is down and
+ * an odd number (> 0) indicates the site is up;
+ */
+struct sm_stat {
+       int state;              /* state # of status monitor */
+};
+
+enum res {
+       stat_succ = 0,          /* status monitor agrees to monitor */
+       stat_fail = 1           /* status monitor cannot monitor */
+};
+
+struct sm_stat_res {
+       res res_stat;
+       int state;
+};
+
+/* 
+ * structure of the status message sent back by the status monitor
+ * when monitor site status changes
+ */
+struct status {
+       string mon_name<SM_MAXSTRLEN>;
+       int state;
+       opaque priv[SM_PRIV_SIZE]; /* stored private information */
+};
+
+%#define SM_INTER_X
diff --git a/utils/statd/stat.c b/utils/statd/stat.c
new file mode 100644 (file)
index 0000000..021e786
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 1995, 1997, 1999 Jeffrey A. Uphoff
+ * Modified by Olaf Kirch, 1996.
+ *
+ * NSM for Linux.
+ */
+
+#include "config.h"
+#include <netdb.h>
+#include "statd.h"
+
+/* 
+ * Services SM_STAT requests.
+ *
+ * According the the X/Open spec's on this procedure: "Implementations
+ * should not rely on this procedure being operative.  In many current
+ * implementations of the NSM it will always return a 'STAT_FAIL'
+ * status."  My implementation is operative; it returns 'STAT_SUCC'
+ * whenever it can resolve the hostname that it's being asked to
+ * monitor, and returns 'STAT_FAIL' otherwise.
+ */
+struct sm_stat_res * 
+sm_stat_1_svc (struct sm_name *argp, struct svc_req *rqstp)
+{
+  static sm_stat_res result;
+
+  if (gethostbyname (argp->mon_name) == NULL) {
+    log (L_WARNING, "gethostbyname error for %s", argp->mon_name);
+    result.res_stat = STAT_FAIL;
+    dprintf (L_DEBUG, "STAT_FAIL for %s", argp->mon_name);
+  } else {
+    result.res_stat = STAT_SUCC;
+    dprintf (L_DEBUG, "STAT_SUCC for %s", argp->mon_name);
+  }
+  result.state = MY_STATE;
+  return(&result);
+}
diff --git a/utils/statd/statd.c b/utils/statd/statd.c
new file mode 100644 (file)
index 0000000..3b76e30
--- /dev/null
@@ -0,0 +1,122 @@
+/* 
+ * Copyright (C) 1995, 1997-1999 Jeffrey A. Uphoff
+ * Modified by Olaf Kirch, Oct. 1996.
+ * Modified by H.J. Lu, 1998.
+ *
+ * NSM for Linux.
+ */
+
+#include "config.h"
+#include <limits.h>
+#include <signal.h>
+#include <unistd.h>
+#include <string.h>
+#include <rpc/rpc.h>
+#include <rpc/pmap_clnt.h>
+#include "statd.h"
+#include "version.h"
+
+short int restart = 0;
+int    _rpcpmstart = 0;        /* flags for tirpc rpcgen */
+int    _rpcfdtype = 0;
+int    _rpcsvcdirty = 0;
+
+extern void sm_prog_1 (struct svc_req *, register SVCXPRT);
+
+#ifdef SIMULATIONS
+extern void simulator (int, char **);
+#endif
+
+
+/*
+ * Signal handler.
+ */
+static void 
+killer (int sig)
+{
+  log (L_FATAL, "Caught signal %d, un-registering and exiting.", sig);
+  pmap_unset (SM_PROG, SM_VERS);
+  exit (0);
+}
+
+
+/* 
+ * Entry routine/main loop.
+ */
+int
+main (int argc, char **argv)
+{
+  int pid;
+  int foreground = 0;
+
+  log_init (argv[0]);
+
+  if (argc == 2 && strcmp (argv [1], "-F") == 0) {
+    foreground = 1;
+    argc--;
+    argv++;
+  }
+
+#ifdef SIMULATIONS
+  if (argc > 1)
+    simulator (--argc, ++argv);        /* simulator() does exit() */
+#endif
+  
+  if (!foreground) {
+    int filedes;
+
+    if ((pid = fork ()) < 0) {
+      perror ("Could not fork");
+      exit (1);
+    } else if (pid != 0) {
+      /* Parent. */
+      exit (0);
+    }
+    /* Child.  */
+    setsid ();
+    chdir (DIR_BASE);
+
+    for (filedes = 0; filedes < OPEN_MAX; filedes++) {
+      close (filedes);
+    }
+  }
+
+  /* Child. */
+  signal (SIGHUP, killer);
+  signal (SIGINT, killer);
+  signal (SIGTERM, killer);
+
+  for (;;) {
+    pmap_unset (SM_PROG, SM_VERS);
+    change_state ();
+    shuffle_dirs ();
+    notify_hosts ();
+    ++restart;
+    do_regist (SM_PROG, sm_prog_1);
+    my_svc_run ();             /* I rolled my own, Olaf made it better... */
+  }
+  return 0;
+}
+
+
+/*
+ * Register services.
+ */
+void
+do_regist(u_long prog, void (*sm_prog_1)())
+/* do_regist(u_long prog, __dispatch_fn_t sm_prog_1) */
+{
+  SVCXPRT        *transp;
+
+  if ((transp = svcudp_create(RPC_ANYSOCK)) == NULL)
+    die("cannot create udp service.");
+
+  if (!svc_register(transp, prog, SM_VERS, sm_prog_1, IPPROTO_UDP))
+    die("unable to register (SM_PROG, SM_VERS, udp).");
+
+  if ((transp = svctcp_create(RPC_ANYSOCK, 0, 0)) == NULL)
+    die("cannot create tcp service.");
+
+  if (!svc_register(transp, prog, SM_VERS, sm_prog_1, IPPROTO_TCP))
+    die("unable to register (SM_PROG, SM_VERS, tcp).");
+}
diff --git a/utils/statd/statd.h b/utils/statd/statd.h
new file mode 100644 (file)
index 0000000..77a179a
--- /dev/null
@@ -0,0 +1,58 @@
+/* 
+ * Copyright (C) 1995-1997, 1999 Jeffrey A. Uphoff
+ * Modified by Olaf Kirch, Dec. 1996.
+ *
+ * NSM for Linux.
+ */
+
+#include "config.h"
+#include "sm_inter.h"
+#include "system.h"
+#include "log.h"
+
+/*
+ * Paths and filenames.
+ */
+#if defined(NFS_STATEDIR)
+# define DIR_BASE      NFS_STATEDIR "/"
+#else
+# define DIR_BASE      "/var/lib/nfs/"
+#endif
+#define SM_DIR         DIR_BASE "sm"
+#define SM_BAK_DIR     DIR_BASE "sm.bak"
+#define SM_STAT_PATH   DIR_BASE "state"
+
+/*
+ * Status definitions.
+ */
+#define STAT_FAIL      stat_fail
+#define STAT_SUCC      stat_succ
+
+/*
+ * Function prototypes.
+ */
+extern void    change_state(void);
+extern void    do_regist(u_long, void (*)());
+extern void    my_svc_run(void);
+extern void    notify_hosts(void);
+extern void    shuffle_dirs(void);
+extern int     process_notify_list(void);
+extern int     process_reply(FD_SET_TYPE *);
+extern char *  xstrdup(const char *);
+extern void *  xmalloc(size_t);
+extern void    xunlink (char *, char *, short int);
+
+/*
+ * Host status structure and macros.
+ */
+stat_chge              SM_stat_chge;
+#define MY_NAME                SM_stat_chge.mon_name
+#define MY_STATE       SM_stat_chge.state
+
+/*
+ * Some timeout values.  (Timeout values are in whole seconds.)
+ */
+#define CALLBACK_TIMEOUT        3 /* For client call-backs. */
+#define NOTIFY_TIMEOUT          5 /* For status-change notifications. */
+#define SELECT_TIMEOUT         10 /* Max select() timeout when work to do. */
+#define MAX_TRIES               5 /* Max number of tries for any host. */
diff --git a/utils/statd/statd.man b/utils/statd/statd.man
new file mode 100644 (file)
index 0000000..373cf77
--- /dev/null
@@ -0,0 +1,53 @@
+.\"
+.\" statd(8)
+.\"
+.\" Copyright (C) 1999 Olaf Kirch <okir@monad.swb.de>
+.\" Modified by Jeffrey A. Uphoff, 1999.
+.TH rpc.statd 8 "11 June 1999"
+.SH NAME
+rpc.statd \- NSM status monitor
+.SH SYNOPSIS
+.B "/usr/sbin/rpc.statd [-F]
+.SH DESCRIPTION
+The
+.B rpc.statd
+server implements the NSM (Network Status Monitor) RPC protocol.
+This service is somewhat misnomed, since it doesn't actually provide
+active monitoring as one might suspect; instead, NSM implements a
+reboot notification service. It is used by the NFS file locking service,
+.BR rpc.lockd ,
+to implement lock recovery when the NFS server machine crashes and
+reboots.
+.SS Operation
+For each NFS client or server machine to be monitored,
+.B rpc.statd
+creates a file in
+.BR /var/lib/nfs/sm .
+When starting, it iterates through these files and notifies the
+peer
+.B rpc.statd
+on those machines.
+.SH OPTIONS
+.TP
+.B -F
+By default,
+.B rpc.statd
+forks and puts itself in the background when started. The
+.B -F
+argument tells it to remain in the foreground. This option is
+mainly for debugging purposes.
+.SH FILES
+.BR /var/lib/nfs/sm/state
+.br
+.BR /var/lib/nfs/sm/*
+.br
+.BR /var/lib/nfs/sm.bak/*
+.SH SEE ALSO
+.BR rpc.nfsd(8)
+.SH AUTHORS
+.br
+Jeff Uphoff <juphoff@transmeta.com>
+.br
+Olaf Kirch <okir@monad.swb.de>
+.br
+H.J. Lu <hjl@gnu.org>
diff --git a/utils/statd/state.c b/utils/statd/state.c
new file mode 100644 (file)
index 0000000..101c00b
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 1995-1997, 1999 Jeffrey A. Uphoff
+ * Modified by Olaf Kirch, 1996.
+ * Modified by H.J. Lu, 1998.
+ *
+ * NSM for Linux.
+ */
+
+#include "config.h"
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include "statd.h"
+
+
+/* 
+ * Most NSM's keep the status number in an ASCII file.  I'm keeping it
+ * as an int (4-byte binary) for now...
+ */
+void
+change_state (void)
+{
+  int fd, size;
+  extern short int restart;
+  
+  if ((fd = open (SM_STAT_PATH, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR)) == -1)
+    die ("open (%s): %s", SM_STAT_PATH, strerror (errno));
+
+  if ((size = read (fd, &MY_STATE, sizeof MY_STATE)) == -1)
+    die ("read (%s): %s", SM_STAT_PATH, strerror (errno));
+
+  if (size != 0 && size != sizeof MY_STATE) {
+    log (L_ERROR, "Error in status file format...correcting.");
+
+    if (close (fd) == -1)
+      die ("close (%s): %s", SM_STAT_PATH, strerror (errno));
+
+    if ((fd = creat (SM_STAT_PATH, S_IRUSR | S_IWUSR)) == -1)
+      die ("creat (%s): %s", SM_STAT_PATH, strerror (errno));
+  }
+  log (L_DEBUG, "New state: %u", (++MY_STATE % 2) ? MY_STATE : ++MY_STATE);
+
+  if (lseek (fd, 0, SEEK_SET) == -1)
+    die ("lseek (%s): %s", SM_STAT_PATH, strerror (errno));
+
+  if (write (fd, &MY_STATE, sizeof MY_STATE) != sizeof MY_STATE)
+    die ("write (%s): %s", SM_STAT_PATH, strerror (errno));
+
+  if (fsync (fd) == -1)
+    log (L_ERROR, "fsync (%s): %s", SM_STAT_PATH, strerror (errno));
+
+  if (close (fd) == -1)
+    log (L_ERROR, "close (%s): %s", SM_STAT_PATH, strerror (errno));
+
+  if (!restart) {
+    char fullhost[SM_MAXSTRLEN + 1];
+    struct hostent *hostinfo;
+
+    if (gethostname (fullhost, SM_MAXSTRLEN) == -1)
+      die ("gethostname: %s", strerror (errno));
+
+    if ((hostinfo = gethostbyname (fullhost)) == NULL)
+      log (L_ERROR, "gethostbyname error for %s", fullhost);
+    else {
+      strncpy (fullhost, hostinfo->h_name, sizeof (fullhost) - 1);
+      fullhost[sizeof (fullhost) - 1] = '\0';
+    }
+
+    MY_NAME = xstrdup (fullhost);
+  }
+}
+
+
+/* 
+ * Fairly traditional use of two directories for this.
+ */
+void 
+shuffle_dirs (void)
+{
+  DIR *nld;
+  struct dirent *de;
+  struct stat st;
+  char *src, *dst;
+  int len1, len2, len;
+  
+  if (stat (SM_DIR, &st) == -1 && errno != ENOENT)
+    die ("stat (%s): %s", SM_DIR, strerror (errno));
+
+  if (!S_ISDIR (st.st_mode))
+    if (mkdir (SM_DIR, S_IRWXU) == -1)
+      die ("mkdir (%s): %s", SM_DIR, strerror (errno));
+
+  memset (&st, 0, sizeof st);
+
+  if (stat (SM_BAK_DIR, &st) == -1 && errno != ENOENT)
+    die ("stat (%s): %s", SM_BAK_DIR, strerror (errno));
+
+  if (!S_ISDIR (st.st_mode))
+    if (mkdir (SM_BAK_DIR, S_IRWXU) == -1)
+      die ("mkdir (%s): %s", SM_BAK_DIR, strerror (errno));
+
+  if (!(nld = opendir (SM_DIR)))
+    die ("opendir (%s): %s", SM_DIR, strerror (errno));
+
+  len1=strlen(SM_DIR);
+  len2=strlen(SM_BAK_DIR);
+  while ((de = readdir (nld))) {
+    if (de->d_name[0] == '.')
+      continue;
+    len=strlen(de->d_name);
+    src=xmalloc(len1+len+2);
+    dst=xmalloc(len2+len+2);
+    sprintf (src, "%s/%s", SM_DIR, de->d_name);
+    sprintf (dst, "%s/%s", SM_BAK_DIR, de->d_name);
+    if (rename (src, dst) == -1)
+      die ("rename (%s to %s): %s", SM_DIR, SM_BAK_DIR, strerror (errno));
+    free(src);
+    free(dst);
+  }
+  if (closedir (nld) == -1)
+    log (L_ERROR, "closedir (%s): %s", SM_DIR, strerror (errno));
+}
diff --git a/utils/statd/svc_run.c b/utils/statd/svc_run.c
new file mode 100644 (file)
index 0000000..8f6d9fe
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 1984 Sun Microsystems, Inc.
+ * Modified by Jeffrey A. Uphoff, 1995, 1997-1999.
+ * Modified by Olaf Kirch, 1996.
+ *
+ * NSM for Linux.
+ */
+
+/* 
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part.  Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ * 
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ * 
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ * 
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ * 
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ * 
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California  94043
+ */
+
+/* 
+ * This has been modified for my own evil purposes to prevent deadlocks
+ * when two hosts start NSM's simultaneously and try to notify each
+ * other (which mainly occurs during testing), or to stop and smell the
+ * roses when I have callbacks due.
+ * --Jeff Uphoff.
+ */
+
+/* 
+ * This is the RPC server side idle loop.
+ * Wait for input, call server program.
+ */
+#include "config.h"
+#include <errno.h>
+#include "statd.h"
+#include "notlist.h"
+
+static int     svc_stop = 0;
+
+/*
+ * This is the global notify list onto which all SM_NOTIFY and CALLBACK
+ * requests are put.
+ */
+notify_list *  notify = NULL;
+
+/*
+ * Jump-off function.
+ */
+void
+my_svc_exit(void)
+{
+       svc_stop = 1;
+}
+
+
+/*
+ * The heart of the server.  A crib from libc for the most part...
+ */
+void
+my_svc_run(void)
+{
+       FD_SET_TYPE     readfds;
+       int             selret;
+       time_t          now;
+
+       svc_stop = 0;
+
+       for (;;) {
+               if (svc_stop)
+                       return;
+
+               /* Ah, there are some notifications to be processed */
+               while (notify && NL_WHEN(notify) <= time(&now)) {
+                       process_notify_list();
+               }
+
+               readfds = SVC_FDSET;
+               if (notify) {
+                       struct timeval  tv;
+
+                       tv.tv_sec  = NL_WHEN(notify) - now;
+                       tv.tv_usec = 0;
+                       dprintf(L_DEBUG, "Waiting for reply... (timeo %d)",
+                                                       tv.tv_sec);
+                       selret = select(FD_SETSIZE, &readfds,
+                               (void *) 0, (void *) 0, &tv);
+               } else {
+                       dprintf(L_DEBUG, "Waiting for client connections.");
+                       selret = select(FD_SETSIZE, &readfds,
+                               (void *) 0, (void *) 0, (struct timeval *) 0);
+               }
+
+               switch (selret) {
+               case -1:
+                       if (errno == EINTR || errno == ECONNREFUSED
+                        || errno == ENETUNREACH || errno == EHOSTUNREACH)
+                               continue;
+                       log(L_ERROR, "my_svc_run() - select: %m");
+                       return;
+
+               case 0:
+                       /* A notify/callback timed out. */
+                       continue;
+
+               default:
+                       selret -= process_reply(&readfds);
+                       if (selret)
+                               svc_getreqset(&readfds);
+               }
+       }
+}
diff --git a/utils/statd/system.h b/utils/statd/system.h
new file mode 100644 (file)
index 0000000..a1739c4
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 1996 Olaf Kirch
+ * Modified by Jeffrey A. Uphoff, 1997, 1999.
+ *
+ * NSM for Linux.
+ */
+
+/*
+ * System-dependent declarations
+ */
+
+#ifdef FD_SETSIZE
+# define FD_SET_TYPE   fd_set
+# define SVC_FDSET     svc_fdset
+#else
+# define FD_SET_TYPE   int
+# define SVC_FDSET     svc_fds
+#endif
diff --git a/utils/statd/version.h b/utils/statd/version.h
new file mode 100644 (file)
index 0000000..12f16bd
--- /dev/null
@@ -0,0 +1,7 @@
+/*
+ * Copyright (C) 1997-1999 Jeffrey A. Uphoff
+ *
+ * NSM for Linux.
+ */
+
+#define STATD_RELEASE "1.1.1"