summaryrefslogtreecommitdiffstats
path: root/support/nfs
diff options
context:
space:
mode:
Diffstat (limited to 'support/nfs')
-rw-r--r--support/nfs/Makefile.am17
-rw-r--r--support/nfs/Makefile.in916
-rw-r--r--support/nfs/atomicio.c57
-rw-r--r--support/nfs/cacheio.c253
-rw-r--r--support/nfs/closeall.c39
-rw-r--r--support/nfs/conffile.c2345
-rw-r--r--support/nfs/exports.c999
-rw-r--r--support/nfs/getport.c1127
-rw-r--r--support/nfs/mydaemon.c153
-rw-r--r--support/nfs/nfs_mntent.c240
-rw-r--r--support/nfs/rmtab.c173
-rw-r--r--support/nfs/rpc_socket.c560
-rw-r--r--support/nfs/rpcdispatch.c69
-rw-r--r--support/nfs/rpcmisc.c213
-rw-r--r--support/nfs/strlcat.c78
-rw-r--r--support/nfs/strlcpy.c74
-rw-r--r--support/nfs/svc_create.c525
-rw-r--r--support/nfs/svc_socket.c218
-rw-r--r--support/nfs/wildmat.c182
-rw-r--r--support/nfs/xcommon.c191
-rw-r--r--support/nfs/xio.c170
-rw-r--r--support/nfs/xlog.c254
22 files changed, 8853 insertions, 0 deletions
diff --git a/support/nfs/Makefile.am b/support/nfs/Makefile.am
new file mode 100644
index 0000000..2e1577c
--- /dev/null
+++ b/support/nfs/Makefile.am
@@ -0,0 +1,17 @@
+## Process this file with automake to produce Makefile.in
+
+noinst_LIBRARIES =
+noinst_LTLIBRARIES = libnfs.la libnfsconf.la
+
+libnfs_la_SOURCES = exports.c rmtab.c xio.c rpcmisc.c rpcdispatch.c \
+ xcommon.c wildmat.c mydaemon.c \
+ rpc_socket.c getport.c \
+ svc_socket.c cacheio.c closeall.c nfs_mntent.c \
+ svc_create.c atomicio.c strlcat.c strlcpy.c
+libnfs_la_LIBADD = libnfsconf.la
+libnfs_la_CPPFLAGS = $(AM_CPPFLAGS) $(CPPFLAGS) -I$(top_srcdir)/support/reexport
+
+libnfsconf_la_SOURCES = conffile.c xlog.c
+
+MAINTAINERCLEANFILES = Makefile.in
+
diff --git a/support/nfs/Makefile.in b/support/nfs/Makefile.in
new file mode 100644
index 0000000..c4e2f8f
--- /dev/null
+++ b/support/nfs/Makefile.in
@@ -0,0 +1,916 @@
+# Makefile.in generated by automake 1.16.5 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2021 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = support/nfs
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/aclocal/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/aclocal/bsdsignals.m4 \
+ $(top_srcdir)/aclocal/getrandom.m4 \
+ $(top_srcdir)/aclocal/ipv6.m4 \
+ $(top_srcdir)/aclocal/kerberos5.m4 \
+ $(top_srcdir)/aclocal/keyutils.m4 \
+ $(top_srcdir)/aclocal/libblkid.m4 \
+ $(top_srcdir)/aclocal/libcap.m4 \
+ $(top_srcdir)/aclocal/libevent.m4 \
+ $(top_srcdir)/aclocal/libpthread.m4 \
+ $(top_srcdir)/aclocal/libsqlite3.m4 \
+ $(top_srcdir)/aclocal/libtirpc.m4 \
+ $(top_srcdir)/aclocal/libxml2.m4 \
+ $(top_srcdir)/aclocal/nfs-utils.m4 \
+ $(top_srcdir)/aclocal/rpcsec_vers.m4 \
+ $(top_srcdir)/aclocal/tcp-wrappers.m4 \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/support/include/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+LIBRARIES = $(noinst_LIBRARIES)
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+libnfs_la_DEPENDENCIES = libnfsconf.la
+am_libnfs_la_OBJECTS = libnfs_la-exports.lo libnfs_la-rmtab.lo \
+ libnfs_la-xio.lo libnfs_la-rpcmisc.lo libnfs_la-rpcdispatch.lo \
+ libnfs_la-xcommon.lo libnfs_la-wildmat.lo \
+ libnfs_la-mydaemon.lo libnfs_la-rpc_socket.lo \
+ libnfs_la-getport.lo libnfs_la-svc_socket.lo \
+ libnfs_la-cacheio.lo libnfs_la-closeall.lo \
+ libnfs_la-nfs_mntent.lo libnfs_la-svc_create.lo \
+ libnfs_la-atomicio.lo libnfs_la-strlcat.lo \
+ libnfs_la-strlcpy.lo
+libnfs_la_OBJECTS = $(am_libnfs_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+libnfsconf_la_LIBADD =
+am_libnfsconf_la_OBJECTS = conffile.lo xlog.lo
+libnfsconf_la_OBJECTS = $(am_libnfsconf_la_OBJECTS)
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/support/include
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/conffile.Plo \
+ ./$(DEPDIR)/libnfs_la-atomicio.Plo \
+ ./$(DEPDIR)/libnfs_la-cacheio.Plo \
+ ./$(DEPDIR)/libnfs_la-closeall.Plo \
+ ./$(DEPDIR)/libnfs_la-exports.Plo \
+ ./$(DEPDIR)/libnfs_la-getport.Plo \
+ ./$(DEPDIR)/libnfs_la-mydaemon.Plo \
+ ./$(DEPDIR)/libnfs_la-nfs_mntent.Plo \
+ ./$(DEPDIR)/libnfs_la-rmtab.Plo \
+ ./$(DEPDIR)/libnfs_la-rpc_socket.Plo \
+ ./$(DEPDIR)/libnfs_la-rpcdispatch.Plo \
+ ./$(DEPDIR)/libnfs_la-rpcmisc.Plo \
+ ./$(DEPDIR)/libnfs_la-strlcat.Plo \
+ ./$(DEPDIR)/libnfs_la-strlcpy.Plo \
+ ./$(DEPDIR)/libnfs_la-svc_create.Plo \
+ ./$(DEPDIR)/libnfs_la-svc_socket.Plo \
+ ./$(DEPDIR)/libnfs_la-wildmat.Plo \
+ ./$(DEPDIR)/libnfs_la-xcommon.Plo \
+ ./$(DEPDIR)/libnfs_la-xio.Plo ./$(DEPDIR)/xlog.Plo
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libnfs_la_SOURCES) $(libnfsconf_la_SOURCES)
+DIST_SOURCES = $(libnfs_la_SOURCES) $(libnfsconf_la_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@
+ALLOCA = @ALLOCA@
+AMTAR = @AMTAR@
+AM_CFLAGS = @AM_CFLAGS@
+AM_CPPFLAGS = @AM_CPPFLAGS@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CSCOPE = @CSCOPE@
+CTAGS = @CTAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CXXFLAGS_FOR_BUILD = @CXXFLAGS_FOR_BUILD@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+ETAGS = @ETAGS@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILECMD = @FILECMD@
+GREP = @GREP@
+GSSAPI_CFLAGS = @GSSAPI_CFLAGS@
+GSSAPI_LIBS = @GSSAPI_LIBS@
+GSSD = @GSSD@
+GSSGLUE_CFLAGS = @GSSGLUE_CFLAGS@
+GSSGLUE_LIBS = @GSSGLUE_LIBS@
+GSSKRB_CFLAGS = @GSSKRB_CFLAGS@
+GSSKRB_LIBS = @GSSKRB_LIBS@
+HAVE_GETRANDOM = @HAVE_GETRANDOM@
+HAVE_LIBWRAP = @HAVE_LIBWRAP@
+HAVE_TCP_WRAPPER = @HAVE_TCP_WRAPPER@
+IDMAPD = @IDMAPD@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+K5VERS = @K5VERS@
+KRBCFLAGS = @KRBCFLAGS@
+KRBDIR = @KRBDIR@
+KRBLDFLAGS = @KRBLDFLAGS@
+KRBLIBS = @KRBLIBS@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBLKID = @LIBBLKID@
+LIBBSD = @LIBBSD@
+LIBCAP = @LIBCAP@
+LIBCRYPT = @LIBCRYPT@
+LIBEVENT = @LIBEVENT@
+LIBKEYUTILS = @LIBKEYUTILS@
+LIBMOUNT = @LIBMOUNT@
+LIBMOUNT_CFLAGS = @LIBMOUNT_CFLAGS@
+LIBMOUNT_LIBS = @LIBMOUNT_LIBS@
+LIBNSL = @LIBNSL@
+LIBOBJS = @LIBOBJS@
+LIBPTHREAD = @LIBPTHREAD@
+LIBS = @LIBS@
+LIBSOCKET = @LIBSOCKET@
+LIBSQLITE = @LIBSQLITE@
+LIBTIRPC = @LIBTIRPC@
+LIBTOOL = @LIBTOOL@
+LIBWRAP = @LIBWRAP@
+LIBXML2 = @LIBXML2@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_PLUGINS = @PATH_PLUGINS@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+RANLIB = @RANLIB@
+RELEASE = @RELEASE@
+RPCGEN_PATH = @RPCGEN_PATH@
+RPCSECGSS_CFLAGS = @RPCSECGSS_CFLAGS@
+RPCSECGSS_LIBS = @RPCSECGSS_LIBS@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+SVCGSSD = @SVCGSSD@
+TIRPC_CFLAGS = @TIRPC_CFLAGS@
+TIRPC_LIBS = @TIRPC_LIBS@
+VERSION = @VERSION@
+XML2_CFLAGS = @XML2_CFLAGS@
+XML2_LIBS = @XML2_LIBS@
+_rpc_pipefsmount = @_rpc_pipefsmount@
+_statedir = @_statedir@
+_sysconfdir = @_sysconfdir@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+enable_gss = @enable_gss@
+enable_ipv6 = @enable_ipv6@
+enable_mountconfig = @enable_mountconfig@
+enable_nfsv4 = @enable_nfsv4@
+enable_nfsv41 = @enable_nfsv41@
+enable_svcgss = @enable_svcgss@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+kprefix = @kprefix@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+mountfile = @mountfile@
+nfsconfig = @nfsconfig@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+rpc_pipefsmount = @rpc_pipefsmount@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+startstatd = @startstatd@
+statdpath = @statdpath@
+statduser = @statduser@
+statedir = @statedir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+unitdir = @unitdir@
+noinst_LIBRARIES =
+noinst_LTLIBRARIES = libnfs.la libnfsconf.la
+libnfs_la_SOURCES = exports.c rmtab.c xio.c rpcmisc.c rpcdispatch.c \
+ xcommon.c wildmat.c mydaemon.c \
+ rpc_socket.c getport.c \
+ svc_socket.c cacheio.c closeall.c nfs_mntent.c \
+ svc_create.c atomicio.c strlcat.c strlcpy.c
+
+libnfs_la_LIBADD = libnfsconf.la
+libnfs_la_CPPFLAGS = $(AM_CPPFLAGS) $(CPPFLAGS) -I$(top_srcdir)/support/reexport
+libnfsconf_la_SOURCES = conffile.c xlog.c
+MAINTAINERCLEANFILES = Makefile.in
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu support/nfs/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu support/nfs/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstLIBRARIES:
+ -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES)
+
+clean-noinstLTLIBRARIES:
+ -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+ @list='$(noinst_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libnfs.la: $(libnfs_la_OBJECTS) $(libnfs_la_DEPENDENCIES) $(EXTRA_libnfs_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libnfs_la_OBJECTS) $(libnfs_la_LIBADD) $(LIBS)
+
+libnfsconf.la: $(libnfsconf_la_OBJECTS) $(libnfsconf_la_DEPENDENCIES) $(EXTRA_libnfsconf_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libnfsconf_la_OBJECTS) $(libnfsconf_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/conffile.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnfs_la-atomicio.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnfs_la-cacheio.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnfs_la-closeall.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnfs_la-exports.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnfs_la-getport.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnfs_la-mydaemon.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnfs_la-nfs_mntent.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnfs_la-rmtab.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnfs_la-rpc_socket.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnfs_la-rpcdispatch.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnfs_la-rpcmisc.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnfs_la-strlcat.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnfs_la-strlcpy.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnfs_la-svc_create.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnfs_la-svc_socket.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnfs_la-wildmat.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnfs_la-xcommon.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnfs_la-xio.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xlog.Plo@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+libnfs_la-exports.lo: exports.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnfs_la-exports.lo -MD -MP -MF $(DEPDIR)/libnfs_la-exports.Tpo -c -o libnfs_la-exports.lo `test -f 'exports.c' || echo '$(srcdir)/'`exports.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnfs_la-exports.Tpo $(DEPDIR)/libnfs_la-exports.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='exports.c' object='libnfs_la-exports.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnfs_la-exports.lo `test -f 'exports.c' || echo '$(srcdir)/'`exports.c
+
+libnfs_la-rmtab.lo: rmtab.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnfs_la-rmtab.lo -MD -MP -MF $(DEPDIR)/libnfs_la-rmtab.Tpo -c -o libnfs_la-rmtab.lo `test -f 'rmtab.c' || echo '$(srcdir)/'`rmtab.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnfs_la-rmtab.Tpo $(DEPDIR)/libnfs_la-rmtab.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rmtab.c' object='libnfs_la-rmtab.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnfs_la-rmtab.lo `test -f 'rmtab.c' || echo '$(srcdir)/'`rmtab.c
+
+libnfs_la-xio.lo: xio.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnfs_la-xio.lo -MD -MP -MF $(DEPDIR)/libnfs_la-xio.Tpo -c -o libnfs_la-xio.lo `test -f 'xio.c' || echo '$(srcdir)/'`xio.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnfs_la-xio.Tpo $(DEPDIR)/libnfs_la-xio.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='xio.c' object='libnfs_la-xio.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnfs_la-xio.lo `test -f 'xio.c' || echo '$(srcdir)/'`xio.c
+
+libnfs_la-rpcmisc.lo: rpcmisc.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnfs_la-rpcmisc.lo -MD -MP -MF $(DEPDIR)/libnfs_la-rpcmisc.Tpo -c -o libnfs_la-rpcmisc.lo `test -f 'rpcmisc.c' || echo '$(srcdir)/'`rpcmisc.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnfs_la-rpcmisc.Tpo $(DEPDIR)/libnfs_la-rpcmisc.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rpcmisc.c' object='libnfs_la-rpcmisc.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnfs_la-rpcmisc.lo `test -f 'rpcmisc.c' || echo '$(srcdir)/'`rpcmisc.c
+
+libnfs_la-rpcdispatch.lo: rpcdispatch.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnfs_la-rpcdispatch.lo -MD -MP -MF $(DEPDIR)/libnfs_la-rpcdispatch.Tpo -c -o libnfs_la-rpcdispatch.lo `test -f 'rpcdispatch.c' || echo '$(srcdir)/'`rpcdispatch.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnfs_la-rpcdispatch.Tpo $(DEPDIR)/libnfs_la-rpcdispatch.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rpcdispatch.c' object='libnfs_la-rpcdispatch.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnfs_la-rpcdispatch.lo `test -f 'rpcdispatch.c' || echo '$(srcdir)/'`rpcdispatch.c
+
+libnfs_la-xcommon.lo: xcommon.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnfs_la-xcommon.lo -MD -MP -MF $(DEPDIR)/libnfs_la-xcommon.Tpo -c -o libnfs_la-xcommon.lo `test -f 'xcommon.c' || echo '$(srcdir)/'`xcommon.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnfs_la-xcommon.Tpo $(DEPDIR)/libnfs_la-xcommon.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='xcommon.c' object='libnfs_la-xcommon.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnfs_la-xcommon.lo `test -f 'xcommon.c' || echo '$(srcdir)/'`xcommon.c
+
+libnfs_la-wildmat.lo: wildmat.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnfs_la-wildmat.lo -MD -MP -MF $(DEPDIR)/libnfs_la-wildmat.Tpo -c -o libnfs_la-wildmat.lo `test -f 'wildmat.c' || echo '$(srcdir)/'`wildmat.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnfs_la-wildmat.Tpo $(DEPDIR)/libnfs_la-wildmat.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='wildmat.c' object='libnfs_la-wildmat.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnfs_la-wildmat.lo `test -f 'wildmat.c' || echo '$(srcdir)/'`wildmat.c
+
+libnfs_la-mydaemon.lo: mydaemon.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnfs_la-mydaemon.lo -MD -MP -MF $(DEPDIR)/libnfs_la-mydaemon.Tpo -c -o libnfs_la-mydaemon.lo `test -f 'mydaemon.c' || echo '$(srcdir)/'`mydaemon.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnfs_la-mydaemon.Tpo $(DEPDIR)/libnfs_la-mydaemon.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mydaemon.c' object='libnfs_la-mydaemon.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnfs_la-mydaemon.lo `test -f 'mydaemon.c' || echo '$(srcdir)/'`mydaemon.c
+
+libnfs_la-rpc_socket.lo: rpc_socket.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnfs_la-rpc_socket.lo -MD -MP -MF $(DEPDIR)/libnfs_la-rpc_socket.Tpo -c -o libnfs_la-rpc_socket.lo `test -f 'rpc_socket.c' || echo '$(srcdir)/'`rpc_socket.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnfs_la-rpc_socket.Tpo $(DEPDIR)/libnfs_la-rpc_socket.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rpc_socket.c' object='libnfs_la-rpc_socket.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnfs_la-rpc_socket.lo `test -f 'rpc_socket.c' || echo '$(srcdir)/'`rpc_socket.c
+
+libnfs_la-getport.lo: getport.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnfs_la-getport.lo -MD -MP -MF $(DEPDIR)/libnfs_la-getport.Tpo -c -o libnfs_la-getport.lo `test -f 'getport.c' || echo '$(srcdir)/'`getport.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnfs_la-getport.Tpo $(DEPDIR)/libnfs_la-getport.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='getport.c' object='libnfs_la-getport.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnfs_la-getport.lo `test -f 'getport.c' || echo '$(srcdir)/'`getport.c
+
+libnfs_la-svc_socket.lo: svc_socket.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnfs_la-svc_socket.lo -MD -MP -MF $(DEPDIR)/libnfs_la-svc_socket.Tpo -c -o libnfs_la-svc_socket.lo `test -f 'svc_socket.c' || echo '$(srcdir)/'`svc_socket.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnfs_la-svc_socket.Tpo $(DEPDIR)/libnfs_la-svc_socket.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='svc_socket.c' object='libnfs_la-svc_socket.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnfs_la-svc_socket.lo `test -f 'svc_socket.c' || echo '$(srcdir)/'`svc_socket.c
+
+libnfs_la-cacheio.lo: cacheio.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnfs_la-cacheio.lo -MD -MP -MF $(DEPDIR)/libnfs_la-cacheio.Tpo -c -o libnfs_la-cacheio.lo `test -f 'cacheio.c' || echo '$(srcdir)/'`cacheio.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnfs_la-cacheio.Tpo $(DEPDIR)/libnfs_la-cacheio.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cacheio.c' object='libnfs_la-cacheio.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnfs_la-cacheio.lo `test -f 'cacheio.c' || echo '$(srcdir)/'`cacheio.c
+
+libnfs_la-closeall.lo: closeall.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnfs_la-closeall.lo -MD -MP -MF $(DEPDIR)/libnfs_la-closeall.Tpo -c -o libnfs_la-closeall.lo `test -f 'closeall.c' || echo '$(srcdir)/'`closeall.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnfs_la-closeall.Tpo $(DEPDIR)/libnfs_la-closeall.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='closeall.c' object='libnfs_la-closeall.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnfs_la-closeall.lo `test -f 'closeall.c' || echo '$(srcdir)/'`closeall.c
+
+libnfs_la-nfs_mntent.lo: nfs_mntent.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnfs_la-nfs_mntent.lo -MD -MP -MF $(DEPDIR)/libnfs_la-nfs_mntent.Tpo -c -o libnfs_la-nfs_mntent.lo `test -f 'nfs_mntent.c' || echo '$(srcdir)/'`nfs_mntent.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnfs_la-nfs_mntent.Tpo $(DEPDIR)/libnfs_la-nfs_mntent.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nfs_mntent.c' object='libnfs_la-nfs_mntent.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnfs_la-nfs_mntent.lo `test -f 'nfs_mntent.c' || echo '$(srcdir)/'`nfs_mntent.c
+
+libnfs_la-svc_create.lo: svc_create.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnfs_la-svc_create.lo -MD -MP -MF $(DEPDIR)/libnfs_la-svc_create.Tpo -c -o libnfs_la-svc_create.lo `test -f 'svc_create.c' || echo '$(srcdir)/'`svc_create.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnfs_la-svc_create.Tpo $(DEPDIR)/libnfs_la-svc_create.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='svc_create.c' object='libnfs_la-svc_create.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnfs_la-svc_create.lo `test -f 'svc_create.c' || echo '$(srcdir)/'`svc_create.c
+
+libnfs_la-atomicio.lo: atomicio.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnfs_la-atomicio.lo -MD -MP -MF $(DEPDIR)/libnfs_la-atomicio.Tpo -c -o libnfs_la-atomicio.lo `test -f 'atomicio.c' || echo '$(srcdir)/'`atomicio.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnfs_la-atomicio.Tpo $(DEPDIR)/libnfs_la-atomicio.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='atomicio.c' object='libnfs_la-atomicio.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnfs_la-atomicio.lo `test -f 'atomicio.c' || echo '$(srcdir)/'`atomicio.c
+
+libnfs_la-strlcat.lo: strlcat.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnfs_la-strlcat.lo -MD -MP -MF $(DEPDIR)/libnfs_la-strlcat.Tpo -c -o libnfs_la-strlcat.lo `test -f 'strlcat.c' || echo '$(srcdir)/'`strlcat.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnfs_la-strlcat.Tpo $(DEPDIR)/libnfs_la-strlcat.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='strlcat.c' object='libnfs_la-strlcat.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnfs_la-strlcat.lo `test -f 'strlcat.c' || echo '$(srcdir)/'`strlcat.c
+
+libnfs_la-strlcpy.lo: strlcpy.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libnfs_la-strlcpy.lo -MD -MP -MF $(DEPDIR)/libnfs_la-strlcpy.Tpo -c -o libnfs_la-strlcpy.lo `test -f 'strlcpy.c' || echo '$(srcdir)/'`strlcpy.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnfs_la-strlcpy.Tpo $(DEPDIR)/libnfs_la-strlcpy.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='strlcpy.c' object='libnfs_la-strlcpy.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnfs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnfs_la-strlcpy.lo `test -f 'strlcpy.c' || echo '$(srcdir)/'`strlcpy.c
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LIBRARIES) $(LTLIBRARIES)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+ -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES)
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLIBRARIES \
+ clean-noinstLTLIBRARIES mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/conffile.Plo
+ -rm -f ./$(DEPDIR)/libnfs_la-atomicio.Plo
+ -rm -f ./$(DEPDIR)/libnfs_la-cacheio.Plo
+ -rm -f ./$(DEPDIR)/libnfs_la-closeall.Plo
+ -rm -f ./$(DEPDIR)/libnfs_la-exports.Plo
+ -rm -f ./$(DEPDIR)/libnfs_la-getport.Plo
+ -rm -f ./$(DEPDIR)/libnfs_la-mydaemon.Plo
+ -rm -f ./$(DEPDIR)/libnfs_la-nfs_mntent.Plo
+ -rm -f ./$(DEPDIR)/libnfs_la-rmtab.Plo
+ -rm -f ./$(DEPDIR)/libnfs_la-rpc_socket.Plo
+ -rm -f ./$(DEPDIR)/libnfs_la-rpcdispatch.Plo
+ -rm -f ./$(DEPDIR)/libnfs_la-rpcmisc.Plo
+ -rm -f ./$(DEPDIR)/libnfs_la-strlcat.Plo
+ -rm -f ./$(DEPDIR)/libnfs_la-strlcpy.Plo
+ -rm -f ./$(DEPDIR)/libnfs_la-svc_create.Plo
+ -rm -f ./$(DEPDIR)/libnfs_la-svc_socket.Plo
+ -rm -f ./$(DEPDIR)/libnfs_la-wildmat.Plo
+ -rm -f ./$(DEPDIR)/libnfs_la-xcommon.Plo
+ -rm -f ./$(DEPDIR)/libnfs_la-xio.Plo
+ -rm -f ./$(DEPDIR)/xlog.Plo
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/conffile.Plo
+ -rm -f ./$(DEPDIR)/libnfs_la-atomicio.Plo
+ -rm -f ./$(DEPDIR)/libnfs_la-cacheio.Plo
+ -rm -f ./$(DEPDIR)/libnfs_la-closeall.Plo
+ -rm -f ./$(DEPDIR)/libnfs_la-exports.Plo
+ -rm -f ./$(DEPDIR)/libnfs_la-getport.Plo
+ -rm -f ./$(DEPDIR)/libnfs_la-mydaemon.Plo
+ -rm -f ./$(DEPDIR)/libnfs_la-nfs_mntent.Plo
+ -rm -f ./$(DEPDIR)/libnfs_la-rmtab.Plo
+ -rm -f ./$(DEPDIR)/libnfs_la-rpc_socket.Plo
+ -rm -f ./$(DEPDIR)/libnfs_la-rpcdispatch.Plo
+ -rm -f ./$(DEPDIR)/libnfs_la-rpcmisc.Plo
+ -rm -f ./$(DEPDIR)/libnfs_la-strlcat.Plo
+ -rm -f ./$(DEPDIR)/libnfs_la-strlcpy.Plo
+ -rm -f ./$(DEPDIR)/libnfs_la-svc_create.Plo
+ -rm -f ./$(DEPDIR)/libnfs_la-svc_socket.Plo
+ -rm -f ./$(DEPDIR)/libnfs_la-wildmat.Plo
+ -rm -f ./$(DEPDIR)/libnfs_la-xcommon.Plo
+ -rm -f ./$(DEPDIR)/libnfs_la-xio.Plo
+ -rm -f ./$(DEPDIR)/xlog.Plo
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-noinstLIBRARIES \
+ clean-noinstLTLIBRARIES cscopelist-am ctags ctags-am distclean \
+ distclean-compile distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am install-man \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-strip installcheck installcheck-am installdirs \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
+ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/support/nfs/atomicio.c b/support/nfs/atomicio.c
new file mode 100644
index 0000000..0e81838
--- /dev/null
+++ b/support/nfs/atomicio.c
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2002 Marius Aamodt Eriksen <marius@monkey.org>
+ * Copyright (c) 1995,1999 Theo de Raadt. All rights reserved.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "nfslib.h"
+
+/*
+ * ensure all of data on socket comes through. f==read || f==write
+ */
+ssize_t atomicio(ssize_t(*f) (int, void *, size_t), int fd, void *_s, size_t n)
+{
+ char *s = _s;
+ ssize_t res, pos = 0;
+
+ while ((ssize_t)n > pos) {
+ res = (f) (fd, s + pos, n - pos);
+ switch (res) {
+ case -1:
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ /* FALLTHRU */
+ case 0:
+ if (pos != 0)
+ return pos;
+ return res;
+ default:
+ pos += res;
+ }
+ }
+ return pos;
+}
diff --git a/support/nfs/cacheio.c b/support/nfs/cacheio.c
new file mode 100644
index 0000000..bd4da0e
--- /dev/null
+++ b/support/nfs/cacheio.c
@@ -0,0 +1,253 @@
+/*
+ * support/nfs/cacheio.c
+ * support IO on the cache channel files in 2.5 and beyond.
+ * These use 'qwords' which are like words, but with a little quoting.
+ *
+ */
+
+
+/*
+ * Support routines for text-based upcalls.
+ * Fields are separated by spaces.
+ * Fields are either mangled to quote space tab newline slosh with slosh
+ * or a hexified with a leading \x
+ * Record is terminated with newline.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <nfslib.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdio_ext.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <time.h>
+#include <errno.h>
+
+void qword_add(char **bpp, int *lp, char *str)
+{
+ char *bp = *bpp;
+ int len = *lp;
+ char c;
+
+ if (len < 0) return;
+
+ while ((c=*str++) && len > 0)
+ switch(c) {
+ case ' ':
+ case '\t':
+ case '\n':
+ case '\\':
+ if (len >= 4) {
+ *bp++ = '\\';
+ *bp++ = '0' + ((c & 0300)>>6);
+ *bp++ = '0' + ((c & 0070)>>3);
+ *bp++ = '0' + ((c & 0007)>>0);
+ }
+ len -= 4;
+ break;
+ default:
+ *bp++ = c;
+ len--;
+ }
+ if (c || len <1) len = -1;
+ else {
+ *bp++ = ' ';
+ len--;
+ }
+ *bpp = bp;
+ *lp = len;
+}
+
+void qword_addhex(char **bpp, int *lp, char *buf, int blen)
+{
+ char *bp = *bpp;
+ int len = *lp;
+
+ if (len < 0) return;
+
+ if (len > 2) {
+ *bp++ = '\\';
+ *bp++ = 'x';
+ len -= 2;
+ while (blen && len >= 2) {
+ unsigned char c = *buf++;
+ *bp++ = '0' + ((c&0xf0)>>4) + (c>=0xa0)*('a'-'9'-1);
+ *bp++ = '0' + (c&0x0f) + ((c&0x0f)>=0x0a)*('a'-'9'-1);
+ len -= 2;
+ blen--;
+ }
+ }
+ if (blen || len<1) len = -1;
+ else {
+ *bp++ = ' ';
+ len--;
+ }
+ *bpp = bp;
+ *lp = len;
+}
+
+void qword_addint(char **bpp, int *lp, int n)
+{
+ int len;
+
+ len = snprintf(*bpp, *lp, "%d ", n);
+ if (len > *lp)
+ len = *lp;
+ *bpp += len;
+ *lp -= len;
+}
+
+void qword_adduint(char **bpp, int *lp, unsigned int n)
+{
+ int len;
+
+ len = snprintf(*bpp, *lp, "%u ", n);
+ if (len > *lp)
+ len = *lp;
+ *bpp += len;
+ *lp -= len;
+}
+
+void qword_addeol(char **bpp, int *lp)
+{
+ if (*lp <= 0)
+ return;
+ **bpp = '\n';
+ (*bpp)++;
+ (*lp)--;
+}
+
+#define isodigit(c) (isdigit(c) && c <= '7')
+int qword_get(char **bpp, char *dest, int bufsize)
+{
+ /* return bytes copied, or -1 on error */
+ char *bp = *bpp;
+ int len = 0;
+
+ while (*bp == ' ') bp++;
+
+ if (bp[0] == '\\' && bp[1] == 'x') {
+ /* HEX STRING */
+ bp += 2;
+ while (isxdigit(bp[0]) && isxdigit(bp[1]) && len < bufsize) {
+ int byte = isdigit(*bp) ? *bp-'0' : toupper(*bp)-'A'+10;
+ bp++;
+ byte <<= 4;
+ byte |= isdigit(*bp) ? *bp-'0' : toupper(*bp)-'A'+10;
+ *dest++ = byte;
+ bp++;
+ len++;
+ }
+ } else {
+ /* text with \nnn octal quoting */
+ while (*bp != ' ' && *bp != '\n' && *bp && len < bufsize-1) {
+ if (*bp == '\\' &&
+ isodigit(bp[1]) && (bp[1] <= '3') &&
+ isodigit(bp[2]) &&
+ isodigit(bp[3])) {
+ int byte = (*++bp -'0');
+ bp++;
+ byte = (byte << 3) | (*bp++ - '0');
+ byte = (byte << 3) | (*bp++ - '0');
+ *dest++ = byte;
+ len++;
+ } else {
+ *dest++ = *bp++;
+ len++;
+ }
+ }
+ }
+
+ if (*bp != ' ' && *bp != '\n' && *bp != '\0')
+ return -1;
+ while (*bp == ' ') bp++;
+ *bpp = bp;
+ *dest = '\0';
+ return len;
+}
+
+int qword_get_int(char **bpp, int *anint)
+{
+ char buf[50];
+ char *ep;
+ int rv;
+ int len = qword_get(bpp, buf, 50);
+ if (len < 0) return -1;
+ if (len ==0) return -1;
+ rv = strtol(buf, &ep, 0);
+ if (*ep) return -1;
+ *anint = rv;
+ return 0;
+}
+
+int qword_get_uint(char **bpp, unsigned int *anint)
+{
+ char buf[50];
+ char *ep;
+ unsigned int rv;
+ int len = qword_get(bpp, buf, 50);
+ if (len < 0) return -1;
+ if (len ==0) return -1;
+ rv = strtoul(buf, &ep, 0);
+ if (*ep) return -1;
+ *anint = rv;
+ return 0;
+}
+
+/* flush the kNFSd caches.
+ * Set the flush time to the mtime of the etab state file or
+ * if force, to now.
+ * the caches to flush are:
+ * auth.unix.ip nfsd.export nfsd.fh
+ */
+
+void
+cache_flush(void)
+{
+ int c;
+ char stime[32];
+ char path[200];
+ time_t now;
+ /* Note: the order of these caches is important.
+ * They need to be flushed in dependancy order. So
+ * a cache that references items in another cache,
+ * as nfsd.fh entries reference items in nfsd.export,
+ * must be flushed before the cache that it references.
+ */
+ static char *cachelist[] = {
+ "auth.unix.ip",
+ "auth.unix.gid",
+ "nfsd.fh",
+ "nfsd.export",
+ NULL
+ };
+ now = time(0);
+
+ /* Since v4.16-rc2-3-g3b68e6ee3cbd the timestamp written is ignored.
+ * It is safest always to flush caches if there is any doubt.
+ * For earlier kernels, writing the next second from now is
+ * the best we can do.
+ */
+ sprintf(stime, "%" PRId64 "\n", (int64_t)now+1);
+ for (c=0; cachelist[c]; c++) {
+ int fd;
+ sprintf(path, "/proc/net/rpc/%s/flush", cachelist[c]);
+ fd = open(path, O_RDWR);
+ if (fd >= 0) {
+ if (write(fd, stime, strlen(stime)) != (ssize_t)strlen(stime)) {
+ xlog_warn("Writing to '%s' failed: errno %d (%s)",
+ path, errno, strerror(errno));
+ }
+ close(fd);
+ }
+ }
+}
diff --git a/support/nfs/closeall.c b/support/nfs/closeall.c
new file mode 100644
index 0000000..e07253e
--- /dev/null
+++ b/support/nfs/closeall.c
@@ -0,0 +1,39 @@
+/*
+ * support/nfs/closeall.c
+ * Close all file descriptors greater than some limit,
+ * Use readdir "/proc/self/fd" to avoid excess close(2) calls.
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <errno.h>
+
+#include "nfslib.h"
+
+void
+closeall(int min)
+{
+ char *endp;
+ long n;
+ DIR *dir = opendir("/proc/self/fd");
+
+ if (dir != NULL) {
+ int dfd = dirfd(dir);
+ struct dirent *d;
+
+ while ((d = readdir(dir)) != NULL) {
+ errno = 0;
+ n = strtol(d->d_name, &endp, 10);
+ if (!errno && *endp == '\0' && endp != d->d_name &&
+ n >= min && n != dfd)
+ (void) close(n);
+ }
+ closedir(dir);
+ } else {
+ int fd = sysconf(_SC_OPEN_MAX);
+ while (--fd >= min)
+ if(fd >= 0)
+ (void) close(fd);
+ }
+}
diff --git a/support/nfs/conffile.c b/support/nfs/conffile.c
new file mode 100644
index 0000000..fd4a17a
--- /dev/null
+++ b/support/nfs/conffile.c
@@ -0,0 +1,2345 @@
+/* $OpenBSD: conf.c,v 1.55 2003/06/03 14:28:16 ho Exp $ */
+/* $EOM: conf.c,v 1.48 2000/12/04 02:04:29 angelos Exp $ */
+
+/*
+ * Copyright (c) 1998, 1999, 2000, 2001 Niklas Hallqvist. All rights reserved.
+ * Copyright (c) 2000, 2001, 2002 Håkan Olsson. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * This code was written under funding by Ericsson Radio Systems.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <err.h>
+#include <syslog.h>
+#include <libgen.h>
+#include <sys/file.h>
+#include <time.h>
+#include <dirent.h>
+
+#include "conffile.h"
+#include "xlog.h"
+
+#define CONF_FILE_EXT ".conf"
+#define CONF_FILE_EXT_LEN ((int) (sizeof(CONF_FILE_EXT) - 1))
+
+#pragma GCC visibility push(hidden)
+
+static void conf_load_defaults(void);
+static char * conf_readfile(const char *path);
+static int conf_set(int , const char *, const char *, const char *,
+ const char *, int , int );
+static void conf_parse(int trans, char *buf,
+ char **section, char **subsection, const char *filename);
+
+struct conf_trans {
+ TAILQ_ENTRY (conf_trans) link;
+ int trans;
+ enum conf_op { CONF_SET, CONF_REMOVE, CONF_REMOVE_SECTION } op;
+ char *section;
+ char *arg;
+ char *tag;
+ char *value;
+ int override;
+ int is_default;
+};
+
+TAILQ_HEAD (conf_trans_head, conf_trans) conf_trans_queue;
+
+/*
+ * Radix-64 Encoding.
+ */
+
+static const uint8_t asc2bin[] =
+{
+ 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 62, 255, 255, 255, 63,
+ 52, 53, 54, 55, 56, 57, 58, 59,
+ 60, 61, 255, 255, 255, 255, 255, 255,
+ 255, 0, 1, 2, 3, 4, 5, 6,
+ 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22,
+ 23, 24, 25, 255, 255, 255, 255, 255,
+ 255, 26, 27, 28, 29, 30, 31, 32,
+ 33, 34, 35, 36, 37, 38, 39, 40,
+ 41, 42, 43, 44, 45, 46, 47, 48,
+ 49, 50, 51, 255, 255, 255, 255, 255
+};
+
+struct conf_binding {
+ LIST_ENTRY (conf_binding) link;
+ char *section;
+ char *arg;
+ char *tag;
+ char *value;
+ int is_default;
+};
+
+LIST_HEAD (conf_bindings, conf_binding) conf_bindings[256];
+
+const char *modified_by = NULL;
+
+static __inline__ uint8_t
+conf_hash(const char *s)
+{
+ uint8_t hash = 0;
+
+ while (*s) {
+ hash = ((hash << 1) | (hash >> 7)) ^ tolower (*s);
+ s++;
+ }
+ return hash;
+}
+
+/*
+ * free all the component parts of a conf_binding struct
+ */
+static void free_confbind(struct conf_binding *cb)
+{
+ if (!cb)
+ return;
+ if (cb->section)
+ free(cb->section);
+ if (cb->arg)
+ free(cb->arg);
+ if (cb->tag)
+ free(cb->tag);
+ if (cb->value)
+ free(cb->value);
+ free(cb);
+}
+
+static void free_conftrans(struct conf_trans *ct)
+{
+ if (!ct)
+ return;
+ if (ct->section)
+ free(ct->section);
+ if (ct->arg)
+ free(ct->arg);
+ if (ct->tag)
+ free(ct->tag);
+ if (ct->value)
+ free(ct->value);
+ free(ct);
+}
+
+/*
+ * Insert a tag-value combination from LINE (the equal sign is at POS)
+ */
+static int
+conf_remove_now(const char *section, const char *tag)
+{
+ struct conf_binding *cb, *next;
+
+ cb = LIST_FIRST(&conf_bindings[conf_hash (section)]);
+ for (; cb; cb = next) {
+ next = LIST_NEXT(cb, link);
+ if (strcasecmp(cb->section, section) == 0
+ && strcasecmp(cb->tag, tag) == 0) {
+ LIST_REMOVE(cb, link);
+ xlog(LOG_INFO,"[%s]:%s->%s removed", section, tag, cb->value);
+ free_confbind(cb);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static int
+conf_remove_section_now(const char *section)
+{
+ struct conf_binding *cb, *next;
+ int unseen = 1;
+
+ cb = LIST_FIRST(&conf_bindings[conf_hash (section)]);
+ for (; cb; cb = next) {
+ next = LIST_NEXT(cb, link);
+ if (strcasecmp(cb->section, section) == 0) {
+ unseen = 0;
+ LIST_REMOVE(cb, link);
+ xlog(LOG_INFO, "[%s]:%s->%s removed", section, cb->tag, cb->value);
+ free_confbind(cb);
+ }
+ }
+ return unseen;
+}
+
+/*
+ * Insert a tag-value combination from LINE (the equal sign is at POS)
+ * into SECTION of our configuration database.
+ */
+static int
+conf_set_now(const char *section, const char *arg, const char *tag,
+ const char *value, int override, int is_default)
+{
+ struct conf_binding *node = 0;
+
+ if (override)
+ conf_remove_now(section, tag);
+ else if (conf_get_section(section, arg, tag)) {
+ if (!is_default) {
+ xlog(LOG_INFO, "conf_set: duplicate tag [%s]:%s, ignoring...",
+ section, tag);
+ }
+ return 1;
+ }
+ node = calloc(1, sizeof *node);
+ if (!node) {
+ xlog_warn("conf_set: calloc (1, %lu) failed", (unsigned long)sizeof *node);
+ return 1;
+ }
+ node->section = strdup(section);
+ if (arg)
+ node->arg = strdup(arg);
+ node->tag = strdup(tag);
+ node->value = strdup(value);
+ node->is_default = is_default;
+ LIST_INSERT_HEAD(&conf_bindings[conf_hash (section)], node, link);
+ return 0;
+}
+
+/* Attempt to construct a relative path to the new file */
+static char *
+relative_path(const char *oldfile, const char *newfile)
+{
+ char *tmpcopy = NULL;
+ char *dir = NULL;
+ char *relpath = NULL;
+ size_t pathlen;
+
+ if (!newfile)
+ return strdup(oldfile);
+
+ if (newfile[0] == '/')
+ return strdup(newfile);
+
+ tmpcopy = strdup(oldfile);
+ if (!tmpcopy)
+ goto mem_err;
+
+ dir = dirname(tmpcopy);
+
+ pathlen = strlen(dir) + strlen(newfile) + 2;
+ relpath = calloc(1, pathlen);
+ if (!relpath)
+ goto mem_err;
+
+ snprintf(relpath, pathlen, "%s/%s", dir, newfile);
+
+ free(tmpcopy);
+ return relpath;
+mem_err:
+ if (tmpcopy)
+ free(tmpcopy);
+ return NULL;
+}
+
+
+/*
+ * Parse the line LINE of SZ bytes. Skip Comments, recognize section
+ * headers and feed tag-value pairs into our configuration database.
+ */
+static void
+conf_parse_line(int trans, char *line, const char *filename, int lineno, char **section, char **subsection)
+{
+ char *val, *ptr;
+ char *inc_section = NULL, *inc_subsection = NULL;
+ char *relpath, *subconf;
+
+ /* Strip off any leading blanks */
+ while (isspace(*line))
+ line++;
+
+ /* Ignore blank lines */
+ if (*line == '\0')
+ return;
+
+ /* Lines starting with '#' or ';' are comments. */
+ if (*line == '#' || *line == ';')
+ return;
+
+ /* '[section]' parsing... */
+ if (*line == '[') {
+ line++;
+
+ if (*section) {
+ free(*section);
+ *section = NULL;
+ }
+ if (*subsection) {
+ free(*subsection);
+ *subsection = NULL;
+ }
+
+ /* Strip off any blanks after '[' */
+ while (isblank(*line))
+ line++;
+
+ /* find the closing ] */
+ ptr = strchr(line, ']');
+ if (ptr == NULL) {
+ xlog_warn("config error at %s:%d: "
+ "non-matched ']', ignoring until next section",
+ filename, lineno);
+ return;
+ }
+
+ /* just ignore everything after the closing ] */
+ *(ptr--) = '\0';
+
+ /* Strip off any blanks before ']' */
+ while (ptr >= line && isblank(*ptr))
+ *(ptr--)='\0';
+
+ /* look for an arg to split from the section name */
+ val = strchr(line, '"');
+ if (val != NULL) {
+ ptr = val - 1;
+ *(val++) = '\0';
+
+ /* trim away any whitespace before the " */
+ while (ptr > line && isblank(*ptr))
+ *(ptr--)='\0';
+ }
+
+ /* copy the section name */
+ *section = strdup(line);
+ if (!*section) {
+ xlog_warn("config error at %s:%d:"
+ "malloc failed", filename, lineno);
+ return;
+ }
+
+ /* there is no arg, we are done */
+ if (val == NULL)
+ return;
+
+ /* check for the closing " */
+ ptr = strchr(val, '"');
+ if (ptr == NULL) {
+ xlog_warn("config error at %s:%d: "
+ "non-matched '\"', ignoring until next section",
+ filename, lineno);
+ return;
+ }
+ *ptr = '\0';
+ *subsection = strdup(val);
+ if (!*subsection)
+ xlog_warn("config error at %s:%d: "
+ "malloc failed", filename, lineno);
+ return;
+ }
+
+ /* Deal with assignments. */
+ ptr = strchr(line, '=');
+
+ /* not an assignment line */
+ if (ptr == NULL) {
+ /* Other non-empty lines are weird. */
+ if (line[strspn(line, " \t")])
+ xlog_warn("config error at %s:%d: "
+ "line not empty and not an assignment",
+ filename, lineno);
+ return;
+ }
+
+ /* If no section, we are ignoring the line. */
+ if (!*section) {
+ xlog_warn("config error at %s:%d: "
+ "ignoring line not in a section",
+ filename, lineno);
+ return;
+ }
+
+ val = ptr + 1;
+ *(ptr--) = '\0';
+
+ /* strip spaces before and after the = */
+ while (ptr >= line && isblank(*ptr))
+ *(ptr--)='\0';
+ while (*val != '\0' && isblank(*val))
+ val++;
+
+ if (*val == '"') {
+ val++;
+ ptr = strchr(val, '"');
+ if (ptr == NULL) {
+ xlog_warn("config error at %s:%d: "
+ "unmatched quotes",filename, lineno);
+ return;
+ }
+ *ptr = '\0';
+ } else
+ if (*val == '\'') {
+ val++;
+ ptr = strchr(val, '\'');
+ if (ptr == NULL) {
+ xlog_warn("config error at %s:%d: "
+ "unmatched quotes", filename, lineno);
+ return;
+ }
+ *ptr = '\0';
+ } else {
+ /* Trim any trailing spaces and comments */
+ if ((ptr=strchr(val, '#'))!=NULL)
+ *ptr = '\0';
+ if ((ptr=strchr(val, ';'))!=NULL)
+ *ptr = '\0';
+
+ ptr = val + strlen(val) - 1;
+ while (ptr > val && isspace(*ptr))
+ *(ptr--) = '\0';
+ }
+
+ if (*line == '\0') {
+ xlog_warn("config error at %s:%d: "
+ "missing tag in assignment", filename, lineno);
+ return;
+ }
+
+ if (strcasecmp(line, "include")==0) {
+ /* load and parse subordinate config files */
+ _Bool optional = false;
+
+ if (val && *val == '-') {
+ optional = true;
+ val++;
+ }
+
+ relpath = relative_path(filename, val);
+ if (relpath == NULL) {
+ if (!optional)
+ xlog_warn("config error at %s:%d: error loading included config",
+ filename, lineno);
+ return;
+ }
+
+ subconf = conf_readfile(relpath);
+ if (subconf == NULL) {
+ if (!optional)
+ xlog_warn("config error at %s:%d: error loading included config",
+ filename, lineno);
+ if (relpath)
+ free(relpath);
+ return;
+ }
+
+ /* copy the section data so the included file can inherit it
+ * without accidentally changing it for us */
+ if (*section != NULL) {
+ inc_section = strdup(*section);
+ if (*subsection != NULL)
+ inc_subsection = strdup(*subsection);
+ }
+
+ conf_parse(trans, subconf, &inc_section, &inc_subsection, relpath);
+
+ if (inc_section)
+ free(inc_section);
+ if (inc_subsection)
+ free(inc_subsection);
+ if (relpath)
+ free(relpath);
+ free(subconf);
+ } else {
+ /* XXX Perhaps should we not ignore errors? */
+ conf_set(trans, *section, *subsection, line, val, 1, 0);
+ }
+}
+
+/* Parse the mapped configuration file. */
+static void
+conf_parse(int trans, char *buf, char **section, char **subsection, const char *filename)
+{
+ char *cp = buf;
+ char *bufend = NULL;
+ char *line;
+ int lineno = 0;
+
+ line = cp;
+ bufend = buf + strlen(buf);
+ while (cp < bufend) {
+ if (*cp == '\n') {
+ /* Check for escaped newlines. */
+ if (cp > buf && *(cp - 1) == '\\')
+ *(cp - 1) = *cp = ' ';
+ else {
+ *cp = '\0';
+ lineno++;
+ conf_parse_line(trans, line, filename, lineno, section, subsection);
+ line = cp + 1;
+ }
+ }
+ cp++;
+ }
+ if (cp != line)
+ xlog_warn("conf_parse: last line non-terminated, ignored.");
+}
+
+static void
+conf_load_defaults(void)
+{
+ /* No defaults */
+ return;
+}
+
+static char *
+conf_readfile(const char *path)
+{
+ struct stat sb;
+ if (!path) {
+ xlog(L_ERROR, "conf_readfile: no path given");
+ return NULL;
+ }
+
+ if ((stat (path, &sb) == 0) || (errno != ENOENT)) {
+ char *new_conf_addr = NULL;
+ off_t sz;
+ int fd = open (path, O_RDONLY, 0);
+
+ if (fd == -1) {
+ xlog_warn("conf_readfile: open (\"%s\", O_RDONLY) failed", path);
+ return NULL;
+ }
+
+ /* Grab a shared lock to ensure its not mid-rewrite */
+ if (flock(fd, LOCK_SH)) {
+ xlog_warn("conf_readfile: attempt to grab read lock failed: %s",
+ strerror(errno));
+ goto fail;
+ }
+
+ /* only after we have the lock, check the file size ready to read it */
+ sz = lseek(fd, 0, SEEK_END);
+ if (sz < 0) {
+ xlog_warn("conf_readfile: unable to determine file size: %s",
+ strerror(errno));
+ goto fail;
+ }
+ lseek(fd, 0, SEEK_SET);
+
+ new_conf_addr = malloc(sz+1);
+ if (!new_conf_addr) {
+ xlog_warn("conf_readfile: malloc (%lu) failed", (unsigned long)sz);
+ goto fail;
+ }
+
+ /* XXX I assume short reads won't happen here. */
+ if (read (fd, new_conf_addr, sz) != (int)sz) {
+ xlog_warn("conf_readfile: read (%d, %p, %lu) failed",
+ fd, new_conf_addr, (unsigned long)sz);
+ goto fail;
+ }
+ close(fd);
+
+ /* XXX Should we not care about errors and rollback? */
+ new_conf_addr[sz] = '\0';
+ return new_conf_addr;
+ fail:
+ close(fd);
+ if (new_conf_addr)
+ free(new_conf_addr);
+ }
+ return NULL;
+}
+
+/* remove and free up any existing config state */
+static void conf_free_bindings(void)
+{
+ unsigned int i;
+ for (i = 0; i < sizeof conf_bindings / sizeof conf_bindings[0]; i++) {
+ struct conf_binding *cb, *next;
+
+ cb = LIST_FIRST(&conf_bindings[i]);
+ for (; cb; cb = next) {
+ next = LIST_NEXT(cb, link);
+ LIST_REMOVE(cb, link);
+ free_confbind(cb);
+ }
+ LIST_INIT(&conf_bindings[i]);
+ }
+}
+
+static int
+conf_load_files(int trans, const char *conf_file)
+{
+ char *conf_data;
+ char *section = NULL;
+ char *subsection = NULL;
+
+ conf_data = conf_readfile(conf_file);
+ if (conf_data == NULL)
+ return 1;
+
+ /* Load default configuration values. */
+ conf_load_defaults();
+
+ /* Parse config contents into the transaction queue */
+ conf_parse(trans, conf_data, &section, &subsection, conf_file);
+ if (section)
+ free(section);
+ if (subsection)
+ free(subsection);
+ free(conf_data);
+
+ return 0;
+}
+/* Open the config file and map it into our address space, then parse it. */
+static int
+conf_load_file(const char *conf_file)
+{
+ int trans;
+ char * conf_data;
+
+ trans = conf_begin();
+ conf_data = conf_readfile(conf_file);
+
+ if (conf_data == NULL)
+ return 1;
+
+ /* Load default configuration values. */
+ conf_load_defaults();
+
+ /* Parse config contents into the transaction queue */
+ char *section = NULL;
+ char *subsection = NULL;
+ conf_parse(trans, conf_data, &section, &subsection, conf_file);
+ if (section) free(section);
+ if (subsection) free(subsection);
+ free(conf_data);
+
+ /* Free potential existing configuration. */
+ conf_free_bindings();
+
+ /* Apply the new configuration values */
+ conf_end(trans, 1);
+ return 0;
+}
+
+static void
+conf_init_dir(const char *conf_file)
+{
+ struct dirent **namelist = NULL;
+ char *dname, fname[PATH_MAX], *cname;
+ int n = 0, nfiles = 0, i, fname_len, dname_len;
+ int trans, rv, path_len;
+
+ dname = malloc(strlen(conf_file) + 3);
+ if (dname == NULL) {
+ xlog(L_WARNING, "conf_init_dir: malloc: %s", strerror(errno));
+ return;
+ }
+ sprintf(dname, "%s.d", conf_file);
+
+ n = scandir(dname, &namelist, NULL, versionsort);
+ if (n < 0) {
+ if (errno != ENOENT) {
+ xlog(L_WARNING, "conf_init_dir: scandir %s: %s",
+ dname, strerror(errno));
+ }
+ free(dname);
+ return;
+ } else if (n == 0) {
+ free(dname);
+ return;
+ }
+
+ trans = conf_begin();
+ dname_len = strlen(dname);
+ for (i = 0; i < n; i++ ) {
+ struct dirent *d = namelist[i];
+
+ switch (d->d_type) {
+ case DT_UNKNOWN:
+ case DT_REG:
+ case DT_LNK:
+ break;
+ default:
+ continue;
+ }
+ if (*d->d_name == '.')
+ continue;
+
+ fname_len = strlen(d->d_name);
+ path_len = (fname_len + dname_len);
+ if (!fname_len || path_len > PATH_MAX) {
+ xlog(L_WARNING, "conf_init_dir: Too long file name: %s in %s",
+ d->d_name, dname);
+ continue;
+ }
+
+ /*
+ * Check the naming of the file. Only process files
+ * that end with CONF_FILE_EXT
+ */
+ if (fname_len <= CONF_FILE_EXT_LEN) {
+ xlog(D_GENERAL, "conf_init_dir: %s: name too short",
+ d->d_name);
+ continue;
+ }
+ cname = (d->d_name + (fname_len - CONF_FILE_EXT_LEN));
+ if (strcmp(cname, CONF_FILE_EXT) != 0) {
+ xlog(D_GENERAL, "conf_init_dir: %s: invalid file extension",
+ d->d_name);
+ continue;
+ }
+
+ rv = snprintf(fname, PATH_MAX, "%s/%s", dname, d->d_name);
+ if (rv < path_len) {
+ xlog(L_WARNING, "conf_init_dir: file name: %s/%s too short",
+ d->d_name, dname);
+ continue;
+ }
+
+ if (conf_load_files(trans, fname))
+ continue;
+ nfiles++;
+ }
+
+ if (nfiles) {
+ /* Apply the configuration values */
+ conf_end(trans, 1);
+ }
+ for (i = 0; i < n; i++)
+ free(namelist[i]);
+ free(namelist);
+ free(dname);
+
+ return;
+}
+
+int
+conf_init_file(const char *conf_file)
+{
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < sizeof conf_bindings / sizeof conf_bindings[0]; i++)
+ LIST_INIT (&conf_bindings[i]);
+
+ TAILQ_INIT (&conf_trans_queue);
+
+ if (conf_file == NULL)
+ conf_file=NFS_CONFFILE;
+
+ /*
+ * First parse the give config file
+ * then parse the config.conf.d directory
+ * (if it exists)
+ *
+ */
+ ret = conf_load_file(conf_file);
+
+ /*
+ * When the same variable is set in both files
+ * the conf.d file will override the config file.
+ * This allows automated admin systems to
+ * have the final say.
+ */
+ conf_init_dir(conf_file);
+
+ return ret;
+}
+
+/*
+ * Empty the config and free up any used memory
+ */
+void
+conf_cleanup(void)
+{
+ conf_free_bindings();
+
+ struct conf_trans *node, *next;
+ for (node = TAILQ_FIRST(&conf_trans_queue); node; node = next) {
+ next = TAILQ_NEXT(node, link);
+ TAILQ_REMOVE (&conf_trans_queue, node, link);
+ free_conftrans(node);
+ }
+ TAILQ_INIT(&conf_trans_queue);
+}
+
+/*
+ * Return the numeric value denoted by TAG in section SECTION or DEF
+ * if that tag does not exist.
+ */
+int
+conf_get_num(const char *section, const char *tag, int def)
+{
+ char *value = conf_get_str(section, tag);
+
+ if (value)
+ return atoi(value);
+
+ return def;
+}
+
+/*
+ * Return the Boolean value denoted by TAG in section SECTION, or DEF
+ * if that tags does not exist.
+ * FALSE is returned for case-insensitive comparisons with 0, f, false, n, no, off
+ * TRUE is returned for 1, t, true, y, yes, on
+ * A failure to match one of these results in DEF
+ */
+_Bool
+conf_get_bool(const char *section, const char *tag, _Bool def)
+{
+ char *value = conf_get_str(section, tag);
+
+ if (!value)
+ return def;
+ if (strcasecmp(value, "1") == 0 ||
+ strcasecmp(value, "t") == 0 ||
+ strcasecmp(value, "true") == 0 ||
+ strcasecmp(value, "y") == 0 ||
+ strcasecmp(value, "yes") == 0 ||
+ strcasecmp(value, "on") == 0)
+ return true;
+
+ if (strcasecmp(value, "0") == 0 ||
+ strcasecmp(value, "f") == 0 ||
+ strcasecmp(value, "false") == 0 ||
+ strcasecmp(value, "n") == 0 ||
+ strcasecmp(value, "no") == 0 ||
+ strcasecmp(value, "off") == 0)
+ return false;
+ return def;
+}
+
+/* Validate X according to the range denoted by TAG in section SECTION. */
+int
+conf_match_num(const char *section, const char *tag, int x)
+{
+ char *value = conf_get_str (section, tag);
+ int val, min, max, n;
+
+ if (!value)
+ return 0;
+ n = sscanf (value, "%d,%d:%d", &val, &min, &max);
+ switch (n) {
+ case 1:
+ xlog(LOG_INFO, "conf_match_num: %s:%s %d==%d?", section, tag, val, x);
+ return x == val;
+ case 3:
+ xlog(LOG_INFO, "conf_match_num: %s:%s %d<=%d<=%d?", section,
+ tag, min, x, max);
+ return min <= x && max >= x;
+ default:
+ xlog(LOG_INFO, "conf_match_num: section %s tag %s: invalid number spec %s",
+ section, tag, value);
+ }
+ return 0;
+}
+
+/* Return the string value denoted by TAG in section SECTION. */
+char *
+conf_get_str(const char *section, const char *tag)
+{
+ return conf_get_section(section, NULL, tag);
+}
+
+/* Return the string value denoted by TAG in section SECTION,
+ * unless it is not set, in which case return def
+ */
+char *
+conf_get_str_with_def(const char *section, const char *tag, char *def)
+{
+ char * result = conf_get_section(section, NULL, tag);
+ if (!result)
+ return def;
+ return result;
+}
+
+/*
+ * Retrieve an entry without interpreting its contents
+ */
+char *
+conf_get_entry(const char *section, const char *arg, const char *tag)
+{
+ struct conf_binding *cb;
+
+ cb = LIST_FIRST (&conf_bindings[conf_hash (section)]);
+ for (; cb; cb = LIST_NEXT (cb, link)) {
+ if (strcasecmp(section, cb->section) != 0)
+ continue;
+ if (arg && (cb->arg == NULL || strcasecmp(arg, cb->arg) != 0))
+ continue;
+ if (!arg && cb->arg)
+ continue;
+ if (strcasecmp(tag, cb->tag) != 0)
+ continue;
+ return cb->value;
+ }
+ return 0;
+}
+
+/*
+ * Find a section that may or may not have an argument
+ */
+char *
+conf_get_section(const char *section, const char *arg, const char *tag)
+{
+ struct conf_binding *cb;
+retry:
+ cb = LIST_FIRST (&conf_bindings[conf_hash (section)]);
+ for (; cb; cb = LIST_NEXT (cb, link)) {
+ if (strcasecmp(section, cb->section) != 0)
+ continue;
+ if (arg && (cb->arg == NULL || strcasecmp(arg, cb->arg) != 0))
+ continue;
+ if (!arg && cb->arg)
+ continue;
+ if (strcasecmp(tag, cb->tag) != 0)
+ continue;
+ if (cb->value[0] == '$') {
+ /* expand $name from [environment] section,
+ * or from environment
+ */
+ char *env = getenv(cb->value+1);
+ if (env && *env)
+ return env;
+ section = "environment";
+ tag = cb->value + 1;
+ goto retry;
+ }
+ return cb->value;
+ }
+ return 0;
+}
+
+/*
+ * Build a list of string values out of the comma separated value denoted by
+ * TAG in SECTION.
+ */
+struct conf_list *
+conf_get_list(const char *section, const char *tag)
+{
+ char *liststr = 0, *p, *field, *t;
+ struct conf_list *list = 0;
+ struct conf_list_node *node;
+
+ list = malloc (sizeof *list);
+ if (!list)
+ goto cleanup;
+ TAILQ_INIT (&list->fields);
+ list->cnt = 0;
+ liststr = conf_get_str(section, tag);
+ if (!liststr)
+ goto cleanup;
+ liststr = strdup (liststr);
+ if (!liststr)
+ goto cleanup;
+ p = liststr;
+ while ((field = strsep (&p, ",")) != NULL) {
+ /* Skip leading whitespace */
+ while (isspace (*field))
+ field++;
+ /* Skip trailing whitespace */
+ if (p) {
+ for (t = p - 1; t > field && isspace (*t); t--)
+ *t = '\0';
+ }
+ if (*field == '\0') {
+ xlog(LOG_INFO, "conf_get_list: empty field, ignoring...");
+ continue;
+ }
+ list->cnt++;
+ node = calloc (1, sizeof *node);
+ if (!node)
+ goto cleanup;
+ node->field = strdup (field);
+ if (!node->field) {
+ free(node);
+ goto cleanup;
+ }
+ TAILQ_INSERT_TAIL (&list->fields, node, link);
+ }
+ free (liststr);
+ return list;
+
+cleanup:
+ if (list)
+ conf_free_list(list);
+ if (liststr)
+ free(liststr);
+ return 0;
+}
+
+struct conf_list *
+conf_get_tag_list(const char *section, const char *arg)
+{
+ struct conf_list *list = 0;
+ struct conf_list_node *node;
+ struct conf_binding *cb;
+
+ list = malloc(sizeof *list);
+ if (!list)
+ goto cleanup;
+ TAILQ_INIT(&list->fields);
+ list->cnt = 0;
+ cb = LIST_FIRST(&conf_bindings[conf_hash (section)]);
+ for (; cb; cb = LIST_NEXT(cb, link)) {
+ if (strcasecmp (section, cb->section) == 0) {
+ if (arg != NULL && strcasecmp(arg, cb->arg) != 0)
+ continue;
+ list->cnt++;
+ node = calloc(1, sizeof *node);
+ if (!node)
+ goto cleanup;
+ node->field = strdup(cb->tag);
+ if (!node->field) {
+ free(node);
+ goto cleanup;
+ }
+ TAILQ_INSERT_TAIL(&list->fields, node, link);
+ }
+ }
+ return list;
+
+cleanup:
+ if (list)
+ conf_free_list(list);
+ return 0;
+}
+
+/* Decode a PEM encoded buffer. */
+int
+conf_decode_base64 (uint8_t *out, uint32_t *len, const unsigned char *buf)
+{
+ uint32_t c = 0;
+ uint8_t c1, c2, c3, c4;
+
+ while (*buf) {
+ if (*buf > 127 || (c1 = asc2bin[*buf]) == 255)
+ return 0;
+
+ buf++;
+ if (*buf > 127 || (c2 = asc2bin[*buf]) == 255)
+ return 0;
+
+ buf++;
+ if (*buf == '=') {
+ c3 = c4 = 0;
+ c++;
+
+ /* Check last four bit */
+ if (c2 & 0xF)
+ return 0;
+
+ if (strcmp((char *)buf, "==") == 0)
+ buf++;
+ else
+ return 0;
+ } else if (*buf > 127 || (c3 = asc2bin[*buf]) == 255)
+ return 0;
+ else {
+ if (*++buf == '=') {
+ c4 = 0;
+ c += 2;
+
+ /* Check last two bit */
+ if (c3 & 3)
+ return 0;
+
+ if (strcmp((char *)buf, "="))
+ return 0;
+ } else if (*buf > 127 || (c4 = asc2bin[*buf]) == 255)
+ return 0;
+ else
+ c += 3;
+ }
+
+ buf++;
+ *out++ = (c1 << 2) | (c2 >> 4);
+ *out++ = (c2 << 4) | (c3 >> 2);
+ *out++ = (c3 << 6) | c4;
+ }
+
+ *len = c;
+ return 1;
+}
+
+void
+conf_free_list(struct conf_list *list)
+{
+ struct conf_list_node *node = TAILQ_FIRST(&list->fields);
+
+ while (node) {
+ TAILQ_REMOVE(&list->fields, node, link);
+ if (node->field)
+ free(node->field);
+ free (node);
+ node = TAILQ_FIRST(&list->fields);
+ }
+ free (list);
+}
+
+int
+conf_begin(void)
+{
+ static int seq = 0;
+
+ return ++seq;
+}
+
+static struct conf_trans *
+conf_trans_node(int transaction, enum conf_op op)
+{
+ struct conf_trans *node;
+
+ node = calloc (1, sizeof *node);
+ if (!node) {
+ xlog_warn("conf_trans_node: calloc (1, %lu) failed",
+ (unsigned long)sizeof *node);
+ return 0;
+ }
+ node->trans = transaction;
+ node->op = op;
+ TAILQ_INSERT_TAIL (&conf_trans_queue, node, link);
+ return node;
+}
+
+/* Queue a set operation. */
+static int
+conf_set(int transaction, const char *section, const char *arg,
+ const char *tag, const char *value, int override, int is_default)
+{
+ struct conf_trans *node;
+
+ if (!value || !*value)
+ return 0;
+ node = conf_trans_node(transaction, CONF_SET);
+ if (!node)
+ return 1;
+ node->section = strdup(section);
+ if (!node->section) {
+ xlog_warn("conf_set: strdup(\"%s\") failed", section);
+ goto fail;
+ }
+ /* Make Section names case-insensitive */
+ upper2lower(node->section);
+
+ if (arg) {
+ node->arg = strdup(arg);
+ if (!node->arg) {
+ xlog_warn("conf_set: strdup(\"%s\") failed", arg);
+ goto fail;
+ }
+ } else
+ node->arg = NULL;
+
+ node->tag = strdup(tag);
+ if (!node->tag) {
+ xlog_warn("conf_set: strdup(\"%s\") failed", tag);
+ goto fail;
+ }
+ node->value = strdup(value);
+ if (!node->value) {
+ xlog_warn("conf_set: strdup(\"%s\") failed", value);
+ goto fail;
+ }
+ node->override = override;
+ node->is_default = is_default;
+ return 0;
+
+fail:
+ free_conftrans(node);
+ return 1;
+}
+
+/* Queue a remove operation. */
+int
+conf_remove(int transaction, const char *section, const char *tag)
+{
+ struct conf_trans *node;
+
+ node = conf_trans_node(transaction, CONF_REMOVE);
+ if (!node)
+ goto fail;
+ node->section = strdup(section);
+ if (!node->section) {
+ xlog_warn("conf_remove: strdup(\"%s\") failed", section);
+ goto fail;
+ }
+ node->tag = strdup(tag);
+ if (!node->tag) {
+ xlog_warn("conf_remove: strdup(\"%s\") failed", tag);
+ goto fail;
+ }
+ return 0;
+
+fail:
+ free_conftrans(node);
+ return 1;
+}
+
+/* Queue a remove section operation. */
+int
+conf_remove_section(int transaction, const char *section)
+{
+ struct conf_trans *node;
+
+ node = conf_trans_node(transaction, CONF_REMOVE_SECTION);
+ if (!node)
+ goto fail;
+ node->section = strdup(section);
+ if (!node->section) {
+ xlog_warn("conf_remove_section: strdup(\"%s\") failed", section);
+ goto fail;
+ }
+ return 0;
+
+fail:
+ free_conftrans(node);
+ return 1;
+}
+
+/* Execute all queued operations for this transaction. Cleanup. */
+int
+conf_end(int transaction, int commit)
+{
+ struct conf_trans *node, *next;
+
+ for (node = TAILQ_FIRST(&conf_trans_queue); node; node = next) {
+ next = TAILQ_NEXT(node, link);
+ if (node->trans == transaction) {
+ if (commit) {
+ switch (node->op) {
+ case CONF_SET:
+ conf_set_now(node->section, node->arg,
+ node->tag, node->value, node->override,
+ node->is_default);
+ break;
+ case CONF_REMOVE:
+ conf_remove_now(node->section, node->tag);
+ break;
+ case CONF_REMOVE_SECTION:
+ conf_remove_section_now(node->section);
+ break;
+ default:
+ xlog(LOG_INFO, "conf_end: unknown operation: %d", node->op);
+ }
+ }
+ TAILQ_REMOVE (&conf_trans_queue, node, link);
+ free_conftrans(node);
+ }
+ }
+ return 0;
+}
+
+/*
+ * Dump running configuration upon SIGUSR1.
+ * Configuration is "stored in reverse order", so reverse it again.
+ */
+struct dumper {
+ char *section;
+ char *arg;
+ char *tag;
+ char *value;
+ struct dumper *next;
+};
+
+/*
+ * Test if two nodes belong to the same (sub)sections
+ */
+static int
+dumper_section_compare(const struct dumper *nodea, const struct dumper *nodeb)
+{
+ int ret;
+
+ /* missing node, shouldnt happen */
+ if (!nodea || !nodeb)
+ return -1;
+
+ /* no section names at all, they are equal */
+ if (!nodea->section && !nodeb->section)
+ return 0;
+
+ /* if only one has a section name, the blank one goes first */
+ if (!nodea->section && nodeb->section)
+ return -1;
+
+ if (nodea->section && !nodeb->section)
+ return 1;
+
+ /* both have section names, but do they match */
+ ret = strcmp(nodea->section, nodeb->section);
+
+ /* section names differ, that was easy */
+ if (ret != 0)
+ return ret;
+
+ /* sections matched, but how about sub-sections,
+ * again, if only one has a value the blank goes first
+ */
+ if (!nodea->arg && nodeb->arg)
+ return -1;
+
+ if (nodea->arg && !nodeb->arg)
+ return 1;
+
+ /* both have sub-section args and they differ */
+ if (nodea->arg && nodeb->arg
+ && (ret=strcmp(nodea->arg, nodeb->arg))!=0)
+ return ret;
+
+ return 0;
+}
+
+/* If a string starts or ends with a space it should be quoted */
+static bool
+should_escape(const char *text)
+{
+ int len;
+
+ /* no string, no escaping needed */
+ if (!text)
+ return false;
+
+ /* first character is a space */
+ if (isspace(text[0]))
+ return true;
+
+ /* last character is a space */
+ len = strlen(text);
+ if (isspace(text[len-1]))
+ return true;
+
+ return false;
+}
+
+static void
+conf_report_dump_text(struct dumper *head, FILE *ff)
+{
+ const struct dumper *node = head;
+ const struct dumper *last = NULL;
+
+ for (node=head; node!=NULL; node=node->next) {
+ /* starting a new section, print the section header */
+ if (dumper_section_compare(last, node)!=0) {
+ if (node != head)
+ fprintf(ff, "\n");
+ if (node->arg)
+ fprintf(ff, "[%s \"%s\"]\n", node->section, node->arg);
+ else
+ fprintf(ff, "[%s]\n", node->section);
+ }
+
+ /* now print the tag and its value */
+ fprintf(ff, " %s", node->tag);
+ if (node->value) {
+ if (should_escape(node->value))
+ fprintf(ff, " = \"%s\"", node->value);
+ else
+ fprintf(ff, " = %s", node->value);
+ }
+ fprintf(ff, "\n");
+
+ last = node;
+ }
+}
+
+/* sort by tag compare function */
+static int
+dumper_compare(const void *a, const void *b)
+{
+ const struct dumper *nodea = *(struct dumper **)a;
+ const struct dumper *nodeb = *(struct dumper **)b;
+ int ret;
+
+ /* missing node, shouldnt happen */
+ if (!nodea || !nodeb)
+ return -1;
+
+ /* are the two nodes in different (sub)sections */
+ ret = dumper_section_compare(nodea, nodeb);
+ if (ret != 0)
+ return ret;
+
+ /* sub-sections match (both blank, or both same)
+ * so we compare the tag names
+ */
+
+ /* blank tags shouldnt happen, but paranoia */
+ if (!nodea->tag && !nodeb->tag)
+ return 0;
+
+ /* still shouldnt happen, but use the blank-goes-first logic */
+ if (!nodea->tag && nodeb->tag)
+ return -1;
+ if ( nodea->tag && !nodeb->tag)
+ return 1;
+
+ /* last test, compare the tags directly */
+ ret = strcmp(nodea->tag, nodeb->tag);
+ return ret;
+}
+
+/* sort all of the report nodes */
+static struct dumper *
+conf_report_sort(struct dumper *start)
+{
+ struct dumper **list;
+ struct dumper *node;
+ unsigned int count = 0;
+ unsigned int i=0;
+
+ /* how long is this list */
+ for (node=start; node!=NULL; node=node->next)
+ count++;
+
+ /* no need to sort a list with less than 2 items */
+ if (count < 2)
+ return start;
+
+ /* build an array of all the nodes */
+ list = calloc(count, sizeof(struct dumper *));
+ if (!list)
+ goto mem_err;
+
+ for (node=start,i=0; node!=NULL; node=node->next) {
+ list[i++] = node;
+ }
+
+ /* sort the array alphabetically by section and tag */
+ qsort(list, count, sizeof(struct dumper *), dumper_compare);
+
+ /* rebuild the linked list in sorted order */
+ for (i=0; i<count-1; i++) {
+ list[i]->next = list[i+1];
+ }
+ list[count-1]->next = NULL;
+
+ /* remember the new head of list and discard the sorting array */
+ node = list[0];
+ free(list);
+
+ /* return the new head of list */
+ return node;
+
+mem_err:
+ free(list);
+ return NULL;
+}
+
+/* Output a copy of the current configuration to file */
+void
+conf_report(FILE *outfile)
+{
+ struct conf_binding *cb = NULL;
+ unsigned int i;
+ struct dumper *dumper = NULL, *dnode = NULL;
+
+ xlog(LOG_INFO, "conf_report: dumping running configuration");
+
+ /* build a linked list of all the config nodes */
+ for (i = 0; i < sizeof conf_bindings / sizeof conf_bindings[0]; i++) {
+ for (cb = LIST_FIRST(&conf_bindings[i]); cb; cb = LIST_NEXT(cb, link)) {
+ struct dumper *newnode = calloc(1, sizeof (struct dumper));
+ if (!newnode)
+ goto mem_fail;
+
+ newnode->next = dumper;
+ dumper = newnode;
+
+ newnode->section = cb->section;
+ newnode->arg = cb->arg;
+ newnode->tag = cb->tag;
+ newnode->value = cb->value;
+ }
+ }
+
+ /* sort the list then print it */
+ dumper = conf_report_sort(dumper);
+ conf_report_dump_text(dumper, outfile);
+ goto cleanup;
+
+mem_fail:
+ xlog_warn("conf_report: malloc/calloc failed");
+cleanup:
+ /* traverse the linked list freeing all the nodes */
+ while ((dnode = dumper) != 0) {
+ dumper = dumper->next;
+ free(dnode);
+ }
+ return;
+}
+
+/* struct and queue for buffing output lines */
+TAILQ_HEAD(tailhead, outbuffer);
+
+struct outbuffer {
+ TAILQ_ENTRY(outbuffer) link;
+ char *text;
+};
+
+static struct outbuffer *
+make_outbuffer(char *line)
+{
+ struct outbuffer *new;
+
+ if (line == NULL)
+ return NULL;
+
+ new = calloc(1, sizeof(struct outbuffer));
+ if (new == NULL) {
+ xlog(L_ERROR, "malloc error creating outbuffer");
+ return NULL;
+ }
+ new->text = line;
+ return new;
+}
+
+/* compose a properly escaped tag=value line */
+static char *
+make_tagline(const char *tag, const char *value)
+{
+ char *line;
+ int ret;
+
+ if (!value)
+ return NULL;
+
+ if (should_escape(value))
+ ret = asprintf(&line, "%s = \"%s\"\n", tag, value);
+ else
+ ret = asprintf(&line, "%s = %s\n", tag, value);
+
+ if (ret == -1) {
+ xlog(L_ERROR, "malloc error composing a tag line");
+ return NULL;
+ }
+ return line;
+}
+
+/* compose a section header line */
+static char *
+make_section(const char *section, const char *arg)
+{
+ char *line;
+ int ret;
+
+ if (arg)
+ ret = asprintf(&line, "[%s \"%s\"]\n", section, arg);
+ else
+ ret = asprintf(&line, "[%s]\n", section);
+
+ if (ret == -1) {
+ xlog(L_ERROR, "malloc error composing section header");
+ return NULL;
+ }
+ return line;
+}
+
+/* compose a comment line (with or without tag) */
+static char *
+make_comment(const char *tag, const char *comment)
+{
+ char *line;
+ int ret;
+
+ if (tag == NULL || *tag == '\0') {
+ ret = asprintf(&line, "# %s\n", comment);
+ } else {
+ ret = asprintf(&line, "# %s: %s\n", tag, comment);
+ }
+
+ if (ret == -1) {
+ xlog(L_ERROR, "malloc error composing header");
+ return NULL;
+ }
+
+ return line;
+}
+
+/* compose a 'file modified' comment */
+static char *
+make_timestamp(const char *tag, time_t when)
+{
+ struct tm *tstamp;
+ char datestr[80];
+ char *result = NULL, *tmpstr = NULL;
+ int ret;
+
+ tstamp = localtime(&when);
+ if (strftime(datestr, sizeof(datestr), "%b %d %Y %H:%M:%S", tstamp) == 0) {
+ xlog(L_ERROR, "error composing date");
+ datestr[0] = '\0';
+ }
+
+ if (modified_by) {
+ ret = asprintf(&tmpstr, "%s on %s", modified_by, datestr);
+ if (ret == -1) {
+ xlog(L_ERROR, "malloc error composing a time stamp");
+ return NULL;
+ }
+ result = make_comment(tag, tmpstr);
+ free(tmpstr);
+ } else {
+ result = make_comment(tag, datestr);
+ }
+ return result;
+}
+
+/* does the supplied line contain the named section header */
+static bool
+is_section(const char *line, const char *section, const char *arg)
+{
+ char *end;
+ char *name;
+ char *sub;
+ bool found = false;
+
+ /* Not a valid section name */
+ if (strcmp(section, "#") == 0)
+ return false;
+
+ /* skip leading white space */
+ while (*line == '[' || isspace(*line))
+ line++;
+
+ name = strdup(line);
+ if (name == NULL) {
+ xlog_warn("conf_write: malloc failed ");
+ return false;
+ }
+
+ /* find the end */
+ end = strchr(name, ']');
+
+ /* malformed line */
+ if (end == NULL) {
+ xlog_warn("conf_write: warning: malformed section name");
+ goto cleanup;
+ }
+
+ while (*end && ( *end == ']' || isblank(*end)))
+ *(end--) = '\0';
+
+ /* is there a subsection name (aka arg) */
+ sub = strchr(name, '"');
+ if (sub) {
+ end = sub - 1;
+ *(sub++) = '\0';
+
+ /* trim whitespace between section name and arg */
+ while (end > name && isblank(*end))
+ *(end--) = '\0';
+
+ /* trim off closing quote */
+ end = strchr(sub, '"');
+ if (end == NULL) {
+ xlog_warn("conf_write: warning: malformed sub-section name");
+ goto cleanup;
+ }
+ *end = '\0';
+ }
+
+ /* ready to compare */
+ if (strcasecmp(section, name)!=0)
+ goto cleanup;
+
+ if (arg != NULL) {
+ if (sub == NULL || strcasecmp(arg, sub)!=0)
+ goto cleanup;
+ } else {
+ if (sub != NULL)
+ goto cleanup;
+ }
+
+ found = true;
+
+cleanup:
+ free(name);
+ return found;
+}
+
+/* check that line contains the specified tag assignment */
+static bool
+is_tag(const char *line, const char *tagname)
+{
+ char *end;
+ char *name;
+ bool found = false;
+
+ /* quick check, is this even an assignment line */
+ end = strchr(line, '=');
+ if (end == NULL)
+ return false;
+
+ /* skip leading white space before tag name */
+ while (isblank(*line))
+ line++;
+
+ name = strdup(line);
+ if (name == NULL) {
+ xlog_warn("conf_write: malloc failed");
+ return false;
+ }
+
+ /* trim any newline characters */
+ end = strchr(name, '\n');
+ if (end)
+ *end = '\0';
+ end = strchr(name, '\r');
+ if (end)
+ *end = '\0';
+
+ /* find the assignment equals sign */
+ end = strchr(name, '=');
+
+ /* malformed line, i swear the equals was there earlier */
+ if (end == NULL) {
+ xlog_warn("conf_write: warning: malformed tag name");
+ goto cleanup;
+ }
+
+ /* trim trailing whitespace after tag name */
+ do {
+ *(end--) = '\0';
+ }while (end > name && *end && isblank(*end));
+
+ /* quoted string, take contents of quotes only */
+ if (*name == '"') {
+ char * new = strdup(name+1);
+ end = strchr(new, '"');
+ if (end != NULL) {
+ *end = 0;
+ free(name);
+ name = new;
+ } else {
+ free(new);
+ }
+ }
+
+ /* now compare */
+ if (strcasecmp(tagname, name) == 0)
+ found = true;
+
+cleanup:
+ free(name);
+ return found;
+}
+
+/* is this an empty line ? */
+static bool
+is_empty(const char *line)
+{
+ const char *p = line;
+
+ if (line == NULL)
+ return true;
+ if (*line == '\0')
+ return true;
+
+ while (*p != '\0' && isspace(*p))
+ p++;
+
+ if (*p == '\0')
+ return true;
+
+ return false;
+}
+
+/* is this line just a comment ? */
+static bool
+is_comment(const char *line)
+{
+ if (line == NULL)
+ return false;
+
+ while (isblank(*line))
+ line++;
+
+ if (*line == '#')
+ return true;
+
+ return false;
+}
+
+/* check that line contains the specified comment header */
+static bool
+is_taggedcomment(const char *line, const char *field)
+{
+ char *end;
+ char *name;
+ bool found = false;
+
+ if (line == NULL)
+ return false;
+
+ while (isblank(*line))
+ line++;
+
+ if (*line != '#')
+ return false;
+
+ line++;
+
+ /* quick check, is this even a likely formatted line */
+ end = strchr(line, ':');
+ if (end == NULL)
+ return false;
+
+ /* skip leading white space before field name */
+ while (isblank(*line))
+ line++;
+
+ name = strdup(line);
+ if (name == NULL) {
+ xlog_warn("conf_write: malloc failed");
+ return false;
+ }
+
+ /* strip trailing spaces from the name */
+ end = strchr(name, ':');
+ if (end) *(end--) = 0;
+ while (end && end > name && isblank(*end))
+ *(end--)=0;
+
+ if (strcasecmp(name, field)==0)
+ found = true;
+
+ free(name);
+ return found;
+}
+
+
+/* delete a buffer queue whilst optionally outputting to file */
+static int
+flush_outqueue(struct tailhead *queue, FILE *fout)
+{
+ int ret = 0;
+ while (queue->tqh_first != NULL) {
+ struct outbuffer *ob = queue->tqh_first;
+ TAILQ_REMOVE(queue, ob, link);
+ if (ob->text) {
+ if (fout) {
+ ret = fprintf(fout, "%s", ob->text);
+ if (ret == -1) {
+ xlog(L_ERROR, "Error writing to config file: %s",
+ strerror(errno));
+ fout = NULL;
+ }
+ }
+ free(ob->text);
+ }
+ free(ob);
+ }
+ if (ret == -1)
+ return 1;
+ return 0;
+}
+
+/* append one queue to another */
+static void
+append_queue(struct tailhead *inq, struct tailhead *outq)
+{
+ while (inq->tqh_first != NULL) {
+ struct outbuffer *ob = inq->tqh_first;
+ TAILQ_REMOVE(inq, ob, link);
+ TAILQ_INSERT_TAIL(outq, ob, link);
+ }
+}
+
+/* read one line of text from a file, growing the buffer as necessary */
+static int
+read_line(char **buff, int *buffsize, FILE *in)
+{
+ char *readp;
+ int used = 0;
+ bool again = false;
+
+ /* make sure we have a buffer to read into */
+ if (*buff == NULL) {
+ *buffsize = 4096;
+ *buff = calloc(1, *buffsize);
+ if (*buff == NULL) {
+ xlog(L_ERROR, "malloc error for read buffer");
+ return -1;
+ }
+ }
+
+ readp = *buff;
+
+ do {
+ int len;
+
+ /* read in a chunk */
+ if (fgets(readp, *buffsize-used, in)==NULL)
+ return -1;
+
+ len = strlen(*buff);
+ if (len == 0)
+ return -1;
+
+ /* was this the end of a line, or partial read */
+ readp = *buff + len - 1;
+
+ if (*readp != '\n' && *readp !='\r') {
+ /* no nl/cr must be partial read, go again */
+ readp++;
+ again = true;
+ } else {
+ /* that was a normal end of line */
+ again = false;
+ }
+
+ /* do we need more space */
+ if (again && *buffsize - len < 1024) {
+ int offset = readp - *buff;
+ char *newbuff;
+ *buffsize += 4096;
+ newbuff = realloc(*buff, *buffsize);
+ if (newbuff == NULL) {
+ xlog(L_ERROR, "malloc error reading line");
+ return -1;
+ }
+ *buff = newbuff;
+ readp = newbuff + offset;
+ }
+ } while(again);
+ return 0;
+}
+
+/* append a line to the given location in the queue */
+static int
+append_line(struct tailhead *queue, struct outbuffer *entry, char *line)
+{
+ int ret = 0;
+ char *end;
+ bool splitmode = false;
+ char *start = line;
+
+ if (line == NULL)
+ return -1;
+
+ /* if there are \n's in the middle of the string
+ * then we need to split it into folded lines */
+ do {
+ char *thisline;
+ struct outbuffer *qbuff;
+
+ end = strchr(start, '\n');
+ if (end && *(end+1) != '\0') {
+ *end = '\0';
+
+ ret = asprintf(&thisline, "%s\\\n", start);
+ if (ret == -1) {
+ xlog(L_ERROR, "malloc error composing output");
+ return -1;
+ }
+ splitmode = true;
+ start = end+1;
+ } else {
+ end = NULL;
+ if (splitmode) {
+ thisline = strdup(start);
+ if (thisline == NULL)
+ return -1;
+ } else {
+ thisline = start;
+ }
+ }
+
+ qbuff = make_outbuffer(thisline);
+ if (qbuff == NULL)
+ return -1;
+
+ if (entry) {
+ TAILQ_INSERT_AFTER(queue, entry, qbuff, link);
+ entry = TAILQ_NEXT(entry, link);
+ } else {
+ TAILQ_INSERT_TAIL(queue, qbuff, link);
+ }
+ }while (end != NULL);
+
+ /* we malloced copies of this, so free the original */
+ if (splitmode)
+ free(line);
+
+ return 0;
+}
+
+/* is this a "folded" line, i.e. ends in backslash */
+static bool
+is_folded(const char *line)
+{
+ const char *end;
+ if (line == NULL)
+ return false;
+
+ end = line + strlen(line);
+ while (end > line) {
+ end--;
+ if (*end != '\n' && *end != '\r')
+ break;
+ }
+
+ if (*end == '\\')
+ return true;
+
+ return false;
+}
+
+static int
+lock_file(FILE *f)
+{
+ int ret;
+ ret = flock(fileno(f), LOCK_EX);
+ if (ret)
+ xlog(L_ERROR, "Error could not lock the file");
+ return ret;
+}
+
+/***
+ * Write a value to an nfs.conf style filename
+ *
+ * create the file if it doesnt already exist
+ * if value==NULL removes the setting (if present)
+ */
+int
+conf_write(const char *filename, const char *section, const char *arg,
+ const char *tag, const char *value)
+{
+ FILE *infile = NULL;
+ int ret = 1;
+ struct tailhead outqueue;
+ struct tailhead inqueue;
+ char * buff = NULL;
+ int buffsize = 0;
+ time_t now = time(NULL);
+
+ TAILQ_INIT(&inqueue);
+ TAILQ_INIT(&outqueue);
+
+ if (!filename) {
+ xlog_warn("conf_write: no filename supplied");
+ return ret;
+ }
+
+ if (!section || !tag) {
+ xlog_warn("conf_write: section or tag name missing");
+ return ret;
+ }
+
+ infile = fopen(filename, "r+");
+ if (!infile) {
+ if (!value) {
+ xlog_warn("conf_write: config file \"%s\" not found, nothing to do", filename);
+ ret = 0;
+ goto cleanup;
+ }
+
+ xlog_warn("conf_write: config file \"%s\" not found, creating.", filename);
+ infile = fopen(filename, "wx");
+ if (!infile) {
+ xlog(L_ERROR, "conf_write: Error creating config file \"%s\".", filename);
+ goto cleanup;
+ }
+
+ if (lock_file(infile))
+ goto cleanup;
+
+ if (strcmp(section, "#") == 0) {
+ if (append_line(&inqueue, NULL, make_comment(tag, value)))
+ goto cleanup;
+ } else {
+ if (append_line(&inqueue, NULL, make_section(section, arg)))
+ goto cleanup;
+
+ if (append_line(&inqueue, NULL, make_tagline(tag, value)))
+ goto cleanup;
+ }
+
+ append_queue(&inqueue, &outqueue);
+ } else
+ if (strcmp(section, "#") == 0) {
+ /* Adding a comment line */
+ struct outbuffer *where = NULL;
+ struct outbuffer *next = NULL;
+ bool found = false;
+ int err = 0;
+
+ if (lock_file(infile))
+ goto cleanup;
+
+ buffsize = 4096;
+ buff = calloc(1, buffsize);
+ if (buff == NULL) {
+ xlog(L_ERROR, "malloc error for read buffer");
+ goto cleanup;
+ }
+ buff[0] = '\0';
+
+ /* read in the file */
+ do {
+ if (*buff != '\0'
+ && !is_taggedcomment(buff, "Modified")) {
+ if (append_line(&inqueue, NULL, strdup(buff)))
+ goto cleanup;
+ }
+
+ err = read_line(&buff, &buffsize, infile);
+ } while (err == 0);
+
+ /* if a tagged comment, look for an existing instance */
+ if (tag && *tag != '\0') {
+ where = TAILQ_FIRST(&inqueue);
+ while (where != NULL) {
+ next = TAILQ_NEXT(where, link);
+ struct outbuffer *prev = TAILQ_PREV(where, tailhead, link);
+ if (is_taggedcomment(where->text, tag)) {
+ TAILQ_REMOVE(&inqueue, where, link);
+ free(where->text);
+ free(where);
+ found = true;
+ if (append_line(&inqueue, prev, make_comment(tag, value)))
+ goto cleanup;
+ }
+ where = next;
+ }
+ }
+ /* it wasn't tagged or we didn't find it */
+ if (!found) {
+ /* does the file end in a blank line or a comment */
+ if (!TAILQ_EMPTY(&inqueue)) {
+ struct outbuffer *tail = TAILQ_LAST(&inqueue, tailhead);
+ if (tail && !is_empty(tail->text) && !is_comment(tail->text)) {
+ /* no, so add one for clarity */
+ if (append_line(&inqueue, NULL, strdup("\n")))
+ goto cleanup;
+ }
+ }
+ /* add the new comment line */
+ if (append_line(&inqueue, NULL, make_comment(tag, value)))
+ goto cleanup;
+ }
+ /* move everything over to the outqueue for writing */
+ append_queue(&inqueue, &outqueue);
+ } else {
+ bool found = false;
+ int err = 0;
+
+ if (lock_file(infile))
+ goto cleanup;
+
+ buffsize = 4096;
+ buff = calloc(1, buffsize);
+ if (buff == NULL) {
+ xlog(L_ERROR, "malloc error for read buffer");
+ goto cleanup;
+ }
+
+ buff[0] = '\0';
+ do {
+ struct outbuffer *where = NULL;
+
+ /* read in one section worth of lines */
+ do {
+ if (*buff != '\0'
+ && !is_taggedcomment(buff, "Modified")) {
+ if (append_line(&inqueue, NULL, strdup(buff)))
+ goto cleanup;
+ }
+
+ err = read_line(&buff, &buffsize, infile);
+ } while (err == 0 && buff[0] != '[');
+
+ /* find the section header */
+ where = TAILQ_FIRST(&inqueue);
+ while (where != NULL) {
+ if (where->text != NULL && where->text[0] == '[')
+ break;
+ where = TAILQ_NEXT(where, link);
+ }
+
+ /* this is the section we care about */
+ if (where != NULL && is_section(where->text, section, arg)) {
+ struct outbuffer *section_start = where;
+
+ /* is there an existing assignment */
+ while ((where = TAILQ_NEXT(where, link)) != NULL) {
+ if (is_tag(where->text, tag)) {
+ found = true;
+ break;
+ }
+ }
+
+ /* no active assignment, but is there a commented one */
+ if (!found) {
+ where = section_start;
+ while ((where = TAILQ_NEXT(where, link)) != NULL) {
+ if (is_comment(where->text)) {
+ char *cline = where->text;
+ while (isspace(*cline))
+ cline++;
+
+ if (*cline != '#')
+ continue;
+ cline++;
+
+ if (is_tag(cline, tag)) {
+ found = true;
+ break;
+ }
+ }
+ }
+ }
+
+ /* replace the located tag with an updated one */
+ if (found) {
+ struct outbuffer *prev = TAILQ_PREV(where, tailhead, link);
+ bool again = false;
+
+ /* remove current tag */
+ do {
+ struct outbuffer *next = TAILQ_NEXT(where, link);
+ TAILQ_REMOVE(&inqueue, where, link);
+ if (is_folded(where->text))
+ again = true;
+ else
+ again = false;
+ free(where->text);
+ free(where);
+ where = next;
+ } while(again && where != NULL);
+
+ /* insert new tag */
+ if (value) {
+ if (append_line(&inqueue, prev, make_tagline(tag, value)))
+ goto cleanup;
+ }
+ } else
+ /* no existing assignment found and we need to add one */
+ if (value) {
+ /* rewind past blank lines and comments */
+ struct outbuffer *tail = TAILQ_LAST(&inqueue, tailhead);
+
+ /* comments immediately before a section usually relate
+ * to the section below them */
+ while (tail != NULL && is_comment(tail->text))
+ tail = TAILQ_PREV(tail, tailhead, link);
+
+ /* there is usually blank line(s) between sections */
+ while (tail != NULL && is_empty(tail->text))
+ tail = TAILQ_PREV(tail, tailhead, link);
+
+ /* now add the tag here */
+ if (append_line(&inqueue, tail, make_tagline(tag, value)))
+ goto cleanup;
+
+ found = true;
+ }
+ }
+
+ /* EOF and correct section not found, so add one */
+ if (err && !found && value) {
+ /* did the last section end in a blank line */
+ struct outbuffer *tail = TAILQ_LAST(&inqueue, tailhead);
+ if (tail && !is_empty(tail->text)) {
+ /* no, so add one for clarity */
+ if (append_line(&inqueue, NULL, strdup("\n")))
+ goto cleanup;
+ }
+
+ /* add the new section header */
+ if (append_line(&inqueue, NULL, make_section(section, arg)))
+ goto cleanup;
+
+ /* now add the tag */
+ if (append_line(&inqueue, NULL, make_tagline(tag, value)))
+ goto cleanup;
+ }
+
+ /* we are done with this section, move it to the out queue */
+ append_queue(&inqueue, &outqueue);
+ } while(err == 0);
+ }
+
+ if (modified_by) {
+ /* check for and update the Modified header */
+ /* does the file end in a blank line or a comment */
+ if (!TAILQ_EMPTY(&outqueue)) {
+ struct outbuffer *tail = TAILQ_LAST(&outqueue, tailhead);
+ if (tail && !is_empty(tail->text) && !is_comment(tail->text)) {
+ /* no, so add one for clarity */
+ if (append_line(&outqueue, NULL, strdup("\n")))
+ goto cleanup;
+ }
+ }
+
+ /* now append the modified date comment */
+ if (append_line(&outqueue, NULL, make_timestamp("Modified", now)))
+ goto cleanup;
+ }
+
+ /* now rewind and overwrite the file with the updated data */
+ rewind(infile);
+
+ if (ftruncate(fileno(infile), 0)) {
+ xlog(L_ERROR, "Error truncating config file");
+ goto cleanup;
+ }
+
+ if (flush_outqueue(&outqueue, infile))
+ goto cleanup;
+
+ if (infile) {
+ fclose(infile);
+ infile = NULL;
+ }
+
+ ret = 0;
+
+cleanup:
+ flush_outqueue(&inqueue, NULL);
+ flush_outqueue(&outqueue, NULL);
+
+ if (buff)
+ free(buff);
+ if (infile)
+ fclose(infile);
+ return ret;
+}
diff --git a/support/nfs/exports.c b/support/nfs/exports.c
new file mode 100644
index 0000000..15dc574
--- /dev/null
+++ b/support/nfs/exports.c
@@ -0,0 +1,999 @@
+/*
+ * support/nfs/export.c
+ *
+ * Parse the exports file. Derived from the unfsd implementation.
+ *
+ * Authors: Donald J. Becker, <becker@super.org>
+ * Rick Sladkey, <jrs@world.std.com>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ * Olaf Kirch, <okir@monad.swb.de>
+ * Alexander O. Yuriev, <alex@bach.cis.temple.edu>
+ *
+ * This software maybe be used for any purpose provided
+ * the above copyright notice is retained. It is supplied
+ * as is, with no warranty expressed or implied.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/param.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <errno.h>
+#include "nfslib.h"
+#include "exportfs.h"
+#include "xmalloc.h"
+#include "xlog.h"
+#include "xio.h"
+#include "pseudoflavors.h"
+#include "reexport.h"
+
+#define EXPORT_DEFAULT_FLAGS \
+ (NFSEXP_READONLY|NFSEXP_ROOTSQUASH|NFSEXP_GATHERED_WRITES|NFSEXP_NOSUBTREECHECK)
+
+struct flav_info flav_map[] = {
+ { "krb5", RPC_AUTH_GSS_KRB5, 1},
+ { "krb5i", RPC_AUTH_GSS_KRB5I, 1},
+ { "krb5p", RPC_AUTH_GSS_KRB5P, 1},
+ { "unix", AUTH_UNIX, 0},
+ { "sys", AUTH_SYS, 0},
+ { "null", AUTH_NULL, 0},
+ { "none", AUTH_NONE, 0},
+};
+
+const int flav_map_size = sizeof(flav_map)/sizeof(flav_map[0]);
+
+int default_ttl = 30 * 60;
+
+static char *efname = NULL;
+static XFILE *efp = NULL;
+static int first;
+static int has_default_opts, has_default_subtree_opts;
+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, int warn, int *had_subtree_opt_ptr);
+static int parsesquash(char *list, int **idp, int *lenp, char **ep);
+static int parsenum(char **cpp);
+static void freesquash(void);
+static void syntaxerr(char *msg);
+static struct flav_info *find_flavor(char *name);
+
+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");
+ efname = strdup(fname);
+ first = 1;
+}
+
+static void init_exportent (struct exportent *ee, int fromkernel)
+{
+ ee->e_flags = EXPORT_DEFAULT_FLAGS;
+ /* some kernels assume the default is sync rather than
+ * async. More recent kernels always report one or other,
+ * but this test makes sure we assume same as kernel
+ * Ditto for wgather
+ */
+ if (fromkernel) {
+ ee->e_flags &= ~NFSEXP_ASYNC;
+ ee->e_flags &= ~NFSEXP_GATHERED_WRITES;
+ }
+ ee->e_anonuid = 65534;
+ ee->e_anongid = 65534;
+ ee->e_squids = NULL;
+ ee->e_sqgids = NULL;
+ ee->e_mountpoint = NULL;
+ ee->e_fslocmethod = FSLOC_NONE;
+ ee->e_fslocdata = NULL;
+ ee->e_secinfo[0].flav = NULL;
+ ee->e_xprtsec[0].info = NULL;
+ ee->e_nsquids = 0;
+ ee->e_nsqgids = 0;
+ ee->e_uuid = NULL;
+ ee->e_ttl = default_ttl;
+ ee->e_reexport = REEXP_NONE;
+}
+
+struct exportent *
+getexportent(int fromkernel, int fromexports)
+{
+ static struct exportent ee, def_ee;
+ char exp[512], *hostname;
+ char rpath[MAXPATHLEN+1];
+ char *opt, *sp;
+ int ok;
+
+ if (!efp)
+ return NULL;
+
+ freesquash();
+
+ if (first || (ok = getexport(exp, sizeof(exp))) == 0) {
+ has_default_opts = 0;
+ has_default_subtree_opts = 0;
+
+ init_exportent(&def_ee, fromkernel);
+
+ ok = getpath(def_ee.e_path, sizeof(def_ee.e_path));
+ if (ok <= 0)
+ return NULL;
+
+ ok = getexport(exp, sizeof(exp));
+ }
+ if (ok < 0) {
+ xlog(L_ERROR, "expected client(options...)");
+ return NULL;
+ }
+ first = 0;
+
+ /*
+ * Check for default options. The kernel will never have default
+ * options in /proc/fs/nfs/exports, however due to the initial '-' in
+ * the -test-client- string from the test export we have to check that
+ * we're not reading from the kernel.
+ */
+ if (exp[0] == '-' && !fromkernel) {
+ if (parseopts(exp + 1, &def_ee, 0, &has_default_subtree_opts) < 0)
+ return NULL;
+
+ has_default_opts = 1;
+
+ ok = getexport(exp, sizeof(exp));
+ if (ok < 0) {
+ xlog(L_ERROR, "expected client(options...)");
+ return NULL;
+ }
+ }
+
+ xfree(ee.e_hostname);
+ xfree(ee.e_realpath);
+ ee = def_ee;
+
+ /* Check for default client */
+ if (ok == 0)
+ exp[0] = '\0';
+
+ hostname = exp;
+ if ((opt = strchr(exp, '(')) != NULL) {
+ if (opt == exp) {
+ xlog(L_WARNING, "No host name given with %s %s, suggest *%s to avoid warning", ee.e_path, exp, exp);
+ hostname = "*";
+ }
+ *opt++ = '\0';
+ if (!(sp = strchr(opt, ')')) || sp[1] != '\0') {
+ syntaxerr("bad option list");
+ return NULL;
+ }
+ *sp = '\0';
+ } else {
+ if (!has_default_opts)
+ xlog(L_WARNING, "No options for %s %s: suggest %s(sync) to avoid warning", ee.e_path, exp, exp);
+ }
+ ee.e_hostname = xstrdup(hostname);
+
+ if (parseopts(opt, &ee, fromexports && !has_default_subtree_opts, NULL) < 0) {
+ if(ee.e_hostname)
+ {
+ xfree(ee.e_hostname);
+ ee.e_hostname=NULL;
+ }
+ if(ee.e_uuid)
+ {
+ xfree(ee.e_uuid);
+ ee.e_uuid=NULL;
+ }
+
+ return NULL;
+ }
+ /* 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';
+ }
+
+ return &ee;
+}
+
+static const struct secinfo_flag_displaymap {
+ unsigned int flag;
+ const char *set;
+ const char *unset;
+} secinfo_flag_displaymap[] = {
+ { NFSEXP_READONLY, "ro", "rw" },
+ { NFSEXP_INSECURE_PORT, "insecure", "secure" },
+ { NFSEXP_ROOTSQUASH, "root_squash", "no_root_squash" },
+ { NFSEXP_ALLSQUASH, "all_squash", "no_all_squash" },
+ { 0, NULL, NULL }
+};
+
+static void secinfo_flags_show(FILE *fp, unsigned int flags, unsigned int mask)
+{
+ const struct secinfo_flag_displaymap *p;
+
+ for (p = &secinfo_flag_displaymap[0]; p->flag != 0; p++) {
+ if (!(mask & p->flag))
+ continue;
+ fprintf(fp, ",%s", (flags & p->flag) ? p->set : p->unset);
+ }
+}
+
+void secinfo_show(FILE *fp, struct exportent *ep)
+{
+ const struct export_features *ef;
+ struct sec_entry *p1, *p2;
+
+ ef = get_export_features();
+
+ if (ep->e_secinfo[0].flav == NULL)
+ secinfo_addflavor(find_flavor("sys"), ep);
+ for (p1=ep->e_secinfo; p1->flav; p1=p2) {
+ fprintf(fp, ",sec=%s", p1->flav->flavour);
+ for (p2=p1+1; (p2->flav != NULL) && (p1->flags == p2->flags);
+ p2++) {
+ fprintf(fp, ":%s", p2->flav->flavour);
+ }
+ secinfo_flags_show(fp, p1->flags, ef->secinfo_flags);
+ }
+}
+
+void xprtsecinfo_show(FILE *fp, struct exportent *ep)
+{
+ struct xprtsec_entry *p1, *p2;
+
+ for (p1 = ep->e_xprtsec; p1->info; p1 = p2) {
+ fprintf(fp, ",xprtsec=%s", p1->info->name);
+ for (p2 = p1 + 1; p2->info && (p1->flags == p2->flags); p2++)
+ fprintf(fp, ":%s", p2->info->name);
+ }
+}
+
+static void
+fprintpath(FILE *fp, const char *path)
+{
+ int i;
+ for (i=0; path[i]; i++)
+ if (iscntrl(path[i]) || path[i] == '"' || path[i] == '\\' || path[i] == '#' || isspace(path[i]))
+ fprintf(fp, "\\%03o", path[i]);
+ else
+ fprintf(fp, "%c", path[i]);
+}
+
+void
+putexportent(struct exportent *ep)
+{
+ FILE *fp;
+ int *id, i;
+
+ if (!efp)
+ return;
+
+ fp = efp->x_fp;
+ fprintpath(fp, ep->e_path);
+ fprintf(fp, "\t%s(", 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, "%shide,", (ep->e_flags & NFSEXP_NOHIDE)?
+ "no" : "");
+ fprintf(fp, "%scrossmnt,", (ep->e_flags & NFSEXP_CROSSMOUNT)?
+ "" : "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, "%ssubtree_check,", (ep->e_flags & NFSEXP_NOSUBTREECHECK)?
+ "no_" : "");
+ fprintf(fp, "%ssecure_locks,", (ep->e_flags & NFSEXP_NOAUTHNLM)?
+ "in" : "");
+ fprintf(fp, "%sacl,", (ep->e_flags & NFSEXP_NOACL)?
+ "no_" : "");
+ if (ep->e_flags & NFSEXP_NOREADDIRPLUS)
+ fprintf(fp, "nordirplus,");
+ if (ep->e_flags & NFSEXP_SECURITY_LABEL)
+ fprintf(fp, "security_label,");
+ fprintf(fp, "%spnfs,", (ep->e_flags & NFSEXP_PNFS)? "" : "no_");
+ if (ep->e_flags & NFSEXP_FSID) {
+ fprintf(fp, "fsid=%d,", ep->e_fsid);
+ }
+ if (ep->e_uuid)
+ fprintf(fp, "fsid=%s,", ep->e_uuid);
+
+ if (ep->e_reexport) {
+ fprintf(fp, "reexport=");
+ switch (ep->e_reexport) {
+ case REEXP_AUTO_FSIDNUM:
+ fprintf(fp, "auto-fsidnum");
+ break;
+ case REEXP_PREDEFINED_FSIDNUM:
+ fprintf(fp, "predefined-fsidnum");
+ break;
+ default:
+ xlog(L_ERROR, "unknown reexport method %i", ep->e_reexport);
+ fprintf(fp, "none");
+ }
+ fprintf(fp, ",");
+ }
+
+ if (ep->e_mountpoint)
+ fprintf(fp, "mountpoint%s%s,",
+ ep->e_mountpoint[0]?"=":"", ep->e_mountpoint);
+ switch (ep->e_fslocmethod) {
+ case FSLOC_NONE:
+ break;
+ case FSLOC_REFER:
+ fprintf(fp, "refer=");
+ fprintpath(fp, ep->e_fslocdata);
+ fprintf(fp, ",");
+ break;
+ case FSLOC_REPLICA:
+ fprintf(fp, "replicas=");
+ fprintpath(fp, ep->e_fslocdata);
+ fprintf(fp, ",");
+ break;
+#ifdef DEBUG
+ case FSLOC_STUB:
+ fprintf(fp, "fsloc=stub,");
+ break;
+#endif
+ default:
+ xlog(L_ERROR, "unknown fsloc method 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", ep->e_anonuid, ep->e_anongid);
+ secinfo_show(fp, ep);
+ xprtsecinfo_show(fp, ep);
+ fprintf(fp, ")\n");
+}
+
+void
+endexportent(void)
+{
+ if (efp)
+ xfclose(efp);
+ efp = NULL;
+ if (efname)
+ free(efname);
+ efname = 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));
+ }
+ if (src->e_mountpoint)
+ dst->e_mountpoint = strdup(src->e_mountpoint);
+ if (src->e_fslocdata)
+ dst->e_fslocdata = strdup(src->e_fslocdata);
+ if (src->e_uuid)
+ dst->e_uuid = strdup(src->e_uuid);
+ dst->e_hostname = NULL;
+ dst->e_realpath = NULL;
+}
+
+struct exportent *
+mkexportent(char *hname, char *path, char *options)
+{
+ static struct exportent ee;
+
+ init_exportent(&ee, 0);
+
+ xfree(ee.e_hostname);
+ ee.e_hostname = xstrdup(hname);
+ xfree(ee.e_realpath);
+ ee.e_realpath = NULL;
+
+ if (strlen(path) >= sizeof(ee.e_path)) {
+ xlog(L_ERROR, "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';
+ if (parseopts(options, &ee, 0, NULL) < 0)
+ return NULL;
+ return &ee;
+}
+
+int
+updateexportent(struct exportent *eep, char *options)
+{
+ if (parseopts(options, eep, 0, NULL) < 0)
+ return 0;
+ return 1;
+}
+
+
+static int valid_uuid(char *uuid)
+{
+ /* must have 32 hex digits */
+ int cnt;
+ for (cnt = 0 ; *uuid; uuid++)
+ if (isxdigit(*uuid))
+ cnt++;
+ return cnt == 32;
+}
+
+/*
+ * Append the given flavor to the exportent's e_secinfo array, or
+ * do nothing if it's already there. Returns the index of flavor
+ * in the resulting array in any case.
+ */
+int secinfo_addflavor(struct flav_info *flav, struct exportent *ep)
+{
+ struct sec_entry *p;
+
+ for (p=ep->e_secinfo; p->flav; p++) {
+ if (p->flav == flav || p->flav->fnum == flav->fnum)
+ return p - ep->e_secinfo;
+ }
+ if (p - ep->e_secinfo >= SECFLAVOR_COUNT) {
+ xlog(L_ERROR, "more than %d security flavors on an export\n",
+ SECFLAVOR_COUNT);
+ return -1;
+ }
+ p->flav = flav;
+ p->flags = ep->e_flags;
+ (p+1)->flav = NULL;
+ return p - ep->e_secinfo;
+}
+
+static struct flav_info *find_flavor(char *name)
+{
+ struct flav_info *flav;
+ for (flav = flav_map; flav < flav_map + flav_map_size; flav++)
+ if (strcmp(flav->flavour, name) == 0)
+ return flav;
+ return NULL;
+}
+
+/* @str is a colon seperated list of security flavors. Their order
+ * is recorded in @ep, and a bitmap corresponding to the list is returned.
+ * A zero return indicates an error.
+ */
+static unsigned int parse_flavors(char *str, struct exportent *ep)
+{
+ unsigned int out=0;
+ char *flavor;
+ int bit;
+
+ while ( (flavor=strsep(&str, ":")) ) {
+ struct flav_info *flav = find_flavor(flavor);
+ if (flav == NULL) {
+ xlog(L_ERROR, "unknown flavor %s\n", flavor);
+ return 0;
+ }
+ bit = secinfo_addflavor(flav, ep);
+ if (bit < 0)
+ return 0;
+ out |= 1<<bit;
+ }
+ return out;
+}
+
+static const struct xprtsec_info xprtsec_name2info[] = {
+ { "none", NFSEXP_XPRTSEC_NONE },
+ { "tls", NFSEXP_XPRTSEC_TLS },
+ { "mtls", NFSEXP_XPRTSEC_MTLS },
+ { NULL, 0 }
+};
+
+static const struct xprtsec_info *find_xprtsec_info(const char *name)
+{
+ const struct xprtsec_info *info;
+
+ for (info = xprtsec_name2info; info->name; info++)
+ if (strcmp(info->name, name) == 0)
+ return info;
+ return NULL;
+}
+
+/*
+ * Append the given xprtsec mode to the exportent's e_xprtsec array,
+ * or do nothing if it's already there. Returns the index of flavor in
+ * the resulting array in any case.
+ */
+static int xprtsec_addmode(const struct xprtsec_info *info, struct exportent *ep)
+{
+ struct xprtsec_entry *p;
+
+ for (p = ep->e_xprtsec; p->info; p++)
+ if (p->info == info || p->info->number == info->number)
+ return p - ep->e_xprtsec;
+
+ if (p - ep->e_xprtsec >= XPRTSECMODE_COUNT) {
+ xlog(L_ERROR, "more than %d xprtsec modes on an export\n",
+ XPRTSECMODE_COUNT);
+ return -1;
+ }
+ p->info = info;
+ p->flags = ep->e_flags;
+ (p + 1)->info = NULL;
+ return p - ep->e_xprtsec;
+}
+
+/*
+ * @str is a colon seperated list of transport layer security modes.
+ * Their order is recorded in @ep, and a bitmap corresponding to the
+ * list is returned.
+ *
+ * A zero return indicates an error.
+ */
+static unsigned int parse_xprtsec(char *str, struct exportent *ep)
+{
+ unsigned int out = 0;
+ char *name;
+
+ while ((name = strsep(&str, ":"))) {
+ const struct xprtsec_info *info = find_xprtsec_info(name);
+ int bit;
+
+ if (!info) {
+ xlog(L_ERROR, "unknown xprtsec mode %s\n", name);
+ return 0;
+ }
+ bit = xprtsec_addmode(info, ep);
+ if (bit < 0)
+ return 0;
+ out |= 1 << bit;
+ }
+ return out;
+}
+
+/* Sets the bits in @mask for the appropriate security flavor flags. */
+static void setflags(int mask, unsigned int active, struct exportent *ep)
+{
+ int bit=0;
+
+ ep->e_flags |= mask;
+
+ while (active) {
+ if (active & 1)
+ ep->e_secinfo[bit].flags |= mask;
+ bit++;
+ active >>= 1;
+ }
+}
+
+/* Clears the bits in @mask for the appropriate security flavor flags. */
+static void clearflags(int mask, unsigned int active, struct exportent *ep)
+{
+ int bit=0;
+
+ ep->e_flags &= ~mask;
+
+ while (active) {
+ if (active & 1)
+ ep->e_secinfo[bit].flags &= ~mask;
+ bit++;
+ active >>= 1;
+ }
+}
+
+/*
+ * For those flags which are not allowed to vary by pseudoflavor,
+ * ensure that the export flags agree with the flags on each
+ * pseudoflavor:
+ */
+void fix_pseudoflavor_flags(struct exportent *ep)
+{
+ struct export_features *ef;
+ struct sec_entry *p;
+
+ ef = get_export_features();
+ for (p = ep->e_secinfo; p->flav; p++)
+ p->flags |= ep->e_flags & ~ef->secinfo_flags;
+}
+
+/*
+ * Parse option string pointed to by cp and set mount options accordingly.
+ */
+static int
+parseopts(char *cp, struct exportent *ep, int warn, int *had_subtree_opt_ptr)
+{
+ int had_subtree_opt = 0;
+ char *flname = efname?efname:"command line";
+ int flline = efp?efp->x_line:0;
+ unsigned int active = 0;
+ int saw_reexport = 0;
+
+ squids = ep->e_squids; nsquids = ep->e_nsquids;
+ sqgids = ep->e_sqgids; nsqgids = ep->e_nsqgids;
+ if (!cp)
+ goto out;
+
+ while (isblank(*cp))
+ cp++;
+
+ while (*cp) {
+ char *opt = strdup(cp);
+ char *optstart = cp;
+ while (*cp && *cp != ',')
+ cp++;
+ if (*cp) {
+ opt[cp-optstart] = '\0';
+ cp++;
+ }
+
+ /* process keyword */
+ if (strcmp(opt, "ro") == 0)
+ setflags(NFSEXP_READONLY, active, ep);
+ else if (strcmp(opt, "rw") == 0)
+ clearflags(NFSEXP_READONLY, active, ep);
+ else if (!strcmp(opt, "secure"))
+ clearflags(NFSEXP_INSECURE_PORT, active, ep);
+ else if (!strcmp(opt, "insecure"))
+ setflags(NFSEXP_INSECURE_PORT, active, ep);
+ else if (!strcmp(opt, "sync"))
+ clearflags(NFSEXP_ASYNC, active, ep);
+ else if (!strcmp(opt, "async"))
+ setflags(NFSEXP_ASYNC, active, ep);
+ else if (!strcmp(opt, "nordirplus"))
+ setflags(NFSEXP_NOREADDIRPLUS, active, ep);
+ else if (!strcmp(opt, "security_label"))
+ setflags(NFSEXP_SECURITY_LABEL, active, ep);
+ else if (!strcmp(opt, "nohide"))
+ setflags(NFSEXP_NOHIDE, active, ep);
+ else if (!strcmp(opt, "hide"))
+ clearflags(NFSEXP_NOHIDE, active, ep);
+ else if (!strcmp(opt, "crossmnt"))
+ setflags(NFSEXP_CROSSMOUNT, active, ep);
+ else if (!strcmp(opt, "nocrossmnt"))
+ clearflags(NFSEXP_CROSSMOUNT, active, ep);
+ else if (!strcmp(opt, "wdelay"))
+ setflags(NFSEXP_GATHERED_WRITES, active, ep);
+ else if (!strcmp(opt, "no_wdelay"))
+ clearflags(NFSEXP_GATHERED_WRITES, active, ep);
+ else if (strcmp(opt, "root_squash") == 0)
+ setflags(NFSEXP_ROOTSQUASH, active, ep);
+ else if (!strcmp(opt, "no_root_squash"))
+ clearflags(NFSEXP_ROOTSQUASH, active, ep);
+ else if (strcmp(opt, "all_squash") == 0)
+ setflags(NFSEXP_ALLSQUASH, active, ep);
+ else if (strcmp(opt, "no_all_squash") == 0)
+ clearflags(NFSEXP_ALLSQUASH, active, ep);
+ else if (strcmp(opt, "subtree_check") == 0) {
+ had_subtree_opt = 1;
+ clearflags(NFSEXP_NOSUBTREECHECK, active, ep);
+ } else if (strcmp(opt, "no_subtree_check") == 0) {
+ had_subtree_opt = 1;
+ setflags(NFSEXP_NOSUBTREECHECK, active, ep);
+ } else if (strcmp(opt, "auth_nlm") == 0)
+ clearflags(NFSEXP_NOAUTHNLM, active, ep);
+ else if (strcmp(opt, "no_auth_nlm") == 0)
+ setflags(NFSEXP_NOAUTHNLM, active, ep);
+ else if (strcmp(opt, "secure_locks") == 0)
+ clearflags(NFSEXP_NOAUTHNLM, active, ep);
+ else if (strcmp(opt, "insecure_locks") == 0)
+ setflags(NFSEXP_NOAUTHNLM, active, ep);
+ else if (strcmp(opt, "acl") == 0)
+ clearflags(NFSEXP_NOACL, active, ep);
+ else if (strcmp(opt, "no_acl") == 0)
+ setflags(NFSEXP_NOACL, active, ep);
+ else if (!strcmp(opt, "pnfs"))
+ setflags(NFSEXP_PNFS, active, ep);
+ else if (!strcmp(opt, "no_pnfs"))
+ clearflags(NFSEXP_PNFS, active, ep);
+ else if (strncmp(opt, "anonuid=", 8) == 0) {
+ char *oe;
+ ep->e_anonuid = strtol(opt+8, &oe, 10);
+ if (opt[8]=='\0' || *oe != '\0') {
+ xlog(L_ERROR, "%s: %d: bad anonuid \"%s\"\n",
+ flname, flline, opt);
+bad_option:
+ free(opt);
+ return -1;
+ }
+ } else if (strncmp(opt, "anongid=", 8) == 0) {
+ char *oe;
+ ep->e_anongid = strtol(opt+8, &oe, 10);
+ if (opt[8]=='\0' || *oe != '\0') {
+ xlog(L_ERROR, "%s: %d: bad anongid \"%s\"\n",
+ flname, flline, opt);
+ goto bad_option;
+ }
+ } else if (strncmp(opt, "squash_uids=", 12) == 0) {
+ if (parsesquash(opt+12, &squids, &nsquids, &cp) < 0) {
+ goto bad_option;
+ }
+ } else if (strncmp(opt, "squash_gids=", 12) == 0) {
+ if (parsesquash(opt+12, &sqgids, &nsqgids, &cp) < 0) {
+ goto bad_option;
+ }
+ } else if (strncmp(opt, "fsid=", 5) == 0) {
+ char *oe;
+
+ if (saw_reexport) {
+ xlog(L_ERROR, "%s:%d: 'fsid=' has to be before 'reexport=' %s\n",
+ flname, flline, opt);
+ goto bad_option;
+ }
+
+ if (strcmp(opt+5, "root") == 0) {
+ ep->e_fsid = 0;
+ setflags(NFSEXP_FSID, active, ep);
+ } else {
+ ep->e_fsid = strtoul(opt+5, &oe, 0);
+ if (opt[5]!='\0' && *oe == '\0')
+ setflags(NFSEXP_FSID, active, ep);
+ else if (valid_uuid(opt+5))
+ ep->e_uuid = strdup(opt+5);
+ else {
+ xlog(L_ERROR, "%s: %d: bad fsid \"%s\"\n",
+ flname, flline, opt);
+ goto bad_option;
+ }
+ }
+ } else if (strcmp(opt, "mountpoint")==0 ||
+ strcmp(opt, "mp") == 0 ||
+ strncmp(opt, "mountpoint=", 11)==0 ||
+ strncmp(opt, "mp=", 3) == 0) {
+ char * mp = strchr(opt, '=');
+ if (mp)
+ ep->e_mountpoint = strdup(mp+1);
+ else
+ ep->e_mountpoint = strdup("");
+#ifdef DEBUG
+ } else if (strncmp(opt, "fsloc=", 6) == 0) {
+ if (strcmp(opt+6, "stub") == 0)
+ ep->e_fslocmethod = FSLOC_STUB;
+ else {
+ xlog(L_ERROR, "%s:%d: bad option %s\n",
+ flname, flline, opt);
+ goto bad_option;
+ }
+#endif
+ } else if (strncmp(opt, "refer=", 6) == 0) {
+ ep->e_fslocmethod = FSLOC_REFER;
+ ep->e_fslocdata = strdup(opt+6);
+ } else if (strncmp(opt, "replicas=", 9) == 0) {
+ ep->e_fslocmethod = FSLOC_REPLICA;
+ ep->e_fslocdata = strdup(opt+9);
+ } else if (strncmp(opt, "sec=", 4) == 0) {
+ active = parse_flavors(opt+4, ep);
+ if (!active)
+ goto bad_option;
+ } else if (strncmp(opt, "xprtsec=", 8) == 0) {
+ if (!parse_xprtsec(opt + 8, ep))
+ goto bad_option;
+ } else if (strncmp(opt, "reexport=", 9) == 0) {
+ char *strategy = strchr(opt, '=');
+
+ if (!strategy) {
+ xlog(L_ERROR, "%s:%d: bad option %s\n",
+ flname, flline, opt);
+ goto bad_option;
+ }
+ strategy++;
+
+ if (saw_reexport) {
+ xlog(L_ERROR, "%s:%d: only one 'reexport=' is allowed%s\n",
+ flname, flline, opt);
+ goto bad_option;
+ }
+
+ if (strcmp(strategy, "auto-fsidnum") == 0) {
+ ep->e_reexport = REEXP_AUTO_FSIDNUM;
+ } else if (strcmp(strategy, "predefined-fsidnum") == 0) {
+ ep->e_reexport = REEXP_PREDEFINED_FSIDNUM;
+ } else if (strcmp(strategy, "none") == 0) {
+ ep->e_reexport = REEXP_NONE;
+ } else {
+ xlog(L_ERROR, "%s:%d: bad option %s\n",
+ flname, flline, strategy);
+ goto bad_option;
+ }
+
+ if (reexpdb_apply_reexport_settings(ep, flname, flline) != 0)
+ goto bad_option;
+
+ if (ep->e_fsid)
+ setflags(NFSEXP_FSID, active, ep);
+
+ saw_reexport = 1;
+ } else {
+ xlog(L_ERROR, "%s:%d: unknown keyword \"%s\"\n",
+ flname, flline, opt);
+ setflags(NFSEXP_ALLSQUASH | NFSEXP_READONLY, active, ep);
+ goto bad_option;
+ }
+ free(opt);
+ while (isblank(*cp))
+ cp++;
+ }
+
+ fix_pseudoflavor_flags(ep);
+ ep->e_squids = squids;
+ ep->e_sqgids = sqgids;
+ ep->e_nsquids = nsquids;
+ ep->e_nsqgids = nsqgids;
+
+out:
+ if (warn && !had_subtree_opt)
+ xlog(L_WARNING, "%s [%d]: Neither 'subtree_check' or 'no_subtree_check' specified for export \"%s:%s\".\n"
+ " Assuming default behaviour ('no_subtree_check').\n"
+ " NOTE: this default has changed since nfs-utils version 1.0.x\n",
+
+ flname, flline,
+ ep->e_hostname, ep->e_path);
+ if (had_subtree_opt_ptr)
+ *had_subtree_opt_ptr = had_subtree_opt;
+
+ 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");
+ xfree(id);
+ 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");
+ xfree(id);
+ return -1;
+ }
+ cp++;
+ } while(1);
+
+ if (**ep == ',') (*ep)++;
+
+ *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
+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, "%s:%d: syntax error",
+ efname?"command line":efname, efp->x_line);
+ return ok;
+}
+
+static void
+syntaxerr(char *msg)
+{
+ xlog(L_ERROR, "%s:%d: syntax error: %s",
+ efname, efp?efp->x_line:0, msg);
+}
+struct export_features *get_export_features(void)
+{
+ static char *path = "/proc/fs/nfsd/export_features";
+ static struct export_features ef;
+ static int cached = 0;
+ char buf[50];
+ int c;
+ int fd;
+
+ if (cached)
+ return &ef;
+
+ ef.flags = NFSEXP_OLDFLAGS;
+ ef.secinfo_flags = NFSEXP_OLD_SECINFO_FLAGS;
+
+ fd = open(path, O_RDONLY);
+ if (fd == -1)
+ goto good;
+ c = read(fd, buf, 50);
+ close(fd);
+ if (c == -1)
+ goto err;
+ buf[c] = 0;
+ c = sscanf(buf, "%x %x", &ef.flags, &ef.secinfo_flags);
+ if (c != 2)
+ goto err;
+good:
+ cached = 1;
+ return &ef;
+err:
+ xlog(L_WARNING, "unexpected error reading %s", path);
+ return &ef;
+}
diff --git a/support/nfs/getport.c b/support/nfs/getport.c
new file mode 100644
index 0000000..813f7bf
--- /dev/null
+++ b/support/nfs/getport.c
@@ -0,0 +1,1127 @@
+/*
+ * Provide a variety of APIs that query an rpcbind daemon to
+ * discover RPC service ports and allowed protocol version
+ * numbers.
+ *
+ * Copyright (C) 2008 Oracle Corporation. All rights reserved.
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 0211-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+
+#include <rpc/rpc.h>
+#include <rpc/pmap_prot.h>
+
+#ifdef HAVE_LIBTIRPC
+#include <netconfig.h>
+#include <rpc/rpcb_prot.h>
+#endif
+
+#include "sockaddr.h"
+#include "nfsrpc.h"
+#include "nfslib.h"
+
+/*
+ * Try a local socket first to access the local rpcbind daemon
+ *
+ * Rpcbind's local socket service does not seem to be working.
+ * Disable this logic for now.
+ */
+#ifdef HAVE_LIBTIRPC
+#undef NFS_GP_LOCAL
+#else /* !HAVE_LIBTIRPC */
+#undef NFS_GP_LOCAL
+#endif /* !HAVE_LIBTIRPC */
+
+#ifdef HAVE_LIBTIRPC
+static const rpcvers_t default_rpcb_version = RPCBVERS_4;
+#else /* !HAVE_LIBTIRPC */
+static const rpcvers_t default_rpcb_version = PMAPVERS;
+#endif /* !HAVE_LIBTIRPC */
+
+/*
+ * Historical: Map TCP connect timeouts to timeout
+ * error code used by UDP.
+ */
+static void
+nfs_gp_map_tcp_errorcodes(const unsigned short protocol)
+{
+ if (protocol != IPPROTO_TCP)
+ return;
+
+ switch (rpc_createerr.cf_error.re_errno) {
+ case ETIMEDOUT:
+ rpc_createerr.cf_stat = RPC_TIMEDOUT;
+ break;
+ case ECONNREFUSED:
+ rpc_createerr.cf_stat = RPC_CANTRECV;
+ break;
+ }
+}
+
+/*
+ * There's no easy way to tell how the local system's networking
+ * and rpcbind is configured (ie. whether we want to use IPv6 or
+ * IPv4 loopback to contact RPC services on the local host). We
+ * punt and simply try to look up "localhost".
+ *
+ * Returns TRUE on success.
+ */
+static int nfs_gp_loopback_address(struct sockaddr *sap, socklen_t *salen)
+{
+ struct addrinfo *gai_results;
+ int ret = 0;
+
+ if (getaddrinfo("localhost", NULL, NULL, &gai_results))
+ return 0;
+
+ if (*salen >= gai_results->ai_addrlen) {
+ memcpy(sap, gai_results->ai_addr,
+ gai_results->ai_addrlen);
+ *salen = gai_results->ai_addrlen;
+ ret = 1;
+ }
+
+ nfs_freeaddrinfo(gai_results);
+ return ret;
+}
+
+/*
+ * Look up a network service in /etc/services and return the
+ * network-order port number of that service.
+ */
+static in_port_t nfs_gp_getservbyname(const char *service,
+ const unsigned short protocol)
+{
+ const struct addrinfo gai_hint = {
+ .ai_family = AF_INET,
+ .ai_protocol = protocol,
+ .ai_flags = AI_PASSIVE,
+ };
+ struct addrinfo *gai_results;
+ const struct sockaddr_in *sin;
+ in_port_t port;
+
+ if (getaddrinfo(NULL, service, &gai_hint, &gai_results) != 0)
+ return 0;
+
+ sin = (const struct sockaddr_in *)gai_results->ai_addr;
+ port = sin->sin_port;
+
+ nfs_freeaddrinfo(gai_results);
+ return port;
+}
+
+/*
+ * Discover the port number that should be used to contact an
+ * rpcbind service. This will detect if the port has a local
+ * value that may have been set in /etc/services.
+ *
+ * Returns network byte-order port number of rpcbind service
+ * on this system.
+ */
+static in_port_t nfs_gp_get_rpcb_port(const unsigned short protocol)
+{
+ static const char *rpcb_netnametbl[] = {
+ "rpcbind",
+ "portmapper",
+ "sunrpc",
+ NULL,
+ };
+ unsigned int i;
+
+ for (i = 0; rpcb_netnametbl[i] != NULL; i++) {
+ in_port_t port;
+
+ port = nfs_gp_getservbyname(rpcb_netnametbl[i], protocol);
+ if (port != 0)
+ return port;
+ }
+
+ return (in_port_t)htons((uint16_t)PMAPPORT);
+}
+
+/*
+ * Set up an RPC client for communicating with an rpcbind daemon at
+ * @sap over @transport with protocol version @version.
+ *
+ * Returns a pointer to a prepared RPC client if successful, and
+ * @timeout is initialized; caller must destroy a non-NULL returned RPC
+ * client. Otherwise returns NULL, and rpc_createerr.cf_stat is set to
+ * reflect the error.
+ */
+static CLIENT *nfs_gp_get_rpcbclient(struct sockaddr *sap,
+ const socklen_t salen,
+ const unsigned short transport,
+ const rpcvers_t version,
+ struct timeval *timeout)
+{
+ static const char *rpcb_pgmtbl[] = {
+ "rpcbind",
+ "portmap",
+ "portmapper",
+ "sunrpc",
+ NULL,
+ };
+ rpcprog_t rpcb_prog = nfs_getrpcbyname(RPCBPROG, rpcb_pgmtbl);
+ CLIENT *clnt;
+
+ nfs_set_port(sap, ntohs(nfs_gp_get_rpcb_port(transport)));
+ clnt = nfs_get_rpcclient(sap, salen, transport, rpcb_prog,
+ version, timeout);
+ nfs_gp_map_tcp_errorcodes(transport);
+ return clnt;
+}
+
+/**
+ * nfs_get_proto - Convert a netid to an address family and protocol number
+ * @netid: C string containing a netid
+ * @family: OUT: address family
+ * @protocol: OUT: protocol number
+ *
+ * Returns 1 and fills in @protocol if the netid was recognized;
+ * otherwise zero is returned.
+ */
+#ifdef HAVE_LIBTIRPC
+int
+nfs_get_proto(const char *netid, sa_family_t *family, unsigned long *protocol)
+{
+ struct netconfig *nconf;
+ struct protoent *proto;
+
+ /*
+ * IANA does not define a protocol number for rdma netids,
+ * since "rdma" is not an IP protocol.
+ */
+ if (strcmp(netid, "rdma") == 0) {
+ *family = AF_INET;
+ *protocol = NFSPROTO_RDMA;
+ return 1;
+ }
+ if (strcmp(netid, "rdma6") == 0) {
+ *family = AF_INET6;
+ *protocol = NFSPROTO_RDMA;
+ return 1;
+ }
+
+ nconf = getnetconfigent(netid);
+ if (nconf == NULL)
+ return 0;
+
+ proto = getprotobyname(nconf->nc_proto);
+ if (proto == NULL) {
+ freenetconfigent(nconf);
+ return 0;
+ }
+
+ *family = AF_UNSPEC;
+ if (strcmp(nconf->nc_protofmly, NC_INET) == 0)
+ *family = AF_INET;
+ if (strcmp(nconf->nc_protofmly, NC_INET6) == 0)
+ *family = AF_INET6;
+ freenetconfigent(nconf);
+
+ *protocol = (unsigned long)proto->p_proto;
+ return 1;
+}
+#else /* !HAVE_LIBTIRPC */
+int
+nfs_get_proto(const char *netid, sa_family_t *family, unsigned long *protocol)
+{
+ struct protoent *proto;
+
+ /*
+ * IANA does not define a protocol number for rdma netids,
+ * since "rdma" is not an IP protocol.
+ */
+ if (strcmp(netid, "rdma") == 0) {
+ *family = AF_INET;
+ *protocol = NFSPROTO_RDMA;
+ return 1;
+ }
+
+ proto = getprotobyname(netid);
+ if (proto == NULL)
+ return 0;
+
+ *family = AF_INET;
+ *protocol = (unsigned long)proto->p_proto;
+ return 1;
+}
+#endif /* !HAVE_LIBTIRPC */
+
+/**
+ * nfs_get_netid - Convert a protocol family and protocol name to a netid
+ * @family: protocol family
+ * @protocol: protocol number
+ *
+ * One of the arguments passed when querying remote rpcbind services
+ * via rpcbind v3 or v4 is a netid string. This replaces the pm_prot
+ * field used in legacy PMAP_GETPORT calls.
+ *
+ * RFC 1833 says netids are not standard but rather defined on the local
+ * host. There are, however, standard definitions for nc_protofmly and
+ * nc_proto that can be used to derive a netid string on the local host,
+ * based on the contents of /etc/netconfig.
+ *
+ * Walk through the local netconfig database and grab the netid of the
+ * first entry that matches @family and @protocol and whose netid string
+ * fits in the provided buffer.
+ *
+ * Returns a '\0'-terminated string if successful. Caller must
+ * free the returned string. Otherwise NULL is returned, and
+ * rpc_createerr.cf_stat is set to reflect the error.
+ */
+#ifdef HAVE_LIBTIRPC
+char *nfs_get_netid(const sa_family_t family, const unsigned long protocol)
+{
+ char *nc_protofmly, *nc_proto, *nc_netid;
+ struct netconfig *nconf;
+ struct protoent *proto;
+ void *handle;
+
+ switch (family) {
+ case AF_LOCAL:
+ case AF_INET:
+ nc_protofmly = NC_INET;
+ break;
+ case AF_INET6:
+ nc_protofmly = NC_INET6;
+ break;
+ default:
+ goto out;
+ }
+
+ proto = getprotobynumber(protocol);
+ if (proto == NULL)
+ goto out;
+ nc_proto = proto->p_name;
+
+ handle = setnetconfig();
+ while ((nconf = getnetconfig(handle)) != NULL) {
+
+ if (nconf->nc_protofmly != NULL &&
+ strcmp(nconf->nc_protofmly, nc_protofmly) != 0)
+ continue;
+ if (nconf->nc_proto != NULL &&
+ strcmp(nconf->nc_proto, nc_proto) != 0)
+ continue;
+
+ nc_netid = strdup(nconf->nc_netid);
+ endnetconfig(handle);
+
+ if (nc_netid == NULL)
+ rpc_createerr.cf_stat = RPC_SYSTEMERROR;
+ return nc_netid;
+ }
+ endnetconfig(handle);
+
+out:
+ rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
+ return NULL;
+}
+#else /* !HAVE_LIBTIRPC */
+char *nfs_get_netid(const sa_family_t family, const unsigned long protocol)
+{
+ struct protoent *proto;
+ char *netid;
+
+ if (family != AF_INET)
+ goto out;
+ proto = getprotobynumber((int)protocol);
+ if (proto == NULL)
+ goto out;
+
+ netid = strdup(proto->p_name);
+ if (netid == NULL)
+ rpc_createerr.cf_stat = RPC_SYSTEMERROR;
+ return netid;
+
+out:
+ rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
+ return NULL;
+}
+#endif /* !HAVE_LIBTIRPC */
+
+/*
+ * Extract a port number from a universal address, and terminate the
+ * string in @addrstr just after the address part.
+ *
+ * Returns -1 if unsuccesful; otherwise a decoded port number (possibly 0)
+ * is returned.
+ */
+static int nfs_gp_universal_porthelper(char *addrstr)
+{
+ char *p, *endptr;
+ unsigned long portlo, porthi;
+ int port = -1;
+
+ p = strrchr(addrstr, '.');
+ if (p == NULL)
+ goto out;
+ portlo = strtoul(p + 1, &endptr, 10);
+ if (*endptr != '\0' || portlo > 255)
+ goto out;
+ *p = '\0';
+
+ p = strrchr(addrstr, '.');
+ if (p == NULL)
+ goto out;
+ porthi = strtoul(p + 1, &endptr, 10);
+ if (*endptr != '\0' || porthi > 255)
+ goto out;
+ *p = '\0';
+ port = (porthi << 8) | portlo;
+
+out:
+ return port;
+}
+
+/**
+ * nfs_universal2port - extract port number from a "universal address"
+ * @uaddr: '\0'-terminated C string containing a universal address
+ *
+ * Universal addresses (defined in RFC 1833) are used when calling an
+ * rpcbind daemon via protocol versions 3 or 4..
+ *
+ * Returns -1 if unsuccesful; otherwise a decoded port number (possibly 0)
+ * is returned.
+ */
+int nfs_universal2port(const char *uaddr)
+{
+ char *addrstr;
+ int port = -1;
+
+ addrstr = strdup(uaddr);
+ if (addrstr != NULL) {
+ port = nfs_gp_universal_porthelper(addrstr);
+ free(addrstr);
+ }
+ return port;
+}
+
+/**
+ * nfs_sockaddr2universal - convert a sockaddr to a "universal address"
+ * @sap: pointer to a socket address
+ *
+ * Universal addresses (defined in RFC 1833) are used when calling an
+ * rpcbind daemon via protocol versions 3 or 4..
+ *
+ * Returns a '\0'-terminated string if successful; caller must free
+ * the returned string. Otherwise NULL is returned and
+ * rpc_createerr.cf_stat is set to reflect the error.
+ *
+ * inet_ntop(3) is used here, since getnameinfo(3) is not available
+ * in some earlier glibc releases, and we don't require support for
+ * scope IDs for universal addresses.
+ */
+char *nfs_sockaddr2universal(const struct sockaddr *sap)
+{
+ const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sap;
+ const struct sockaddr_un *sun = (const struct sockaddr_un *)sap;
+ const struct sockaddr_in *sin = (const struct sockaddr_in *)sap;
+ char buf[INET6_ADDRSTRLEN + 8 /* for port information */];
+ uint16_t port;
+ size_t count;
+ char *result;
+ int len;
+
+ switch (sap->sa_family) {
+ case AF_LOCAL:
+ return strndup(sun->sun_path, sizeof(sun->sun_path));
+ case AF_INET:
+ if (inet_ntop(AF_INET, (const void *)&sin->sin_addr.s_addr,
+ buf, (socklen_t)sizeof(buf)) == NULL)
+ goto out_err;
+ port = ntohs(sin->sin_port);
+ break;
+ case AF_INET6:
+ if (inet_ntop(AF_INET6, (const void *)&sin6->sin6_addr,
+ buf, (socklen_t)sizeof(buf)) == NULL)
+ goto out_err;
+ port = ntohs(sin6->sin6_port);
+ break;
+ default:
+ goto out_err;
+ }
+
+ count = sizeof(buf) - strlen(buf);
+ len = snprintf(buf + strlen(buf), count, ".%u.%u",
+ (unsigned)(port >> 8), (unsigned)(port & 0xff));
+ /* before glibc 2.0.6, snprintf(3) could return -1 */
+ if (len < 0 || (size_t)len > count)
+ goto out_err;
+
+ result = strdup(buf);
+ if (result != NULL)
+ return result;
+
+out_err:
+ rpc_createerr.cf_stat = RPC_N2AXLATEFAILURE;
+ return NULL;
+}
+
+/*
+ * Send a NULL request to the indicated RPC service.
+ *
+ * Returns 1 if the service responded; otherwise 0;
+ */
+static int nfs_gp_ping(CLIENT *client, struct timeval timeout)
+{
+ enum clnt_stat status;
+
+ status = CLNT_CALL(client, NULLPROC,
+ (xdrproc_t)xdr_void, NULL,
+ (xdrproc_t)xdr_void, NULL,
+ timeout);
+
+ if (status != RPC_SUCCESS) {
+ rpc_createerr.cf_stat = status;
+ CLNT_GETERR(client, &rpc_createerr.cf_error);
+ }
+ return (int)(status == RPC_SUCCESS);
+}
+
+#ifdef HAVE_LIBTIRPC
+
+/*
+ * Initialize the rpcb argument for a GETADDR request.
+ *
+ * Returns 1 if successful, and caller must free strings pointed
+ * to by r_netid and r_addr; otherwise 0.
+ */
+static int nfs_gp_init_rpcb_parms(const struct sockaddr *sap,
+ const rpcprog_t program,
+ const rpcvers_t version,
+ const unsigned short protocol,
+ struct rpcb *parms)
+{
+ char *netid, *addr;
+
+ netid = nfs_get_netid(sap->sa_family, protocol);
+ if (netid == NULL)
+ return 0;
+
+ addr = nfs_sockaddr2universal(sap);
+ if (addr == NULL) {
+ free(netid);
+ return 0;
+ }
+
+ memset(parms, 0, sizeof(*parms));
+ parms->r_prog = program;
+ parms->r_vers = version;
+ parms->r_netid = netid;
+ parms->r_addr = addr;
+ parms->r_owner = "";
+
+ return 1;
+}
+
+static void nfs_gp_free_rpcb_parms(struct rpcb *parms)
+{
+ free(parms->r_netid);
+ free(parms->r_addr);
+}
+
+/*
+ * Try rpcbind GETADDR via version 4. If that fails, try same
+ * request via version 3.
+ *
+ * Returns non-zero port number on success; otherwise returns
+ * zero. rpccreateerr is set to reflect the nature of the error.
+ */
+static unsigned short nfs_gp_rpcb_getaddr(CLIENT *client,
+ struct rpcb *parms,
+ struct timeval timeout)
+{
+ rpcvers_t rpcb_version;
+ struct rpc_err rpcerr;
+ int port = 0;
+
+ for (rpcb_version = RPCBVERS_4;
+ rpcb_version >= RPCBVERS_3;
+ rpcb_version--) {
+ enum clnt_stat status;
+ char *uaddr = NULL;
+
+ CLNT_CONTROL(client, CLSET_VERS, (void *)&rpcb_version);
+ status = CLNT_CALL(client, (rpcproc_t)RPCBPROC_GETADDR,
+ (xdrproc_t)xdr_rpcb, (void *)parms,
+ (xdrproc_t)xdr_wrapstring, (void *)&uaddr,
+ timeout);
+
+ switch (status) {
+ case RPC_SUCCESS:
+ if ((uaddr == NULL) || (uaddr[0] == '\0')) {
+ rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
+ return 0;
+ }
+
+ port = nfs_universal2port(uaddr);
+ xdr_free((xdrproc_t)xdr_wrapstring, (char *)&uaddr);
+ if (port == -1) {
+ rpc_createerr.cf_stat = RPC_N2AXLATEFAILURE;
+ return 0;
+ }
+ return (unsigned short)port;
+ case RPC_PROGVERSMISMATCH:
+ clnt_geterr(client, &rpcerr);
+ if (rpcerr.re_vers.low > RPCBVERS4)
+ return 0;
+ continue;
+ case RPC_PROCUNAVAIL:
+ case RPC_PROGUNAVAIL:
+ continue;
+ default:
+ /* Most likely RPC_TIMEDOUT or RPC_CANTRECV */
+ rpc_createerr.cf_stat = status;
+ clnt_geterr(client, &rpc_createerr.cf_error);
+ return 0;
+ }
+
+ }
+
+ if (port == 0) {
+ rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
+ clnt_geterr(client, &rpc_createerr.cf_error);
+ }
+ return port;
+}
+
+#endif /* HAVE_LIBTIRPC */
+
+/*
+ * Try GETPORT request via rpcbind version 2.
+ *
+ * Returns non-zero port number on success; otherwise returns
+ * zero. rpccreateerr is set to reflect the nature of the error.
+ */
+static unsigned long nfs_gp_pmap_getport(CLIENT *client,
+ struct pmap *parms,
+ struct timeval timeout)
+{
+ enum clnt_stat status;
+ unsigned long port;
+
+ status = CLNT_CALL(client, (rpcproc_t)PMAPPROC_GETPORT,
+ (xdrproc_t)xdr_pmap, (void *)parms,
+ (xdrproc_t)xdr_u_long, (void *)&port,
+ timeout);
+
+ if (status != RPC_SUCCESS) {
+ rpc_createerr.cf_stat = status;
+ CLNT_GETERR(client, &rpc_createerr.cf_error);
+ port = 0;
+ } else if (port == 0)
+ rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
+
+ return port;
+}
+
+#ifdef HAVE_LIBTIRPC
+
+static unsigned short nfs_gp_getport_rpcb(CLIENT *client,
+ const struct sockaddr *sap,
+ const rpcprog_t program,
+ const rpcvers_t version,
+ const unsigned short protocol,
+ struct timeval timeout)
+{
+ unsigned short port = 0;
+ struct rpcb parms;
+
+ if (nfs_gp_init_rpcb_parms(sap, program, version,
+ protocol, &parms) != 0) {
+ port = nfs_gp_rpcb_getaddr(client, &parms, timeout);
+ nfs_gp_free_rpcb_parms(&parms);
+ }
+
+ return port;
+}
+
+#endif /* HAVE_LIBTIRPC */
+
+static unsigned long nfs_gp_getport_pmap(CLIENT *client,
+ const rpcprog_t program,
+ const rpcvers_t version,
+ const unsigned short protocol,
+ struct timeval timeout)
+{
+ struct pmap parms = {
+ .pm_prog = program,
+ .pm_vers = version,
+ .pm_prot = protocol,
+ };
+ rpcvers_t pmap_version = PMAPVERS;
+
+ CLNT_CONTROL(client, CLSET_VERS, (void *)&pmap_version);
+ return nfs_gp_pmap_getport(client, &parms, timeout);
+}
+
+/*
+ * Try an AF_INET6 request via rpcbind v4/v3; try an AF_INET
+ * request via rpcbind v2.
+ *
+ * Returns non-zero port number on success; otherwise returns
+ * zero. rpccreateerr is set to reflect the nature of the error.
+ */
+static unsigned short nfs_gp_getport(CLIENT *client,
+ const struct sockaddr *sap,
+ const rpcprog_t program,
+ const rpcvers_t version,
+ const unsigned short protocol,
+ struct timeval timeout)
+{
+ switch (sap->sa_family) {
+#ifdef HAVE_LIBTIRPC
+ case AF_INET6:
+ return nfs_gp_getport_rpcb(client, sap, program,
+ version, protocol, timeout);
+#endif /* HAVE_LIBTIRPC */
+ case AF_INET:
+ return nfs_gp_getport_pmap(client, program, version,
+ protocol, timeout);
+ }
+
+ rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
+ return 0;
+}
+
+/**
+ * nfs_rpc_ping - Determine if RPC service is responding to requests
+ * @sap: pointer to address of server to query (port is already filled in)
+ * @salen: length of server address
+ * @program: requested RPC program number
+ * @version: requested RPC version number
+ * @protocol: requested IPPROTO_ value of transport protocol
+ * @timeout: pointer to request timeout (NULL means use default timeout)
+ *
+ * Returns 1 if the remote service responded without an error; otherwise
+ * zero.
+ */
+int nfs_rpc_ping(const struct sockaddr *sap, const socklen_t salen,
+ const rpcprog_t program, const rpcvers_t version,
+ const unsigned short protocol, const struct timeval *timeout)
+{
+ union nfs_sockaddr address;
+ struct sockaddr *saddr = &address.sa;
+ CLIENT *client;
+ struct timeval tout = { -1, 0 };
+ int result = 0;
+
+ if (timeout != NULL)
+ tout = *timeout;
+
+ nfs_clear_rpc_createerr();
+
+ memcpy(saddr, sap, (size_t)salen);
+ client = nfs_get_rpcclient(saddr, salen, protocol,
+ program, version, &tout);
+ if (client != NULL) {
+ result = nfs_gp_ping(client, tout);
+ nfs_gp_map_tcp_errorcodes(protocol);
+ CLNT_DESTROY(client);
+ }
+
+ return result;
+}
+
+/**
+ * nfs_getport - query server's rpcbind to get port number for an RPC service
+ * @sap: pointer to address of server to query
+ * @salen: length of server's address
+ * @program: requested RPC program number
+ * @version: requested RPC version number
+ * @protocol: IPPROTO_ value of requested transport protocol
+ *
+ * Uses any acceptable rpcbind version to discover the port number for the
+ * RPC service described by the given [program, version, transport] tuple.
+ * Uses a quick timeout and an ephemeral source port. Supports AF_INET and
+ * AF_INET6 server addresses.
+ *
+ * Returns a positive integer representing the port number of the RPC
+ * service advertised by the server (in host byte order), or zero if the
+ * service is not advertised or there was some problem querying the server's
+ * rpcbind daemon. rpccreateerr is set to reflect the underlying cause of
+ * the error.
+ *
+ * There are a variety of ways to choose which transport and rpcbind versions
+ * to use. We chose to conserve local resources and try to avoid incurring
+ * timeouts.
+ *
+ * Transport
+ * To provide rudimentary support for traversing firewalls, query the remote
+ * using the same transport as the requested service. This provides some
+ * guarantee that the requested transport is available between this client
+ * and the server, and if the caller specifically requests TCP, for example,
+ * this may be becuase a firewall is in place that blocks UDP traffic. We
+ * could try both, but that could involve a lengthy timeout in several cases,
+ * and would often consume an extra ephemeral port.
+ *
+ * Rpcbind version
+ * To avoid using up too many ephemeral ports, AF_INET queries use tried-and-
+ * true rpcbindv2, and don't try the newer versions; and AF_INET6 queries use
+ * rpcbindv4, then rpcbindv3 on the same socket. The newer rpcbind protocol
+ * versions can adequately detect if a remote RPC service does not support
+ * AF_INET6 at all. The rpcbind socket is re-used in an attempt to keep the
+ * overall number of consumed ephemeral ports low.
+ */
+unsigned short nfs_getport(const struct sockaddr *sap,
+ const socklen_t salen,
+ const rpcprog_t program,
+ const rpcvers_t version,
+ const unsigned short protocol)
+{
+ union nfs_sockaddr address;
+ struct sockaddr *saddr = &address.sa;
+ struct timeval timeout = { -1, 0 };
+ unsigned short port = 0;
+ CLIENT *client;
+
+ nfs_clear_rpc_createerr();
+
+ memcpy(saddr, sap, (size_t)salen);
+ client = nfs_gp_get_rpcbclient(saddr, salen, protocol,
+ default_rpcb_version, &timeout);
+ if (client != NULL) {
+ port = nfs_gp_getport(client, saddr, program,
+ version, protocol, timeout);
+ CLNT_DESTROY(client);
+ }
+
+ return port;
+}
+
+/**
+ * nfs_getport_ping - query server's rpcbind and do RPC ping to verify result
+ * @sap: IN: pointer to address of server to query;
+ * OUT: pointer to updated address
+ * @salen: length of server's address
+ * @program: requested RPC program number
+ * @version: requested RPC version number
+ * @protocol: IPPROTO_ value of requested transport protocol
+ *
+ * Uses any acceptable rpcbind version to discover the port number for the
+ * RPC service described by the given [program, version, transport] tuple.
+ * Uses a quick timeout and an ephemeral source port. Supports AF_INET and
+ * AF_INET6 server addresses.
+ *
+ * Returns a 1 and sets the port number in the passed-in server address
+ * if both the query and the ping were successful; otherwise zero.
+ * rpccreateerr is set to reflect the underlying cause of the error.
+ */
+int nfs_getport_ping(struct sockaddr *sap, const socklen_t salen,
+ const rpcprog_t program, const rpcvers_t version,
+ const unsigned short protocol)
+{
+ struct timeval timeout = { -1, 0 };
+ unsigned short port = 0;
+ CLIENT *client;
+ int result = 0;
+
+ nfs_clear_rpc_createerr();
+
+ client = nfs_gp_get_rpcbclient(sap, salen, protocol,
+ default_rpcb_version, &timeout);
+ if (client != NULL) {
+ port = nfs_gp_getport(client, sap, program,
+ version, protocol, timeout);
+ CLNT_DESTROY(client);
+ client = NULL;
+ }
+
+ if (port != 0) {
+ union nfs_sockaddr address;
+ struct sockaddr *saddr = &address.sa;
+
+ memcpy(saddr, sap, (size_t)salen);
+ nfs_set_port(saddr, port);
+
+ nfs_clear_rpc_createerr();
+
+ client = nfs_get_rpcclient(saddr, salen, protocol,
+ program, version, &timeout);
+ if (client != NULL) {
+ result = nfs_gp_ping(client, timeout);
+ nfs_gp_map_tcp_errorcodes(protocol);
+ CLNT_DESTROY(client);
+ }
+ }
+
+ if (result)
+ nfs_set_port(sap, port);
+
+ return result;
+}
+
+/**
+ * nfs_getlocalport - query local rpcbind to get port number for an RPC service
+ * @program: requested RPC program number
+ * @version: requested RPC version number
+ * @protocol: IPPROTO_ value of requested transport protocol
+ *
+ * Uses any acceptable rpcbind version to discover the port number for the
+ * RPC service described by the given [program, version, transport] tuple.
+ * Uses a quick timeout and an ephemeral source port. Supports AF_INET and
+ * AF_INET6 local addresses.
+ *
+ * Returns a positive integer representing the port number of the RPC
+ * service advertised by the server (in host byte order), or zero if the
+ * service is not advertised or there was some problem querying the server's
+ * rpcbind daemon. rpccreateerr is set to reflect the underlying cause of
+ * the error.
+ *
+ * Try an AF_LOCAL connection first. The rpcbind daemon implementation should
+ * listen on AF_LOCAL.
+ *
+ * If that doesn't work (for example, if portmapper is running, or rpcbind
+ * isn't listening on /run/rpcbind.sock), send a query via UDP to localhost
+ * (UDP doesn't leave a socket in TIME_WAIT, and the timeout is a relatively
+ * short 3 seconds).
+ */
+unsigned short nfs_getlocalport(const rpcprot_t program,
+ const rpcvers_t version,
+ const unsigned short protocol)
+{
+ union nfs_sockaddr address;
+ struct sockaddr *lb_addr = &address.sa;
+ socklen_t lb_len = sizeof(*lb_addr);
+ unsigned short port = 0;
+
+#ifdef NFS_GP_LOCAL
+ const struct sockaddr_un sun = {
+ .sun_family = AF_LOCAL,
+ .sun_path = _PATH_RPCBINDSOCK,
+ };
+ const struct sockaddr *sap = (struct sockaddr *)&sun;
+ const socklen_t salen = SUN_LEN(&sun);
+ CLIENT *client;
+ struct timeval timeout = { -1, 0 };
+
+ nfs_clear_rpc_createerr();
+
+ client = nfs_gp_get_rpcbclient(sap, salen, 0, RPCBVERS_4, &timeout);
+ if (client != NULL) {
+ struct rpcb parms;
+
+ if (nfs_gp_init_rpcb_parms(sap, program, version,
+ protocol, &parms) != 0) {
+ port = nfs_gp_rpcb_getaddr(client, &parms, timeout);
+ nfs_gp_free_rpcb_parms(&parms);
+ }
+ CLNT_DESTROY(client);
+ }
+#endif /* NFS_GP_LOCAL */
+
+ if (port == 0) {
+ nfs_clear_rpc_createerr();
+
+ if (nfs_gp_loopback_address(lb_addr, &lb_len)) {
+ port = nfs_getport(lb_addr, lb_len,
+ program, version, protocol);
+ } else
+ rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
+ }
+
+ return port;
+}
+
+/**
+ * nfs_rpcb_getaddr - query rpcbind via rpcbind versions 4 and 3
+ * @sap: pointer to address of server to query
+ * @salen: length of server address
+ * @transport: transport protocol to use for the query
+ * @addr: pointer to r_addr address
+ * @program: requested RPC program number
+ * @version: requested RPC version number
+ * @protocol: requested IPPROTO_ value of transport protocol
+ * @timeout: pointer to request timeout (NULL means use default timeout)
+ *
+ * Returns a positive integer representing the port number of the RPC
+ * service advertised by the server (in host byte order), or zero if the
+ * service is not advertised or there was some problem querying the
+ * server's rpcbind daemon. rpccreateerr is set to reflect the
+ * underlying cause of the error.
+ *
+ * This function provides similar functionality to nfs_pmap_getport(),
+ * but performs the rpcbind lookup via rpcbind version 4. If the server
+ * doesn't support rpcbind version 4, it will retry with version 3.
+ * The GETADDR procedure is exactly the same in these two versions of
+ * the rpcbind protocol, so the socket, RPC client, and arguments are
+ * re-used when retrying, saving ephemeral port space.
+ *
+ * These RPC procedures take a universal address as an argument, so the
+ * query will fail if the remote rpcbind daemon doesn't find an entry
+ * with a matching address. A matching address includes an ANYADDR
+ * address of the same address family. In this way an RPC server can
+ * advertise via rpcbind that it does not support AF_INET6.
+ */
+#ifdef HAVE_LIBTIRPC
+
+unsigned short nfs_rpcb_getaddr(const struct sockaddr *sap,
+ const socklen_t salen,
+ const unsigned short transport,
+ const struct sockaddr *addr,
+ const rpcprog_t program,
+ const rpcvers_t version,
+ const unsigned short protocol,
+ const struct timeval *timeout)
+{
+ union nfs_sockaddr address;
+ struct sockaddr *saddr = &address.sa;
+ CLIENT *client;
+ struct rpcb parms;
+ struct timeval tout = { -1, 0 };
+ unsigned short port = 0;
+
+ if (timeout != NULL)
+ tout = *timeout;
+
+ nfs_clear_rpc_createerr();
+
+ memcpy(saddr, sap, (size_t)salen);
+ client = nfs_gp_get_rpcbclient(saddr, salen, transport,
+ RPCBVERS_4, &tout);
+ if (client != NULL) {
+ if (nfs_gp_init_rpcb_parms(addr, program, version,
+ protocol, &parms) != 0) {
+ port = nfs_gp_rpcb_getaddr(client, &parms, tout);
+ nfs_gp_free_rpcb_parms(&parms);
+ }
+ CLNT_DESTROY(client);
+ }
+
+ return port;
+}
+
+#else /* !HAVE_LIBTIRPC */
+
+unsigned short nfs_rpcb_getaddr(__attribute__((unused)) const struct sockaddr *sap,
+ __attribute__((unused)) const socklen_t salen,
+ __attribute__((unused)) const unsigned short transport,
+ __attribute__((unused)) const struct sockaddr *addr,
+ __attribute__((unused)) const rpcprog_t program,
+ __attribute__((unused)) const rpcvers_t version,
+ __attribute__((unused)) const unsigned short protocol,
+ __attribute__((unused)) const struct timeval *timeout)
+{
+ nfs_clear_rpc_createerr();
+
+ rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
+ return 0;
+}
+
+#endif /* !HAVE_LIBTIRPC */
+
+/**
+ * nfs_pmap_getport - query rpcbind via the portmap protocol (rpcbindv2)
+ * @sin: pointer to AF_INET address of server to query
+ * @transport: transport protocol to use for the query
+ * @program: requested RPC program number
+ * @version: requested RPC version number
+ * @protocol: requested IPPROTO_ value of transport protocol
+ * @timeout: pointer to request timeout (NULL means use default timeout)
+ *
+ * Returns a positive integer representing the port number of the RPC service
+ * advertised by the server (in host byte order), or zero if the service is
+ * not advertised or there was some problem querying the server's rpcbind
+ * daemon. rpccreateerr is set to reflect the underlying cause of the error.
+ *
+ * nfs_pmap_getport() is very similar to pmap_getport(), except that:
+ *
+ * 1. This version always tries to use an ephemeral port, since reserved
+ * ports are not needed for GETPORT queries. This conserves the very
+ * limited reserved port space, helping reduce failed socket binds
+ * during mount storms.
+ *
+ * 2. This version times out quickly by default. It time-limits the
+ * connect process as well as the actual RPC call, and even allows the
+ * caller to specify the timeout.
+ *
+ * 3. This version shares code with the rpcbindv3 and rpcbindv4 query
+ * functions. It can use a TI-RPC generated CLIENT.
+ */
+unsigned long nfs_pmap_getport(const struct sockaddr_in *sin,
+ const unsigned short transport,
+ const unsigned long program,
+ const unsigned long version,
+ const unsigned long protocol,
+ const struct timeval *timeout)
+{
+ struct sockaddr_in address;
+ struct sockaddr *saddr = (struct sockaddr *)&address;
+ CLIENT *client;
+ struct pmap parms = {
+ .pm_prog = program,
+ .pm_vers = version,
+ .pm_prot = protocol,
+ };
+ struct timeval tout = { -1, 0 };
+ unsigned long port = 0;
+
+ if (timeout != NULL)
+ tout = *timeout;
+
+ nfs_clear_rpc_createerr();
+
+ memcpy(saddr, sin, sizeof(address));
+ client = nfs_gp_get_rpcbclient(saddr, (socklen_t)sizeof(*sin),
+ transport, PMAPVERS, &tout);
+ if (client != NULL) {
+ port = nfs_gp_pmap_getport(client, &parms, tout);
+ CLNT_DESTROY(client);
+ }
+
+ return port;
+}
+
+static const char *nfs_ns_pgmtbl[] = {
+ "status",
+ NULL,
+};
+
+/*
+ * nfs_probe_statd - use nfs_pmap_getport to see if statd is running locally
+ *
+ * Returns non-zero if statd is running locally.
+ */
+int nfs_probe_statd(void)
+{
+ struct sockaddr_in addr = {
+ .sin_family = AF_INET,
+ .sin_addr.s_addr = htonl(INADDR_LOOPBACK),
+ };
+ rpcprog_t program = nfs_getrpcbyname(NSMPROG, nfs_ns_pgmtbl);
+
+ return nfs_getport_ping((struct sockaddr *)(char *)&addr, sizeof(addr),
+ program, (rpcvers_t)1, IPPROTO_UDP);
+}
diff --git a/support/nfs/mydaemon.c b/support/nfs/mydaemon.c
new file mode 100644
index 0000000..d1cf08d
--- /dev/null
+++ b/support/nfs/mydaemon.c
@@ -0,0 +1,153 @@
+/*
+ mydaemon.c
+
+ Copyright (c) 2000 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>.
+ Copyright (c) 2002 Andy Adamson <andros@UMICH.EDU>.
+ Copyright (c) 2002 Marius Aamodt Eriksen <marius@UMICH.EDU>.
+ Copyright (c) 2002 J. Bruce Fields <bfields@UMICH.EDU>.
+ Copyright (c) 2013 Jeff Layton <jlayton@redhat.com>
+
+ All rights reserved, all wrongs reversed.
+
+ 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. 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 ``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.
+
+*/
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <xlog.h>
+
+#include "nfslib.h"
+
+static int pipefds[2] = { -1, -1};
+
+/**
+ * daemon_init - initial daemon setup
+ * @fg: whether to run in the foreground
+ *
+ * This function is like daemon(), but with our own special sauce to delay
+ * the exit of the parent until the child is set up properly. A pipe is created
+ * between parent and child. The parent process will wait to exit until the
+ * child dies or writes an int on the pipe signaling its status.
+ */
+void
+daemon_init(bool fg)
+{
+ int pid, status, tempfd;
+
+ if (fg)
+ return;
+
+ if (pipe(pipefds) < 0) {
+ xlog_err("mydaemon: pipe() failed: errno %d (%s)\n",
+ errno, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ pid = fork();
+ if (pid < 0) {
+ xlog_err("mydaemon: fork() failed: errno %d (%s)\n",
+ errno, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ if (pid > 0) {
+ /* Parent */
+ close(pipefds[1]);
+ if (read(pipefds[0], &status, sizeof(status)) != sizeof(status))
+ exit(EXIT_FAILURE);
+ exit(status);
+ }
+
+ /* Child */
+ close(pipefds[0]);
+ setsid ();
+
+ if (chdir ("/")) {
+ xlog_err("mydaemon: chdir() failed: errno %d (%s)\n",
+ errno, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ while (pipefds[1] <= 2) {
+ pipefds[1] = dup(pipefds[1]);
+ if (pipefds[1] < 0) {
+ xlog_err("mydaemon: dup() failed: errno %d (%s)\n",
+ errno, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ tempfd = open("/dev/null", O_RDWR);
+ if (tempfd < 0) {
+ xlog_err("mydaemon: can't open /dev/null: errno %d "
+ "(%s)\n", errno, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ dup2(tempfd, 0);
+ dup2(tempfd, 1);
+ dup2(tempfd, 2);
+ close(tempfd);
+ closelog();
+ dup2(pipefds[1], 3);
+ pipefds[1] = 3;
+ closeall(4);
+}
+
+/**
+ * daemon_ready - tell interested parties that the daemon is ready
+ *
+ * This function tells e.g. the parent process that the daemon is up
+ * and running.
+ */
+void
+daemon_ready(void)
+{
+ int status = 0;
+
+ if (pipefds[1] > 0) {
+ if (write(pipefds[1], &status, sizeof(status)) != sizeof(status)) {
+ xlog_err("WARN: writing to parent pipe failed: errno "
+ "%d (%s)\n", errno, strerror(errno));
+ }
+ close(pipefds[1]);
+ pipefds[1] = -1;
+ }
+}
+
diff --git a/support/nfs/nfs_mntent.c b/support/nfs/nfs_mntent.c
new file mode 100644
index 0000000..25e5944
--- /dev/null
+++ b/support/nfs/nfs_mntent.c
@@ -0,0 +1,240 @@
+/* Private version of the libc *mntent() routines. */
+/* Note slightly different prototypes. */
+
+/* 1999-02-22 Arkadiusz Miskiewicz <misiek@pld.ORG.PL>
+ * - added Native Language Support
+ *
+ * 2006-06-08 Amit Gud <agud@redhat.com>
+ * - Moved to nfs-utils/support/nfs from util-linux/mount
+ */
+
+#include <stdio.h>
+#include <string.h> /* for strchr */
+#include <ctype.h> /* for isdigit */
+#include <sys/stat.h> /* for umask */
+#include <unistd.h> /* for ftruncate */
+#include <errno.h> /* for errno */
+
+#include "nfs_mntent.h"
+#include "nls.h"
+#include "xcommon.h"
+
+/* Unfortunately the classical Unix /etc/mtab and /etc/fstab
+ do not handle directory names containing spaces.
+ Here we mangle them, replacing a space by \040.
+ What do other Unices do? */
+
+static unsigned char need_escaping[] = { ' ', '\t', '\n', '\\' };
+
+static char *
+mangle(const char *arg) {
+ const unsigned char *s = (const unsigned char *)arg;
+ char *ss, *sp;
+ unsigned int n;
+
+ n = strlen(arg);
+ ss = sp = xmalloc(4*n+1);
+ while(1) {
+ for (n = 0; n < sizeof(need_escaping); n++) {
+ if (*s == need_escaping[n]) {
+ *sp++ = '\\';
+ *sp++ = '0' + ((*s & 0300) >> 6);
+ *sp++ = '0' + ((*s & 070) >> 3);
+ *sp++ = '0' + (*s & 07);
+ goto next;
+ }
+ }
+ *sp++ = *s;
+ if (*s == 0)
+ break;
+ next:
+ s++;
+ }
+ return ss;
+}
+
+static int
+is_space_or_tab (char c) {
+ return (c == ' ' || c == '\t');
+}
+
+static char *
+skip_spaces(char *s) {
+ while (is_space_or_tab(*s))
+ s++;
+ return s;
+}
+
+static char *
+skip_nonspaces(char *s) {
+ while (*s && !is_space_or_tab(*s))
+ s++;
+ return s;
+}
+
+#define isoctal(a) (((a) & ~7) == '0')
+
+/* returns malloced pointer - no more strdup required */
+static char *
+unmangle(char *s) {
+ char *ret, *ss, *sp;
+
+ ss = skip_nonspaces(s);
+ ret = sp = xmalloc(ss-s+1);
+ while(s != ss) {
+ if (*s == '\\' && isoctal(s[1]) && isoctal(s[2]) && isoctal(s[3])) {
+ *sp++ = 64*(s[1] & 7) + 8*(s[2] & 7) + (s[3] & 7);
+ s += 4;
+ } else
+ *sp++ = *s++;
+ }
+ *sp = 0;
+ return ret;
+}
+
+/*
+ * fstat'ing the file and allocating a buffer holding all of it
+ * may be a bad idea: if the file is /proc/mounts, the stat
+ * returns 0.
+ * (On the other hand, mangling and unmangling is meaningless
+ * for /proc/mounts.)
+ */
+
+mntFILE *
+nfs_setmntent (const char *file, char *mode) {
+ mntFILE *mfp = xmalloc(sizeof(*mfp));
+ mode_t old_umask = umask(077);
+
+ mfp->mntent_fp = fopen(file, mode);
+ umask(old_umask);
+ mfp->mntent_file = xstrdup(file);
+ mfp->mntent_errs = (mfp->mntent_fp == NULL);
+ mfp->mntent_softerrs = 0;
+ mfp->mntent_lineno = 0;
+ return mfp;
+}
+
+void
+nfs_endmntent (mntFILE *mfp) {
+ if (mfp) {
+ if (mfp->mntent_fp)
+ fclose(mfp->mntent_fp);
+ if (mfp->mntent_file)
+ free(mfp->mntent_file);
+ free(mfp);
+ }
+}
+
+int
+nfs_addmntent (mntFILE *mfp, struct mntent *mnt) {
+ char *m1, *m2, *m3, *m4;
+ int res;
+ off_t length;
+
+ if (fseek (mfp->mntent_fp, 0, SEEK_END))
+ return 1; /* failure */
+ length = ftell(mfp->mntent_fp);
+
+ m1 = mangle(mnt->mnt_fsname);
+ m2 = mangle(mnt->mnt_dir);
+ m3 = mangle(mnt->mnt_type);
+ m4 = mangle(mnt->mnt_opts);
+
+ res = fprintf (mfp->mntent_fp, "%s %s %s %s %d %d\n",
+ m1, m2, m3, m4, mnt->mnt_freq, mnt->mnt_passno);
+
+ free(m1);
+ free(m2);
+ free(m3);
+ free(m4);
+ if (res >= 0) {
+ res = fflush(mfp->mntent_fp);
+ if (res < 0) {
+ nfs_error("Cant't flush out mtab: %s", strerror(errno));
+ /* Avoid leaving a corrupt mtab file */
+ if (ftruncate(fileno(mfp->mntent_fp), length))
+ {/* Ignore this failure; Why confuse things */}
+ }
+ }
+ return (res < 0) ? 1 : 0;
+}
+
+/* Read the next entry from the file fp. Stop reading at an incorrect entry. */
+struct mntent *
+nfs_getmntent (mntFILE *mfp) {
+ static char buf[4096];
+ static struct mntent me;
+ char *s;
+
+ again:
+ if (mfp->mntent_errs || mfp->mntent_softerrs >= ERR_MAX)
+ return NULL;
+
+ /* read the next non-blank non-comment line */
+ do {
+ if (fgets (buf, sizeof(buf), mfp->mntent_fp) == NULL)
+ return NULL;
+
+ mfp->mntent_lineno++;
+ s = strchr (buf, '\n');
+ if (s == NULL) {
+ /* Missing final newline? Otherwise extremely */
+ /* long line - assume file was corrupted */
+ if (feof(mfp->mntent_fp)) {
+ fprintf(stderr, _("[mntent]: warning: no final "
+ "newline at the end of %s\n"),
+ mfp->mntent_file);
+ s = strchr (buf, 0);
+ } else {
+ mfp->mntent_errs = 1;
+ goto err;
+ }
+ }
+ *s = 0;
+ if (--s >= buf && *s == '\r')
+ *s = 0;
+ s = skip_spaces(buf);
+ } while (*s == '\0' || *s == '#');
+
+ me.mnt_fsname = unmangle(s);
+ s = skip_nonspaces(s);
+ s = skip_spaces(s);
+ me.mnt_dir = unmangle(s);
+ s = skip_nonspaces(s);
+ s = skip_spaces(s);
+ me.mnt_type = unmangle(s);
+ s = skip_nonspaces(s);
+ s = skip_spaces(s);
+ me.mnt_opts = unmangle(s);
+ s = skip_nonspaces(s);
+ s = skip_spaces(s);
+
+ if (isdigit(*s)) {
+ me.mnt_freq = atoi(s);
+ while(isdigit(*s)) s++;
+ } else
+ me.mnt_freq = 0;
+ if(*s && !is_space_or_tab(*s))
+ goto err;
+
+ s = skip_spaces(s);
+ if(isdigit(*s)) {
+ me.mnt_passno = atoi(s);
+ while(isdigit(*s)) s++;
+ } else
+ me.mnt_passno = 0;
+ if(*s && !is_space_or_tab(*s))
+ goto err;
+
+ /* allow more stuff, e.g. comments, on this line */
+
+ return &me;
+
+ err:
+ mfp->mntent_softerrs++;
+ fprintf(stderr, _("[mntent]: line %d in %s is bad%s\n"),
+ mfp->mntent_lineno, mfp->mntent_file,
+ (mfp->mntent_errs || mfp->mntent_softerrs >= ERR_MAX) ?
+ _("; rest of file ignored") : "");
+ goto again;
+}
diff --git a/support/nfs/rmtab.c b/support/nfs/rmtab.c
new file mode 100644
index 0000000..154b26f
--- /dev/null
+++ b/support/nfs/rmtab.c
@@ -0,0 +1,173 @@
+/*
+ * support/nfs/rmtab.c
+ *
+ * Handling for rmtab.
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <signal.h>
+#include "nfslib.h"
+
+/*
+ * Colons in incoming IPv6 presentation addresses have to
+ * replaced with another character, since rmtab already
+ * uses colons to delineate fields.
+ *
+ * Use a printable character, but one that would never be
+ * found in a presentation address or domain name
+ */
+#define IPV6_COLON ';'
+
+#define LINELEN (2048)
+
+static FILE *rmfp = NULL;
+
+struct state_paths rmtab;
+
+int
+setrmtabent(char *type)
+{
+ if (rmfp)
+ fclose(rmfp);
+ rmfp = fsetrmtabent(rmtab.statefn, 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, long *pos)
+{
+ return fgetrmtabent(rmfp, log, pos);
+}
+
+struct rmtabent *
+fgetrmtabent(FILE *fp, int log, long *pos)
+{
+ static struct rmtabent re;
+ char *count, *host, *path, *c;
+ static char buf[LINELEN];
+
+ errno = 0;
+ if (!fp)
+ return NULL;
+ do {
+ if (pos)
+ *pos = ftell (fp);
+ if (fgets(buf, sizeof(buf)-1, fp) == NULL)
+ return NULL;
+ host = buf;
+ if ((path = strchr(host, '\n')) != NULL)
+ *path = '\0';
+ if (!(path = strchr(host, ':'))) {
+ if (log)
+ xlog(L_ERROR, "malformed entry in rmtab file");
+ errno = EINVAL;
+ return NULL;
+ }
+ *path++ = '\0';
+ count = strchr(path, ':');
+ if (count) {
+ *count++ = '\0';
+ re.r_count = strtol (count, NULL, 0);
+ }
+ else
+ re.r_count = 1;
+ } while (0);
+
+ strncpy(re.r_client, host, sizeof (re.r_client) - 1);
+ re.r_client[sizeof (re.r_client) - 1] = '\0';
+ for (c = re.r_client; *c != '\0'; c++)
+ if (*c == IPV6_COLON)
+ *c = ':';
+
+ strncpy(re.r_path, path, sizeof (re.r_path) - 1);
+ re.r_path[sizeof (re.r_path) - 1] = '\0';
+
+ return &re;
+}
+
+void
+putrmtabent(struct rmtabent *rep, long *pos)
+{
+ fputrmtabent(rmfp, rep, pos);
+}
+
+void
+fputrmtabent(FILE *fp, struct rmtabent *rep, long *pos)
+{
+ static char buf[LINELEN];
+ char *c;
+
+ if (!fp || (pos && fseek (fp, *pos, SEEK_SET) != 0))
+ return;
+
+ /*
+ * To avoid confusing the token parser in fgetrmtabent(),
+ * convert colons in incoming IPv6 presentation addresses
+ * to semicolons.
+ */
+ if (strlen(rep->r_client) > sizeof(buf)) {
+ xlog(L_ERROR, "client name too large");
+ return;
+ }
+ strncpy(buf, rep->r_client, sizeof(buf));
+ for (c = buf; *c != '\0'; c++)
+ if (*c == ':')
+ *c = IPV6_COLON;
+
+ (void)fprintf(fp, "%s:%s:0x%.8x\n", buf, rep->r_path, rep->r_count);
+}
+
+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/rpc_socket.c b/support/nfs/rpc_socket.c
new file mode 100644
index 0000000..5fabf5a
--- /dev/null
+++ b/support/nfs/rpc_socket.c
@@ -0,0 +1,560 @@
+/*
+ * Generic RPC client socket-level APIs for nfs-utils
+ *
+ * Copyright (C) 2008 Oracle Corporation. All rights reserved.
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 0211-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include <stdbool.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+
+#include <rpc/rpc.h>
+#include <rpc/pmap_prot.h>
+
+#include "sockaddr.h"
+#include "nfsrpc.h"
+
+#ifdef HAVE_LIBTIRPC
+#include <netconfig.h>
+#include <rpc/rpcb_prot.h>
+#endif /* HAVE_LIBTIRPC */
+
+/*
+ * If "-1" is specified in the tv_sec field, use these defaults instead.
+ */
+#define NFSRPC_TIMEOUT_UDP (3)
+#define NFSRPC_TIMEOUT_TCP (10)
+
+
+/*
+ * Set up an RPC client for communicating via a AF_LOCAL socket.
+ *
+ * @timeout is initialized upon return
+ *
+ * Returns a pointer to a prepared RPC client if successful; caller
+ * must destroy a non-NULL returned RPC client. Otherwise NULL, and
+ * rpc_createerr.cf_stat is set to reflect the error.
+ */
+static CLIENT *nfs_get_localclient(const struct sockaddr *sap,
+ const socklen_t salen,
+ const rpcprog_t program,
+ const rpcvers_t version,
+ struct timeval *timeout)
+{
+#ifdef HAVE_LIBTIRPC
+ struct sockaddr_storage address;
+ const struct netbuf nbuf = {
+ .maxlen = sizeof(struct sockaddr_un),
+ .len = (size_t)salen,
+ .buf = &address,
+ };
+#else
+ (void) salen;
+#endif /* HAVE_LIBTIRPC */
+ CLIENT *client;
+ int sock;
+
+ sock = socket(AF_LOCAL, SOCK_STREAM, 0);
+ if (sock == -1) {
+ rpc_createerr.cf_stat = RPC_SYSTEMERROR;
+ rpc_createerr.cf_error.re_errno = errno;
+ return NULL;
+ }
+
+ if (timeout->tv_sec == -1)
+ timeout->tv_sec = NFSRPC_TIMEOUT_TCP;
+
+#ifdef HAVE_LIBTIRPC
+ memcpy(nbuf.buf, sap, (size_t)salen);
+ client = clnt_vc_create(sock, &nbuf, program, version, 0, 0);
+#else /* !HAVE_LIBTIRPC */
+ client = clntunix_create((struct sockaddr_un *)sap,
+ program, version, &sock, 0, 0);
+#endif /* !HAVE_LIBTIRPC */
+ if (client != NULL)
+ CLNT_CONTROL(client, CLSET_FD_CLOSE, NULL);
+ else
+ (void)close(sock);
+
+ return client;
+}
+
+#ifdef HAVE_LIBTIRPC
+
+/*
+ * Bind a socket using an unused privileged source port.
+ *
+ * Returns zero on success, or returns -1 on error. errno is
+ * set to reflect the nature of the error.
+ */
+static int nfs_bindresvport(const int sock, const sa_family_t family)
+{
+ struct sockaddr_in sin = {
+ .sin_family = AF_INET,
+ .sin_addr.s_addr = htonl(INADDR_ANY),
+ };
+ struct sockaddr_in6 sin6 = {
+ .sin6_family = AF_INET6,
+ .sin6_addr = IN6ADDR_ANY_INIT,
+ };
+
+ switch (family) {
+ case AF_INET:
+ return bindresvport_sa(sock, (struct sockaddr *)(char *)&sin);
+ case AF_INET6:
+ return bindresvport_sa(sock, (struct sockaddr *)(char *)&sin6);
+ }
+
+ errno = EAFNOSUPPORT;
+ return -1;
+}
+
+#else /* !HAVE_LIBTIRPC */
+
+/*
+ * Bind a socket using an unused privileged source port.
+ *
+ * Returns zero on success, or returns -1 on error. errno is
+ * set to reflect the nature of the error.
+ */
+static int nfs_bindresvport(const int sock, const sa_family_t family)
+{
+ if (family != AF_INET) {
+ errno = EAFNOSUPPORT;
+ return -1;
+ }
+
+ return bindresvport(sock, NULL);
+}
+
+#endif /* !HAVE_LIBTIRPC */
+
+/*
+ * Perform a non-blocking connect on the socket fd.
+ *
+ * @timeout is modified to contain the time remaining (i.e. time provided
+ * minus time elasped).
+ *
+ * Returns zero on success, or returns -1 on error. errno is
+ * set to reflect the nature of the error.
+ */
+static int nfs_connect_nb(const int fd, const struct sockaddr *sap,
+ const socklen_t salen, struct timeval *timeout)
+{
+ int flags, ret;
+ fd_set rset;
+
+ flags = fcntl(fd, F_GETFL, 0);
+ if (flags < 0)
+ return -1;
+
+ ret = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
+ if (ret < 0)
+ return -1;
+
+ /*
+ * From here on subsequent sys calls could change errno so
+ * we set ret = -errno to capture it in case we decide to
+ * use it later.
+ */
+ ret = connect(fd, sap, salen);
+ if (ret < 0 && errno != EINPROGRESS && errno != EINTR) {
+ ret = -1;
+ goto done;
+ }
+
+ if (ret == 0)
+ goto done;
+
+ /* now wait */
+ FD_ZERO(&rset);
+ FD_SET(fd, &rset);
+
+ while ((ret = select(fd + 1, NULL, &rset, NULL, timeout)) < 0) {
+ if (errno != EINTR) {
+ ret = -1;
+ goto done;
+ } else {
+ continue;
+ }
+ }
+ if (ret == 0) {
+ errno = ETIMEDOUT;
+ ret = -1;
+ goto done;
+ }
+
+ if (FD_ISSET(fd, &rset)) {
+ int status;
+ socklen_t len = (socklen_t)sizeof(ret);
+
+ status = getsockopt(fd, SOL_SOCKET, SO_ERROR, &ret, &len);
+ if (status < 0) {
+ ret = -1;
+ goto done;
+ }
+
+ /* Oops - something wrong with connect */
+ if (ret != 0) {
+ errno = ret;
+ ret = -1;
+ }
+ }
+
+done:
+ (void)fcntl(fd, F_SETFL, flags);
+ return ret;
+}
+
+/*
+ * Set up an RPC client for communicating via a datagram socket.
+ * A connected UDP socket is used to detect a missing remote
+ * listener as quickly as possible.
+ *
+ * @timeout is initialized upon return
+ *
+ * Returns a pointer to a prepared RPC client if successful; caller
+ * must destroy a non-NULL returned RPC client. Otherwise NULL, and
+ * rpc_createerr.cf_stat is set to reflect the error.
+ */
+static CLIENT *nfs_get_udpclient(const struct sockaddr *sap,
+ const socklen_t salen,
+ const rpcprog_t program,
+ const rpcvers_t version,
+ struct timeval *timeout,
+ const int resvport)
+{
+ CLIENT *client;
+ int ret = 0;
+ int sock = 0;
+#ifdef HAVE_LIBTIRPC
+ struct sockaddr_storage address;
+ const struct netbuf nbuf = {
+ .maxlen = salen,
+ .len = salen,
+ .buf = &address,
+ };
+
+#else /* !HAVE_LIBTIRPC */
+
+ if (sap->sa_family != AF_INET) {
+ rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
+ return NULL;
+ }
+#endif /* !HAVE_LIBTIRPC */
+
+ sock = socket((int)sap->sa_family, SOCK_DGRAM, IPPROTO_UDP);
+ if (sock == -1) {
+ rpc_createerr.cf_stat = RPC_SYSTEMERROR;
+ rpc_createerr.cf_error.re_errno = errno;
+ return NULL;
+ }
+
+ if (resvport) {
+ ret = nfs_bindresvport(sock, sap->sa_family);
+
+ if (ret < 0) {
+ rpc_createerr.cf_stat = RPC_SYSTEMERROR;
+ rpc_createerr.cf_error.re_errno = errno;
+ (void)close(sock);
+ return NULL;
+ }
+ }
+
+ if (timeout->tv_sec == -1)
+ timeout->tv_sec = NFSRPC_TIMEOUT_UDP;
+
+ ret = nfs_connect_nb(sock, sap, salen, timeout);
+ if (ret != 0) {
+ rpc_createerr.cf_stat = RPC_SYSTEMERROR;
+ rpc_createerr.cf_error.re_errno = errno;
+ (void)close(sock);
+ return NULL;
+ }
+
+#ifdef HAVE_LIBTIRPC
+ memcpy(nbuf.buf, sap, (size_t)salen);
+ client = clnt_dg_create(sock, &nbuf, program, version, 0, 0);
+#else /* !HAVE_LIBTIRPC */
+ client = clntudp_create((struct sockaddr_in *)sap, program,
+ version, *timeout, &sock);
+#endif /* !HAVE_LIBTIRPC */
+ if (client != NULL) {
+ struct timeval retry_timeout = { 1, 0 };
+ CLNT_CONTROL(client, CLSET_RETRY_TIMEOUT,
+ (char *)&retry_timeout);
+ CLNT_CONTROL(client, CLSET_FD_CLOSE, NULL);
+ } else
+ (void)close(sock);
+
+ return client;
+}
+
+/*
+ * Set up and connect an RPC client for communicating via a stream socket.
+ *
+ * @timeout is initialized upon return
+ *
+ * Returns a pointer to a prepared and connected RPC client if
+ * successful; caller must destroy a non-NULL returned RPC client.
+ * Otherwise NULL, and rpc_createerr.cf_stat is set to reflect the
+ * error.
+ */
+static CLIENT *nfs_get_tcpclient(const struct sockaddr *sap,
+ const socklen_t salen,
+ const rpcprog_t program,
+ const rpcvers_t version,
+ struct timeval *timeout,
+ const int resvport)
+{
+ CLIENT *client;
+ int ret = 0;
+ int sock = 0;
+#ifdef HAVE_LIBTIRPC
+ struct sockaddr_storage address;
+ const struct netbuf nbuf = {
+ .maxlen = salen,
+ .len = salen,
+ .buf = &address,
+ };
+
+#else /* !HAVE_LIBTIRPC */
+
+ if (sap->sa_family != AF_INET) {
+ rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
+ return NULL;
+ }
+#endif /* !HAVE_LIBTIRPC */
+
+ sock = socket((int)sap->sa_family, SOCK_STREAM, IPPROTO_TCP);
+ if (sock == -1) {
+ rpc_createerr.cf_stat = RPC_SYSTEMERROR;
+ rpc_createerr.cf_error.re_errno = errno;
+ return NULL;
+ }
+
+ if (resvport) {
+ ret = nfs_bindresvport(sock, sap->sa_family);
+
+ if (ret < 0) {
+ rpc_createerr.cf_stat = RPC_SYSTEMERROR;
+ rpc_createerr.cf_error.re_errno = errno;
+ (void)close(sock);
+ return NULL;
+ }
+ }
+
+ if (timeout->tv_sec == -1)
+ timeout->tv_sec = NFSRPC_TIMEOUT_TCP;
+
+ ret = nfs_connect_nb(sock, sap, salen, timeout);
+ if (ret != 0) {
+ rpc_createerr.cf_stat = RPC_SYSTEMERROR;
+ rpc_createerr.cf_error.re_errno = errno;
+ (void)close(sock);
+ return NULL;
+ }
+
+#ifdef HAVE_LIBTIRPC
+ memcpy(nbuf.buf, sap, (size_t)salen);
+ client = clnt_vc_create(sock, &nbuf, program, version, 0, 0);
+#else /* !HAVE_LIBTIRPC */
+ client = clnttcp_create((struct sockaddr_in *)sap,
+ program, version, &sock, 0, 0);
+#endif /* !HAVE_LIBTIRPC */
+ if (client != NULL)
+ CLNT_CONTROL(client, CLSET_FD_CLOSE, NULL);
+ else
+ (void)close(sock);
+
+ return client;
+}
+
+/**
+ * nfs_get_rpcclient - acquire an RPC client
+ * @sap: pointer to socket address of RPC server
+ * @salen: length of socket address
+ * @transport: IPPROTO_ value of transport protocol to use
+ * @program: RPC program number
+ * @version: RPC version number
+ * @timeout: pointer to request timeout (must not be NULL)
+ *
+ * Set up an RPC client for communicating with an RPC program @program
+ * and @version on the server @sap over @transport. An unprivileged
+ * source port is used.
+ *
+ * Returns a pointer to a prepared RPC client if successful, and
+ * @timeout is initialized; caller must destroy a non-NULL returned RPC
+ * client. Otherwise returns NULL, and rpc_createerr.cf_stat is set to
+ * reflect the error.
+ */
+CLIENT *nfs_get_rpcclient(const struct sockaddr *sap,
+ const socklen_t salen,
+ const unsigned short transport,
+ const rpcprog_t program,
+ const rpcvers_t version,
+ struct timeval *timeout)
+{
+ nfs_clear_rpc_createerr();
+
+ switch (sap->sa_family) {
+ case AF_LOCAL:
+ return nfs_get_localclient(sap, salen, program,
+ version, timeout);
+ case AF_INET:
+ case AF_INET6:
+ if (nfs_get_port(sap) == 0) {
+ rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
+ return NULL;
+ }
+ break;
+ default:
+ rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
+ return NULL;
+ }
+
+ switch (transport) {
+ case IPPROTO_TCP:
+ return nfs_get_tcpclient(sap, salen, program, version,
+ timeout, 0);
+ case 0:
+ case IPPROTO_UDP:
+ return nfs_get_udpclient(sap, salen, program, version,
+ timeout, 0);
+ }
+
+ rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
+ return NULL;
+}
+
+/**
+ * nfs_get_priv_rpcclient - acquire an RPC client
+ * @sap: pointer to socket address of RPC server
+ * @salen: length of socket address
+ * @transport: IPPROTO_ value of transport protocol to use
+ * @program: RPC program number
+ * @version: RPC version number
+ * @timeout: pointer to request timeout (must not be NULL)
+ *
+ * Set up an RPC client for communicating with an RPC program @program
+ * and @version on the server @sap over @transport. A privileged
+ * source port is used.
+ *
+ * Returns a pointer to a prepared RPC client if successful, and
+ * @timeout is initialized; caller must destroy a non-NULL returned RPC
+ * client. Otherwise returns NULL, and rpc_createerr.cf_stat is set to
+ * reflect the error.
+ */
+CLIENT *nfs_get_priv_rpcclient(const struct sockaddr *sap,
+ const socklen_t salen,
+ const unsigned short transport,
+ const rpcprog_t program,
+ const rpcvers_t version,
+ struct timeval *timeout)
+{
+ nfs_clear_rpc_createerr();
+
+ switch (sap->sa_family) {
+ case AF_LOCAL:
+ return nfs_get_localclient(sap, salen, program,
+ version, timeout);
+ case AF_INET:
+ case AF_INET6:
+ if (nfs_get_port(sap) == 0) {
+ rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
+ return NULL;
+ }
+ break;
+ default:
+ rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
+ return NULL;
+ }
+
+ switch (transport) {
+ case IPPROTO_TCP:
+ return nfs_get_tcpclient(sap, salen, program, version,
+ timeout, 1);
+ case 0:
+ case IPPROTO_UDP:
+ return nfs_get_udpclient(sap, salen, program, version,
+ timeout, 1);
+ }
+
+ rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
+ return NULL;
+}
+
+/**
+ * nfs_getrpcbyname - convert an RPC program name to a rpcprog_t
+ * @program: default program number to use if names not found in db
+ * @table: pointer to table of 'char *' names to try to find
+ *
+ * Returns program number of first name to be successfully looked
+ * up, or the default program number if all lookups fail.
+ */
+rpcprog_t nfs_getrpcbyname(const rpcprog_t program, const char *table[])
+{
+#ifdef HAVE_GETRPCBYNAME
+ struct rpcent *entry;
+ unsigned int i;
+
+ if (table != NULL)
+ for (i = 0; table[i] != NULL; i++) {
+ entry = getrpcbyname(table[i]);
+ if (entry)
+ return (rpcprog_t)entry->r_number;
+ }
+#endif /* HAVE_GETRPCBYNAME */
+
+ return program;
+}
+
+/*
+ * AUTH_SYS doesn't allow more than 16 gids in the supplemental group list.
+ * If there are more than that, trying to determine which ones to include
+ * in the list is problematic. This function creates an auth handle that
+ * only has the primary gid in the supplemental gids list. It's intended to
+ * be used for protocols where credentials really don't matter much (the MNT
+ * protocol, for instance).
+ */
+AUTH *
+nfs_authsys_create(void)
+{
+ char machname[MAXHOSTNAMELEN + 1];
+ uid_t uid = geteuid();
+ gid_t gid = getegid();
+
+ if (gethostname(machname, sizeof(machname)) == -1)
+ return NULL;
+
+ return authunix_create(machname, uid, gid, 1, &gid);
+}
diff --git a/support/nfs/rpcdispatch.c b/support/nfs/rpcdispatch.c
new file mode 100644
index 0000000..7329f41
--- /dev/null
+++ b/support/nfs/rpcdispatch.c
@@ -0,0 +1,69 @@
+/*
+ * support/nfs/rcpdispatch.c
+ *
+ * Generic RPC dispatcher.
+ *
+ * Copyright (C) 1995, 1996, Olaf Kirch <okir@monad.swb.de>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <rpc/rpc.h>
+#include <rpc/pmap_clnt.h>
+#include <signal.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <string.h>
+#include "rpcmisc.h"
+#include "xlog.h"
+
+void
+rpc_dispatch(struct svc_req *rqstp, SVCXPRT *transp,
+ struct rpc_dtable *dtable, int nvers,
+ void *argp, void *resp)
+{
+ struct rpc_dentry *dent;
+ int rq_vers = (int)rqstp->rq_vers;
+
+ if (rq_vers < 1 || rq_vers > nvers) {
+ svcerr_progvers(transp, 1, nvers);
+ return;
+ }
+ dtable += (rq_vers - 1);
+ if (rqstp->rq_proc > dtable->nproc) {
+ svcerr_noproc(transp);
+ return;
+ }
+
+ if (dtable->nproc <= rqstp->rq_proc) {
+ 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);
+ }
+}
diff --git a/support/nfs/rpcmisc.c b/support/nfs/rpcmisc.c
new file mode 100644
index 0000000..d84c04f
--- /dev/null
+++ b/support/nfs/rpcmisc.c
@@ -0,0 +1,213 @@
+/*
+ * Miscellaneous functions for RPC service 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <rpc/rpc.h>
+#include <rpc/pmap_clnt.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <memory.h>
+#include <errno.h>
+#include <unistd.h>
+#include <time.h>
+#include "nfslib.h"
+#include "rpcmisc.h"
+
+#if SIZEOF_SOCKLEN_T - 0 == 0
+#define socklen_t int
+#endif
+
+#define _RPCSVC_CLOSEDOWN 120
+int _rpcpmstart = 0;
+unsigned int _rpcprotobits = (NFSCTL_UDPBIT|NFSCTL_TCPBIT);
+int _rpcsvcdirty = 0;
+
+static void
+closedown(int sig)
+{
+ (void) signal(sig, closedown);
+
+ if (_rpcsvcdirty == 0) {
+ static int size;
+ int i, openfd;
+
+ if (NFSCTL_TCPISSET(_rpcprotobits) == 0)
+ 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);
+}
+
+/*
+ * Create listener socket for a given port
+ *
+ * Return an open network socket on success; otherwise return -1
+ * if some error occurs.
+ */
+static int
+makesock(int port, int proto)
+{
+ struct sockaddr_in sin;
+ int sock, sock_type, val;
+
+ sock_type = (proto == IPPROTO_UDP) ? SOCK_DGRAM : SOCK_STREAM;
+ sock = socket(AF_INET, sock_type, proto);
+ if (sock < 0) {
+ xlog(L_FATAL, "Could not make a socket: %s",
+ strerror(errno));
+ return -1;
+ }
+ memset((char *) &sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = htonl(INADDR_ANY);
+ sin.sin_port = htons(port);
+
+ val = 1;
+ if (proto == IPPROTO_TCP)
+ if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
+ &val, sizeof(val)) < 0)
+ xlog(L_ERROR, "setsockopt failed: %s",
+ strerror(errno));
+
+ if (bind(sock, (struct sockaddr *) &sin, sizeof(sin)) == -1) {
+ xlog(L_FATAL, "Could not bind name to socket: %s",
+ strerror(errno));
+ close(sock);
+ return -1;
+ }
+
+ return svcsock_nonblock(sock);
+}
+
+void
+rpc_init(char *name, int prog, int vers,
+ void (*dispatch)(struct svc_req *, register SVCXPRT *),
+ int defport)
+{
+ struct sockaddr_in saddr;
+ SVCXPRT *transp;
+ int sock;
+ socklen_t asize;
+
+ asize = sizeof(saddr);
+ sock = 0;
+ if (getsockname(0, (struct sockaddr *) &saddr, &asize) == 0
+ && saddr.sin_family == AF_INET) {
+ socklen_t ssize = sizeof(int);
+ int fdtype = 0;
+ if (getsockopt(0, SOL_SOCKET, SO_TYPE,
+ (char *)&fdtype, &ssize) == -1)
+ xlog(L_FATAL, "getsockopt failed: %s", strerror(errno));
+ /* inetd passes a UDP socket or a listening TCP socket.
+ * listen will fail on a connected TCP socket(passed by rsh).
+ */
+ if (!(fdtype == SOCK_STREAM && listen(0,5) == -1)) {
+ switch(fdtype) {
+ case SOCK_DGRAM:
+ NFSCTL_UDPSET(_rpcprotobits);
+ break;
+ case SOCK_STREAM:
+ NFSCTL_TCPSET(_rpcprotobits);
+ break;
+ default:
+ xlog(L_FATAL, "getsockopt returns bad socket type: %d", fdtype);
+ }
+ _rpcpmstart = 1;
+ }
+ }
+ if (!_rpcpmstart) {
+ pmap_unset(prog, vers);
+ sock = RPC_ANYSOCK;
+ }
+
+ if (NFSCTL_UDPISSET(_rpcprotobits)) {
+ static SVCXPRT *last_transp = NULL;
+
+ if (_rpcpmstart == 0) {
+ if (last_transp
+ && (!defport || defport == last_transp->xp_port)) {
+ transp = last_transp;
+ goto udp_transport;
+ }
+ if (defport == 0)
+ sock = RPC_ANYSOCK;
+ else
+ sock = makesock(defport, IPPROTO_UDP);
+ }
+ if (sock == RPC_ANYSOCK)
+ sock = svcudp_socket (prog);
+ transp = svcudp_create(sock);
+ if (transp == NULL) {
+ xlog(L_FATAL, "cannot create udp service.");
+ }
+ udp_transport:
+ if (!svc_register(transp, prog, vers, dispatch, IPPROTO_UDP)) {
+ xlog(L_FATAL, "unable to register (%s, %d, udp).",
+ name, vers);
+ }
+ last_transp = transp;
+ }
+
+ if (NFSCTL_TCPISSET(_rpcprotobits)) {
+ static SVCXPRT *last_transp = NULL;
+
+ if (_rpcpmstart == 0) {
+ if (last_transp
+ && (!defport || defport == last_transp->xp_port)) {
+ transp = last_transp;
+ goto tcp_transport;
+ }
+ if (defport == 0)
+ sock = RPC_ANYSOCK;
+ else
+ sock = makesock(defport, IPPROTO_TCP);
+ }
+ if (sock == RPC_ANYSOCK)
+ sock = svctcp_socket (prog, 1);
+ transp = svctcp_create(sock, 0, 0);
+ if (transp == NULL) {
+ xlog(L_FATAL, "cannot create tcp service.");
+ }
+ tcp_transport:
+ if (!svc_register(transp, prog, vers, dispatch, IPPROTO_TCP)) {
+ xlog(L_FATAL, "unable to register (%s, %d, tcp).",
+ name, vers);
+ }
+ last_transp = transp;
+ }
+
+ if (_rpcpmstart) {
+ signal(SIGALRM, closedown);
+ alarm(_RPCSVC_CLOSEDOWN);
+ }
+}
diff --git a/support/nfs/strlcat.c b/support/nfs/strlcat.c
new file mode 100644
index 0000000..0edee14
--- /dev/null
+++ b/support/nfs/strlcat.c
@@ -0,0 +1,78 @@
+/* $OpenBSD: strlcat.c,v 1.8 2001/05/13 15:40:15 deraadt Exp $ */
+
+/*
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ * 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. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``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 AUTHOR 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.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char *rcsid = "$OpenBSD: strlcat.c,v 1.8 2001/05/13 15:40:15 deraadt Exp $";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+#include <string.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include "nfslib.h"
+
+/*
+ * Appends src to string dst of size siz (unlike strncat, siz is the
+ * full size of dst, not space left). At most siz-1 characters
+ * will be copied. Always NUL terminates (unless siz <= strlen(dst)).
+ * Returns strlen(src) + MIN(siz, strlen(initial dst)).
+ * If retval >= siz, truncation occurred.
+ */
+size_t
+strlcat(char *dst,
+ const char *src,
+ size_t siz)
+{
+ register char *d = dst;
+ register const char *s = src;
+ register size_t n = siz;
+ size_t dlen;
+
+ /* Find the end of dst and adjust bytes left but don't go past end */
+ while (n-- != 0 && *d != '\0')
+ d++;
+ dlen = d - dst;
+ n = siz - dlen;
+
+ if (n == 0)
+ return(dlen + strlen(s));
+ while (*s != '\0') {
+ if (n != 1) {
+ *d++ = *s;
+ n--;
+ }
+ s++;
+ }
+ *d = '\0';
+
+ return(dlen + (s - src)); /* count does not include NUL */
+}
diff --git a/support/nfs/strlcpy.c b/support/nfs/strlcpy.c
new file mode 100644
index 0000000..23e3ae9
--- /dev/null
+++ b/support/nfs/strlcpy.c
@@ -0,0 +1,74 @@
+/* $OpenBSD: strlcpy.c,v 1.5 2001/05/13 15:40:16 deraadt Exp $ */
+
+/*
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ * 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. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``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 AUTHOR 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.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char *rcsid = "$OpenBSD: strlcpy.c,v 1.5 2001/05/13 15:40:16 deraadt Exp $";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+#include <string.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include "nfslib.h"
+
+/*
+ * Copy src to string dst of size siz. At most siz-1 characters
+ * will be copied. Always NUL terminates (unless siz == 0).
+ * Returns strlen(src); if retval >= siz, truncation occurred.
+ */
+size_t
+strlcpy(char *dst,
+ const char *src,
+ size_t siz)
+{
+ register char *d = dst;
+ register const char *s = src;
+ register size_t n = siz;
+
+ /* Copy as many bytes as will fit */
+ if (n != 0 && --n != 0) {
+ do {
+ if ((*d++ = *s++) == 0)
+ break;
+ } while (--n != 0);
+ }
+
+ /* Not enough room in dst, add NUL and traverse rest of src */
+ if (n == 0) {
+ if (siz != 0)
+ *d = '\0'; /* NUL-terminate dst */
+ while (*s++)
+ ;
+ }
+
+ return(s - src - 1); /* count does not include NUL */
+}
diff --git a/support/nfs/svc_create.c b/support/nfs/svc_create.c
new file mode 100644
index 0000000..976c2d2
--- /dev/null
+++ b/support/nfs/svc_create.c
@@ -0,0 +1,525 @@
+/*
+ * Copyright 2009 Oracle. All rights reserved.
+ *
+ * This file is part of nfs-utils.
+ *
+ * nfs-utils 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.
+ *
+ * nfs-utils 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 nfs-utils. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <memory.h>
+#include <signal.h>
+#include <unistd.h>
+#include <errno.h>
+#include <netdb.h>
+#include "nfslib.h"
+
+#include <netinet/in.h>
+
+#include <sys/socket.h>
+#include <sys/resource.h>
+
+#include <rpc/rpc.h>
+#include <rpc/svc.h>
+
+#ifdef HAVE_TCP_WRAPPER
+#include "tcpwrapper.h"
+#endif
+
+#include "sockaddr.h"
+#include "rpcmisc.h"
+#include "xlog.h"
+
+#ifdef HAVE_LIBTIRPC
+
+#include <rpc/rpc_com.h>
+
+#define SVC_CREATE_XPRT_CACHE_SIZE (8)
+static SVCXPRT *svc_create_xprt_cache[SVC_CREATE_XPRT_CACHE_SIZE] = { NULL, };
+
+/*
+ * Cache an SVC xprt, in case there are more programs or versions to
+ * register against it.
+ */
+static void
+svc_create_cache_xprt(SVCXPRT *xprt)
+{
+ unsigned int i;
+
+ /* Check if we've already got this one... */
+ for (i = 0; i < SVC_CREATE_XPRT_CACHE_SIZE; i++)
+ if (svc_create_xprt_cache[i] == xprt)
+ return;
+
+ /* No, we don't. Cache it. */
+ for (i = 0; i < SVC_CREATE_XPRT_CACHE_SIZE; i++)
+ if (svc_create_xprt_cache[i] == NULL) {
+ svc_create_xprt_cache[i] = xprt;
+ return;
+ }
+
+ xlog(L_ERROR, "%s: Failed to cache an xprt", __func__);
+}
+
+/*
+ * Find a previously cached SVC xprt structure with the given bind address
+ * and transport semantics.
+ *
+ * Returns pointer to a cached SVC xprt.
+ *
+ * If no matching SVC XPRT can be found, NULL is returned.
+ */
+static SVCXPRT *
+svc_create_find_xprt(const struct sockaddr *bindaddr, const struct netconfig *nconf)
+{
+ unsigned int i;
+
+ for (i = 0; i < SVC_CREATE_XPRT_CACHE_SIZE; i++) {
+ SVCXPRT *xprt = svc_create_xprt_cache[i];
+ struct sockaddr *sap;
+
+ if (xprt == NULL)
+ continue;
+ if (strcmp(nconf->nc_netid, xprt->xp_netid) != 0)
+ continue;
+ sap = (struct sockaddr *)xprt->xp_ltaddr.buf;
+ if (!nfs_compare_sockaddr(bindaddr, sap))
+ continue;
+ return xprt;
+ }
+ return NULL;
+}
+
+/*
+ * Set up an appropriate bind address, given @port and @nconf.
+ *
+ * Returns getaddrinfo(3) results if successful. Caller must
+ * invoke freeaddrinfo(3) on these results.
+ *
+ * Otherwise NULL is returned if an error occurs.
+ */
+__attribute__((__malloc__))
+static struct addrinfo *
+svc_create_bindaddr(struct netconfig *nconf, const uint16_t port)
+{
+ struct addrinfo *ai = NULL;
+ struct addrinfo hint = {
+ .ai_flags = AI_PASSIVE | AI_NUMERICSERV,
+ };
+ char buf[8];
+ int error;
+
+ if (strcmp(nconf->nc_protofmly, NC_INET) == 0)
+ hint.ai_family = AF_INET;
+#ifdef IPV6_SUPPORTED
+ else if (strcmp(nconf->nc_protofmly, NC_INET6) == 0)
+ hint.ai_family = AF_INET6;
+#endif /* IPV6_SUPPORTED */
+ else {
+ xlog(L_ERROR, "Unrecognized bind address family: %s",
+ nconf->nc_protofmly);
+ return NULL;
+ }
+
+ if (strcmp(nconf->nc_proto, NC_UDP) == 0)
+ hint.ai_protocol = (int)IPPROTO_UDP;
+ else if (strcmp(nconf->nc_proto, NC_TCP) == 0)
+ hint.ai_protocol = (int)IPPROTO_TCP;
+ else {
+ xlog(L_ERROR, "Unrecognized bind address protocol: %s",
+ nconf->nc_proto);
+ return NULL;
+ }
+
+ (void)snprintf(buf, sizeof(buf), "%u", port);
+ error = getaddrinfo(NULL, buf, &hint, &ai);
+ if (error != 0) {
+ xlog(L_ERROR, "Failed to construct bind address: %s",
+ gai_strerror(error));
+ return NULL;
+ }
+
+ return ai;
+}
+
+/*
+ * Create a listener socket on a specific bindaddr, and set
+ * special socket options to allow it to share the same port
+ * as other listeners.
+ *
+ * Returns an open, bound, and possibly listening network
+ * socket on success.
+ *
+ * Otherwise returns -1 if some error occurs.
+ */
+static int
+svc_create_sock(const struct sockaddr *sap, socklen_t salen,
+ struct netconfig *nconf)
+{
+ int fd, type, protocol;
+ int one = 1;
+
+ switch(nconf->nc_semantics) {
+ case NC_TPI_CLTS:
+ type = SOCK_DGRAM;
+ break;
+ case NC_TPI_COTS_ORD:
+ type = SOCK_STREAM;
+ break;
+ default:
+ xlog(D_GENERAL, "%s: Unrecognized bind address semantics: %lu",
+ __func__, nconf->nc_semantics);
+ return -1;
+ }
+
+ if (strcmp(nconf->nc_proto, NC_UDP) == 0)
+ protocol = (int)IPPROTO_UDP;
+ else if (strcmp(nconf->nc_proto, NC_TCP) == 0)
+ protocol = (int)IPPROTO_TCP;
+ else {
+ xlog(D_GENERAL, "%s: Unrecognized bind address protocol: %s",
+ __func__, nconf->nc_proto);
+ return -1;
+ }
+
+ fd = socket((int)sap->sa_family, type, protocol);
+ if (fd == -1) {
+ xlog(L_ERROR, "Could not make a socket: (%d) %m",
+ errno);
+ return -1;
+ }
+
+#ifdef IPV6_SUPPORTED
+ if (sap->sa_family == AF_INET6) {
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY,
+ &one, sizeof(one)) == -1) {
+ xlog(L_ERROR, "Failed to set IPV6_V6ONLY: (%d) %m",
+ errno);
+ (void)close(fd);
+ return -1;
+ }
+ }
+#endif /* IPV6_SUPPORTED */
+
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
+ &one, sizeof(one)) == -1) {
+ xlog(L_ERROR, "Failed to set SO_REUSEADDR: (%d) %m",
+ errno);
+ (void)close(fd);
+ return -1;
+ }
+
+ if (bind(fd, sap, salen) == -1) {
+ xlog(L_ERROR, "Could not bind socket: (%d) %m",
+ errno);
+ (void)close(fd);
+ return -1;
+ }
+
+ if (nconf->nc_semantics == NC_TPI_COTS_ORD)
+ if (listen(fd, SOMAXCONN) == -1) {
+ xlog(L_ERROR, "Could not listen on socket: (%d) %m",
+ errno);
+ (void)close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+/*
+ * The simple case is allowing the TI-RPC library to create a
+ * transport itself, given just the bind address and transport
+ * semantics.
+ *
+ * Our local xprt cache is ignored in this path, since the
+ * caller is not interested in sharing listeners or ports, and
+ * the library automatically avoids ports already in use.
+ *
+ * Returns the count of started listeners (one or zero).
+ */
+static unsigned int
+svc_create_nconf_rand_port(const char *name, const rpcprog_t program,
+ const rpcvers_t version,
+ void (*dispatch)(struct svc_req *, SVCXPRT *),
+ struct netconfig *nconf)
+{
+ struct t_bind bindaddr;
+ struct addrinfo *ai;
+ SVCXPRT *xprt;
+
+ ai = svc_create_bindaddr(nconf, 0);
+ if (ai == NULL)
+ return 0;
+
+ bindaddr.addr.buf = ai->ai_addr;
+ bindaddr.qlen = SOMAXCONN;
+
+ xprt = svc_tli_create(RPC_ANYFD, nconf, &bindaddr, 0, 0);
+ nfs_freeaddrinfo(ai);
+ if (xprt == NULL) {
+ xlog(L_ERROR, "Failed to create listener xprt "
+ "(%s, %u, %s)", name, version, nconf->nc_netid);
+ return 0;
+ }
+ if (svcsock_nonblock(xprt->xp_fd) < 0) {
+ /* close() already done by svcsock_nonblock() */
+ xprt->xp_fd = RPC_ANYFD;
+ SVC_DESTROY(xprt);
+ return 0;
+ }
+
+ rpc_createerr.cf_stat = rpc_createerr.cf_error.re_errno = 0;
+ if (!svc_reg(xprt, program, version, dispatch, nconf)) {
+ /* svc_reg(3) destroys @xprt in this case */
+ xlog(L_ERROR, "Failed to register (%s, %u, %s): %s",
+ name, version, nconf->nc_netid,
+ clnt_spcreateerror("svc_reg() err"));
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * If a port is specified on the command line, that port value will be
+ * the same for all listeners created here. Create each listener
+ * socket in advance and set SO_REUSEADDR, rather than allowing the
+ * RPC library to create the listeners for us on a randomly chosen
+ * port via svc_tli_create(RPC_ANYFD).
+ *
+ * Some callers want to listen for more than one RPC version using the
+ * same port number. For example, mountd could want to listen for MNT
+ * version 1, 2, and 3 requests. This means mountd must use the same
+ * set of listener sockets for multiple RPC versions, since, on one
+ * system, you can't have two listener sockets with the exact same
+ * bind address (and port) and transport protocol.
+ *
+ * To accomplish this, this function caches xprts as they are created.
+ * This cache is checked to see if a previously created xprt can be
+ * used, before creating a new xprt for this [program, version]. If
+ * there is a cached xprt with the same bindaddr and transport
+ * semantics, we simply register the new version with that xprt,
+ * rather than creating a fresh xprt for it.
+ *
+ * The xprt cache implemented here is local to a process. Two
+ * separate RPC daemons can not share a set of listeners.
+ *
+ * Returns the count of started listeners (one or zero).
+ */
+static unsigned int
+svc_create_nconf_fixed_port(const char *name, const rpcprog_t program,
+ const rpcvers_t version,
+ void (*dispatch)(struct svc_req *, SVCXPRT *),
+ const uint16_t port, struct netconfig *nconf)
+{
+ struct addrinfo *ai;
+ SVCXPRT *xprt;
+
+ ai = svc_create_bindaddr(nconf, port);
+ if (ai == NULL)
+ return 0;
+
+ xprt = svc_create_find_xprt(ai->ai_addr, nconf);
+ if (xprt == NULL) {
+ int fd;
+
+ fd = svc_create_sock(ai->ai_addr, ai->ai_addrlen, nconf);
+ fd = svcsock_nonblock(fd);
+ if (fd == -1)
+ goto out_free;
+
+ xprt = svc_tli_create(fd, nconf, NULL, 0, 0);
+ if (xprt == NULL) {
+ xlog(D_GENERAL, "Failed to create listener xprt "
+ "(%s, %u, %s)", name, version, nconf->nc_netid);
+ (void)close(fd);
+ goto out_free;
+ }
+ }
+
+ if (!svc_reg(xprt, program, version, dispatch, nconf)) {
+ /* svc_reg(3) destroys @xprt in this case */
+ xlog(D_GENERAL, "Failed to register (%s, %u, %s)",
+ name, version, nconf->nc_netid);
+ goto out_free;
+ }
+
+ svc_create_cache_xprt(xprt);
+
+ nfs_freeaddrinfo(ai);
+ return 1;
+
+out_free:
+ nfs_freeaddrinfo(ai);
+ return 0;
+}
+
+static unsigned int
+svc_create_nconf(const char *name, const rpcprog_t program,
+ const rpcvers_t version,
+ void (*dispatch)(struct svc_req *, SVCXPRT *),
+ const uint16_t port, struct netconfig *nconf)
+{
+ if (port != 0)
+ return svc_create_nconf_fixed_port(name, program,
+ version, dispatch, port, nconf);
+
+ return svc_create_nconf_rand_port(name, program,
+ version, dispatch, nconf);
+}
+
+/**
+ * nfs_svc_create - start up RPC svc listeners
+ * @name: C string containing name of new service
+ * @program: RPC program number to register
+ * @version: RPC version number to register
+ * @dispatch: address of function that handles incoming RPC requests
+ * @port: if not zero, transport listens on this port
+ *
+ * Sets up network transports for receiving RPC requests, and starts
+ * the RPC dispatcher. Returns the number of started network transports.
+ */
+unsigned int
+nfs_svc_create(char *name, const rpcprog_t program, const rpcvers_t version,
+ void (*dispatch)(struct svc_req *, SVCXPRT *),
+ const uint16_t port)
+{
+ const struct sigaction create_sigaction = {
+ .sa_handler = SIG_IGN,
+ };
+ int maxrec = RPC_MAXDATASIZE;
+ unsigned int visible, up, servport;
+ struct netconfig *nconf;
+ void *handlep;
+
+ /*
+ * Ignore SIGPIPE to avoid exiting sideways when peers
+ * close their TCP connection while we're trying to reply
+ * to them.
+ */
+ (void)sigaction(SIGPIPE, &create_sigaction, NULL);
+
+ /*
+ * Setting MAXREC also enables non-blocking mode for tcp connections.
+ * This avoids DOS attacks by a client sending many requests but never
+ * reading the reply:
+ * - if a second request already is present for reading in the socket,
+ * after the first request just was read, libtirpc will break the
+ * connection. Thus an attacker can't simply send requests as fast as
+ * he can without waiting for the response.
+ * - if the write buffer of the socket is full, the next write() will
+ * fail with EAGAIN. libtirpc will retry the write in a loop for max.
+ * 2 seconds. If write still fails, the connection will be closed.
+ */
+ rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrec);
+
+ handlep = setnetconfig();
+ if (handlep == NULL) {
+ xlog(L_ERROR, "Failed to access local netconfig database: %s",
+ nc_sperror());
+ return 0;
+ }
+
+ visible = 0;
+ up = 0;
+ while ((nconf = getnetconfig(handlep)) != NULL) {
+ if (!(nconf->nc_flag & NC_VISIBLE))
+ continue;
+ visible++;
+
+ if (!strcmp(nconf->nc_proto, NC_UDP) && !NFSCTL_UDPISSET(_rpcprotobits))
+ continue;
+
+ if (!strcmp(nconf->nc_proto, NC_TCP) && !NFSCTL_TCPISSET(_rpcprotobits))
+ continue;
+
+ if (port == 0)
+ servport = getservport(program, nconf->nc_proto);
+ else
+ servport = port;
+
+ up += svc_create_nconf(name, program, version, dispatch,
+ servport, nconf);
+ }
+
+ if (visible == 0)
+ xlog(L_ERROR, "Failed to find any visible netconfig entries");
+
+ if (endnetconfig(handlep) == -1)
+ xlog(L_ERROR, "Failed to close local netconfig database: %s",
+ nc_sperror());
+
+ return up;
+}
+
+/**
+ * nfs_svc_unregister - remove service registrations from local rpcbind database
+ * @program: RPC program number to unregister
+ * @version: RPC version number to unregister
+ *
+ * Removes all registrations for [ @program, @version ] .
+ */
+void
+nfs_svc_unregister(const rpcprog_t program, const rpcvers_t version)
+{
+ if (rpcb_unset(program, version, NULL) == FALSE)
+ xlog(D_GENERAL, "Failed to unregister program %lu, version %lu",
+ (unsigned long)program, (unsigned long)version);
+}
+
+#else /* !HAVE_LIBTIRPC */
+
+/**
+ * nfs_svc_create - start up RPC svc listeners
+ * @name: C string containing name of new service
+ * @program: RPC program number to register
+ * @version: RPC version number to register
+ * @dispatch: address of function that handles incoming RPC requests
+ * @port: if not zero, transport listens on this port
+ *
+ * Sets up network transports for receiving RPC requests, and starts
+ * the RPC dispatcher. Returns the number of started network transports.
+ */
+unsigned int
+nfs_svc_create(char *name, const rpcprog_t program, const rpcvers_t version,
+ void (*dispatch)(struct svc_req *, SVCXPRT *),
+ const uint16_t port)
+{
+ rpc_init(name, (int)program, (int)version, dispatch, (int)port);
+ return 1;
+}
+
+/**
+ * nfs_svc_unregister - remove service registrations from local rpcbind database
+ * @program: RPC program number to unregister
+ * @version: RPC version number to unregister
+ *
+ * Removes all registrations for [ @program, @version ] .
+ */
+void
+nfs_svc_unregister(const rpcprog_t program, const rpcvers_t version)
+{
+ if (pmap_unset((unsigned long)program, (unsigned long)version) == FALSE)
+ xlog(D_GENERAL, "Failed to unregister program %lu, version %lu",
+ (unsigned long)program, (unsigned long)version);
+}
+
+#endif /* !HAVE_LIBTIRPC */
diff --git a/support/nfs/svc_socket.c b/support/nfs/svc_socket.c
new file mode 100644
index 0000000..2e8fe1a
--- /dev/null
+++ b/support/nfs/svc_socket.c
@@ -0,0 +1,218 @@
+/* Copyright (C) 2002 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 0211-1301 USA */
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <rpc/rpc.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+#include <errno.h>
+#include "xlog.h"
+#include "rpcmisc.h"
+#include "nfslib.h"
+
+#include "config.h"
+
+#ifdef _LIBC
+# include <libintl.h>
+#else
+# ifndef _
+# define _(s) (s)
+# endif
+# define __socket(d, t, p) socket ((d), (t), (p))
+# define __close(f) close ((f))
+#endif
+
+int getservport(u_long number, const char *proto)
+{
+ char servdata[1024];
+ struct rpcent *rpcp;
+ struct servent servbuf, *servp = NULL;
+ int ret = 0;
+#ifdef HAVE_GETRPCBYNUMBER_R
+ char rpcdata[1024];
+ struct rpcent rpcbuf;
+
+ ret = getrpcbynumber_r(number, &rpcbuf, rpcdata, sizeof rpcdata,
+ &rpcp);
+#else
+ rpcp = getrpcbynumber(number);
+#endif
+
+ if (ret == 0 && rpcp != NULL) {
+ /* First try name. */
+ ret = getservbyname_r(rpcp->r_name, proto, &servbuf, servdata,
+ sizeof servdata, &servp);
+ if ((ret != 0 || servp == NULL) && rpcp->r_aliases) {
+ const char **a;
+
+ /* Then we try aliases. */
+ for (a = (const char **) rpcp->r_aliases; *a != NULL; a++) {
+ ret = getservbyname_r(*a, proto, &servbuf, servdata,
+ sizeof servdata, &servp);
+ if (ret == 0 && servp != NULL)
+ break;
+ }
+ }
+ }
+
+ if (ret == 0 && servp != NULL)
+ return ntohs(servp->s_port);
+
+ return 0;
+}
+
+int
+svcsock_nonblock(int sock)
+{
+ int flags;
+
+ if (sock < 0)
+ return sock;
+
+ /* This socket might be shared among multiple processes
+ * if mountd is run multi-threaded. So it is safest to
+ * make it non-blocking, else all threads might wake
+ * one will get the data, and the others will block
+ * indefinitely.
+ * In all cases, transaction on this socket are atomic
+ * (accept for TCP, packet-read and packet-write for UDP)
+ * so O_NONBLOCK will not confuse unprepared code causing
+ * it to corrupt messages.
+ * It generally safest to have O_NONBLOCK when doing an accept
+ * as if we get a RST after the SYN and before accept runs,
+ * we can block despite being told there was an acceptable
+ * connection.
+ */
+ if ((flags = fcntl(sock, F_GETFL)) < 0)
+ xlog(L_ERROR, "svc_socket: can't get socket flags: %m");
+ else if (fcntl(sock, F_SETFL, flags|O_NONBLOCK) < 0)
+ xlog(L_ERROR, "svc_socket: can't set socket flags: %m");
+ else
+ return sock;
+
+ (void) __close(sock);
+ return -1;
+}
+
+static int
+svc_socket (u_long number, int type, int protocol, int reuse)
+{
+ struct sockaddr_in addr;
+ socklen_t len = sizeof (struct sockaddr_in);
+ int sock, ret;
+ const char *proto = protocol == IPPROTO_TCP ? "tcp" : "udp";
+
+ if ((sock = __socket (AF_INET, type, protocol)) < 0)
+ {
+ xlog(L_ERROR, "svc_socket: socket creation problem: %m");
+ return sock;
+ }
+
+ if (reuse)
+ {
+ ret = 1;
+ ret = setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, &ret,
+ sizeof (ret));
+ if (ret < 0)
+ {
+ xlog(L_ERROR, "svc_socket: socket reuse problem: %m");
+ (void) __close(sock);
+ return ret;
+ }
+ }
+
+ memset (&addr, 0, sizeof (addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(getservport(number, proto));
+
+ if (bind(sock, (struct sockaddr *) &addr, len) < 0)
+ {
+ xlog(L_ERROR, "svc_socket: bind problem: %m");
+ (void) __close(sock);
+ sock = -1;
+ }
+
+ return svcsock_nonblock(sock);
+}
+
+/*
+ * Create and bind a TCP socket based on program number
+ */
+int
+svctcp_socket (u_long number, int reuse)
+{
+ return svc_socket (number, SOCK_STREAM, IPPROTO_TCP, reuse);
+}
+
+/*
+ * Create and bind a UDP socket based on program number
+ */
+int
+svcudp_socket (u_long number)
+{
+ return svc_socket (number, SOCK_DGRAM, IPPROTO_UDP, FALSE);
+}
+
+#ifdef TEST
+static int
+check (u_long number, u_short port, int protocol, int reuse)
+{
+ int socket;
+ int result;
+ struct sockaddr_in addr;
+ socklen_t len = sizeof (struct sockaddr_in);
+
+ if (protocol == IPPROTO_TCP)
+ socket = svctcp_socket (number, reuse);
+ else
+ socket = svcudp_socket (number);
+
+ if (socket < 0)
+ return 1;
+
+ result = getsockname (socket, (struct sockaddr *) &addr, &len);
+ if (result == 0)
+ {
+ if (port != 0 && ntohs (addr.sin_port) != port)
+ printf ("Program: %ld, expect port: %d, got: %d\n",
+ number, port, ntohs (addr.sin_port));
+ else
+ printf ("Program: %ld, port: %d\n",
+ number, ntohs (addr.sin_port));
+ }
+
+ close (socket);
+ return result;
+}
+
+int
+main (void)
+{
+ int result = 0;
+
+ result += check (100001, 0, IPPROTO_TCP, 0);
+ result += check (100001, 0, IPPROTO_UDP, 0);
+ result += check (100003, 2049, IPPROTO_TCP, 1);
+ result += check (100003, 2049, IPPROTO_UDP, 1);
+
+ return result;
+}
+#endif
diff --git a/support/nfs/wildmat.c b/support/nfs/wildmat.c
new file mode 100644
index 0000000..437b2d1
--- /dev/null
+++ b/support/nfs/wildmat.c
@@ -0,0 +1,182 @@
+/* $Revision: 0.2.18.1 $
+**
+** Do shell-style pattern matching for ?, \, [], and * characters.
+** Might not be robust in face of malformed patterns; e.g., "foo[a-"
+** could cause a segmentation violation. It is 8bit clean.
+**
+** Written by Rich $alz, mirror!rs, Wed Nov 26 19:03:17 EST 1986.
+** Rich $alz is now <rsalz@osf.org>.
+** April, 1991: Replaced mutually-recursive calls with in-line code
+** for the star character.
+**
+** Special thanks to Lars Mathiesen <thorinn@diku.dk> for the ABORT code.
+** This can greatly speed up failing wildcard patterns. For example:
+** pattern: -*-*-*-*-*-*-12-*-*-*-m-*-*-*
+** text 1: -adobe-courier-bold-o-normal--12-120-75-75-m-70-iso8859-1
+** text 2: -adobe-courier-bold-o-normal--12-120-75-75-X-70-iso8859-1
+** Text 1 matches with 51 calls, while text 2 fails with 54 calls. Without
+** the ABORT code, it takes 22310 calls to fail. Ugh. The following
+** explanation is from Lars:
+** The precondition that must be fulfilled is that DoMatch will consume
+** at least one character in text. This is true if *p is neither '*' nor
+** '\0'.) The last return has ABORT instead of FALSE to avoid quadratic
+** behaviour in cases like pattern "*a*b*c*d" with text "abcxxxxx". With
+** FALSE, each star-loop has to run to the end of the text; with ABORT
+** only the last one does.
+**
+** Once the control of one instance of DoMatch enters the star-loop, that
+** instance will return either TRUE or ABORT, and any calling instance
+** will therefore return immediately after (without calling recursively
+** again). In effect, only one star-loop is ever active. It would be
+** possible to modify the code to maintain this context explicitly,
+** eliminating all recursive calls at the cost of some complication and
+** loss of clarity (and the ABORT stuff seems to be unclear enough by
+** itself). I think it would be unwise to try to get this into a
+** released version unless you have a good test data base to try it out
+** on.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <ctype.h>
+#include "nfslib.h"
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#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(char *text, 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(char *text, char *p)
+{
+#ifdef OPTIMIZE_JUST_STAR
+ if (p[0] == '*' && p[1] == '\0')
+ return TRUE;
+#endif /* OPTIMIZE_JUST_STAR */
+ return DoMatch(text, p) == TRUE;
+}
+
+
+
+#if defined(TEST)
+#include <stdio.h>
+
+/* Yes, we use gets not fgets. Sue me. */
+extern char *gets();
+
+
+int
+main()
+{
+ char p[80];
+ char text[80];
+
+ printf("Wildmat tester. Enter pattern, then strings to test.\n");
+ printf("A blank line gets prompts for a new pattern; a blank pattern\n");
+ printf("exits the program.\n");
+
+ for ( ; ; ) {
+ printf("\nEnter pattern: ");
+ (void)fflush(stdout);
+ if (gets(p) == NULL || p[0] == '\0')
+ break;
+ for ( ; ; ) {
+ printf("Enter text: ");
+ (void)fflush(stdout);
+ if (gets(text) == NULL)
+ exit(0);
+ if (text[0] == '\0')
+ /* Blank line; go back and get a new pattern. */
+ break;
+ printf(" %s\n", wildmat(text, p) ? "YES" : "NO");
+ }
+ }
+
+ exit(0);
+ /* NOTREACHED */
+}
+#endif /* defined(TEST) */
diff --git a/support/nfs/xcommon.c b/support/nfs/xcommon.c
new file mode 100644
index 0000000..3989f0b
--- /dev/null
+++ b/support/nfs/xcommon.c
@@ -0,0 +1,191 @@
+/*
+ * xcommon.c - various functions put together to avoid basic error checking.
+ *
+ * added fcntl locking by Kjetil T. (kjetilho@math.uio.no) - aeb, 950927
+ *
+ * 1999-02-22 Arkadiusz Miskiewicz <misiek@pld.ORG.PL>
+ * - added Native Language Support
+ *
+ * 2006-06-06 Amit Gud <agud@redhat.com>
+ * - Moved code snippets here from mount/sundries.c of util-linux
+ * and merged code from support/nfs/xmalloc.c by Olaf Kirch <okir@monad.swb.de> here.
+ */
+
+#include <unistd.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "xcommon.h"
+#include "nls.h" /* _() */
+
+void (*at_die)(void ) = NULL;
+
+char *
+xstrndup (const char *s, int n) {
+ char *t;
+
+ if (s == NULL)
+ die (EX_SOFTWARE, _("bug in xstrndup call"));
+
+ t = xmalloc(n+1);
+ strncpy(t,s,n);
+ t[n] = 0;
+
+ return t;
+}
+
+char *
+xstrconcat2 (const char *s, const char *t) {
+ char *res;
+
+ if (!s) s = "";
+ if (!t) t = "";
+ res = xmalloc(strlen(s) + strlen(t) + 1);
+ strcpy(res, s);
+ strcat(res, t);
+ return res;
+}
+
+/* frees its first arg - typical use: s = xstrconcat3(s,t,u); */
+char *
+xstrconcat3 (const char *s, const char *t, const char *u) {
+ char *res;
+
+ int dofree = 1;
+
+ if (!s) s = "", dofree=0;
+ if (!t) t = "";
+ if (!u) u = "";
+ res = xmalloc(strlen(s) + strlen(t) + strlen(u) + 1);
+ strcpy(res, s);
+ strcat(res, t);
+ strcat(res, u);
+ if (dofree)
+ free((void *) s);
+ return res;
+}
+
+/* frees its first arg - typical use: s = xstrconcat4(s,t,u,v); */
+char *
+xstrconcat4 (const char *s, const char *t, const char *u, const char *v) {
+ char *res;
+
+ int dofree = 1;
+
+ if (!s) s = "", dofree=0;
+ if (!t) t = "";
+ if (!u) u = "";
+ if (!v) v = "";
+ res = xmalloc(strlen(s) + strlen(t) + strlen(u) + strlen(v) + 1);
+ strcpy(res, s);
+ strcat(res, t);
+ strcat(res, u);
+ strcat(res, v);
+ if (dofree)
+ free((void *) s);
+ return res;
+}
+
+/* Non-fatal error. Print message and return. */
+/* (print the message in a single printf, in an attempt
+ to avoid mixing output of several threads) */
+void
+nfs_error (const char *fmt, ...) {
+ va_list args;
+ char *fmt2;
+
+ fmt2 = xstrconcat2 (fmt, "\n");
+ va_start (args, fmt);
+ vfprintf (stderr, fmt2, args);
+ va_end (args);
+ free (fmt2);
+}
+
+/* Make a canonical pathname from PATH. Returns a freshly malloced string.
+ It is up the *caller* to ensure that the PATH is sensible. i.e.
+ canonicalize ("/dev/fd0/.") returns "/dev/fd0" even though ``/dev/fd0/.''
+ is not a legal pathname for ``/dev/fd0''. Anything we cannot parse
+ we return unmodified. */
+char *canonicalize (const char *path) {
+ char canonical[PATH_MAX+2];
+
+ if (path == NULL)
+ return NULL;
+
+#if 1
+ if (streq(path, "none") ||
+ streq(path, "proc") ||
+ streq(path, "devpts"))
+ return xstrdup(path);
+#endif
+ if (realpath (path, canonical))
+ return xstrdup(canonical);
+
+ return xstrdup(path);
+}
+
+/* Fatal error. Print message and exit. */
+void
+die(int err, const char *fmt, ...) {
+ va_list args;
+
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ fprintf(stderr, "\n");
+ va_end(args);
+
+ if (at_die)
+ (*at_die)();
+
+ exit(err);
+}
+
+static void
+die_if_null(void *t) {
+ if (t == NULL)
+ die(EX_SYSERR, _("not enough memory"));
+}
+
+void *
+xmalloc (size_t size) {
+ void *t;
+
+ if (size == 0)
+ return NULL;
+
+ t = malloc(size);
+ die_if_null(t);
+
+ return t;
+}
+
+void *
+xrealloc (void *p, size_t size) {
+ void *t;
+
+ t = realloc(p, size);
+ die_if_null(t);
+
+ return t;
+}
+
+void
+xfree(void *ptr)
+{
+ free(ptr);
+}
+
+char *
+xstrdup (const char *s) {
+ char *t;
+
+ if (s == NULL)
+ return NULL;
+
+ t = strdup(s);
+ die_if_null(t);
+
+ return t;
+}
diff --git a/support/nfs/xio.c b/support/nfs/xio.c
new file mode 100644
index 0000000..6962751
--- /dev/null
+++ b/support/nfs/xio.c
@@ -0,0 +1,170 @@
+/*
+ * support/nfs/xio.c
+ *
+ * Simple I/O functions for the parsing of /etc/exports and /etc/nfsclients.
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <signal.h>
+#include <unistd.h>
+#include <errno.h>
+#include "xmalloc.h"
+#include "xlog.h"
+#include "xio.h"
+
+XFILE *
+xfopen(char *fname, char *type)
+{
+ XFILE *xfp;
+ FILE *fp;
+
+ if (!(fp = fopen(fname, type)))
+ return NULL;
+ xfp = (XFILE *) xmalloc(sizeof(*xfp));
+ xfp->x_fp = fp;
+ xfp->x_line = 1;
+
+ return xfp;
+}
+
+void
+xfclose(XFILE *xfp)
+{
+ fclose(xfp->x_fp);
+ xfree(xfp);
+}
+
+int
+xflock(char *fname, char *type)
+{
+ int readonly = !strcmp(type, "r");
+ struct flock fl = { readonly? F_RDLCK : F_WRLCK, SEEK_SET, 0, 0, 0 };
+ int fd;
+
+ if (readonly)
+ fd = open(fname, (O_RDONLY|O_CREAT), 0600);
+ else
+ fd = open(fname, (O_RDWR|O_CREAT), 0600);
+ if (fd < 0) {
+ xlog(L_WARNING, "could not open %s for locking: errno %d (%s)",
+ fname, errno, strerror(errno));
+ return -1;
+ }
+
+ if (fcntl(fd, F_SETLKW, &fl) < 0) {
+ xlog(L_WARNING, "failed to lock %s: errno %d (%s)",
+ fname, errno, strerror(errno));
+ close(fd);
+ fd = -1;
+ }
+
+ return fd;
+}
+
+void
+xfunlock(int fd)
+{
+ close(fd);
+}
+
+#define isoctal(x) (isdigit(x) && ((x)<'8'))
+int
+xgettok(XFILE *xfp, char sepa, char *tok, int len)
+{
+ int i = 0;
+ int c = 0;
+ int quoted=0;
+
+ while (i < len && (c = xgetc(xfp)) != EOF &&
+ (quoted || (c != sepa && !isspace(c)))) {
+ if (c == '"') {
+ quoted = !quoted;
+ continue;
+ }
+ tok[i++] = c;
+ if (i >= 4 &&
+ tok[i-4] == '\\' &&
+ isoctal(tok[i-3]) &&
+ isoctal(tok[i-2]) &&
+ isoctal(tok[i-1]) &&
+ ((tok[i]=0),
+ (c = strtol(tok+i-3,NULL, 8)) < 256)) {
+ i -= 4;
+ tok[i++] = c;
+ }
+ }
+ if (c == '\n')
+ xungetc(c, xfp);
+ if (!i)
+ return 0;
+ if (i >= len || (sepa && c != sepa))
+ return -1;
+ tok[i] = '\0';
+ return 1;
+}
+
+int
+xgetc(XFILE *xfp)
+{
+ int 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 == '\n')
+ xfp->x_line++;
+ return c;
+}
+
+void
+xungetc(int c, XFILE *xfp)
+{
+ if (c == EOF)
+ return;
+
+ ungetc(c, xfp->x_fp);
+ if (c == '\n')
+ xfp->x_line--;
+}
+
+void
+xskip(XFILE *xfp, char *str)
+{
+ int c;
+
+ while ((c = xgetc(xfp)) != EOF) {
+ if (c == '#')
+ c = xskipcomment(xfp);
+ if (strchr(str, c) == NULL)
+ break;
+ }
+ xungetc(c, xfp);
+}
+
+char
+xskipcomment(XFILE *xfp)
+{
+ int 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..fa125ce
--- /dev/null
+++ b/support/nfs/xlog.c
@@ -0,0 +1,254 @@
+/*
+ * support/nfs/xlog.c
+ *
+ * This module handles the logging of requests.
+ *
+ * TODO: Merge the two "XXX_log() calls.
+ *
+ * Authors: Donald J. Becker, <becker@super.org>
+ * Rick Sladkey, <jrs@world.std.com>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ * Olaf Kirch, <okir@monad.swb.de>
+ *
+ * This software maybe be used for any purpose provided
+ * the above copyright notice is retained. It is supplied
+ * as is, with no warranty expressed or implied.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <signal.h>
+#include <time.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <syslog.h>
+#include <errno.h>
+#include "nfslib.h"
+#include "conffile.h"
+
+#undef VERBOSE_PRINTF
+
+#pragma GCC visibility push(hidden)
+
+static int log_stderr = 1;
+static int log_syslog = 1;
+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 */
+
+int export_errno = 0;
+
+static void xlog_toggle(int sig);
+static struct xlog_debugfac debugnames[] = {
+ { "0", 0, },
+ { "general", D_GENERAL, },
+ { "call", D_CALL, },
+ { "auth", D_AUTH, },
+ { "parse", D_PARSE, },
+ { "all", D_ALL, },
+ { "1", D_ALL, },
+ { NULL, 0, },
+};
+
+void
+xlog_open(char *progname)
+{
+ openlog(progname, LOG_PID, LOG_DAEMON);
+
+ 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_stderr(int on)
+{
+ log_stderr = on;
+}
+
+void
+xlog_syslog(int on)
+{
+ log_syslog = on;
+}
+
+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;
+ }
+ if (tbl->df_fac)
+ xlog_config(tbl->df_fac, on);
+}
+
+void
+xlog_set_debug(char *service)
+{
+ struct conf_list *kinds;
+ struct conf_list_node *n;
+
+ kinds = conf_get_list(service, "debug");
+ if (!kinds || !kinds->cnt) {
+ free(kinds);
+ return;
+ }
+ TAILQ_FOREACH(n, &(kinds->fields), link)
+ xlog_sconfig(n->field, 1);
+
+ conf_free_list(kinds);
+}
+
+int
+xlog_enabled(int fac)
+{
+ return (logging && (fac & logmask));
+}
+
+
+/* Write something to the system logfile and/or stderr */
+void
+xlog_backend(int kind, const char *fmt, va_list args)
+{
+ if (!(kind & (L_ALL)) && !(logging && (kind & logmask)))
+ return;
+
+ if (log_stderr) {
+ va_list args2;
+#ifdef VERBOSE_PRINTF
+ time_t now;
+ struct tm *tm;
+
+ time(&now);
+ tm = localtime(&now);
+ fprintf(stderr, "%s[%d] %04d-%02d-%02d %02d:%02d:%02d ",
+ log_name, log_pid,
+ tm->tm_year+1900, tm->tm_mon + 1, tm->tm_mday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
+#else
+ fprintf(stderr, "%s: ", log_name);
+#endif
+ va_copy(args2, args);
+ vfprintf(stderr, fmt, args2);
+ fprintf(stderr, "\n");
+ va_end(args2);
+ }
+
+ if (log_syslog) {
+ switch (kind) {
+ case L_FATAL:
+ vsyslog(LOG_ERR, fmt, args);
+ break;
+ case L_ERROR:
+ vsyslog(LOG_ERR, fmt, args);
+ break;
+ case L_WARNING:
+ vsyslog(LOG_WARNING, fmt, args);
+ break;
+ case L_NOTICE:
+ vsyslog(LOG_NOTICE, fmt, args);
+ break;
+ default:
+ if (!log_stderr)
+ vsyslog(LOG_INFO, fmt, args);
+ break;
+ }
+ }
+
+ if (kind == L_FATAL)
+ exit(1);
+}
+
+void
+xlog(int kind, const char* fmt, ...)
+{
+ va_list args;
+
+ if (kind & (L_ERROR|D_GENERAL))
+ export_errno = 1;
+
+ va_start(args, fmt);
+ xlog_backend(kind, fmt, args);
+ va_end(args);
+}
+
+void
+xlog_warn(const char* fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ xlog_backend(L_WARNING, fmt, args);
+ va_end(args);
+}
+
+
+void
+xlog_err(const char* fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ xlog_backend(L_FATAL, fmt, args);
+ va_end(args);
+}
+
+void
+xlog_errno(int err, const char *fmt, ...)
+{
+ va_list args;
+
+ errno = err;
+ va_start(args, fmt);
+ xlog_backend(L_FATAL, fmt, args);
+ va_end(args);
+}