From 8b7ad01b14df1e7529b9ba8a1ea17df0d6004ef9 Mon Sep 17 00:00:00 2001 From: hjl Date: Mon, 18 Oct 1999 23:21:12 +0000 Subject: [PATCH] Initial revision --- ChangeLog | 178 +++ Makefile | 22 + README | 92 ++ aclocal.m4 | 120 ++ config.mk.in | 79 ++ configure | 1861 +++++++++++++++++++++++++++++ configure.in | 85 ++ etc/redhat/nfs.init | 102 ++ etc/redhat/nfslock.init | 82 ++ install-sh | 238 ++++ linux-nfs/ChangeLog | 78 ++ linux-nfs/INSTALL | 11 + linux-nfs/KNOWNBUGS | 37 + linux-nfs/NEW | 319 +++++ linux-nfs/README | 56 + linux-nfs/THANKS | 10 + linux-nfs/TODO | 121 ++ nfs-utils.spec | 80 ++ rules.mk | 126 ++ support/Makefile | 9 + support/export/Makefile | 27 + support/export/client.c | 298 +++++ support/export/export.c | 259 ++++ support/export/hostname.c | 262 ++++ support/export/keys.c | 72 ++ support/export/mount.x | 345 ++++++ support/export/nfsctl.c | 105 ++ support/export/rmtab.c | 79 ++ support/export/xtab.c | 133 +++ support/include/Makefile | 16 + support/include/config.h.in | 25 + support/include/exportfs.h | 80 ++ support/include/misc.h | 24 + support/include/nfs/debug.h | 75 ++ support/include/nfs/export.h | 26 + support/include/nfs/nfs.h | 145 +++ support/include/nfslib.h | 125 ++ support/include/rpcdispatch.h | 57 + support/include/rpcmisc.h | 58 + support/include/rpcsec.h | 39 + support/include/rpcsvc/nfs_prot.h | 661 ++++++++++ support/include/sys/fs/ext2fs.h | 42 + support/include/version.h | 1 + support/include/xio.h | 26 + support/include/xlog.h | 40 + support/include/xmalloc.h | 16 + support/include/ypupdate.h | 16 + support/lib/Makefile | 13 + support/nfs/Makefile | 13 + support/nfs/clients.c | 324 +++++ support/nfs/exports.c | 440 +++++++ support/nfs/getfh.c | 55 + support/nfs/keytab.c | 129 ++ support/nfs/lockdsvc.c | 20 + support/nfs/nfsclient.c | 32 + support/nfs/nfsctl.c | 24 + support/nfs/nfsexport.c | 32 + support/nfs/nfssvc.c | 22 + support/nfs/rmtab.c | 122 ++ support/nfs/rpcdispatch.c | 112 ++ support/nfs/rpcmisc.c | 230 ++++ support/nfs/wildmat.c | 177 +++ support/nfs/xio.c | 151 +++ support/nfs/xlog.c | 189 +++ support/nfs/xmalloc.c | 48 + support/nfs/ypupdate_xdr.c | 29 + tools/Makefile | 7 + tools/getiversion/Makefile | 11 + tools/getiversion/getiversion.c | 37 + tools/getkversion/Makefile | 12 + tools/getkversion/getkversion.c | 17 + tools/locktest/Makefile | 11 + tools/locktest/testlk.c | 105 ++ tools/nlmtest/Makefile | 26 + tools/nlmtest/README | 5 + tools/nlmtest/host.h | 28 + tools/nlmtest/nlm_prot.x | 183 +++ tools/nlmtest/nlmtest.c | 264 ++++ tools/rpcdebug/Makefile | 11 + tools/rpcdebug/neat_idea.c | 334 ++++++ tools/rpcdebug/rpcdebug.c | 334 ++++++ tools/rpcgen/Makefile | 55 + tools/rpcgen/README | 8 + tools/rpcgen/rpc_clntout.c | 219 ++++ tools/rpcgen/rpc_cout.c | 715 +++++++++++ tools/rpcgen/rpc_hout.c | 492 ++++++++ tools/rpcgen/rpc_main.c | 1066 +++++++++++++++++ tools/rpcgen/rpc_output.h | 16 + tools/rpcgen/rpc_parse.c | 620 ++++++++++ tools/rpcgen/rpc_parse.h | 168 +++ tools/rpcgen/rpc_sample.c | 249 ++++ tools/rpcgen/rpc_scan.c | 475 ++++++++ tools/rpcgen/rpc_scan.h | 105 ++ tools/rpcgen/rpc_svcout.c | 885 ++++++++++++++ tools/rpcgen/rpc_tblout.c | 167 +++ tools/rpcgen/rpc_util.c | 481 ++++++++ tools/rpcgen/rpc_util.h | 168 +++ tools/rpcgen/rpcgen.new.1 | 422 +++++++ utils/Makefile | 10 + utils/exportfs/Makefile | 13 + utils/exportfs/exportfs.c | 391 ++++++ utils/exportfs/exportfs.man | 195 +++ utils/exportfs/exports.man | 306 +++++ utils/lockd/Makefile | 12 + utils/lockd/lockd.c | 35 + utils/mountd/Makefile | 12 + utils/mountd/auth.c | 215 ++++ utils/mountd/mount_dispatch.c | 70 ++ utils/mountd/mount_xdr.c | 79 ++ utils/mountd/mountd.c | 489 ++++++++ utils/mountd/mountd.h | 54 + utils/mountd/mountd.man | 92 ++ utils/mountd/rmtab.c | 173 +++ utils/nfsd/Makefile | 35 + utils/nfsd/nfsd.c | 68 ++ utils/nfsd/nfsd.man | 46 + utils/nfsstat/Makefile | 32 + utils/nfsstat/nfsstat.c | 328 +++++ utils/nfsstat/nfsstat.man | 74 ++ utils/nhfsstone/DISCLAIMER | 33 + utils/nhfsstone/Makefile | 8 + utils/nhfsstone/README | 111 ++ utils/nhfsstone/README.linux | 11 + utils/nhfsstone/nhfsgraph | 23 + utils/nhfsstone/nhfsnums | 22 + utils/nhfsstone/nhfsrun | 59 + utils/nhfsstone/nhfsstone.1 | 381 ++++++ utils/nhfsstone/nhfsstone.c | 1798 ++++++++++++++++++++++++++++ utils/rquotad/Makefile | 13 + utils/rquotad/NEW | 3 + utils/rquotad/README.okir | 3 + utils/rquotad/hasquota.c | 72 ++ utils/rquotad/mntent.h | 112 ++ utils/rquotad/pathnames.h | 39 + utils/rquotad/quotactl.c | 30 + utils/rquotad/rquota.h | 64 + utils/rquotad/rquota.x | 84 ++ utils/rquotad/rquota_server.c | 246 ++++ utils/rquotad/rquota_svc.c | 213 ++++ utils/rquotad/rquota_xdr.c | 123 ++ utils/rquotad/rquotad.man | 41 + utils/showmount/Makefile | 11 + utils/showmount/showmount.c | 287 +++++ utils/showmount/showmount.man | 58 + utils/statd/COPYING | 340 ++++++ utils/statd/COPYRIGHT | 25 + utils/statd/Makefile | 58 + utils/statd/TODO | 13 + utils/statd/callback.c | 53 + utils/statd/log.c | 108 ++ utils/statd/log.h | 41 + utils/statd/misc.c | 72 ++ utils/statd/monitor.c | 287 +++++ utils/statd/notify.c | 75 ++ utils/statd/notlist.c | 125 ++ utils/statd/notlist.h | 111 ++ utils/statd/rmtcall.c | 406 +++++++ utils/statd/sim_sm_inter.x | 32 + utils/statd/simu.c | 29 + utils/statd/simulate.c | 225 ++++ utils/statd/sm_inter.x | 132 ++ utils/statd/stat.c | 37 + utils/statd/statd.c | 122 ++ utils/statd/statd.h | 58 + utils/statd/statd.man | 53 + utils/statd/state.c | 126 ++ utils/statd/svc_run.c | 128 ++ utils/statd/system.h | 18 + utils/statd/version.h | 7 + 169 files changed, 25871 insertions(+) create mode 100644 ChangeLog create mode 100644 Makefile create mode 100644 README create mode 100644 aclocal.m4 create mode 100644 config.mk.in create mode 100755 configure create mode 100644 configure.in create mode 100755 etc/redhat/nfs.init create mode 100755 etc/redhat/nfslock.init create mode 100644 install-sh create mode 100644 linux-nfs/ChangeLog create mode 100644 linux-nfs/INSTALL create mode 100644 linux-nfs/KNOWNBUGS create mode 100644 linux-nfs/NEW create mode 100644 linux-nfs/README create mode 100644 linux-nfs/THANKS create mode 100644 linux-nfs/TODO create mode 100644 nfs-utils.spec create mode 100644 rules.mk create mode 100644 support/Makefile create mode 100644 support/export/Makefile create mode 100644 support/export/client.c create mode 100644 support/export/export.c create mode 100644 support/export/hostname.c create mode 100644 support/export/keys.c create mode 100644 support/export/mount.x create mode 100644 support/export/nfsctl.c create mode 100644 support/export/rmtab.c create mode 100644 support/export/xtab.c create mode 100644 support/include/Makefile create mode 100644 support/include/config.h.in create mode 100644 support/include/exportfs.h create mode 100644 support/include/misc.h create mode 100644 support/include/nfs/debug.h create mode 100644 support/include/nfs/export.h create mode 100644 support/include/nfs/nfs.h create mode 100644 support/include/nfslib.h create mode 100644 support/include/rpcdispatch.h create mode 100644 support/include/rpcmisc.h create mode 100644 support/include/rpcsec.h create mode 100644 support/include/rpcsvc/nfs_prot.h create mode 100644 support/include/sys/fs/ext2fs.h create mode 100644 support/include/version.h create mode 100644 support/include/xio.h create mode 100644 support/include/xlog.h create mode 100644 support/include/xmalloc.h create mode 100644 support/include/ypupdate.h create mode 100644 support/lib/Makefile create mode 100644 support/nfs/Makefile create mode 100644 support/nfs/clients.c create mode 100644 support/nfs/exports.c create mode 100644 support/nfs/getfh.c create mode 100644 support/nfs/keytab.c create mode 100644 support/nfs/lockdsvc.c create mode 100644 support/nfs/nfsclient.c create mode 100644 support/nfs/nfsctl.c create mode 100644 support/nfs/nfsexport.c create mode 100644 support/nfs/nfssvc.c create mode 100644 support/nfs/rmtab.c create mode 100644 support/nfs/rpcdispatch.c create mode 100644 support/nfs/rpcmisc.c create mode 100644 support/nfs/wildmat.c create mode 100644 support/nfs/xio.c create mode 100644 support/nfs/xlog.c create mode 100644 support/nfs/xmalloc.c create mode 100644 support/nfs/ypupdate_xdr.c create mode 100644 tools/Makefile create mode 100644 tools/getiversion/Makefile create mode 100644 tools/getiversion/getiversion.c create mode 100644 tools/getkversion/Makefile create mode 100644 tools/getkversion/getkversion.c create mode 100644 tools/locktest/Makefile create mode 100644 tools/locktest/testlk.c create mode 100644 tools/nlmtest/Makefile create mode 100644 tools/nlmtest/README create mode 100644 tools/nlmtest/host.h create mode 100644 tools/nlmtest/nlm_prot.x create mode 100644 tools/nlmtest/nlmtest.c create mode 100644 tools/rpcdebug/Makefile create mode 100644 tools/rpcdebug/neat_idea.c create mode 100644 tools/rpcdebug/rpcdebug.c create mode 100644 tools/rpcgen/Makefile create mode 100644 tools/rpcgen/README create mode 100644 tools/rpcgen/rpc_clntout.c create mode 100644 tools/rpcgen/rpc_cout.c create mode 100644 tools/rpcgen/rpc_hout.c create mode 100644 tools/rpcgen/rpc_main.c create mode 100644 tools/rpcgen/rpc_output.h create mode 100644 tools/rpcgen/rpc_parse.c create mode 100644 tools/rpcgen/rpc_parse.h create mode 100644 tools/rpcgen/rpc_sample.c create mode 100644 tools/rpcgen/rpc_scan.c create mode 100644 tools/rpcgen/rpc_scan.h create mode 100644 tools/rpcgen/rpc_svcout.c create mode 100644 tools/rpcgen/rpc_tblout.c create mode 100644 tools/rpcgen/rpc_util.c create mode 100644 tools/rpcgen/rpc_util.h create mode 100644 tools/rpcgen/rpcgen.new.1 create mode 100644 utils/Makefile create mode 100644 utils/exportfs/Makefile create mode 100644 utils/exportfs/exportfs.c create mode 100644 utils/exportfs/exportfs.man create mode 100644 utils/exportfs/exports.man create mode 100644 utils/lockd/Makefile create mode 100644 utils/lockd/lockd.c create mode 100644 utils/mountd/Makefile create mode 100644 utils/mountd/auth.c create mode 100644 utils/mountd/mount_dispatch.c create mode 100644 utils/mountd/mount_xdr.c create mode 100644 utils/mountd/mountd.c create mode 100644 utils/mountd/mountd.h create mode 100644 utils/mountd/mountd.man create mode 100644 utils/mountd/rmtab.c create mode 100644 utils/nfsd/Makefile create mode 100644 utils/nfsd/nfsd.c create mode 100644 utils/nfsd/nfsd.man create mode 100644 utils/nfsstat/Makefile create mode 100644 utils/nfsstat/nfsstat.c create mode 100644 utils/nfsstat/nfsstat.man create mode 100644 utils/nhfsstone/DISCLAIMER create mode 100644 utils/nhfsstone/Makefile create mode 100644 utils/nhfsstone/README create mode 100644 utils/nhfsstone/README.linux create mode 100755 utils/nhfsstone/nhfsgraph create mode 100755 utils/nhfsstone/nhfsnums create mode 100755 utils/nhfsstone/nhfsrun create mode 100644 utils/nhfsstone/nhfsstone.1 create mode 100644 utils/nhfsstone/nhfsstone.c create mode 100644 utils/rquotad/Makefile create mode 100644 utils/rquotad/NEW create mode 100644 utils/rquotad/README.okir create mode 100644 utils/rquotad/hasquota.c create mode 100644 utils/rquotad/mntent.h create mode 100644 utils/rquotad/pathnames.h create mode 100644 utils/rquotad/quotactl.c create mode 100644 utils/rquotad/rquota.h create mode 100644 utils/rquotad/rquota.x create mode 100644 utils/rquotad/rquota_server.c create mode 100644 utils/rquotad/rquota_svc.c create mode 100644 utils/rquotad/rquota_xdr.c create mode 100644 utils/rquotad/rquotad.man create mode 100644 utils/showmount/Makefile create mode 100644 utils/showmount/showmount.c create mode 100644 utils/showmount/showmount.man create mode 100644 utils/statd/COPYING create mode 100644 utils/statd/COPYRIGHT create mode 100644 utils/statd/Makefile create mode 100644 utils/statd/TODO create mode 100644 utils/statd/callback.c create mode 100644 utils/statd/log.c create mode 100644 utils/statd/log.h create mode 100644 utils/statd/misc.c create mode 100644 utils/statd/monitor.c create mode 100644 utils/statd/notify.c create mode 100644 utils/statd/notlist.c create mode 100644 utils/statd/notlist.h create mode 100644 utils/statd/rmtcall.c create mode 100644 utils/statd/sim_sm_inter.x create mode 100644 utils/statd/simu.c create mode 100644 utils/statd/simulate.c create mode 100644 utils/statd/sm_inter.x create mode 100644 utils/statd/stat.c create mode 100644 utils/statd/statd.c create mode 100644 utils/statd/statd.h create mode 100644 utils/statd/statd.man create mode 100644 utils/statd/state.c create mode 100644 utils/statd/svc_run.c create mode 100644 utils/statd/system.h create mode 100644 utils/statd/version.h 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 + + * 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 + + * 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 + +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 + + * 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 + #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 + #include + 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(<>,translit(sizeof_$1, [a-z *], [A-Z_P]))dnl + define(<>, 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 + #include + 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 + #include + #include + + 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 <&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 <&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 < +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 < +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 < +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 < +#include +#include +#include +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 +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 +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 < +#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 < + #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 <&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 <&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 <&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 <&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 < +/* 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 <&6 +fi +done + + +cat >> confdefs.h < 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 </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 < 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 <> $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 <> $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 <> $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 <> $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 + . +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 . +4. netgroups patch from Peter Breitenlohner . +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 . +3. Add nfsd.8, mountd.8 and statd.man, by Olaf Kirch + . +4. Update nfsstat.man by Olaf Kirch . +5. Statd fix by Jeff Uphoff . +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 . + +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 . +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 . +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 . +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 + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#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 + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#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 ⅇ + } + /* 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 +#include +#include +#include +#include +#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 + */ + +#include "config.h" + +#include +#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 +#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; + +/* + * The type name is used for arbitrary names (hostnames, groupnames) + */ +typedef string name; + +/* + * 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; + +/* + * 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 + */ + +#include "config.h" + +#include +#include +#include +#include +#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 +#include +#include +#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 + */ + +#include "config.h" + +#include +#include +#include +#include +#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 + */ + +#ifndef EXPORTFS_H +#define EXPORTFS_H + +#include +#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 + */ + +#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 . + */ +#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 +#include +#include +#include +#include + +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 + */ + +#ifndef NFSLIB_H +#define NFSLIB_H + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#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 + * + * 24.05.95 okir + * + */ + +#ifndef RPCDISPATCH_H +#define RPCDISPATCH_H + +#include + +#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 + */ + +#ifndef RPCMISC_H +#define RPCMISC_H + +#include +#include + +#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 + +#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 + +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 + */ + +#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 + */ + +#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 + */ + +#ifndef YPUPDATE_H +#define YPUPDATE_H + +#include + +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 + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#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, + * Rick Sladkey, + * Fred N. van Kempen, + * Olaf Kirch, + * Alexander O. Yuriev, + * + * 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 +#include +#include +#include +#include +#include +#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 ⅇ +} + +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 ⅇ +} + +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 + */ + +#include "config.h" + +#include +#include +#include +#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 + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#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 + */ + +#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 + */ + +#include "config.h" + +#include +#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 + */ + +#include "config.h" + +#include +#include +#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 + */ + +#include "config.h" + +#include +#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 + */ + +#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 + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#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 + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#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, ¤t); + svc_getreqset(&readfds); + sigprocmask(SIG_SETMASK, ¤t, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 . +** April, 1991: Replaced mutually-recursive calls with in-line code +** for the star character. +** +** Special thanks to Lars Mathiesen 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 + +#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 + +/* 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 + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#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, + * Rick Sladkey, + * Fred N. van Kempen, + * Olaf Kirch, + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#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 + */ + +#include "config.h" + +#include +#include +#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 + */ + +#include "config.h" + +#include + +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 +#include +#include +#include +#include + +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 +#include + +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 +#include +#include +#ifdef linux +#include +#endif +#include + +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 +#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; + 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; + 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; + 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 + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#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 + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#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("\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 + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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, "\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 +# +# 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 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 +#include +#include +#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 +#include +#include +#include +#include +#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 +#include +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#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" : 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 \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 \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 \n"); + + f_print(fout, "#include \n"); + f_print(fout, "#include /* getenv, exit */\n"); + if (Cflag) { + f_print (fout, "#include /* for pmap_unset */\n"); + f_print (fout, "#include /* 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 \n"); + timerflag = 1; + } + +#ifndef linux + if( !tirpcflag && inetdflag ) + f_print(fout, "#include /* TIOCNOTTY */\n"); +#endif + if( Cflag && (inetdflag || pmflag ) ) { + f_print(fout, "#ifdef __cplusplus\n"); + f_print(fout, "#include /* getdtablesize, open */\n"); + f_print(fout, "#endif /* __cplusplus */\n"); + + if( tirpcflag ) + f_print(fout, "#include /* setsid */\n"); + } + if( tirpcflag ) + f_print(fout, "#include \n"); + + f_print(fout, "#include \n"); +#ifndef linux + f_print(fout, "#include \n"); +#endif + if (inetdflag || !tirpcflag ) { + f_print(fout, "#include \n"); + f_print(fout, "#include \n"); + } + + if ( (netflag || pmflag) && tirpcflag ) { + f_print(fout, "#include \n"); + } + if (/*timerflag &&*/ tirpcflag) + f_print(fout, "#include /* rlimit */\n"); + if (logflag || inetdflag || pmflag) { +#ifdef linux + f_print(fout, "#include \n"); +#else + f_print(fout, "#ifdef SYSLOG\n"); + f_print(fout, "#include \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 /* for memset */\n"); + if (infile && (include = extendfile(infile, ".h"))) { + f_print(fout, "#include \"%s\"\n", include); + free(include); + } else + f_print(fout, "#include \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 \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 \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 +#include +#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 +#include +#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 +#include +#include +#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 +#include +#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 +#include +#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 +#include +#include +#include +#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 + +#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 + * + * Extensive changes, 1999, Neil Brown + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#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 = ""; + 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 +.\" Modifications 1999 Neil Brown +.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, +.br +Neil Brown, + 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 +#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 + */ + +#include "config.h" + +#include +#include +#include +#include +#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 + */ + +#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 + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 + */ + +#ifndef MOUNTD_H +#define MOUNTD_H + +#include +#include +#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 +.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 + */ + +#include "config.h" + +#include +#include +#include +#include +#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 + */ + +#include "config.h" + +#include +#include +#include +#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 +.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 + */ + +#include "config.h" + +#define NFSSVCSTAT "/proc/net/rpc/nfsd" +#define NFSCLTSTAT "/proc/net/rpc/nfs" + +#include +#include +#include +#include +#include +#include + +#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 +.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, 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 ... +# +# 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 ... +# +# 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. +# + +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 /testdir +(where 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 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef BSD +#include +#define dirent direct +#else +#include +#endif +#include + +#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 + * + * 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 +#include +#include +#include +#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 + +#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 + +__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 + +#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 + * + * 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 +#include +#include + +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 +#endif + +const RQ_PATHLEN = 1024; + +struct getquota_args { + string gqa_pathp; /* path to filesystem of interest */ + int gqa_uid; /* Inquire about quota for uid */ +}; + +struct ext_getquota_args { + string gqa_pathp; /* 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 + * + * 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 +#include "rquota.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 + * + * 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 +#include +#include "rquota.h" +#include +#include +#include +#include +#include +#include +#include + +#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 +#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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +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 +.\" 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 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. + + + Copyright (C) 19yy + + 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. + + , 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(¬ify, 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 +#include +#include +#include +#include +#include +#include +#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 + +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 +#include +#include +#include +#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 +#include +#include +#include +#include +#include +#include +#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 +#include +#include +#include +#include +#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(¬ify, 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 +#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 + +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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(¬ify, lp); + nlist_insert_timer(¬ify, 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(¬ify, 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(¬ify, entry); + nlist_insert_timer(¬ify, 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(¬ify, 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(¬ify, 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 +#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; + 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 +#include +#include +#include +#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 +#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; +}; + +struct my_id { + string my_name; /* 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; /* 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; /* 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; + 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 +#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 +#include +#include +#include +#include +#include +#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 +.\" 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 +.br +Olaf Kirch +.br +H.J. Lu 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 +#include +#include +#include +#include +#include +#include +#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 +#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" -- 2.39.2