summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog178
-rw-r--r--Makefile22
-rw-r--r--README92
-rw-r--r--aclocal.m4120
-rw-r--r--config.mk.in79
-rwxr-xr-xconfigure1861
-rw-r--r--configure.in85
-rwxr-xr-xetc/redhat/nfs.init102
-rwxr-xr-xetc/redhat/nfslock.init82
-rw-r--r--install-sh238
-rw-r--r--linux-nfs/ChangeLog78
-rw-r--r--linux-nfs/INSTALL11
-rw-r--r--linux-nfs/KNOWNBUGS37
-rw-r--r--linux-nfs/NEW319
-rw-r--r--linux-nfs/README56
-rw-r--r--linux-nfs/THANKS10
-rw-r--r--linux-nfs/TODO121
-rw-r--r--nfs-utils.spec80
-rw-r--r--rules.mk126
-rw-r--r--support/Makefile9
-rw-r--r--support/export/Makefile27
-rw-r--r--support/export/client.c298
-rw-r--r--support/export/export.c259
-rw-r--r--support/export/hostname.c262
-rw-r--r--support/export/keys.c72
-rw-r--r--support/export/mount.x345
-rw-r--r--support/export/nfsctl.c105
-rw-r--r--support/export/rmtab.c79
-rw-r--r--support/export/xtab.c133
-rw-r--r--support/include/Makefile16
-rw-r--r--support/include/config.h.in25
-rw-r--r--support/include/exportfs.h80
-rw-r--r--support/include/misc.h24
-rw-r--r--support/include/nfs/debug.h75
-rw-r--r--support/include/nfs/export.h26
-rw-r--r--support/include/nfs/nfs.h145
-rw-r--r--support/include/nfslib.h125
-rw-r--r--support/include/rpcdispatch.h57
-rw-r--r--support/include/rpcmisc.h58
-rw-r--r--support/include/rpcsec.h39
-rw-r--r--support/include/rpcsvc/nfs_prot.h661
-rw-r--r--support/include/sys/fs/ext2fs.h42
-rw-r--r--support/include/version.h1
-rw-r--r--support/include/xio.h26
-rw-r--r--support/include/xlog.h40
-rw-r--r--support/include/xmalloc.h16
-rw-r--r--support/include/ypupdate.h16
-rw-r--r--support/lib/Makefile13
-rw-r--r--support/nfs/Makefile13
-rw-r--r--support/nfs/clients.c324
-rw-r--r--support/nfs/exports.c440
-rw-r--r--support/nfs/getfh.c55
-rw-r--r--support/nfs/keytab.c129
-rw-r--r--support/nfs/lockdsvc.c20
-rw-r--r--support/nfs/nfsclient.c32
-rw-r--r--support/nfs/nfsctl.c24
-rw-r--r--support/nfs/nfsexport.c32
-rw-r--r--support/nfs/nfssvc.c22
-rw-r--r--support/nfs/rmtab.c122
-rw-r--r--support/nfs/rpcdispatch.c112
-rw-r--r--support/nfs/rpcmisc.c230
-rw-r--r--support/nfs/wildmat.c177
-rw-r--r--support/nfs/xio.c151
-rw-r--r--support/nfs/xlog.c189
-rw-r--r--support/nfs/xmalloc.c48
-rw-r--r--support/nfs/ypupdate_xdr.c29
-rw-r--r--tools/Makefile7
-rw-r--r--tools/getiversion/Makefile11
-rw-r--r--tools/getiversion/getiversion.c37
-rw-r--r--tools/getkversion/Makefile12
-rw-r--r--tools/getkversion/getkversion.c17
-rw-r--r--tools/locktest/Makefile11
-rw-r--r--tools/locktest/testlk.c105
-rw-r--r--tools/nlmtest/Makefile26
-rw-r--r--tools/nlmtest/README5
-rw-r--r--tools/nlmtest/host.h28
-rw-r--r--tools/nlmtest/nlm_prot.x183
-rw-r--r--tools/nlmtest/nlmtest.c264
-rw-r--r--tools/rpcdebug/Makefile11
-rw-r--r--tools/rpcdebug/neat_idea.c334
-rw-r--r--tools/rpcdebug/rpcdebug.c334
-rw-r--r--tools/rpcgen/Makefile55
-rw-r--r--tools/rpcgen/README8
-rw-r--r--tools/rpcgen/rpc_clntout.c219
-rw-r--r--tools/rpcgen/rpc_cout.c715
-rw-r--r--tools/rpcgen/rpc_hout.c492
-rw-r--r--tools/rpcgen/rpc_main.c1066
-rw-r--r--tools/rpcgen/rpc_output.h16
-rw-r--r--tools/rpcgen/rpc_parse.c620
-rw-r--r--tools/rpcgen/rpc_parse.h168
-rw-r--r--tools/rpcgen/rpc_sample.c249
-rw-r--r--tools/rpcgen/rpc_scan.c475
-rw-r--r--tools/rpcgen/rpc_scan.h105
-rw-r--r--tools/rpcgen/rpc_svcout.c885
-rw-r--r--tools/rpcgen/rpc_tblout.c167
-rw-r--r--tools/rpcgen/rpc_util.c481
-rw-r--r--tools/rpcgen/rpc_util.h168
-rw-r--r--tools/rpcgen/rpcgen.new.1422
-rw-r--r--utils/Makefile10
-rw-r--r--utils/exportfs/Makefile13
-rw-r--r--utils/exportfs/exportfs.c391
-rw-r--r--utils/exportfs/exportfs.man195
-rw-r--r--utils/exportfs/exports.man306
-rw-r--r--utils/lockd/Makefile12
-rw-r--r--utils/lockd/lockd.c35
-rw-r--r--utils/mountd/Makefile12
-rw-r--r--utils/mountd/auth.c215
-rw-r--r--utils/mountd/mount_dispatch.c70
-rw-r--r--utils/mountd/mount_xdr.c79
-rw-r--r--utils/mountd/mountd.c489
-rw-r--r--utils/mountd/mountd.h54
-rw-r--r--utils/mountd/mountd.man92
-rw-r--r--utils/mountd/rmtab.c173
-rw-r--r--utils/nfsd/Makefile35
-rw-r--r--utils/nfsd/nfsd.c68
-rw-r--r--utils/nfsd/nfsd.man46
-rw-r--r--utils/nfsstat/Makefile32
-rw-r--r--utils/nfsstat/nfsstat.c328
-rw-r--r--utils/nfsstat/nfsstat.man74
-rw-r--r--utils/nhfsstone/DISCLAIMER33
-rw-r--r--utils/nhfsstone/Makefile8
-rw-r--r--utils/nhfsstone/README111
-rw-r--r--utils/nhfsstone/README.linux11
-rwxr-xr-xutils/nhfsstone/nhfsgraph23
-rwxr-xr-xutils/nhfsstone/nhfsnums22
-rwxr-xr-xutils/nhfsstone/nhfsrun59
-rw-r--r--utils/nhfsstone/nhfsstone.1381
-rw-r--r--utils/nhfsstone/nhfsstone.c1798
-rw-r--r--utils/rquotad/Makefile13
-rw-r--r--utils/rquotad/NEW3
-rw-r--r--utils/rquotad/README.okir3
-rw-r--r--utils/rquotad/hasquota.c72
-rw-r--r--utils/rquotad/mntent.h112
-rw-r--r--utils/rquotad/pathnames.h39
-rw-r--r--utils/rquotad/quotactl.c30
-rw-r--r--utils/rquotad/rquota.h64
-rw-r--r--utils/rquotad/rquota.x84
-rw-r--r--utils/rquotad/rquota_server.c246
-rw-r--r--utils/rquotad/rquota_svc.c213
-rw-r--r--utils/rquotad/rquota_xdr.c123
-rw-r--r--utils/rquotad/rquotad.man41
-rw-r--r--utils/showmount/Makefile11
-rw-r--r--utils/showmount/showmount.c287
-rw-r--r--utils/showmount/showmount.man58
-rw-r--r--utils/statd/COPYING340
-rw-r--r--utils/statd/COPYRIGHT25
-rw-r--r--utils/statd/Makefile58
-rw-r--r--utils/statd/TODO13
-rw-r--r--utils/statd/callback.c53
-rw-r--r--utils/statd/log.c108
-rw-r--r--utils/statd/log.h41
-rw-r--r--utils/statd/misc.c72
-rw-r--r--utils/statd/monitor.c287
-rw-r--r--utils/statd/notify.c75
-rw-r--r--utils/statd/notlist.c125
-rw-r--r--utils/statd/notlist.h111
-rw-r--r--utils/statd/rmtcall.c406
-rw-r--r--utils/statd/sim_sm_inter.x32
-rw-r--r--utils/statd/simu.c29
-rw-r--r--utils/statd/simulate.c225
-rw-r--r--utils/statd/sm_inter.x132
-rw-r--r--utils/statd/stat.c37
-rw-r--r--utils/statd/statd.c122
-rw-r--r--utils/statd/statd.h58
-rw-r--r--utils/statd/statd.man53
-rw-r--r--utils/statd/state.c126
-rw-r--r--utils/statd/svc_run.c128
-rw-r--r--utils/statd/system.h18
-rw-r--r--utils/statd/version.h7
169 files changed, 25871 insertions, 0 deletions
diff --git a/ChangeLog b/ChangeLog
new file mode 100644
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
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
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
index 0000000..baa54d1
--- /dev/null
+++ b/aclocal.m4
@@ -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
index 0000000..57d16be
--- /dev/null
+++ b/config.mk.in
@@ -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
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
index 0000000..1d91f5b
--- /dev/null
+++ b/configure.in
@@ -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
index 0000000..6da8e0c
--- /dev/null
+++ b/etc/redhat/nfs.init
@@ -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
index 0000000..1442637
--- /dev/null
+++ b/etc/redhat/nfslock.init
@@ -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
index 0000000..89fc9b0
--- /dev/null
+++ b/install-sh
@@ -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
index 0000000..11ffe15
--- /dev/null
+++ b/linux-nfs/ChangeLog
@@ -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
index 0000000..351f733
--- /dev/null
+++ b/linux-nfs/INSTALL
@@ -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
index 0000000..b0ecd5c
--- /dev/null
+++ b/linux-nfs/KNOWNBUGS
@@ -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
index 0000000..43f5c69
--- /dev/null
+++ b/linux-nfs/NEW
@@ -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
index 0000000..b210c61
--- /dev/null
+++ b/linux-nfs/README
@@ -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
index 0000000..22a80dc
--- /dev/null
+++ b/linux-nfs/THANKS
@@ -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
index 0000000..3a439d4
--- /dev/null
+++ b/linux-nfs/TODO
@@ -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
index 0000000..245c933
--- /dev/null
+++ b/nfs-utils.spec
@@ -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
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
index 0000000..6b8598b
--- /dev/null
+++ b/support/Makefile
@@ -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
index 0000000..1243305
--- /dev/null
+++ b/support/export/Makefile
@@ -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
index 0000000..8c5200a
--- /dev/null
+++ b/support/export/client.c
@@ -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
index 0000000..09efaa8
--- /dev/null
+++ b/support/export/export.c
@@ -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
index 0000000..a37d4de
--- /dev/null
+++ b/support/export/hostname.c
@@ -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
index 0000000..4814808
--- /dev/null
+++ b/support/export/keys.c
@@ -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
index 0000000..f504e7c
--- /dev/null
+++ b/support/export/mount.x
@@ -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
index 0000000..6612a76
--- /dev/null
+++ b/support/export/nfsctl.c
@@ -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
index 0000000..44a0edc
--- /dev/null
+++ b/support/export/rmtab.c
@@ -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
index 0000000..4289d7c
--- /dev/null
+++ b/support/export/xtab.c
@@ -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
index 0000000..e1cbfc4
--- /dev/null
+++ b/support/include/Makefile
@@ -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
index 0000000..f8c1497
--- /dev/null
+++ b/support/include/config.h.in
@@ -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
index 0000000..d440dc1
--- /dev/null
+++ b/support/include/exportfs.h
@@ -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
index 0000000..a3cdcfd
--- /dev/null
+++ b/support/include/misc.h
@@ -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
index 0000000..876b5db
--- /dev/null
+++ b/support/include/nfs/debug.h
@@ -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
index 0000000..80d23fd
--- /dev/null
+++ b/support/include/nfs/export.h
@@ -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
index 0000000..0cfed07
--- /dev/null
+++ b/support/include/nfs/nfs.h
@@ -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
index 0000000..d8be926
--- /dev/null
+++ b/support/include/nfslib.h
@@ -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
index 0000000..866d4bf
--- /dev/null
+++ b/support/include/rpcdispatch.h
@@ -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
index 0000000..06970cd
--- /dev/null
+++ b/support/include/rpcmisc.h
@@ -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
index 0000000..84d4497
--- /dev/null
+++ b/support/include/rpcsec.h
@@ -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
index 0000000..9311341
--- /dev/null
+++ b/support/include/rpcsvc/nfs_prot.h
@@ -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
index 0000000..93b3e2b
--- /dev/null
+++ b/support/include/sys/fs/ext2fs.h
@@ -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
index 0000000..a74ec35
--- /dev/null
+++ b/support/include/version.h
@@ -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
index 0000000..858f5bb
--- /dev/null
+++ b/support/include/xio.h
@@ -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
index 0000000..2a839c7
--- /dev/null
+++ b/support/include/xlog.h
@@ -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
index 0000000..866cfd8
--- /dev/null
+++ b/support/include/xmalloc.h
@@ -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
index 0000000..e0cee15
--- /dev/null
+++ b/support/include/ypupdate.h
@@ -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
index 0000000..b5fa14a
--- /dev/null
+++ b/support/lib/Makefile
@@ -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
index 0000000..ed1e1ff
--- /dev/null
+++ b/support/nfs/Makefile
@@ -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
index 0000000..b1970e0
--- /dev/null
+++ b/support/nfs/clients.c
@@ -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
index 0000000..21b85be
--- /dev/null
+++ b/support/nfs/exports.c
@@ -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
index 0000000..5a6f1a4
--- /dev/null
+++ b/support/nfs/getfh.c
@@ -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
index 0000000..e33dded
--- /dev/null
+++ b/support/nfs/keytab.c
@@ -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
index 0000000..532e721
--- /dev/null
+++ b/support/nfs/lockdsvc.c
@@ -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
index 0000000..5886484
--- /dev/null
+++ b/support/nfs/nfsclient.c
@@ -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
index 0000000..c04588f
--- /dev/null
+++ b/support/nfs/nfsctl.c
@@ -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
index 0000000..ce8b867
--- /dev/null
+++ b/support/nfs/nfsexport.c
@@ -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
index 0000000..7419baf
--- /dev/null
+++ b/support/nfs/nfssvc.c
@@ -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
index 0000000..b9b5ff1
--- /dev/null
+++ b/support/nfs/rmtab.c
@@ -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
index 0000000..e798ea5
--- /dev/null
+++ b/support/nfs/rpcdispatch.c
@@ -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
index 0000000..7b182fd
--- /dev/null
+++ b/support/nfs/rpcmisc.c
@@ -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
index 0000000..8f7b760
--- /dev/null
+++ b/support/nfs/wildmat.c
@@ -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;
+}
+
+
+
+#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
index 0000000..1bcd41b
--- /dev/null
+++ b/support/nfs/xio.c
@@ -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
index 0000000..90c7e63
--- /dev/null
+++ b/support/nfs/xlog.c
@@ -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
index 0000000..9523afc
--- /dev/null
+++ b/support/nfs/xmalloc.c
@@ -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
index 0000000..9fe1098
--- /dev/null
+++ b/support/nfs/ypupdate_xdr.c
@@ -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
index 0000000..6378850
--- /dev/null
+++ b/tools/Makefile
@@ -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
index 0000000..46c7150
--- /dev/null
+++ b/tools/getiversion/Makefile
@@ -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
index 0000000..e9cb391
--- /dev/null
+++ b/tools/getiversion/getiversion.c
@@ -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
index 0000000..be813ad
--- /dev/null
+++ b/tools/getkversion/Makefile
@@ -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
index 0000000..f8faf0a
--- /dev/null
+++ b/tools/getkversion/getkversion.c
@@ -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
index 0000000..e18f0b1
--- /dev/null
+++ b/tools/locktest/Makefile
@@ -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
index 0000000..47eb40a
--- /dev/null
+++ b/tools/locktest/testlk.c
@@ -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
index 0000000..6f29afb
--- /dev/null
+++ b/tools/nlmtest/Makefile
@@ -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
index 0000000..b54cb43
--- /dev/null
+++ b/tools/nlmtest/README
@@ -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
index 0000000..b4f30df
--- /dev/null
+++ b/tools/nlmtest/host.h
@@ -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
index 0000000..a425912
--- /dev/null
+++ b/tools/nlmtest/nlm_prot.x
@@ -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
index 0000000..77dd889
--- /dev/null
+++ b/tools/nlmtest/nlmtest.c
@@ -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
index 0000000..af2e530
--- /dev/null
+++ b/tools/rpcdebug/Makefile
@@ -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
index 0000000..ddaee2e
--- /dev/null
+++ b/tools/rpcdebug/neat_idea.c
@@ -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
index 0000000..b7e7511
--- /dev/null
+++ b/tools/rpcdebug/rpcdebug.c
@@ -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
index 0000000..defa7cb
--- /dev/null
+++ b/tools/rpcgen/Makefile
@@ -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
index 0000000..2f6bbf3
--- /dev/null
+++ b/tools/rpcgen/README
@@ -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
index 0000000..3ea267a
--- /dev/null
+++ b/tools/rpcgen/rpc_clntout.c
@@ -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
index 0000000..9bc20bb
--- /dev/null
+++ b/tools/rpcgen/rpc_cout.c
@@ -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
index 0000000..06835cd
--- /dev/null
+++ b/tools/rpcgen/rpc_hout.c
@@ -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
index 0000000..bd1a2c0
--- /dev/null
+++ b/tools/rpcgen/rpc_main.c
@@ -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
index 0000000..eb25a60
--- /dev/null
+++ b/tools/rpcgen/rpc_output.h
@@ -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
index 0000000..1e5b80a
--- /dev/null
+++ b/tools/rpcgen/rpc_parse.c
@@ -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
index 0000000..be72495
--- /dev/null
+++ b/tools/rpcgen/rpc_parse.h
@@ -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
index 0000000..97b2cd0
--- /dev/null
+++ b/tools/rpcgen/rpc_sample.c
@@ -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
index 0000000..07565a1
--- /dev/null
+++ b/tools/rpcgen/rpc_scan.c
@@ -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
index 0000000..0683dbe
--- /dev/null
+++ b/tools/rpcgen/rpc_scan.h
@@ -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
index 0000000..50c4ff9
--- /dev/null
+++ b/tools/rpcgen/rpc_svcout.c
@@ -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
index 0000000..5ce4015
--- /dev/null
+++ b/tools/rpcgen/rpc_tblout.c
@@ -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
index 0000000..2fd5f59
--- /dev/null
+++ b/tools/rpcgen/rpc_util.c
@@ -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
index 0000000..b0268bb
--- /dev/null
+++ b/tools/rpcgen/rpc_util.h
@@ -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
index 0000000..6f4897f
--- /dev/null
+++ b/tools/rpcgen/rpcgen.new.1
@@ -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
index 0000000..7e58325
--- /dev/null
+++ b/utils/Makefile
@@ -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
index 0000000..851a294
--- /dev/null
+++ b/utils/exportfs/Makefile
@@ -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
index 0000000..44761f8
--- /dev/null
+++ b/utils/exportfs/exportfs.c
@@ -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
index 0000000..0cd5cca
--- /dev/null
+++ b/utils/exportfs/exportfs.man
@@ -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
index 0000000..2863fea
--- /dev/null
+++ b/utils/exportfs/exports.man
@@ -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
index 0000000..557eebe
--- /dev/null
+++ b/utils/lockd/Makefile
@@ -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
index 0000000..05bc999
--- /dev/null
+++ b/utils/lockd/lockd.c
@@ -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
index 0000000..93529a0
--- /dev/null
+++ b/utils/mountd/Makefile
@@ -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
index 0000000..9f63120
--- /dev/null
+++ b/utils/mountd/auth.c
@@ -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
index 0000000..cee1981
--- /dev/null
+++ b/utils/mountd/mount_dispatch.c
@@ -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
index 0000000..87adfa6
--- /dev/null
+++ b/utils/mountd/mount_xdr.c
@@ -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
index 0000000..9f467d1
--- /dev/null
+++ b/utils/mountd/mountd.c
@@ -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
index 0000000..9f9bc1f
--- /dev/null
+++ b/utils/mountd/mountd.h
@@ -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
index 0000000..593037b
--- /dev/null
+++ b/utils/mountd/mountd.man
@@ -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
index 0000000..289a42e
--- /dev/null
+++ b/utils/mountd/rmtab.c
@@ -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
index 0000000..4cc6f8a
--- /dev/null
+++ b/utils/nfsd/Makefile
@@ -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
index 0000000..3a22370
--- /dev/null
+++ b/utils/nfsd/nfsd.c
@@ -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
index 0000000..f415cfd
--- /dev/null
+++ b/utils/nfsd/nfsd.man
@@ -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
index 0000000..e3a9428
--- /dev/null
+++ b/utils/nfsstat/Makefile
@@ -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
index 0000000..55b5cd0
--- /dev/null
+++ b/utils/nfsstat/nfsstat.c
@@ -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
index 0000000..72c8051
--- /dev/null
+++ b/utils/nfsstat/nfsstat.man
@@ -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
index 0000000..afde6a3
--- /dev/null
+++ b/utils/nhfsstone/DISCLAIMER
@@ -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
index 0000000..d73d85a
--- /dev/null
+++ b/utils/nhfsstone/Makefile
@@ -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
index 0000000..f13dde5
--- /dev/null
+++ b/utils/nhfsstone/README
@@ -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
index 0000000..e9b7899
--- /dev/null
+++ b/utils/nhfsstone/README.linux
@@ -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
index 0000000..56e2c77
--- /dev/null
+++ b/utils/nhfsstone/nhfsgraph
@@ -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
index 0000000..aae625d
--- /dev/null
+++ b/utils/nhfsstone/nhfsnums
@@ -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
index 0000000..dfc24eb
--- /dev/null
+++ b/utils/nhfsstone/nhfsrun
@@ -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
index 0000000..e56eb9e
--- /dev/null
+++ b/utils/nhfsstone/nhfsstone.1
@@ -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
index 0000000..034ba79
--- /dev/null
+++ b/utils/nhfsstone/nhfsstone.c
@@ -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
index 0000000..1572655
--- /dev/null
+++ b/utils/rquotad/Makefile
@@ -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
index 0000000..40c6fd2
--- /dev/null
+++ b/utils/rquotad/NEW
@@ -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
index 0000000..08938b9
--- /dev/null
+++ b/utils/rquotad/README.okir
@@ -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
index 0000000..008a93f
--- /dev/null
+++ b/utils/rquotad/hasquota.c
@@ -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
index 0000000..6c58451
--- /dev/null
+++ b/utils/rquotad/mntent.h
@@ -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
index 0000000..6604a18
--- /dev/null
+++ b/utils/rquotad/pathnames.h
@@ -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
index 0000000..30e68a4
--- /dev/null
+++ b/utils/rquotad/quotactl.c
@@ -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
index 0000000..f81e732
--- /dev/null
+++ b/utils/rquotad/rquota.h
@@ -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
index 0000000..120abe5
--- /dev/null
+++ b/utils/rquotad/rquota.x
@@ -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
index 0000000..08c4f8c
--- /dev/null
+++ b/utils/rquotad/rquota_server.c
@@ -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
index 0000000..d402f0b
--- /dev/null
+++ b/utils/rquotad/rquota_svc.c
@@ -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
index 0000000..6e68bd4
--- /dev/null
+++ b/utils/rquotad/rquota_xdr.c
@@ -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
index 0000000..da8fa8c
--- /dev/null
+++ b/utils/rquotad/rquotad.man
@@ -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
index 0000000..c8aa34d
--- /dev/null
+++ b/utils/showmount/Makefile
@@ -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
index 0000000..47b5825
--- /dev/null
+++ b/utils/showmount/showmount.c
@@ -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
index 0000000..63342c7
--- /dev/null
+++ b/utils/showmount/showmount.man
@@ -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
index 0000000..60549be
--- /dev/null
+++ b/utils/statd/COPYING
@@ -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.
+
+ 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.)
+
+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.
+
+ 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.
+
+ 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
+
+ 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
index 0000000..7b91031
--- /dev/null
+++ b/utils/statd/COPYRIGHT
@@ -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
index 0000000..3a3a794
--- /dev/null
+++ b/utils/statd/Makefile
@@ -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
index 0000000..0ee050a
--- /dev/null
+++ b/utils/statd/TODO
@@ -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
index 0000000..e3fad6a
--- /dev/null
+++ b/utils/statd/callback.c
@@ -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
index 0000000..38f7d3a
--- /dev/null
+++ b/utils/statd/log.c
@@ -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
index 0000000..f00bb63
--- /dev/null
+++ b/utils/statd/log.h
@@ -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
index 0000000..42f6e57
--- /dev/null
+++ b/utils/statd/misc.c
@@ -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
index 0000000..5a782dc
--- /dev/null
+++ b/utils/statd/monitor.c
@@ -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
index 0000000..89d2946
--- /dev/null
+++ b/utils/statd/notify.c
@@ -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
index 0000000..bc0c294
--- /dev/null
+++ b/utils/statd/notlist.c
@@ -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
index 0000000..0c6709c
--- /dev/null
+++ b/utils/statd/notlist.h
@@ -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
index 0000000..a08c4b1
--- /dev/null
+++ b/utils/statd/rmtcall.c
@@ -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
index 0000000..4346199
--- /dev/null
+++ b/utils/statd/sim_sm_inter.x
@@ -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
index 0000000..fa4e3a6
--- /dev/null
+++ b/utils/statd/simu.c
@@ -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
index 0000000..4b8d59c
--- /dev/null
+++ b/utils/statd/simulate.c
@@ -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
index 0000000..5232a28
--- /dev/null
+++ b/utils/statd/sm_inter.x
@@ -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
index 0000000..021e786
--- /dev/null
+++ b/utils/statd/stat.c
@@ -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
index 0000000..3b76e30
--- /dev/null
+++ b/utils/statd/statd.c
@@ -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
index 0000000..77a179a
--- /dev/null
+++ b/utils/statd/statd.h
@@ -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
index 0000000..373cf77
--- /dev/null
+++ b/utils/statd/statd.man
@@ -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
index 0000000..101c00b
--- /dev/null
+++ b/utils/statd/state.c
@@ -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
index 0000000..8f6d9fe
--- /dev/null
+++ b/utils/statd/svc_run.c
@@ -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
index 0000000..a1739c4
--- /dev/null
+++ b/utils/statd/system.h
@@ -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
index 0000000..12f16bd
--- /dev/null
+++ b/utils/statd/version.h
@@ -0,0 +1,7 @@
+/*
+ * Copyright (C) 1997-1999 Jeffrey A. Uphoff
+ *
+ * NSM for Linux.
+ */
+
+#define STATD_RELEASE "1.1.1"