From: hjl Date: Mon, 18 Oct 1999 23:21:12 +0000 (+0000) Subject: Initial revision X-Git-Tag: nfs-utils-0-1~1 X-Git-Url: https://git.decadent.org.uk/gitweb/?p=nfs-utils.git;a=commitdiff_plain;h=8b7ad01b14df1e7529b9ba8a1ea17df0d6004ef9 Initial revision --- 8b7ad01b14df1e7529b9ba8a1ea17df0d6004ef9 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"