summaryrefslogtreecommitdiffstats
path: root/utils
diff options
context:
space:
mode:
Diffstat (limited to 'utils')
-rw-r--r--utils/Makefile.am47
-rw-r--r--utils/Makefile.in733
-rw-r--r--utils/blkmapd/Makefile.am19
-rw-r--r--utils/blkmapd/Makefile.in825
-rw-r--r--utils/blkmapd/blkmapd.man72
-rw-r--r--utils/blkmapd/device-discovery.c580
-rw-r--r--utils/blkmapd/device-discovery.h164
-rw-r--r--utils/blkmapd/device-inq.c247
-rw-r--r--utils/blkmapd/device-process.c380
-rw-r--r--utils/blkmapd/dm-device.c525
-rw-r--r--utils/exportd/Makefile.am67
-rw-r--r--utils/exportd/Makefile.in880
-rw-r--r--utils/exportd/exportd.c247
-rw-r--r--utils/exportd/exportd.man141
-rw-r--r--utils/exportfs/Makefile.am18
-rw-r--r--utils/exportfs/Makefile.in920
-rw-r--r--utils/exportfs/exportfs.c768
-rw-r--r--utils/exportfs/exportfs.man337
-rw-r--r--utils/exportfs/exports.man678
-rw-r--r--utils/exportfs/nfsd.man214
-rw-r--r--utils/gssd/.gitignore3
-rw-r--r--utils/gssd/Makefile.am130
-rw-r--r--utils/gssd/Makefile.in1356
-rw-r--r--utils/gssd/context.c59
-rw-r--r--utils/gssd/context.h49
-rw-r--r--utils/gssd/context_heimdal.c275
-rw-r--r--utils/gssd/context_lucid.c327
-rw-r--r--utils/gssd/context_mit.c280
-rw-r--r--utils/gssd/err_util.c86
-rw-r--r--utils/gssd/err_util.h39
-rw-r--r--utils/gssd/gss_names.c141
-rw-r--r--utils/gssd/gss_names.h36
-rw-r--r--utils/gssd/gss_oids.c40
-rw-r--r--utils/gssd/gss_oids.h44
-rw-r--r--utils/gssd/gss_util.c347
-rw-r--r--utils/gssd/gss_util.h56
-rw-r--r--utils/gssd/gssd.c1320
-rw-r--r--utils/gssd/gssd.h124
-rw-r--r--utils/gssd/gssd.man408
-rw-r--r--utils/gssd/gssd_proc.c1075
-rw-r--r--utils/gssd/krb5_util.c1649
-rw-r--r--utils/gssd/krb5_util.h47
-rw-r--r--utils/gssd/svcgssd.c348
-rw-r--r--utils/gssd/svcgssd.h42
-rw-r--r--utils/gssd/svcgssd.man88
-rw-r--r--utils/gssd/svcgssd_krb5.c237
-rw-r--r--utils/gssd/svcgssd_krb5.h37
-rw-r--r--utils/gssd/svcgssd_mech2file.c74
-rw-r--r--utils/gssd/svcgssd_proc.c444
-rw-r--r--utils/gssd/write_bytes.h159
-rw-r--r--utils/idmapd/Makefile.am65
-rw-r--r--utils/idmapd/Makefile.in859
-rw-r--r--utils/idmapd/idmapd.c1091
-rw-r--r--utils/idmapd/idmapd.man134
-rw-r--r--utils/idmapd/nfs_idmap.h64
-rw-r--r--utils/idmapd/queue.h499
-rw-r--r--utils/mount/Makefile.am82
-rw-r--r--utils/mount/Makefile.in976
-rw-r--r--utils/mount/configfile.c347
-rw-r--r--utils/mount/error.c360
-rw-r--r--utils/mount/error.h35
-rw-r--r--utils/mount/fstab.c653
-rw-r--r--utils/mount/fstab.h32
-rw-r--r--utils/mount/mount.c558
-rw-r--r--utils/mount/mount.nfs.man93
-rw-r--r--utils/mount/mount_config.h55
-rw-r--r--utils/mount/mount_constants.h71
-rw-r--r--utils/mount/mount_libmount.c461
-rw-r--r--utils/mount/network.c1801
-rw-r--r--utils/mount/network.h96
-rw-r--r--utils/mount/nfs.man1968
-rw-r--r--utils/mount/nfs4_mount.h73
-rw-r--r--utils/mount/nfs4mount.c481
-rw-r--r--utils/mount/nfs_mount.h83
-rw-r--r--utils/mount/nfsmount.c873
-rw-r--r--utils/mount/nfsmount.conf144
-rw-r--r--utils/mount/nfsmount.conf.man131
-rw-r--r--utils/mount/nfsumount.c351
-rw-r--r--utils/mount/parse_dev.c233
-rw-r--r--utils/mount/parse_dev.h28
-rw-r--r--utils/mount/parse_opt.c610
-rw-r--r--utils/mount/parse_opt.h60
-rw-r--r--utils/mount/stropts.c1310
-rw-r--r--utils/mount/stropts.h30
-rw-r--r--utils/mount/token.c157
-rw-r--r--utils/mount/token.h34
-rw-r--r--utils/mount/umount.nfs.man70
-rw-r--r--utils/mount/utils.c175
-rw-r--r--utils/mount/utils.h36
-rw-r--r--utils/mount/version.h53
-rw-r--r--utils/mountd/Makefile.am70
-rw-r--r--utils/mountd/Makefile.in941
-rw-r--r--utils/mountd/mount_dispatch.c84
-rw-r--r--utils/mountd/mountd.c887
-rw-r--r--utils/mountd/mountd.h54
-rw-r--r--utils/mountd/mountd.man362
-rw-r--r--utils/mountd/rmtab.c254
-rw-r--r--utils/mountd/svc_run.c109
-rw-r--r--utils/nfsd/Makefile.am55
-rw-r--r--utils/nfsd/Makefile.in855
-rw-r--r--utils/nfsd/nfsd.c441
-rw-r--r--utils/nfsd/nfsd.man203
-rw-r--r--utils/nfsd/nfssvc.c438
-rw-r--r--utils/nfsd/nfssvc.h32
-rw-r--r--utils/nfsdcld/Makefile.am15
-rw-r--r--utils/nfsdcld/Makefile.in822
-rw-r--r--utils/nfsdcld/cld-internal.h44
-rw-r--r--utils/nfsdcld/legacy.c171
-rw-r--r--utils/nfsdcld/legacy.h24
-rw-r--r--utils/nfsdcld/nfsdcld.c922
-rw-r--r--utils/nfsdcld/nfsdcld.man221
-rw-r--r--utils/nfsdcld/sqlite.c1427
-rw-r--r--utils/nfsdcld/sqlite.h38
-rw-r--r--utils/nfsdcltrack/Makefile.am22
-rw-r--r--utils/nfsdcltrack/Makefile.in823
-rw-r--r--utils/nfsdcltrack/nfsdcltrack.c639
-rw-r--r--utils/nfsdcltrack/nfsdcltrack.man112
-rw-r--r--utils/nfsdcltrack/sqlite.c604
-rw-r--r--utils/nfsdcltrack/sqlite.h32
-rw-r--r--utils/nfsidmap/Makefile.am14
-rw-r--r--utils/nfsidmap/Makefile.in811
-rw-r--r--utils/nfsidmap/id_resolver.conf1
-rw-r--r--utils/nfsidmap/nfsidmap.c478
-rw-r--r--utils/nfsidmap/nfsidmap.man159
-rw-r--r--utils/nfsref/Makefile.am37
-rw-r--r--utils/nfsref/Makefile.in828
-rw-r--r--utils/nfsref/add.c272
-rw-r--r--utils/nfsref/lookup.c211
-rw-r--r--utils/nfsref/nfsref.c189
-rw-r--r--utils/nfsref/nfsref.h47
-rw-r--r--utils/nfsref/nfsref.man180
-rw-r--r--utils/nfsref/remove.c145
-rw-r--r--utils/nfsstat/Makefile.am12
-rw-r--r--utils/nfsstat/Makefile.in811
-rw-r--r--utils/nfsstat/nfsstat.c1183
-rw-r--r--utils/nfsstat/nfsstat.man173
-rw-r--r--utils/showmount/Makefile.am15
-rw-r--r--utils/showmount/Makefile.in831
-rw-r--r--utils/showmount/showmount.c323
-rw-r--r--utils/showmount/showmount.man65
-rw-r--r--utils/statd/.gitignore1
-rw-r--r--utils/statd/COPYING340
-rw-r--r--utils/statd/Makefile.am93
-rw-r--r--utils/statd/Makefile.in984
-rw-r--r--utils/statd/TODO13
-rw-r--r--utils/statd/callback.c122
-rw-r--r--utils/statd/hostname.c319
-rw-r--r--utils/statd/misc.c51
-rw-r--r--utils/statd/monitor.c399
-rw-r--r--utils/statd/notlist.c246
-rw-r--r--utils/statd/notlist.h65
-rw-r--r--utils/statd/rmtcall.c285
-rw-r--r--utils/statd/sim_sm_inter.x32
-rw-r--r--utils/statd/simu.c59
-rw-r--r--utils/statd/simulate.c226
-rw-r--r--utils/statd/sm-notify.c928
-rw-r--r--utils/statd/sm-notify.man366
-rwxr-xr-xutils/statd/start-statd33
-rw-r--r--utils/statd/stat.c60
-rw-r--r--utils/statd/statd.c549
-rw-r--r--utils/statd/statd.h68
-rw-r--r--utils/statd/statd.man474
-rw-r--r--utils/statd/svc_run.c138
-rw-r--r--utils/statd/system.h18
164 files changed, 55769 insertions, 0 deletions
diff --git a/utils/Makefile.am b/utils/Makefile.am
new file mode 100644
index 0000000..ab58419
--- /dev/null
+++ b/utils/Makefile.am
@@ -0,0 +1,47 @@
+## Process this file with automake to produce Makefile.in
+
+OPTDIRS =
+
+if CONFIG_NFSV4
+OPTDIRS += idmapd
+OPTDIRS += nfsidmap
+endif
+
+if CONFIG_NFSV4SERVER
+OPTDIRS += exportd
+endif
+
+if CONFIG_NFSV41
+OPTDIRS += blkmapd
+endif
+
+if CONFIG_GSS
+OPTDIRS += gssd
+endif
+
+if CONFIG_MOUNT
+OPTDIRS += mount
+endif
+
+if CONFIG_NFSDCLD
+OPTDIRS += nfsdcld
+endif
+
+if CONFIG_NFSDCLTRACK
+OPTDIRS += nfsdcltrack
+endif
+
+if CONFIG_JUNCTION
+OPTDIRS += nfsref
+endif
+
+SUBDIRS = \
+ exportfs \
+ mountd \
+ nfsd \
+ nfsstat \
+ showmount \
+ statd \
+ $(OPTDIRS)
+
+MAINTAINERCLEANFILES = Makefile.in
diff --git a/utils/Makefile.in b/utils/Makefile.in
new file mode 100644
index 0000000..527a6aa
--- /dev/null
+++ b/utils/Makefile.in
@@ -0,0 +1,733 @@
+# 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@
+@CONFIG_NFSV4_TRUE@am__append_1 = idmapd nfsidmap
+@CONFIG_NFSV4SERVER_TRUE@am__append_2 = exportd
+@CONFIG_NFSV41_TRUE@am__append_3 = blkmapd
+@CONFIG_GSS_TRUE@am__append_4 = gssd
+@CONFIG_MOUNT_TRUE@am__append_5 = mount
+@CONFIG_NFSDCLD_TRUE@am__append_6 = nfsdcld
+@CONFIG_NFSDCLTRACK_TRUE@am__append_7 = nfsdcltrack
+@CONFIG_JUNCTION_TRUE@am__append_8 = nfsref
+subdir = utils
+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 =
+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 =
+SOURCES =
+DIST_SOURCES =
+RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \
+ ctags-recursive dvi-recursive html-recursive info-recursive \
+ install-data-recursive install-dvi-recursive \
+ install-exec-recursive install-html-recursive \
+ install-info-recursive install-pdf-recursive \
+ install-ps-recursive install-recursive installcheck-recursive \
+ installdirs-recursive pdf-recursive ps-recursive \
+ tags-recursive uninstall-recursive
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
+ distclean-recursive maintainer-clean-recursive
+am__recursive_targets = \
+ $(RECURSIVE_TARGETS) \
+ $(RECURSIVE_CLEAN_TARGETS) \
+ $(am__extra_recursive_targets)
+AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
+ distdir distdir-am
+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)`
+DIST_SUBDIRS = exportfs mountd nfsd nfsstat showmount statd idmapd \
+ nfsidmap exportd blkmapd gssd mount nfsdcld nfsdcltrack nfsref
+am__DIST_COMMON = $(srcdir)/Makefile.in
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+am__relativize = \
+ dir0=`pwd`; \
+ sed_first='s,^\([^/]*\)/.*$$,\1,'; \
+ sed_rest='s,^[^/]*/*,,'; \
+ sed_last='s,^.*/\([^/]*\)$$,\1,'; \
+ sed_butlast='s,/*[^/]*$$,,'; \
+ while test -n "$$dir1"; do \
+ first=`echo "$$dir1" | sed -e "$$sed_first"`; \
+ if test "$$first" != "."; then \
+ if test "$$first" = ".."; then \
+ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
+ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
+ else \
+ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
+ if test "$$first2" = "$$first"; then \
+ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
+ else \
+ dir2="../$$dir2"; \
+ fi; \
+ dir0="$$dir0"/"$$first"; \
+ fi; \
+ fi; \
+ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
+ done; \
+ reldir="$$dir2"
+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@
+OPTDIRS = $(am__append_1) $(am__append_2) $(am__append_3) \
+ $(am__append_4) $(am__append_5) $(am__append_6) \
+ $(am__append_7) $(am__append_8)
+SUBDIRS = \
+ exportfs \
+ mountd \
+ nfsd \
+ nfsstat \
+ showmount \
+ statd \
+ $(OPTDIRS)
+
+MAINTAINERCLEANFILES = Makefile.in
+all: all-recursive
+
+.SUFFIXES:
+$(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 utils/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu utils/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):
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run 'make' without going through this Makefile.
+# To change the values of 'make' variables: instead of editing Makefiles,
+# (1) if the variable is set in 'config.status', edit 'config.status'
+# (which will cause the Makefiles to be regenerated when you run 'make');
+# (2) otherwise, pass the desired values on the 'make' command line.
+$(am__recursive_targets):
+ @fail=; \
+ if $(am__make_keepgoing); then \
+ failcom='fail=yes'; \
+ else \
+ failcom='exit 1'; \
+ fi; \
+ dot_seen=no; \
+ target=`echo $@ | sed s/-recursive//`; \
+ case "$@" in \
+ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+ *) list='$(SUBDIRS)' ;; \
+ esac; \
+ for subdir in $$list; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ dot_seen=yes; \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done; \
+ if test "$$dot_seen" = "no"; then \
+ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+ fi; test -z "$$fail"
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-recursive
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+ include_option=--etags-include; \
+ empty_fix=.; \
+ else \
+ include_option=--include; \
+ empty_fix=; \
+ fi; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test ! -f $$subdir/TAGS || \
+ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
+ fi; \
+ done; \
+ $(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-recursive
+
+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-recursive
+
+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
+ @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ $(am__make_dryrun) \
+ || test -d "$(distdir)/$$subdir" \
+ || $(MKDIR_P) "$(distdir)/$$subdir" \
+ || exit 1; \
+ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
+ $(am__relativize); \
+ new_distdir=$$reldir; \
+ dir1=$$subdir; dir2="$(top_distdir)"; \
+ $(am__relativize); \
+ new_top_distdir=$$reldir; \
+ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
+ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
+ ($(am__cd) $$subdir && \
+ $(MAKE) $(AM_MAKEFLAGS) \
+ top_distdir="$$new_top_distdir" \
+ distdir="$$new_distdir" \
+ am__remove_distdir=: \
+ am__skip_length_check=: \
+ am__skip_mode_fix=: \
+ distdir) \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-recursive
+all-am: Makefile
+installdirs: installdirs-recursive
+installdirs-am:
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+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-recursive
+
+clean-am: clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-recursive
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+html-am:
+
+info: info-recursive
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-recursive
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-recursive
+
+install-html-am:
+
+install-info: install-info-recursive
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-recursive
+
+install-pdf-am:
+
+install-ps: install-ps-recursive
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: $(am__recursive_targets) install-am install-strip
+
+.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \
+ check-am clean clean-generic clean-libtool cscopelist-am ctags \
+ ctags-am distclean 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 \
+ installdirs-am maintainer-clean maintainer-clean-generic \
+ mostlyclean 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/utils/blkmapd/Makefile.am b/utils/blkmapd/Makefile.am
new file mode 100644
index 0000000..56c8a4b
--- /dev/null
+++ b/utils/blkmapd/Makefile.am
@@ -0,0 +1,19 @@
+## Process this file with automake to produce Makefile.in
+
+man8_MANS = blkmapd.man
+EXTRA_DIST = $(man8_MANS)
+
+AM_CFLAGS += -D_LARGEFILE64_SOURCE
+sbin_PROGRAMS = blkmapd
+
+blkmapd_SOURCES = \
+ device-discovery.c \
+ device-inq.c \
+ device-process.c \
+ dm-device.c \
+ device-discovery.h
+
+blkmapd_LDADD = -ldevmapper ../../support/nfs/libnfs.la
+
+MAINTAINERCLEANFILES = Makefile.in
+
diff --git a/utils/blkmapd/Makefile.in b/utils/blkmapd/Makefile.in
new file mode 100644
index 0000000..c294c0f
--- /dev/null
+++ b/utils/blkmapd/Makefile.in
@@ -0,0 +1,825 @@
+# 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@
+sbin_PROGRAMS = blkmapd$(EXEEXT)
+subdir = utils/blkmapd
+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 =
+am__installdirs = "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man8dir)"
+PROGRAMS = $(sbin_PROGRAMS)
+am_blkmapd_OBJECTS = device-discovery.$(OBJEXT) device-inq.$(OBJEXT) \
+ device-process.$(OBJEXT) dm-device.$(OBJEXT)
+blkmapd_OBJECTS = $(am_blkmapd_OBJECTS)
+blkmapd_DEPENDENCIES = ../../support/nfs/libnfs.la
+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 =
+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)/device-discovery.Po \
+ ./$(DEPDIR)/device-inq.Po ./$(DEPDIR)/device-process.Po \
+ ./$(DEPDIR)/dm-device.Po
+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 = $(blkmapd_SOURCES)
+DIST_SOURCES = $(blkmapd_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+man8dir = $(mandir)/man8
+NROFF = nroff
+MANS = $(man8_MANS)
+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@ -D_LARGEFILE64_SOURCE
+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@
+man8_MANS = blkmapd.man
+EXTRA_DIST = $(man8_MANS)
+blkmapd_SOURCES = \
+ device-discovery.c \
+ device-inq.c \
+ device-process.c \
+ dm-device.c \
+ device-discovery.h
+
+blkmapd_LDADD = -ldevmapper ../../support/nfs/libnfs.la
+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 utils/blkmapd/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu utils/blkmapd/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):
+install-sbinPROGRAMS: $(sbin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(sbindir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-sbinPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(sbindir)" && rm -f $$files
+
+clean-sbinPROGRAMS:
+ @list='$(sbin_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+blkmapd$(EXEEXT): $(blkmapd_OBJECTS) $(blkmapd_DEPENDENCIES) $(EXTRA_blkmapd_DEPENDENCIES)
+ @rm -f blkmapd$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(blkmapd_OBJECTS) $(blkmapd_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/device-discovery.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/device-inq.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/device-process.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dm-device.Po@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 $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-man8: $(man8_MANS)
+ @$(NORMAL_INSTALL)
+ @list1='$(man8_MANS)'; \
+ list2=''; \
+ test -n "$(man8dir)" \
+ && test -n "`echo $$list1$$list2`" \
+ || exit 0; \
+ echo " $(MKDIR_P) '$(DESTDIR)$(man8dir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(man8dir)" || exit 1; \
+ { for i in $$list1; do echo "$$i"; done; \
+ if test -n "$$list2"; then \
+ for i in $$list2; do echo "$$i"; done \
+ | sed -n '/\.8[a-z]*$$/p'; \
+ fi; \
+ } | while read p; do \
+ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; echo "$$p"; \
+ done | \
+ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \
+ sed 'N;N;s,\n, ,g' | { \
+ list=; while read file base inst; do \
+ if test "$$base" = "$$inst"; then list="$$list $$file"; else \
+ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man8dir)/$$inst'"; \
+ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man8dir)/$$inst" || exit $$?; \
+ fi; \
+ done; \
+ for i in $$list; do echo "$$i"; done | $(am__base_list) | \
+ while read files; do \
+ test -z "$$files" || { \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man8dir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(man8dir)" || exit $$?; }; \
+ done; }
+
+uninstall-man8:
+ @$(NORMAL_UNINSTALL)
+ @list='$(man8_MANS)'; test -n "$(man8dir)" || exit 0; \
+ files=`{ for i in $$list; do echo "$$i"; done; \
+ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
+ dir='$(DESTDIR)$(man8dir)'; $(am__uninstall_files_from_dir)
+
+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 $(PROGRAMS) $(MANS)
+installdirs:
+ for dir in "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man8dir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+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-sbinPROGRAMS \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/device-discovery.Po
+ -rm -f ./$(DEPDIR)/device-inq.Po
+ -rm -f ./$(DEPDIR)/device-process.Po
+ -rm -f ./$(DEPDIR)/dm-device.Po
+ -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-man
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-sbinPROGRAMS
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man: install-man8
+
+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)/device-discovery.Po
+ -rm -f ./$(DEPDIR)/device-inq.Po
+ -rm -f ./$(DEPDIR)/device-process.Po
+ -rm -f ./$(DEPDIR)/dm-device.Po
+ -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: uninstall-man uninstall-sbinPROGRAMS
+
+uninstall-man: uninstall-man8
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-sbinPROGRAMS 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-man8 install-pdf \
+ install-pdf-am install-ps install-ps-am install-sbinPROGRAMS \
+ 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 \
+ uninstall-man uninstall-man8 uninstall-sbinPROGRAMS
+
+.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/utils/blkmapd/blkmapd.man b/utils/blkmapd/blkmapd.man
new file mode 100644
index 0000000..4b3d3f0
--- /dev/null
+++ b/utils/blkmapd/blkmapd.man
@@ -0,0 +1,72 @@
+.\"
+.\" Copyright 2011, Jim Rees.
+.\"
+.\" You may distribute under the terms of the GNU General Public
+.\" License as specified in the file COPYING that comes with the
+.\" nfs-utils distribution.
+.\"
+.TH blkmapd 8 "11 August 2011"
+.SH NAME
+blkmapd \- pNFS block layout mapping daemon
+.SH SYNOPSIS
+.B "blkmapd [-h] [-d] [-f]"
+.SH DESCRIPTION
+The
+.B blkmapd
+daemon performs device discovery and mapping for the parallel NFS (pNFS) block layout
+client [RFC5663].
+.PP
+The pNFS block layout protocol builds a complex storage hierarchy from a set
+of
+.I simple volumes.
+These simple volumes are addressed by content, using a signature on the
+volume to uniquely name each one.
+The daemon locates a volume by examining each block device in the system for
+the given signature.
+.PP
+The topology typically consists of a hierarchy of volumes built by striping,
+slicing, and concatenating the simple volumes.
+The
+.B blkmapd
+daemon uses the device-mapper driver to construct logical devices that
+reflect the server topology, and passes these devices to the kernel for use
+by the pNFS block layout client.
+.SH OPTIONS
+.TP
+.B -h
+Display usage message.
+.TP
+.B -d
+Performs device discovery only then exits.
+.TP
+.B -f
+Runs
+.B blkmapd
+in the foreground and sends output to stderr (as opposed to syslogd)
+.SH CONFIGURATION FILE
+The
+.B blkmapd
+daemon recognizes the following value from the
+.B [general]
+section of the
+.I /etc/nfs.conf
+configuration file:
+.TP
+.B pipefs-directory
+Tells
+.B blkmapd
+where to look for the rpc_pipefs filesystem. The default value is
+.IR /var/lib/nfs/rpc_pipefs .
+.SH SEE ALSO
+.BR nfs (5),
+.BR dmsetup (8),
+.BR nfs.conf (5)
+.sp
+RFC 5661 for the NFS version 4.1 specification.
+.br
+RFC 5663 for the pNFS block layout specification.
+.SH AUTHORS
+.br
+Haiying Tang <Tang_Haiying@emc.com>
+.br
+Jim Rees <rees@umich.edu>
diff --git a/utils/blkmapd/device-discovery.c b/utils/blkmapd/device-discovery.c
new file mode 100644
index 0000000..a565fdb
--- /dev/null
+++ b/utils/blkmapd/device-discovery.c
@@ -0,0 +1,580 @@
+/*
+ * device-discovery.c: main function, discovering device and processing
+ * pipe request from kernel.
+ *
+ * Copyright (c) 2010 EMC Corporation, Haiying Tang <Tang_Haiying@emc.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.
+ *
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <sys/sysmacros.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/mount.h>
+#include <sys/select.h>
+#include <sys/inotify.h>
+#include <linux/kdev_t.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_ioctl.h>
+#include <scsi/sg.h>
+#include <signal.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+#include <dirent.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <libgen.h>
+#include <errno.h>
+#include <libdevmapper.h>
+
+#include "device-discovery.h"
+#include "xcommon.h"
+#include "nfslib.h"
+#include "conffile.h"
+
+#define EVENT_SIZE (sizeof(struct inotify_event))
+#define EVENT_BUFSIZE (1024 * EVENT_SIZE)
+
+#define RPCPIPE_DIR NFS_STATEDIR "/rpc_pipefs"
+#define PID_FILE "/run/blkmapd.pid"
+
+#define CONF_SAVE(w, f) do { \
+ char *p = f; \
+ if (p != NULL) \
+ (w) = p; \
+} while (0)
+
+static char bl_pipe_file[PATH_MAX];
+static char nfspipe_dir[PATH_MAX];
+static char rpcpipe_dir[PATH_MAX];
+
+struct bl_disk *visible_disk_list;
+int bl_watch_fd, bl_pipe_fd, nfs_pipedir_wfd, rpc_pipedir_wfd;
+int pidfd = -1;
+
+
+static struct bl_disk_path *bl_get_path(const char *filepath,
+ struct bl_disk_path *paths)
+{
+ struct bl_disk_path *tmp = paths;
+
+ while (tmp) {
+ if (!strcmp(tmp->full_path, filepath))
+ break;
+ tmp = tmp->next;
+ }
+ return tmp;
+}
+
+/*
+ * For multipath devices, devices state could be PASSIVE/ACTIVE/PSEUDO,
+ * where PSEUDO > ACTIVE > PASSIVE. Device with highest state is used to
+ * create pseudo device. So if state is higher, the device path needs to
+ * be updated.
+ * If device-mapper multipath support is a must, pseudo devices should
+ * exist for each multipath device. If not, active device path will be
+ * chosen for device creation.
+ */
+static int bl_update_path(enum bl_path_state_e state, struct bl_disk *disk)
+{
+ struct bl_disk_path *valid_path = disk->valid_path;
+
+ if (valid_path && valid_path->state >= state)
+ return 0;
+ return 1;
+}
+
+static void bl_release_disk(void)
+{
+ struct bl_disk *disk;
+ struct bl_disk_path *path = NULL;
+
+ while (visible_disk_list) {
+ disk = visible_disk_list;
+ path = disk->paths;
+ while (path) {
+ disk->paths = path->next;
+ free(path->full_path);
+ free(path);
+ path = disk->paths;
+ }
+ if (disk->serial)
+ free(disk->serial);
+ visible_disk_list = disk->next;
+ free(disk);
+ }
+}
+
+static void bl_add_disk(char *filepath)
+{
+ struct bl_disk *disk = NULL;
+ int fd = 0;
+ struct stat sb;
+ off_t size = 0;
+ struct bl_serial *serial = NULL;
+ enum bl_path_state_e ap_state;
+ struct bl_disk_path *diskpath = NULL, *path = NULL;
+ dev_t dev;
+
+ fd = open(filepath, O_RDONLY | O_LARGEFILE);
+ if (fd < 0)
+ return;
+
+ if (fstat(fd, &sb)) {
+ close(fd);
+ return;
+ }
+
+ if (!sb.st_size)
+ ioctl(fd, BLKGETSIZE, &size);
+ else
+ size = sb.st_size;
+
+ if (!size) {
+ close(fd);
+ return;
+ }
+
+ dev = sb.st_rdev;
+ serial = bldev_read_serial(fd, filepath);
+ if (!serial) {
+ BL_LOG_ERR("%s: no serial found for %s\n",
+ __func__, filepath);
+ ap_state = BL_PATH_STATE_PASSIVE;
+ } else if (dm_is_dm_major(major(dev)))
+ ap_state = BL_PATH_STATE_PSEUDO;
+ else
+ ap_state = bldev_read_ap_state(fd);
+ close(fd);
+
+ for (disk = visible_disk_list; disk != NULL; disk = disk->next) {
+ /* Already scanned or a partition?
+ * XXX: if released each time, maybe not need to compare
+ */
+ if ((serial->len == disk->serial->len) &&
+ !memcmp(serial->data, disk->serial->data, serial->len)) {
+ diskpath = bl_get_path(filepath, disk->paths);
+ break;
+ }
+ }
+
+ if (disk && diskpath) {
+ bl_free_scsi_string(serial);
+ return;
+ }
+
+ /* add path */
+ path = malloc(sizeof(struct bl_disk_path));
+ if (!path) {
+ BL_LOG_ERR("%s: Out of memory!\n", __func__);
+ goto out_err;
+ }
+ path->next = NULL;
+ path->state = ap_state;
+ path->full_path = strdup(filepath);
+ if (!path->full_path)
+ goto out_err;
+
+ if (!disk) { /* add disk */
+ disk = malloc(sizeof(struct bl_disk));
+ if (!disk) {
+ BL_LOG_ERR("%s: Out of memory!\n", __func__);
+ goto out_err;
+ }
+ disk->next = visible_disk_list;
+ disk->dev = dev;
+ disk->size = size;
+ disk->serial = serial;
+ disk->valid_path = path;
+ disk->paths = path;
+ visible_disk_list = disk;
+ } else {
+ path->next = disk->paths;
+ disk->paths = path;
+ /* check whether we need to update disk info */
+ if (bl_update_path(path->state, disk)) {
+ disk->dev = dev;
+ disk->size = size;
+ disk->valid_path = path;
+ }
+ bl_free_scsi_string(serial);
+ }
+ return;
+
+ out_err:
+ if (path) {
+ if (path->full_path)
+ free(path->full_path);
+ free(path);
+ }
+ bl_free_scsi_string(serial);
+ return;
+}
+
+int bl_discover_devices(void)
+{
+ FILE *f;
+ int n;
+ char buf[PATH_MAX], devname[NAME_MAX], fulldevname[PATH_MAX];
+
+ /* release previous list */
+ bl_release_disk();
+
+ /* scan all block devices */
+ f = fopen("/proc/partitions", "r");
+ if (f == NULL)
+ return 0;
+
+ while (1) {
+ if (fgets(buf, sizeof buf, f) == NULL)
+ break;
+ n = sscanf(buf, "%*d %*d %*d %31s", devname);
+ if (n != 1)
+ continue;
+ snprintf(fulldevname, sizeof fulldevname, "/sys/block/%s",
+ devname);
+ if (access(fulldevname, F_OK) < 0)
+ continue;
+ snprintf(fulldevname, sizeof fulldevname, "/dev/%s", devname);
+ bl_add_disk(fulldevname);
+ }
+
+ fclose(f);
+
+ return 0;
+}
+
+/* process kernel request
+ * return 0: request processed, and no more request waiting;
+ * return 1: request processed, and more requests waiting;
+ * return < 0: error
+ */
+static int bl_disk_inquiry_process(int fd)
+{
+ int ret = 0;
+ struct bl_pipemsg_hdr head;
+ char *buf = NULL;
+ uint32_t major, minor;
+ uint16_t buflen;
+ struct bl_dev_msg reply;
+
+ /* read request */
+ if (atomicio(read, fd, &head, sizeof(head)) != sizeof(head)) {
+ /* Note that an error in this or the next read is pretty
+ * catastrophic, as there is no good way to resync into
+ * the pipe's stream.
+ */
+ BL_LOG_ERR("Read pipefs head error!\n");
+ ret = -EIO;
+ goto out;
+ }
+
+ buflen = head.totallen;
+ buf = malloc(buflen);
+ if (!buf) {
+ BL_LOG_ERR("%s: Out of memory!\n", __func__);
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ if (atomicio(read, fd, buf, buflen) != buflen) {
+ BL_LOG_ERR("Read pipefs content error!\n");
+ ret = -EIO;
+ goto out;
+ }
+
+ reply.status = BL_DEVICE_REQUEST_PROC;
+
+ switch (head.type) {
+ case BL_DEVICE_MOUNT:
+ /*
+ * It shouldn't be necessary to discover devices here, since
+ * process_deviceinfo() will re-discover if it can't find
+ * the devices it needs. But in the case of multipath
+ * devices (ones that appear more than once, for example an
+ * active and a standby LUN), this will re-order them in the
+ * correct priority.
+ */
+ bl_discover_devices();
+ if (!process_deviceinfo(buf, buflen, &major, &minor)) {
+ reply.status = BL_DEVICE_REQUEST_ERR;
+ break;
+ }
+ reply.major = major;
+ reply.minor = minor;
+ break;
+ case BL_DEVICE_UMOUNT:
+ if (!dm_device_remove_all((uint64_t *) buf))
+ reply.status = BL_DEVICE_REQUEST_ERR;
+ break;
+ default:
+ reply.status = BL_DEVICE_REQUEST_ERR;
+ break;
+ }
+
+ /* write to pipefs */
+ if (atomicio((void *)write, fd, &reply, sizeof(reply))
+ != sizeof(reply)) {
+ BL_LOG_ERR("Write pipefs error!\n");
+ ret = -EIO;
+ }
+
+ out:
+ if (buf)
+ free(buf);
+ return ret;
+}
+
+static void bl_watch_dir(const char* dir, int *wd)
+{
+ *wd = inotify_add_watch(bl_watch_fd, dir, IN_CREATE|IN_DELETE);
+ if (*wd < 0)
+ BL_LOG_ERR("failed to watch %s: %s\n", dir, strerror(errno));
+}
+
+static void bl_rpcpipe_cb(void)
+{
+ int rc, curr_byte = 0;
+ char eventArr[EVENT_BUFSIZE];
+ struct inotify_event *event;
+
+ rc = read(bl_watch_fd, &eventArr, EVENT_BUFSIZE);
+ if (rc < 0)
+ BL_LOG_ERR("read event fail: %s", strerror(errno));
+
+ while (rc > curr_byte) {
+ event = (struct inotify_event *)&eventArr[curr_byte];
+ curr_byte += EVENT_SIZE + event->len;
+ if (event->wd == rpc_pipedir_wfd) {
+ if (strncmp(event->name, "nfs", 3))
+ continue;
+ if (event->mask & IN_CREATE) {
+ BL_LOG_WARNING("nfs pipe dir created\n");
+ bl_watch_dir(nfspipe_dir, &nfs_pipedir_wfd);
+ if (bl_pipe_fd >= 0)
+ close(bl_pipe_fd);
+ bl_pipe_fd = open(bl_pipe_file, O_RDWR);
+ if (bl_pipe_fd < 0)
+ BL_LOG_ERR("open %s failed: %s\n",
+ event->name, strerror(errno));
+ } else if (event->mask & IN_DELETE) {
+ BL_LOG_WARNING("nfs pipe dir deleted\n");
+ inotify_rm_watch(bl_watch_fd, nfs_pipedir_wfd);
+ close(bl_pipe_fd);
+ nfs_pipedir_wfd = -1;
+ bl_pipe_fd = -1;
+ }
+ } else if (event->wd == nfs_pipedir_wfd) {
+ if (strncmp(event->name, "blocklayout", 11))
+ continue;
+ if (event->mask & IN_CREATE) {
+ BL_LOG_WARNING("blocklayout pipe file created\n");
+ if (bl_pipe_fd >= 0)
+ close(bl_pipe_fd);
+ bl_pipe_fd = open(bl_pipe_file, O_RDWR);
+ if (bl_pipe_fd < 0)
+ BL_LOG_ERR("open %s failed: %s\n",
+ event->name, strerror(errno));
+ } else if (event->mask & IN_DELETE) {
+ BL_LOG_WARNING("blocklayout pipe file deleted\n");
+ close(bl_pipe_fd);
+ bl_pipe_fd = -1;
+ }
+ }
+ }
+}
+
+static int bl_event_helper(void)
+{
+ fd_set rset;
+ int ret = 0, maxfd;
+
+ for (;;) {
+ FD_ZERO(&rset);
+ FD_SET(bl_watch_fd, &rset);
+ if (bl_pipe_fd > 0)
+ FD_SET(bl_pipe_fd, &rset);
+ maxfd = (bl_watch_fd>bl_pipe_fd)?bl_watch_fd:bl_pipe_fd;
+ switch (select(maxfd + 1, &rset, NULL, NULL, NULL)) {
+ case -1:
+ if (errno == EINTR)
+ continue;
+ else {
+ ret = -errno;
+ goto out;
+ }
+ case 0:
+ goto out;
+ default:
+ if (FD_ISSET(bl_watch_fd, &rset))
+ bl_rpcpipe_cb();
+ else if (bl_pipe_fd > 0 && FD_ISSET(bl_pipe_fd, &rset))
+ ret = bl_disk_inquiry_process(bl_pipe_fd);
+ if (ret)
+ goto out;
+ }
+ }
+ out:
+ return ret;
+}
+
+static void sig_die(int signal)
+{
+ if (pidfd >= 0) {
+ close(pidfd);
+ unlink(PID_FILE);
+ }
+ BL_LOG_ERR("exit on signal(%d)\n", signal);
+ exit(0);
+}
+static void usage(void)
+{
+ fprintf(stderr, "Usage: blkmapd [-hdf]\n" );
+}
+/* Daemon */
+int main(int argc, char **argv)
+{
+ int opt, dflag = 0, fg = 0, ret = 1;
+ char pidbuf[64];
+ char *xrpcpipe_dir = NULL;
+
+ strncpy(rpcpipe_dir, RPCPIPE_DIR, sizeof(rpcpipe_dir));
+ conf_init_file(NFS_CONFFILE);
+ CONF_SAVE(xrpcpipe_dir, conf_get_str("general", "pipefs-directory"));
+ if (xrpcpipe_dir != NULL)
+ strlcpy(rpcpipe_dir, xrpcpipe_dir, sizeof(rpcpipe_dir));
+
+ strncpy(nfspipe_dir, rpcpipe_dir, sizeof(nfspipe_dir));
+ strlcat(nfspipe_dir, "/nfs", sizeof(nfspipe_dir));
+ strncpy(bl_pipe_file, rpcpipe_dir, sizeof(bl_pipe_file));
+ strlcat(bl_pipe_file, "/nfs/blocklayout", sizeof(bl_pipe_file));
+
+ while ((opt = getopt(argc, argv, "hdf")) != -1) {
+ switch (opt) {
+ case 'd':
+ dflag = 1;
+ break;
+ case 'f':
+ fg = 1;
+ break;
+ case 'h':
+ usage();
+ exit(0);
+ default:
+ usage();
+ exit(1);
+
+ }
+ }
+
+ if (fg) {
+ openlog("blkmapd", LOG_PERROR, 0);
+ } else {
+ pid_t pid = fork();
+ if (pid < 0) {
+ BL_LOG_ERR("fork error\n");
+ exit(1);
+ } else if (pid != 0) {
+ pidfd = open(PID_FILE, O_WRONLY | O_CREAT, 0644);
+ if (pidfd < 0) {
+ BL_LOG_ERR("Create pid file %s failed\n", PID_FILE);
+ exit(1);
+ }
+
+ if (lockf(pidfd, F_TLOCK, 0) < 0) {
+ BL_LOG_ERR("Already running; Exiting!");
+ close(pidfd);
+ exit(1);
+ }
+ if (ftruncate(pidfd, 0) < 0)
+ BL_LOG_ERR("ftruncate on %s failed: m\n", PID_FILE);
+ sprintf(pidbuf, "%d\n", pid);
+ if (write(pidfd, pidbuf, strlen(pidbuf)) != (ssize_t)strlen(pidbuf))
+ BL_LOG_ERR("write on %s failed: m\n", PID_FILE);
+ exit(0);
+ }
+
+ (void)setsid();
+ if (chdir("/")) {
+ BL_LOG_ERR("chdir error\n");
+ }
+ int fd = open("/dev/null", O_RDWR, 0);
+ if (fd >= 0) {
+ (void)dup2(fd, STDIN_FILENO);
+ (void)dup2(fd, STDOUT_FILENO);
+ (void)dup2(fd, STDERR_FILENO);
+
+ (void)close(fd);
+ }
+
+ openlog("blkmapd", LOG_PID, 0);
+ }
+
+ signal(SIGINT, sig_die);
+ signal(SIGTERM, sig_die);
+ signal(SIGHUP, SIG_IGN);
+
+ if (dflag) {
+ ret = bl_discover_devices();
+ goto out;
+ }
+
+ if ((bl_watch_fd = inotify_init()) < 0) {
+ BL_LOG_ERR("init inotify failed %s\n", strerror(errno));
+ goto out;
+ }
+
+ /* open pipe file */
+ bl_watch_dir(rpcpipe_dir, &rpc_pipedir_wfd);
+ bl_watch_dir(nfspipe_dir, &nfs_pipedir_wfd);
+
+ bl_pipe_fd = open(bl_pipe_file, O_RDWR);
+ if (bl_pipe_fd < 0)
+ BL_LOG_ERR("open pipe file %s failed: %s\n", bl_pipe_file, strerror(errno));
+
+ while (1) {
+ /* discover device when needed */
+ bl_discover_devices();
+
+ ret = bl_event_helper();
+ if (ret < 0) {
+ /* what should we do with process error? */
+ BL_LOG_ERR("inquiry process return %d\n", ret);
+ }
+ }
+out:
+ if (pidfd >= 0) {
+ close(pidfd);
+ unlink(PID_FILE);
+ }
+
+ exit(ret);
+}
diff --git a/utils/blkmapd/device-discovery.h b/utils/blkmapd/device-discovery.h
new file mode 100644
index 0000000..462aa94
--- /dev/null
+++ b/utils/blkmapd/device-discovery.h
@@ -0,0 +1,164 @@
+/*
+ * bl-device-discovery.h
+ *
+ * Copyright (c) 2010 EMC Corporation, Haiying Tang <Tang_Haiying@emc.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.
+ *
+ * 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.
+ */
+#ifndef BL_DEVICE_DISCOVERY_H
+#define BL_DEVICE_DISCOVERY_H
+
+#include <stdint.h>
+
+enum blk_vol_type {
+ BLOCK_VOLUME_SIMPLE = 0, /* maps to a single LU */
+ BLOCK_VOLUME_SLICE = 1, /* slice of another volume */
+ BLOCK_VOLUME_CONCAT = 2, /* concatenation of multiple volumes */
+ BLOCK_VOLUME_STRIPE = 3, /* striped across multiple volumes */
+ BLOCK_VOLUME_PSEUDO = 4,
+};
+
+/* All disk offset/lengths are stored in 512-byte sectors */
+struct bl_volume {
+ uint32_t bv_type;
+ off_t bv_size;
+ struct bl_volume **bv_vols;
+ int bv_vol_n;
+ union {
+ dev_t bv_dev; /* for BLOCK_VOLUME_SIMPLE(PSEUDO) */
+ off_t bv_stripe_unit; /* for BLOCK_VOLUME_STRIPE(CONCAT) */
+ off_t bv_offset; /* for BLOCK_VOLUME_SLICE */
+ } param;
+};
+
+struct bl_sig_comp {
+ int64_t bs_offset; /* In bytes */
+ uint32_t bs_length; /* In bytes */
+ char *bs_string;
+};
+
+/* Maximum number of signatures components in a simple volume */
+# define BLOCK_MAX_SIG_COMP 16
+
+struct bl_sig {
+ int si_num_comps;
+ struct bl_sig_comp si_comps[BLOCK_MAX_SIG_COMP];
+};
+
+/*
+ * Multipath support: ACTIVE or PSEUDO device is valid,
+ * PASSIVE is a standby for ACTIVE.
+ */
+enum bl_path_state_e {
+ BL_PATH_STATE_PASSIVE = 1,
+ BL_PATH_STATE_ACTIVE = 2,
+ BL_PATH_STATE_PSEUDO = 3,
+};
+
+struct bl_serial {
+ int len;
+ char *data;
+};
+
+struct bl_disk_path {
+ struct bl_disk_path *next;
+ char *full_path;
+ enum bl_path_state_e state;
+};
+
+struct bl_disk {
+ struct bl_disk *next;
+ struct bl_serial *serial;
+ dev_t dev;
+ off_t size; /* in 512-byte sectors */
+ struct bl_disk_path *valid_path;
+ struct bl_disk_path *paths;
+};
+
+struct bl_dev_id {
+ unsigned char type;
+ unsigned char ids;
+ unsigned char reserve;
+ unsigned char len;
+ char data[0];
+};
+
+struct bl_dev_msg {
+ int status;
+ uint32_t major, minor;
+};
+
+struct bl_pipemsg_hdr {
+ uint8_t type;
+ uint16_t totallen; /* length of message excluding hdr */
+};
+
+#define BL_DEVICE_UMOUNT 0x0 /* Umount--delete devices */
+#define BL_DEVICE_MOUNT 0x1 /* Mount--create devices */
+#define BL_DEVICE_REQUEST_INIT 0x0 /* Start request */
+#define BL_DEVICE_REQUEST_PROC 0x1 /* User process succeeds */
+#define BL_DEVICE_REQUEST_ERR 0x2 /* User process fails */
+
+uint32_t *blk_overflow(uint32_t * p, uint32_t * end, size_t nbytes);
+
+#define BLK_READBUF(p, e, nbytes) do { \
+ p = blk_overflow(p, e, nbytes); \
+ if (!p) {\
+ goto out_err;\
+ } \
+} while (0)
+
+#define READ32(x) (x) = ntohl(*p++)
+
+#define READ64(x) do { \
+ (x) = (uint64_t)ntohl(*p++) << 32; \
+ (x) |= ntohl(*p++); \
+} while (0)
+
+#define READ_SECTOR(x) do { \
+ READ64(tmp); \
+ if (tmp & 0x1ff) { \
+ goto out_err; \
+ } \
+ (x) = tmp >> 9; \
+} while (0)
+
+extern struct bl_disk *visible_disk_list;
+uint64_t dm_device_create(struct bl_volume *vols, int num_vols);
+int dm_device_remove_all(uint64_t *dev);
+uint64_t process_deviceinfo(const char *dev_addr_buf,
+ unsigned int dev_addr_len,
+ uint32_t *major, uint32_t *minor);
+
+extern ssize_t atomicio(ssize_t(*f) (int, void *, size_t),
+ int fd, void *_s, size_t n);
+extern struct bl_serial *bl_create_scsi_string(int len, const char *bytes);
+extern void bl_free_scsi_string(struct bl_serial *str);
+extern struct bl_serial *bldev_read_serial(int fd, const char *filename);
+extern enum bl_path_state_e bldev_read_ap_state(int fd);
+extern int bl_discover_devices(void);
+
+#define BL_LOG_INFO(fmt...) syslog(LOG_INFO, fmt)
+#define BL_LOG_WARNING(fmt...) syslog(LOG_WARNING, fmt)
+#define BL_LOG_ERR(fmt...) syslog(LOG_ERR, fmt)
+#define BL_LOG_DEBUG(fmt...) syslog(LOG_DEBUG, fmt)
+#endif
diff --git a/utils/blkmapd/device-inq.c b/utils/blkmapd/device-inq.c
new file mode 100644
index 0000000..9e5749e
--- /dev/null
+++ b/utils/blkmapd/device-inq.c
@@ -0,0 +1,247 @@
+/*
+ * device-inq.c: inquire SCSI device information.
+ *
+ * Copyright (c) 2010 EMC Corporation, Haiying Tang <Tang_Haiying@emc.com>
+ * All rights reserved.
+ *
+ * This program refers to "SCSI Primary Commands - 3 (SPC-3)
+ * at http://www.t10.org and sg_inq.c in sg3_utils-1.26 for
+ * Linux OS SCSI subsystem, by D. Gilbert.
+ *
+ * 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 <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/mount.h>
+#include <sys/select.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_ioctl.h>
+#include <scsi/sg.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <syslog.h>
+#include <dirent.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <errno.h>
+
+#include "device-discovery.h"
+
+#define DEF_ALLOC_LEN 255
+#define MX_ALLOC_LEN (0xc000 + 0x80)
+
+struct bl_serial *bl_create_scsi_string(int len, const char *bytes)
+{
+ struct bl_serial *s;
+
+ s = malloc(sizeof(*s) + len);
+ if (s) {
+ s->data = (char *)&s[1];
+ s->len = len;
+ memcpy(s->data, bytes, len);
+ }
+ return s;
+}
+
+void bl_free_scsi_string(struct bl_serial *str)
+{
+ if (str)
+ free(str);
+}
+
+#define sg_io_ok(io_hdr) \
+ ((((io_hdr).status & 0x7e) == 0) && \
+ ((io_hdr).host_status == 0) && \
+ (((io_hdr).driver_status & 0x0f) == 0))
+
+static int sg_timeout = 1 * 1000;
+
+static int bldev_inquire_page(int fd, int page, char *buffer, int len)
+{
+ unsigned char cmd[] = { INQUIRY, 0, 0, 0, 0, 0 };
+ unsigned char sense_b[28];
+ struct sg_io_hdr io_hdr;
+ if (page >= 0) {
+ cmd[1] = 1;
+ cmd[2] = page;
+ }
+ cmd[3] = (unsigned char)((len >> 8) & 0xff);
+ cmd[4] = (unsigned char)(len & 0xff);
+
+ memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
+ io_hdr.interface_id = 'S';
+ io_hdr.cmd_len = sizeof(cmd);
+ io_hdr.mx_sb_len = sizeof(sense_b);
+ io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
+ io_hdr.dxfer_len = len;
+ io_hdr.dxferp = buffer;
+ io_hdr.cmdp = cmd;
+ io_hdr.sbp = sense_b;
+ io_hdr.timeout = sg_timeout;
+ if (ioctl(fd, SG_IO, &io_hdr) < 0)
+ return -1;
+
+ if (sg_io_ok(io_hdr))
+ return 0;
+ return -1;
+}
+
+static int bldev_inquire_pages(int fd, int page, char **buffer)
+{
+ int status = 0;
+ char *tmp;
+ int len;
+
+ *buffer = calloc(DEF_ALLOC_LEN, sizeof(char));
+ if (!*buffer) {
+ BL_LOG_ERR("%s: Out of memory!\n", __func__);
+ return -ENOMEM;
+ }
+
+ status = bldev_inquire_page(fd, page, *buffer, DEF_ALLOC_LEN);
+ if (status)
+ goto out;
+
+ status = -1;
+ if ((*(*buffer + 1) & 0xff) != page)
+ goto out;
+
+ len = (*(*buffer + 2) << 8) + *(*buffer + 3) + 4;
+ if (len > MX_ALLOC_LEN) {
+ BL_LOG_ERR("SCSI response length too long: %d\n", len);
+ goto out;
+ }
+ if (len > DEF_ALLOC_LEN) {
+ tmp = realloc(*buffer, len);
+ if (!tmp) {
+ BL_LOG_ERR("%s: Out of memory!\n", __func__);
+ status = -ENOMEM;
+ goto out;
+ }
+ *buffer = tmp;
+ status = bldev_inquire_page(fd, page, *buffer, len);
+ if (status)
+ goto out;
+ }
+ status = 0;
+ out:
+ return status;
+}
+
+/* For EMC multipath devices, use VPD page (0xc0) to get status.
+ * For other devices, return ACTIVE for now
+ */
+extern enum bl_path_state_e bldev_read_ap_state(int fd)
+{
+ int status = 0;
+ char *buffer = NULL;
+ enum bl_path_state_e ap_state = BL_PATH_STATE_ACTIVE;
+
+ status = bldev_inquire_pages(fd, 0xc0, &buffer);
+ if (status)
+ goto out;
+
+ if (buffer[4] < 0x02)
+ ap_state = BL_PATH_STATE_PASSIVE;
+ out:
+ if (buffer)
+ free(buffer);
+ return ap_state;
+}
+
+struct bl_serial *bldev_read_serial(int fd, const char *filename)
+{
+ struct bl_serial *serial_out = NULL;
+ int status = 0;
+ char *buffer;
+ struct bl_dev_id *dev_root, *dev_id;
+ unsigned int pos, len, current_id = 0;
+ size_t devid_len = sizeof(struct bl_dev_id) - sizeof(unsigned char);
+
+ status = bldev_inquire_pages(fd, 0x83, &buffer);
+ if (status)
+ goto out;
+
+ dev_root = (struct bl_dev_id *)buffer;
+
+ pos = 0;
+ current_id = 0;
+ len = dev_root->len;
+
+ if (len < devid_len)
+ goto out;
+
+ while (pos < (len - devid_len)) {
+ dev_id = (struct bl_dev_id *)&(dev_root->data[pos]);
+ pos += (dev_id->len + devid_len);
+
+ /* Some targets export zero length EVPD pages,
+ * skip them to not confuse the device id
+ * cache.
+ */
+ if (!dev_id->len)
+ continue;
+
+ if ((dev_id->ids & 0xf) < current_id)
+ continue;
+ switch (dev_id->ids & 0xf) {
+ /* We process SCSI ID with four ID cases: 0, 1, 2 and 3.
+ * When more than one ID is available, priority is
+ * 3>2>1>0.
+ */
+ case 2: /* EUI-64 based */
+ if ((dev_id->len != 8) && (dev_id->len != 12) &&
+ (dev_id->len != 16))
+ break;
+ /* FALLTHRU */
+ case 3: /* NAA */
+ /* TODO: NAA validity judgement too complicated,
+ * so just ingore it here.
+ */
+ if ((dev_id->type & 0xf) != 1) {
+ BL_LOG_ERR("Binary code_set expected\n");
+ break;
+ }
+ /* FALLTHRU */
+ case 0: /* vendor specific */
+ case 1: /* T10 vendor identification */
+ current_id = dev_id->ids & 0xf;
+ if (serial_out)
+ bl_free_scsi_string(serial_out);
+ serial_out = bl_create_scsi_string(dev_id->len,
+ dev_id->data);
+ break;
+ }
+ if (current_id == 3)
+ break;
+ }
+ out:
+ if (!serial_out)
+ serial_out = bl_create_scsi_string(strlen(filename), filename);
+ if (buffer)
+ free(buffer);
+ return serial_out;
+}
diff --git a/utils/blkmapd/device-process.c b/utils/blkmapd/device-process.c
new file mode 100644
index 0000000..f53a616
--- /dev/null
+++ b/utils/blkmapd/device-process.c
@@ -0,0 +1,380 @@
+/*
+ * device-process.c: detailed processing of device information sent
+ * from kernel.
+ *
+ * Copyright (c) 2006 The Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Andy Adamson <andros@citi.umich.edu>
+ * Fred Isaman <iisaman@umich.edu>
+ *
+ * Copyright (c) 2010 EMC Corporation, Haiying Tang <Tang_Haiying@emc.com>
+ *
+ * Used codes in linux/fs/nfs/blocklayout/blocklayoutdev.c.
+ *
+ * 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 <sys/stat.h>
+#include <sys/user.h>
+#include <arpa/inet.h>
+#include <linux/kdev_t.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include "device-discovery.h"
+
+uint32_t *blk_overflow(uint32_t * p, uint32_t * end, size_t nbytes)
+{
+ uint32_t *q = p + ((nbytes + 3) >> 2);
+
+ if (q > end || q < p)
+ return NULL;
+ return p;
+}
+
+static int decode_blk_signature(uint32_t **pp, uint32_t * end,
+ struct bl_sig *sig)
+{
+ int i;
+ uint32_t siglen, *p = *pp;
+
+ BLK_READBUF(p, end, 4);
+ READ32(sig->si_num_comps);
+ if (sig->si_num_comps == 0) {
+ BL_LOG_ERR("0 components in sig\n");
+ goto out_err;
+ }
+ if (sig->si_num_comps >= BLOCK_MAX_SIG_COMP) {
+ BL_LOG_ERR("number of sig comps %i >= BLOCK_MAX_SIG_COMP\n",
+ sig->si_num_comps);
+ goto out_err;
+ }
+ for (i = 0; i < sig->si_num_comps; i++) {
+ struct bl_sig_comp *comp = &sig->si_comps[i];
+
+ BLK_READBUF(p, end, 12);
+ READ64(comp->bs_offset);
+ READ32(siglen);
+ comp->bs_length = siglen;
+ BLK_READBUF(p, end, siglen);
+ /* Note we rely here on fact that sig is used immediately
+ * for mapping, then thrown away.
+ */
+ comp->bs_string = (char *)p;
+ p += ((siglen + 3) >> 2);
+ }
+ *pp = p;
+ return 0;
+ out_err:
+ return -EIO;
+}
+
+/*
+ * Read signature from device and compare to sig_comp
+ * return: 0=match, 1=no match, -1=error
+ */
+static int
+read_cmp_blk_sig(struct bl_disk *disk, int fd, struct bl_sig_comp *comp)
+{
+ const char *dev_name = disk->valid_path->full_path;
+ int ret = -1;
+ ssize_t siglen = comp->bs_length;
+ int64_t bs_offset = comp->bs_offset;
+ char *sig = NULL;
+
+ sig = (char *)malloc(siglen);
+ if (!sig) {
+ BL_LOG_ERR("%s: Out of memory\n", __func__);
+ goto out;
+ }
+
+ if (bs_offset < 0)
+ bs_offset += (((int64_t) disk->size) << 9);
+ if (lseek64(fd, bs_offset, SEEK_SET) == -1) {
+ BL_LOG_ERR("File %s lseek error\n", dev_name);
+ goto out;
+ }
+
+ if (read(fd, sig, siglen) != siglen) {
+ BL_LOG_ERR("File %s read error\n", dev_name);
+ goto out;
+ }
+
+ ret = memcmp(sig, comp->bs_string, siglen);
+
+ out:
+ if (sig)
+ free(sig);
+ return ret;
+}
+
+/*
+ * All signatures in sig must be found on disk for verification.
+ * Returns True if sig matches, False otherwise.
+ */
+static int verify_sig(struct bl_disk *disk, struct bl_sig *sig)
+{
+ const char *dev_name = disk->valid_path->full_path;
+ int fd, i, rv;
+
+ fd = open(dev_name, O_RDONLY | O_LARGEFILE);
+ if (fd < 0) {
+ BL_LOG_ERR("%s: %s could not be opened for read\n", __func__,
+ dev_name);
+ return 0;
+ }
+
+ rv = 1;
+
+ for (i = 0; i < sig->si_num_comps; i++) {
+ if (read_cmp_blk_sig(disk, fd, &sig->si_comps[i])) {
+ rv = 0;
+ break;
+ }
+ }
+
+ if (fd >= 0)
+ close(fd);
+ return rv;
+}
+
+/*
+ * map_sig_to_device()
+ * Given a signature, walk the list of visible disks searching for
+ * a match. Returns True if mapping was done, False otherwise.
+ *
+ * While we're at it, fill in the vol->bv_size.
+ */
+static int map_sig_to_device(struct bl_sig *sig, struct bl_volume *vol)
+{
+ int mapped = 0;
+ struct bl_disk *disk;
+
+ /* scan disk list to find out match device */
+ for (disk = visible_disk_list; disk; disk = disk->next) {
+ /* FIXME: should we use better algorithm for disk scan? */
+ mapped = verify_sig(disk, sig);
+ if (mapped) {
+ BL_LOG_INFO("%s: using device %s\n",
+ __func__, disk->valid_path->full_path);
+ vol->param.bv_dev = disk->dev;
+ vol->bv_size = disk->size;
+ break;
+ }
+ }
+ return mapped;
+}
+
+/* We are given an array of XDR encoded array indices, each of which should
+ * refer to a previously decoded device. Translate into a list of pointers
+ * to the appropriate pnfs_blk_volume's.
+ */
+static int set_vol_array(uint32_t **pp, uint32_t *end,
+ struct bl_volume *vols, int working)
+{
+ int i, index;
+ uint32_t *p = *pp;
+ struct bl_volume **array = vols[working].bv_vols;
+
+ for (i = 0; i < vols[working].bv_vol_n; i++) {
+ BLK_READBUF(p, end, 4);
+ READ32(index);
+ if ((index < 0) || (index >= working)) {
+ BL_LOG_ERR("set_vol_array: Id %i out of range\n",
+ index);
+ goto out_err;
+ }
+ array[i] = &vols[index];
+ }
+ *pp = p;
+ return 0;
+ out_err:
+ return -EIO;
+}
+
+static uint64_t sum_subvolume_sizes(struct bl_volume *vol)
+{
+ int i;
+ uint64_t sum = 0;
+
+ for (i = 0; i < vol->bv_vol_n; i++)
+ sum += vol->bv_vols[i]->bv_size;
+ return sum;
+}
+
+static int
+decode_blk_volume(uint32_t **pp, uint32_t *end, struct bl_volume *vols, int voln,
+ int *array_cnt)
+{
+ int status = 0, j;
+ struct bl_sig sig;
+ uint32_t *p = *pp;
+ struct bl_volume *vol = &vols[voln];
+ uint64_t tmp;
+
+ BLK_READBUF(p, end, 4);
+ READ32(vol->bv_type);
+
+ switch (vol->bv_type) {
+ case BLOCK_VOLUME_SIMPLE:
+ *array_cnt = 0;
+ status = decode_blk_signature(&p, end, &sig);
+ if (status)
+ return status;
+ status = map_sig_to_device(&sig, vol);
+ if (!status) {
+ BL_LOG_ERR("Could not find disk for device\n");
+ return -ENXIO;
+ }
+ BL_LOG_INFO("%s: simple %d\n", __func__, voln);
+ status = 0;
+ break;
+ case BLOCK_VOLUME_SLICE:
+ BLK_READBUF(p, end, 16);
+ READ_SECTOR(vol->param.bv_offset);
+ READ_SECTOR(vol->bv_size);
+ *array_cnt = vol->bv_vol_n = 1;
+ BL_LOG_INFO("%s: slice %d\n", __func__, voln);
+ status = set_vol_array(&p, end, vols, voln);
+ break;
+ case BLOCK_VOLUME_STRIPE:
+ BLK_READBUF(p, end, 8);
+ READ_SECTOR(vol->param.bv_stripe_unit);
+ off_t stripe_unit = vol->param.bv_stripe_unit;
+ /* Check limitations imposed by device-mapper */
+ if ((stripe_unit & (stripe_unit - 1)) != 0
+ || stripe_unit < (off_t) (sysconf(_SC_PAGE_SIZE) >> 9))
+ return -EIO;
+ BLK_READBUF(p, end, 4);
+ READ32(vol->bv_vol_n);
+ if (!vol->bv_vol_n)
+ return -EIO;
+ *array_cnt = vol->bv_vol_n;
+ BL_LOG_INFO("%s: stripe %d nvols=%d unit=%ld\n", __func__, voln,
+ vol->bv_vol_n, (long)stripe_unit);
+ status = set_vol_array(&p, end, vols, voln);
+ if (status)
+ return status;
+ for (j = 1; j < vol->bv_vol_n; j++) {
+ if (vol->bv_vols[j]->bv_size !=
+ vol->bv_vols[0]->bv_size) {
+ BL_LOG_ERR("varying subvol size\n");
+ return -EIO;
+ }
+ }
+ vol->bv_size = vol->bv_vols[0]->bv_size * vol->bv_vol_n;
+ break;
+ case BLOCK_VOLUME_CONCAT:
+ BLK_READBUF(p, end, 4);
+ READ32(vol->bv_vol_n);
+ if (!vol->bv_vol_n)
+ return -EIO;
+ *array_cnt = vol->bv_vol_n;
+ BL_LOG_INFO("%s: concat %d %d\n", __func__, voln,
+ vol->bv_vol_n);
+ status = set_vol_array(&p, end, vols, voln);
+ if (status)
+ return status;
+ vol->bv_size = sum_subvolume_sizes(vol);
+ break;
+ default:
+ BL_LOG_ERR("Unknown volume type %i\n", vol->bv_type);
+ out_err:
+ return -EIO;
+ }
+ *pp = p;
+ return status;
+}
+
+uint64_t process_deviceinfo(const char *dev_addr_buf,
+ unsigned int dev_addr_len,
+ uint32_t *major, uint32_t *minor)
+{
+ int num_vols, i, status, count;
+ uint32_t *p, *end;
+ struct bl_volume *vols = NULL, **arrays = NULL, **arrays_ptr = NULL;
+ uint64_t dev = 0;
+
+ p = (uint32_t *) dev_addr_buf;
+ end = (uint32_t *) ((char *)p + dev_addr_len);
+
+ /* Decode block volume */
+ BLK_READBUF(p, end, 4);
+ READ32(num_vols);
+ BL_LOG_INFO("%s: %d vols\n", __func__, num_vols);
+ if (num_vols <= 0)
+ goto out_err;
+
+ vols = (struct bl_volume *)malloc(num_vols * sizeof(struct bl_volume));
+ if (!vols) {
+ BL_LOG_ERR("%s: Out of memory\n", __func__);
+ goto out_err;
+ }
+
+ /* Each volume in vols array needs its own array. Save time by
+ * allocating them all in one large hunk. Because each volume
+ * array can only reference previous volumes, and because once
+ * a concat or stripe references a volume, it may never be
+ * referenced again, the volume arrays are guaranteed to fit
+ * in the suprisingly small space allocated.
+ */
+ arrays_ptr = arrays =
+ (struct bl_volume **)malloc(num_vols * 2 *
+ sizeof(struct bl_volume *));
+ if (!arrays) {
+ BL_LOG_ERR("%s: Out of memory\n", __func__);
+ goto out_err;
+ }
+
+ for (i = 0; i < num_vols; i++) {
+ vols[i].bv_vols = arrays_ptr;
+ status = decode_blk_volume(&p, end, vols, i, &count);
+ if (status)
+ goto out_err;
+ arrays_ptr += count;
+ }
+
+ if (p != end) {
+ BL_LOG_ERR("p is not equal to end!\n");
+ goto out_err;
+ }
+
+ dev = dm_device_create(vols, num_vols);
+ if (dev) {
+ *major = MAJOR(dev);
+ *minor = MINOR(dev);
+ }
+
+ out_err:
+ if (vols)
+ free(vols);
+ if (arrays)
+ free(arrays);
+ return dev;
+}
diff --git a/utils/blkmapd/dm-device.c b/utils/blkmapd/dm-device.c
new file mode 100644
index 0000000..ee20d54
--- /dev/null
+++ b/utils/blkmapd/dm-device.c
@@ -0,0 +1,525 @@
+/*
+ * dm-device.c: create or remove device via device mapper API.
+ *
+ * Copyright (c) 2010 EMC Corporation, Haiying Tang <Tang_Haiying@emc.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.
+ *
+ * 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 <sys/stat.h>
+#include <linux/kdev_t.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <libdevmapper.h>
+
+#include "device-discovery.h"
+
+#define DM_DEV_NAME_LEN 256
+
+#ifndef DM_MAX_TYPE_NAME
+#define DM_MAX_TYPE_NAME 16
+#endif
+
+#define DM_PARAMS_LEN 512 /* XXX: is this enough for target? */
+#define TYPE_HAS_DEV(type) ((type == BLOCK_VOLUME_SIMPLE) || \
+ (type == BLOCK_VOLUME_PSEUDO))
+
+struct bl_dm_table {
+ uint64_t offset;
+ uint64_t size;
+ char target_type[DM_MAX_TYPE_NAME];
+ char params[DM_PARAMS_LEN];
+ struct bl_dm_table *next;
+};
+
+struct bl_dm_tree {
+ uint64_t dev;
+ struct dm_tree *tree;
+ struct bl_dm_tree *next;
+};
+
+static const char dm_name[] = "pnfs_vol_%u";
+
+static unsigned int dev_count;
+
+static inline struct bl_dm_table *bl_dm_table_alloc(void)
+{
+ return (struct bl_dm_table *)calloc(1, sizeof(struct bl_dm_table));
+}
+
+static void bl_dm_table_free(struct bl_dm_table *bl_table_head)
+{
+ struct bl_dm_table *p;
+
+ while (bl_table_head) {
+ p = bl_table_head->next;
+ free(bl_table_head);
+ bl_table_head = p;
+ }
+}
+
+static void add_to_bl_dm_table(struct bl_dm_table **bl_table_head,
+ struct bl_dm_table *table)
+{
+ struct bl_dm_table *p;
+
+ if (!*bl_table_head) {
+ *bl_table_head = table;
+ return;
+ }
+ p = *bl_table_head;
+ while (p->next)
+ p = p->next;
+ p->next = table;
+}
+
+struct bl_dm_tree *bl_tree_head;
+
+static struct bl_dm_tree *find_bl_dm_tree(uint64_t dev)
+{
+ struct bl_dm_tree *p;
+
+ for (p = bl_tree_head; p; p = p->next) {
+ if (p->dev == dev)
+ break;
+ }
+ return p;
+}
+
+static void del_from_bl_dm_tree(uint64_t dev)
+{
+ struct bl_dm_tree *p, *pre = bl_tree_head;
+
+ for (p = pre; p; p = p->next) {
+ if (p->dev == dev) {
+ pre->next = p->next;
+ if (p == bl_tree_head)
+ bl_tree_head = bl_tree_head->next;
+ free(p);
+ break;
+ }
+ pre = p;
+ }
+}
+
+static void add_to_bl_dm_tree(struct bl_dm_tree *tree)
+{
+ struct bl_dm_tree *p;
+
+ if (!bl_tree_head) {
+ bl_tree_head = tree;
+ return;
+ }
+ p = bl_tree_head;
+ while (p->next)
+ p = p->next;
+ p->next = tree;
+ return;
+}
+
+/*
+ * Create device via device mapper
+ * return 0 when creation failed
+ * return dev no for created device
+ */
+static uint64_t
+dm_device_create_mapped(const char *dev_name, struct bl_dm_table *p)
+{
+ struct dm_task *dmt;
+ struct dm_info dminfo;
+ int ret = 0;
+
+ dmt = dm_task_create(DM_DEVICE_CREATE);
+ if (!dmt) {
+ BL_LOG_ERR("Create dm_task for %s failed\n", dev_name);
+ return 0;
+ }
+ ret = dm_task_set_name(dmt, dev_name);
+ if (!ret)
+ goto err_out;
+
+ while (p) {
+ ret =
+ dm_task_add_target(dmt, p->offset, p->size, p->target_type,
+ p->params);
+ if (!ret)
+ goto err_out;
+ p = p->next;
+ }
+
+ ret = dm_task_run(dmt) && dm_task_get_info(dmt, &dminfo)
+ && dminfo.exists;
+
+ if (!ret)
+ goto err_out;
+
+ dm_task_update_nodes();
+
+ err_out:
+ dm_task_destroy(dmt);
+
+ if (!ret) {
+ BL_LOG_ERR("Create device %s failed\n", dev_name);
+ return 0;
+ }
+ return MKDEV(dminfo.major, dminfo.minor);
+}
+
+static int dm_device_remove_byname(const char *dev_name)
+{
+ struct dm_task *dmt;
+ int ret = 0;
+
+ BL_LOG_INFO("%s: %s\n", __func__, dev_name);
+
+ dmt = dm_task_create(DM_DEVICE_REMOVE);
+ if (!dmt)
+ return 0;
+
+ ret = dm_task_set_name(dmt, dev_name) && dm_task_run(dmt);
+
+ dm_task_update_nodes();
+ dm_task_destroy(dmt);
+
+ return ret;
+}
+
+static int dm_device_remove(uint64_t dev)
+{
+ struct dm_task *dmt;
+ struct dm_names *dmnames;
+ char *name = NULL;
+ int ret = 0;
+
+ /* Look for dev_name via dev, if dev_name could be transferred here,
+ we could jump to DM_DEVICE_REMOVE directly */
+
+ dmt = dm_task_create(DM_DEVICE_LIST);
+ if (!dmt) {
+ BL_LOG_ERR("dm_task creation failed\n");
+ goto out;
+ }
+
+ ret = dm_task_run(dmt);
+ if (!ret) {
+ BL_LOG_ERR("dm_task_run failed\n");
+ goto out;
+ }
+
+ dmnames = dm_task_get_names(dmt);
+ if (!dmnames || !dmnames->dev) {
+ BL_LOG_ERR("dm_task_get_names failed\n");
+ goto out;
+ }
+
+ while (dmnames) {
+ if (dmnames->dev == dev) {
+ name = strdup(dmnames->name);
+ break;
+ }
+ dmnames = (void *)dmnames + dmnames->next;
+ }
+
+ if (!name) {
+ BL_LOG_ERR("Could not find device\n");
+ goto out;
+ }
+
+ dm_task_update_nodes();
+
+ out:
+ if (dmt)
+ dm_task_destroy(dmt);
+
+ /* Start to remove device */
+ if (name) {
+ ret = dm_device_remove_byname(name);
+ free(name);
+ }
+
+ return ret;
+}
+
+static void dm_devicelist_remove(unsigned int start, unsigned int end)
+{
+ char dev_name[DM_DEV_NAME_LEN];
+ unsigned int count;
+
+ if (start >= dev_count || end <= 1 || start >= end - 1)
+ return;
+
+ for (count = end - 1; count > start; count--) {
+ snprintf(dev_name, sizeof dev_name, dm_name, count - 1);
+ dm_device_remove_byname(dev_name);
+ }
+
+ return;
+}
+
+static void bl_dm_remove_tree(uint64_t dev)
+{
+ struct bl_dm_tree *p;
+
+ p = find_bl_dm_tree(dev);
+ if (!p)
+ return;
+
+ dm_tree_free(p->tree);
+ del_from_bl_dm_tree(dev);
+}
+
+static int bl_dm_create_tree(uint64_t dev)
+{
+ struct dm_tree *tree;
+ struct bl_dm_tree *bl_tree;
+
+ bl_tree = find_bl_dm_tree(dev);
+ if (bl_tree)
+ return 1;
+
+ tree = dm_tree_create();
+ if (!tree)
+ return 0;
+
+ if (!dm_tree_add_dev(tree, MAJOR(dev), MINOR(dev))) {
+ dm_tree_free(tree);
+ return 0;
+ }
+
+ bl_tree = malloc(sizeof(struct bl_dm_tree));
+ if (!bl_tree) {
+ dm_tree_free(tree);
+ return 0;
+ }
+
+ bl_tree->dev = dev;
+ bl_tree->tree = tree;
+ bl_tree->next = NULL;
+ add_to_bl_dm_tree(bl_tree);
+
+ return 1;
+}
+
+int dm_device_remove_all(uint64_t *dev)
+{
+ struct bl_dm_tree *p;
+ struct dm_tree_node *node;
+ const char *uuid;
+ int ret = 0;
+ uint32_t major, minor;
+ uint64_t bl_dev;
+
+ memcpy(&major, dev, sizeof(uint32_t));
+ memcpy(&minor, (void *)dev + sizeof(uint32_t), sizeof(uint32_t));
+ bl_dev = MKDEV(major, minor);
+ p = find_bl_dm_tree(bl_dev);
+ if (!p)
+ return ret;
+
+ node = dm_tree_find_node(p->tree, MAJOR(bl_dev), MINOR(bl_dev));
+ if (!node)
+ return ret;
+
+ uuid = dm_tree_node_get_uuid(node);
+ if (!uuid)
+ return ret;
+
+ dm_device_remove(bl_dev);
+ ret = dm_tree_deactivate_children(node, uuid, strlen(uuid));
+ dm_task_update_nodes();
+ bl_dm_remove_tree(bl_dev);
+
+ return ret;
+}
+
+static int dm_device_exists(char *dev_name)
+{
+ char fullname[DM_DEV_NAME_LEN];
+
+ snprintf(fullname, sizeof fullname, "/dev/mapper/%s", dev_name);
+ return (access(fullname, F_OK) >= 0);
+}
+
+/* TODO: check the value for DM_DEV_NAME_LEN, DM_TYPE_LEN, DM_PARAMS_LEN */
+uint64_t dm_device_create(struct bl_volume *vols, int num_vols)
+{
+ uint64_t size, stripe_unit, dev = 0;
+ unsigned int count = dev_count;
+ int volnum, i, pos;
+ struct bl_volume *node;
+ char *tmp;
+ struct bl_dm_table *table = NULL;
+ struct bl_dm_table *bl_table_head = NULL;
+ unsigned int len;
+ char *dev_name = NULL;
+
+ /* Create pseudo device here */
+ for (volnum = 0; volnum < num_vols; volnum++) {
+ node = &vols[volnum];
+ switch (node->bv_type) {
+ case BLOCK_VOLUME_SIMPLE:
+ /* Do not need to create device here */
+ dev = node->param.bv_dev;
+ goto continued;
+ case BLOCK_VOLUME_SLICE:
+ table = bl_dm_table_alloc();
+ if (!table)
+ goto out;
+ table->offset = 0;
+ table->size = node->bv_size;
+ strcpy(table->target_type, "linear");
+ if (!TYPE_HAS_DEV(node->bv_vols[0]->bv_type)) {
+ free(table);
+ goto out;
+ }
+ dev = node->bv_vols[0]->param.bv_dev;
+ tmp = table->params;
+ BL_LOG_INFO("%s: major %llu minor %llu", __func__,
+ (long long unsigned)MAJOR(dev),
+ (long long unsigned)MINOR(dev));
+ if (!dm_format_dev(tmp, DM_PARAMS_LEN,
+ MAJOR(dev), MINOR(dev))) {
+ free(table);
+ goto out;
+ }
+ tmp += strlen(tmp);
+ sprintf(tmp, " %llu",
+ (long long unsigned)node->param.bv_offset);
+ add_to_bl_dm_table(&bl_table_head, table);
+ break;
+ case BLOCK_VOLUME_STRIPE:
+ table = bl_dm_table_alloc();
+ if (!table)
+ goto out;
+ table->offset = 0;
+ /* Truncate size to a stripe unit boundary */
+ stripe_unit = node->param.bv_stripe_unit;
+ table->size =
+ node->bv_size - (node->bv_size % stripe_unit);
+ strcpy(table->target_type, "striped");
+ sprintf(table->params, "%d %llu %n", node->bv_vol_n,
+ (long long unsigned) stripe_unit, &pos);
+ /* Copy subdev major:minor to params */
+ tmp = table->params + pos;
+ len = DM_PARAMS_LEN - pos;
+ for (i = 0; i < node->bv_vol_n; i++) {
+ if (!TYPE_HAS_DEV(node->bv_vols[i]->bv_type)) {
+ free(table);
+ goto out;
+ }
+ dev = node->bv_vols[i]->param.bv_dev;
+ if (!dm_format_dev(tmp, len, MAJOR(dev),
+ MINOR(dev))) {
+ free(table);
+ goto out;
+ }
+ pos = strlen(tmp);
+ tmp += pos;
+ len -= pos;
+ sprintf(tmp, " %d ", 0);
+ tmp += 3;
+ len -= 3;
+ }
+ add_to_bl_dm_table(&bl_table_head, table);
+ break;
+ case BLOCK_VOLUME_CONCAT:
+ size = 0;
+ for (i = 0; i < node->bv_vol_n; i++) {
+ table = bl_dm_table_alloc();
+ if (!table)
+ goto out;
+ table->offset = size;
+ table->size = node->bv_vols[i]->bv_size;
+ if (!TYPE_HAS_DEV(node->bv_vols[i]->bv_type)) {
+ free(table);
+ goto out;
+ }
+ strcpy(table->target_type, "linear");
+ tmp = table->params;
+ dev = node->bv_vols[i]->param.bv_dev;
+ BL_LOG_INFO("%s: major %lu minor %lu", __func__,
+ (long unsigned int)MAJOR(dev),
+ (long unsigned int)MINOR(dev));
+ if (!dm_format_dev(tmp, DM_PARAMS_LEN,
+ MAJOR(dev), MINOR(dev))) {
+ free(table);
+ goto out;
+ }
+ tmp += strlen(tmp);
+ sprintf(tmp, " %d", 0);
+ size += table->size;
+ add_to_bl_dm_table(&bl_table_head, table);
+ }
+ break;
+ default:
+ /* Delete previous temporary devices */
+ dm_devicelist_remove(count, dev_count);
+ goto out;
+ } /* end of swtich */
+ /* Create dev_name here. Name of device is pnfs_vol_XXX */
+ if (dev_name)
+ free(dev_name);
+ dev_name = (char *)calloc(DM_DEV_NAME_LEN, sizeof(char));
+ if (!dev_name) {
+ BL_LOG_ERR("%s: Out of memory\n", __func__);
+ goto out;
+ }
+ do {
+ snprintf(dev_name, DM_DEV_NAME_LEN, dm_name,
+ dev_count++);
+ } while (dm_device_exists(dev_name));
+
+ dev = dm_device_create_mapped(dev_name, bl_table_head);
+ BL_LOG_INFO("%s: %d %s %d:%d\n", __func__, volnum, dev_name,
+ (int) MAJOR(dev), (int) MINOR(dev));
+ if (!dev) {
+ /* Delete previous temporary devices */
+ dm_devicelist_remove(count, dev_count);
+ goto out;
+ }
+ node->param.bv_dev = dev;
+ /* TODO: extend use with PSEUDO later */
+ node->bv_type = BLOCK_VOLUME_PSEUDO;
+
+ continued:
+ if (bl_table_head)
+ bl_dm_table_free(bl_table_head);
+ bl_table_head = NULL;
+ }
+ out:
+ if (bl_table_head) {
+ bl_dm_table_free(bl_table_head);
+ bl_table_head = NULL;
+ }
+ if (dev)
+ bl_dm_create_tree(dev);
+ if (dev_name)
+ free(dev_name);
+ return dev;
+}
diff --git a/utils/exportd/Makefile.am b/utils/exportd/Makefile.am
new file mode 100644
index 0000000..26078c9
--- /dev/null
+++ b/utils/exportd/Makefile.am
@@ -0,0 +1,67 @@
+## Process this file with automake to produce Makefile.in
+
+OPTLIBS =
+if CONFIG_JUNCTION
+OPTLIBS += ../../support/junction/libjunction.la $(LIBXML2)
+endif
+
+man8_MANS = exportd.man
+EXTRA_DIST = $(man8_MANS)
+
+NFSPREFIX = nfsv4.
+KPREFIX = @kprefix@
+sbin_PROGRAMS = exportd
+
+exportd_SOURCES = exportd.c
+exportd_LDADD = ../../support/export/libexport.a \
+ ../../support/nfs/libnfs.la \
+ ../../support/misc/libmisc.a \
+ ../../support/reexport/libreexport.a \
+ $(OPTLIBS) $(LIBBLKID) $(LIBPTHREAD) \
+ -luuid
+
+exportd_CPPFLAGS = $(AM_CPPFLAGS) $(CPPFLAGS) \
+ -I$(top_srcdir)/support/export
+
+MAINTAINERCLEANFILES = Makefile.in
+
+#######################################################################
+# The following allows the current practice of having
+# daemons renamed during the install to include NFSPREFIX
+# and the KPREFIX
+# This could all be done much easier with program_transform_name
+# ( program_transform_name = s/^/$(NFSPREFIX)$(KPREFIX)/ )
+# but that also renames the man pages, which the current
+# practice does not do.
+install-exec-hook:
+ (cd $(DESTDIR)$(sbindir) && \
+ for p in $(sbin_PROGRAMS); do \
+ mv -f $$p$(EXEEXT) $(NFSPREFIX)$(KPREFIX)$$p$(EXEEXT) ;\
+ done)
+uninstall-hook:
+ (cd $(DESTDIR)$(sbindir) && \
+ for p in $(sbin_PROGRAMS); do \
+ rm -f $(NFSPREFIX)$(KPREFIX)$$p$(EXEEXT) ;\
+ done)
+
+
+# XXX This makes some assumptions about what automake does.
+# XXX But there is no install-man-hook or install-man-local.
+install-man: install-man8 install-man-links
+uninstall-man: uninstall-man8 uninstall-man-links
+
+install-man-links:
+ (cd $(DESTDIR)$(man8dir) && \
+ for m in $(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS); do \
+ inst=`echo $$m | sed -e 's/man$$/8/'`; \
+ rm -f $(NFSPREFIX)$$inst ; \
+ $(LN_S) $$inst $(NFSPREFIX)$$inst ; \
+ done)
+
+uninstall-man-links:
+ (cd $(DESTDIR)$(man8dir) && \
+ for m in $(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS); do \
+ inst=`echo $$m | sed -e 's/man$$/8/'`; \
+ rm -f $(NFSPREFIX)$$inst ; \
+ done)
+
diff --git a/utils/exportd/Makefile.in b/utils/exportd/Makefile.in
new file mode 100644
index 0000000..a5f0f63
--- /dev/null
+++ b/utils/exportd/Makefile.in
@@ -0,0 +1,880 @@
+# 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@
+@CONFIG_JUNCTION_TRUE@am__append_1 = ../../support/junction/libjunction.la $(LIBXML2)
+sbin_PROGRAMS = exportd$(EXEEXT)
+subdir = utils/exportd
+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 =
+am__installdirs = "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man8dir)"
+PROGRAMS = $(sbin_PROGRAMS)
+am_exportd_OBJECTS = exportd-exportd.$(OBJEXT)
+exportd_OBJECTS = $(am_exportd_OBJECTS)
+am__DEPENDENCIES_1 =
+@CONFIG_JUNCTION_TRUE@am__DEPENDENCIES_2 = \
+@CONFIG_JUNCTION_TRUE@ ../../support/junction/libjunction.la \
+@CONFIG_JUNCTION_TRUE@ $(am__DEPENDENCIES_1)
+am__DEPENDENCIES_3 = $(am__DEPENDENCIES_2)
+exportd_DEPENDENCIES = ../../support/export/libexport.a \
+ ../../support/nfs/libnfs.la ../../support/misc/libmisc.a \
+ ../../support/reexport/libreexport.a $(am__DEPENDENCIES_3) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+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 =
+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)/exportd-exportd.Po
+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 = $(exportd_SOURCES)
+DIST_SOURCES = $(exportd_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+man8dir = $(mandir)/man8
+NROFF = nroff
+MANS = $(man8_MANS)
+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@
+OPTLIBS = $(am__append_1)
+man8_MANS = exportd.man
+EXTRA_DIST = $(man8_MANS)
+NFSPREFIX = nfsv4.
+KPREFIX = @kprefix@
+exportd_SOURCES = exportd.c
+exportd_LDADD = ../../support/export/libexport.a \
+ ../../support/nfs/libnfs.la \
+ ../../support/misc/libmisc.a \
+ ../../support/reexport/libreexport.a \
+ $(OPTLIBS) $(LIBBLKID) $(LIBPTHREAD) \
+ -luuid
+
+exportd_CPPFLAGS = $(AM_CPPFLAGS) $(CPPFLAGS) \
+ -I$(top_srcdir)/support/export
+
+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 utils/exportd/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu utils/exportd/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):
+install-sbinPROGRAMS: $(sbin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(sbindir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-sbinPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(sbindir)" && rm -f $$files
+
+clean-sbinPROGRAMS:
+ @list='$(sbin_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+exportd$(EXEEXT): $(exportd_OBJECTS) $(exportd_DEPENDENCIES) $(EXTRA_exportd_DEPENDENCIES)
+ @rm -f exportd$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(exportd_OBJECTS) $(exportd_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/exportd-exportd.Po@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 $@ $<
+
+exportd-exportd.o: exportd.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(exportd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT exportd-exportd.o -MD -MP -MF $(DEPDIR)/exportd-exportd.Tpo -c -o exportd-exportd.o `test -f 'exportd.c' || echo '$(srcdir)/'`exportd.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/exportd-exportd.Tpo $(DEPDIR)/exportd-exportd.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='exportd.c' object='exportd-exportd.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(exportd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o exportd-exportd.o `test -f 'exportd.c' || echo '$(srcdir)/'`exportd.c
+
+exportd-exportd.obj: exportd.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(exportd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT exportd-exportd.obj -MD -MP -MF $(DEPDIR)/exportd-exportd.Tpo -c -o exportd-exportd.obj `if test -f 'exportd.c'; then $(CYGPATH_W) 'exportd.c'; else $(CYGPATH_W) '$(srcdir)/exportd.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/exportd-exportd.Tpo $(DEPDIR)/exportd-exportd.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='exportd.c' object='exportd-exportd.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(exportd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o exportd-exportd.obj `if test -f 'exportd.c'; then $(CYGPATH_W) 'exportd.c'; else $(CYGPATH_W) '$(srcdir)/exportd.c'; fi`
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-man8: $(man8_MANS)
+ @$(NORMAL_INSTALL)
+ @list1='$(man8_MANS)'; \
+ list2=''; \
+ test -n "$(man8dir)" \
+ && test -n "`echo $$list1$$list2`" \
+ || exit 0; \
+ echo " $(MKDIR_P) '$(DESTDIR)$(man8dir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(man8dir)" || exit 1; \
+ { for i in $$list1; do echo "$$i"; done; \
+ if test -n "$$list2"; then \
+ for i in $$list2; do echo "$$i"; done \
+ | sed -n '/\.8[a-z]*$$/p'; \
+ fi; \
+ } | while read p; do \
+ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; echo "$$p"; \
+ done | \
+ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \
+ sed 'N;N;s,\n, ,g' | { \
+ list=; while read file base inst; do \
+ if test "$$base" = "$$inst"; then list="$$list $$file"; else \
+ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man8dir)/$$inst'"; \
+ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man8dir)/$$inst" || exit $$?; \
+ fi; \
+ done; \
+ for i in $$list; do echo "$$i"; done | $(am__base_list) | \
+ while read files; do \
+ test -z "$$files" || { \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man8dir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(man8dir)" || exit $$?; }; \
+ done; }
+
+uninstall-man8:
+ @$(NORMAL_UNINSTALL)
+ @list='$(man8_MANS)'; test -n "$(man8dir)" || exit 0; \
+ files=`{ for i in $$list; do echo "$$i"; done; \
+ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
+ dir='$(DESTDIR)$(man8dir)'; $(am__uninstall_files_from_dir)
+
+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 $(PROGRAMS) $(MANS)
+installdirs:
+ for dir in "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man8dir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+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-sbinPROGRAMS \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/exportd-exportd.Po
+ -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-man
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-sbinPROGRAMS
+ @$(NORMAL_INSTALL)
+ $(MAKE) $(AM_MAKEFLAGS) install-exec-hook
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+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)/exportd-exportd.Po
+ -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: uninstall-man uninstall-sbinPROGRAMS
+ @$(NORMAL_INSTALL)
+ $(MAKE) $(AM_MAKEFLAGS) uninstall-hook
+.MAKE: install-am install-exec-am install-strip uninstall-am
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-sbinPROGRAMS 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-exec-hook install-html install-html-am \
+ install-info install-info-am install-man install-man8 \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-sbinPROGRAMS 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 uninstall-hook \
+ uninstall-man uninstall-man8 uninstall-sbinPROGRAMS
+
+.PRECIOUS: Makefile
+
+
+#######################################################################
+# The following allows the current practice of having
+# daemons renamed during the install to include NFSPREFIX
+# and the KPREFIX
+# This could all be done much easier with program_transform_name
+# ( program_transform_name = s/^/$(NFSPREFIX)$(KPREFIX)/ )
+# but that also renames the man pages, which the current
+# practice does not do.
+install-exec-hook:
+ (cd $(DESTDIR)$(sbindir) && \
+ for p in $(sbin_PROGRAMS); do \
+ mv -f $$p$(EXEEXT) $(NFSPREFIX)$(KPREFIX)$$p$(EXEEXT) ;\
+ done)
+uninstall-hook:
+ (cd $(DESTDIR)$(sbindir) && \
+ for p in $(sbin_PROGRAMS); do \
+ rm -f $(NFSPREFIX)$(KPREFIX)$$p$(EXEEXT) ;\
+ done)
+
+# XXX This makes some assumptions about what automake does.
+# XXX But there is no install-man-hook or install-man-local.
+install-man: install-man8 install-man-links
+uninstall-man: uninstall-man8 uninstall-man-links
+
+install-man-links:
+ (cd $(DESTDIR)$(man8dir) && \
+ for m in $(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS); do \
+ inst=`echo $$m | sed -e 's/man$$/8/'`; \
+ rm -f $(NFSPREFIX)$$inst ; \
+ $(LN_S) $$inst $(NFSPREFIX)$$inst ; \
+ done)
+
+uninstall-man-links:
+ (cd $(DESTDIR)$(man8dir) && \
+ for m in $(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS); do \
+ inst=`echo $$m | sed -e 's/man$$/8/'`; \
+ rm -f $(NFSPREFIX)$$inst ; \
+ done)
+
+# 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/utils/exportd/exportd.c b/utils/exportd/exportd.c
new file mode 100644
index 0000000..a2e370a
--- /dev/null
+++ b/utils/exportd/exportd.c
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2021 Red Hat <nfs@redhat.com>
+ *
+ * support/exportd/exportd.c
+ *
+ * Routines used to support NFSv4 exports
+ *
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stddef.h>
+#include <unistd.h>
+#include <signal.h>
+#include <string.h>
+#include <getopt.h>
+#include <errno.h>
+
+#include "nfslib.h"
+#include "conffile.h"
+#include "exportfs.h"
+#include "export.h"
+
+extern void my_svc_run(void);
+
+/* Number of mountd threads to start. Default is 1 and
+ * that's probably enough unless you need hundreds of
+ * clients to be able to mount at once. */
+static int num_threads = 1;
+/* Arbitrary limit on number of threads */
+#define MAX_THREADS 64
+
+int manage_gids;
+int use_ipaddr = -1;
+
+static struct option longopts[] =
+{
+ { "foreground", 0, 0, 'F' },
+ { "debug", 1, 0, 'd' },
+ { "help", 0, 0, 'h' },
+ { "manage-gids", 0, 0, 'g' },
+ { "num-threads", 1, 0, 't' },
+ { "log-auth", 0, 0, 'l' },
+ { "cache-use-ipaddr", 0, 0, 'i' },
+ { "ttl", 0, 0, 'T' },
+ { NULL, 0, 0, 0 }
+};
+static char shortopts[] = "d:fghs:t:liT:";
+
+/*
+ * Signal handlers.
+ */
+inline static void set_signals(void);
+
+inline void
+cleanup_lockfiles (void)
+{
+ unlink(etab.lockfn);
+}
+
+static void
+killer (int sig)
+{
+ if (num_threads > 1) {
+ /* play Kronos and eat our children */
+ kill(0, SIGTERM);
+ cache_wait_for_workers("exportd");
+ }
+ cleanup_lockfiles();
+ free_state_path_names(&etab);
+ xlog (L_NOTICE, "Caught signal %d, exiting.", sig);
+
+ exit(0);
+}
+
+static void
+sig_hup (int UNUSED(sig))
+{
+ /* don't exit on SIGHUP */
+ xlog (L_NOTICE, "Received SIGHUP... Ignoring.\n");
+ return;
+}
+
+inline static void
+set_signals(void)
+{
+ struct sigaction sa;
+
+ sa.sa_handler = SIG_IGN;
+ sa.sa_flags = 0;
+ sigemptyset(&sa.sa_mask);
+ sigaction(SIGPIPE, &sa, NULL);
+ /* WARNING: the following works on Linux and SysV, but not BSD! */
+ sigaction(SIGCHLD, &sa, NULL);
+
+ sa.sa_handler = killer;
+ sigaction(SIGINT, &sa, NULL);
+ sigaction(SIGTERM, &sa, NULL);
+
+ sa.sa_handler = sig_hup;
+ sigaction(SIGHUP, &sa, NULL);
+}
+
+static void
+usage(const char *prog, int n)
+{
+ fprintf(stderr,
+ "Usage: %s [-f|--foreground] [-h|--help] [-d kind|--debug kind]\n"
+" [-g|--manage-gids] [-l|--log-auth] [-i|--cache-use-ipaddr] [-T|--ttl ttl]\n"
+" [-s|--state-directory-path path]\n"
+" [-t num|--num-threads=num]\n", prog);
+ exit(n);
+}
+
+inline static void
+read_exportd_conf(char *progname, char **argv)
+{
+ char *s;
+ int ttl;
+
+ conf_init_file(NFS_CONFFILE);
+
+ xlog_set_debug(progname);
+
+ manage_gids = conf_get_bool("exportd", "manage-gids", manage_gids);
+ num_threads = conf_get_num("exportd", "threads", num_threads);
+ if (conf_get_bool("mountd", "cache-use-ipaddr", 0))
+ use_ipaddr = 2;
+
+ s = conf_get_str("exportd", "state-directory-path");
+ if (s && !state_setup_basedir(argv[0], s))
+ exit(1);
+
+ ttl = conf_get_num("mountd", "ttl", default_ttl);
+ if (ttl > 0)
+ default_ttl = ttl;
+}
+
+int
+main(int argc, char **argv)
+{
+ char *progname;
+ int foreground = 0;
+ int c;
+ int ttl;
+
+ /* Set the basename */
+ if ((progname = strrchr(argv[0], '/')) != NULL)
+ progname++;
+ else
+ progname = argv[0];
+
+ /* Initialize logging. */
+ xlog_open(progname);
+
+ /* Read in config setting */
+ read_exportd_conf(progname, argv);
+
+ while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != EOF) {
+ switch (c) {
+ case 'd':
+ xlog_sconfig(optarg, 1);
+ break;
+ case 'l':
+ xlog_sconfig("auth", 1);
+ break;
+ case 'f':
+ foreground++;
+ break;
+ case 'g':
+ manage_gids = 1;
+ break;
+ case 'h':
+ usage(progname, 0);
+ break;
+ case 'i':
+ use_ipaddr = 2;
+ break;
+ case 'T':
+ ttl = atoi(optarg);
+ if (ttl <= 0) {
+ fprintf(stderr, "%s: bad ttl number of seconds: %s\n",
+ argv[0], optarg);
+ usage(argv[0], 1);
+ }
+ default_ttl = ttl;
+ break;
+ case 's':
+ if (!state_setup_basedir(argv[0], optarg))
+ exit(1);
+ break;
+ case 't':
+ num_threads = atoi (optarg);
+ break;
+ case '?':
+ default:
+ usage(progname, 1);
+ }
+
+ }
+
+ if (!setup_state_path_names(progname, ETAB, ETABTMP, ETABLCK, &etab))
+ return 1;
+
+ if (!foreground)
+ xlog_stderr(0);
+
+ daemon_init(foreground);
+
+ set_signals();
+ daemon_ready();
+
+ /* silently bounds check num_threads */
+ if (foreground)
+ num_threads = 1;
+ else if (num_threads < 1)
+ num_threads = 1;
+ else if (num_threads > MAX_THREADS)
+ num_threads = MAX_THREADS;
+
+ /* Open cache channel files BEFORE forking so each upcall is
+ * only handled by one thread. Kernel provides locking for both
+ * read and write.
+ */
+ cache_open();
+
+ if (cache_fork_workers(progname, num_threads) == 0) {
+ /* We forked, waited, and now need to clean up */
+ cleanup_lockfiles();
+ free_state_path_names(&etab);
+ xlog(L_NOTICE, "%s: no more workers, exiting\n", progname);
+ exit(0);
+ }
+
+ v4clients_init();
+
+ /* Process incoming upcalls */
+ while (cache_process(NULL) >= 0)
+ ;
+
+ xlog(L_ERROR, "%s: process loop terminated unexpectedly(%m). Exiting...\n",
+ progname);
+
+ free_state_path_names(&etab);
+ exit(1);
+}
diff --git a/utils/exportd/exportd.man b/utils/exportd/exportd.man
new file mode 100644
index 0000000..fae434b
--- /dev/null
+++ b/utils/exportd/exportd.man
@@ -0,0 +1,141 @@
+.\"@(#)nfsv4.exportd.8"
+.\"
+.\" Copyright (C) 2021 Red Hat <nfs@redhat.com>
+.\"
+.TH nfsv4.exportd 8 "02 Feb 2021"
+.SH NAME
+nfsv4.exportd \- NFSv4 Server Mount Daemon
+.SH SYNOPSIS
+.BI "/usr/sbin/nfsv4.exportd [" options "]"
+.SH DESCRIPTION
+The
+.B nfsv4.exportd
+is used to manage NFSv4 exports.
+The NFS server
+.RI ( nfsd )
+maintains a cache of authentication and authorization information which
+is used to identify the source of each request, and then what access
+permissions that source has to any local filesystem. When required
+information is not found in the cache, the server sends a request to
+.B nfsv4.exportd
+to fill in the missing information.
+.B nfsv4.exportd
+uses a table of information stored in
+.B /var/lib/nfs/etab
+and maintained by
+.BR exportfs (8),
+possibly based on the contents of
+.BR exports (5),
+to respond to each request.
+.SH OPTIONS
+.TP
+.B \-d kind " or " \-\-debug kind
+Turn on debugging. Valid kinds are: all, auth, call, general and parse.
+.TP
+.BR \-l " or " \-\-log\-auth
+Enable logging of responses to authentication and access requests from
+nfsd. Each response is then cached by the kernel for 30 minutes (or as set by
+.B \-\-ttl
+below), and will be refreshed after 15 minutes (half the ttl time) if
+the relevant client remains active.
+Note that
+.B -l
+is equivalent to
+.B "-d auth"
+and so can be enabled in
+.B /etc/nfs.conf
+with
+.B "\[dq]debug = auth\[dq]"
+in the
+.B "[exportd]"
+section.
+.TP
+.BR \-i " or " \-\-cache\-use\-ipaddr
+Normally each client IP address is matched against each host identifier
+(name, wildcard, netgroup etc) found in
+.B /etc/exports
+and a combined identity is formed from all matching identifiers.
+Often many clients will map to the same combined identity so performing
+this mapping reduces the number of distinct access details that the
+kernel needs to store.
+Specifying the
+.B \-i
+option suppresses this mapping so that access to each filesystem is
+requested and cached separately for each client IP address. Doing this
+can increase the burden of updating the cache slightly, but can make the
+log messages produced by the
+.B -l
+option easier to read.
+.TP
+.B \-T " or " \-\-ttl
+Provide a time-to-live (TTL) for cached information given to the kernel.
+The kernel will normally request an update if the information is needed
+after half of this time has expired. Increasing the provided number,
+which is in seconds, reduces the rate of cache update requests, and this
+is particularly noticeable when these requests are logged with
+.BR \-l .
+However increasing also means that changes to hostname to address
+mappings can take longer to be noticed.
+The default TTL is 1800 (30 minutes).
+.TP
+.B \-F " or " \-\-foreground
+Run in foreground (do not daemonize)
+.TP
+.B \-h " or " \-\-help
+Display usage message.
+.TP
+.BR "\-t N" " or " "\-\-num\-threads=N " or " \-\-num\-threads N "
+This option specifies the number of worker threads that
+.B nfsv4.exports
+spawns. The default is 1 thread, which is probably enough. More
+threads are usually only needed for NFS servers which need to handle
+mount storms of hundreds of NFS mounts in a few seconds, or when
+your DNS server is slow or unreliable.
+.TP
+.BR \-g " or " \-\-manage-gids
+Accept requests from the kernel to map user id numbers into lists of
+group id numbers for use in access control. An NFS request will
+normally (except when using Kerberos or other cryptographic
+authentication) contain a user-id and a list of group-ids. Due to a
+limitation in the NFS protocol, at most 16 groups ids can be listed.
+If you use the
+.B \-g
+flag, then the list of group ids received from the client will be
+replaced by a list of group ids determined by an appropriate lookup on
+the server. Note that the 'primary' group id is not affected so a
+.B newgroup
+command on the client will still be effective. This function requires
+a Linux Kernel with version at least 2.6.21.
+.SH CONFIGURATION FILE
+Many of the options that can be set on the command line can also be
+controlled through values set in the
+.B [exportd]
+or, in some cases, the
+.B [nfsd]
+sections of the
+.I /etc/nfs.conf
+configuration file.
+Values recognized in the
+.B [exportd]
+section include
+.B cache\-use\-ipaddr ,
+.BR ttl ,
+.BR manage-gids ", and"
+.B debug
+which each have the same effect as the option with the same name.
+.SH FILES
+.TP 2.5i
+.I /etc/exports
+input file for
+.BR exportfs ,
+listing exports, export options, and access control lists
+.SH SEE ALSO
+.BR exportfs (8),
+.BR exports (5),
+.BR showmount (8),
+.BR nfs.conf (5),
+.BR firewall-cmd (1),
+.sp
+RFC 7530 - "Network File System (NFS) Version 4 Protocol"
+.br
+RFC 8881 - "Network File System (NFS) Version 4 Minor Version 1 Protocol"
diff --git a/utils/exportfs/Makefile.am b/utils/exportfs/Makefile.am
new file mode 100644
index 0000000..7f8ce9f
--- /dev/null
+++ b/utils/exportfs/Makefile.am
@@ -0,0 +1,18 @@
+## Process this file with automake to produce Makefile.in
+
+man5_MANS = exports.man
+man7_MANS = nfsd.man
+man8_MANS = exportfs.man
+
+EXTRA_DIST = $(man5_MANS) $(man7_MANS) $(man8_MANS)
+sbin_PROGRAMS = exportfs
+exportfs_SOURCES = exportfs.c
+exportfs_LDADD = ../../support/export/libexport.a \
+ ../../support/nfs/libnfs.la \
+ ../../support/misc/libmisc.a \
+ ../../support/reexport/libreexport.a \
+ $(LIBWRAP) $(LIBNSL) $(LIBPTHREAD)
+
+exportfs_CPPFLAGS = $(AM_CPPFLAGS) $(CPPFLAGS) -I$(top_srcdir)/support/reexport
+
+MAINTAINERCLEANFILES = Makefile.in
diff --git a/utils/exportfs/Makefile.in b/utils/exportfs/Makefile.in
new file mode 100644
index 0000000..3fd8dc0
--- /dev/null
+++ b/utils/exportfs/Makefile.in
@@ -0,0 +1,920 @@
+# 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@
+sbin_PROGRAMS = exportfs$(EXEEXT)
+subdir = utils/exportfs
+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 =
+am__installdirs = "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man5dir)" \
+ "$(DESTDIR)$(man7dir)" "$(DESTDIR)$(man8dir)"
+PROGRAMS = $(sbin_PROGRAMS)
+am_exportfs_OBJECTS = exportfs-exportfs.$(OBJEXT)
+exportfs_OBJECTS = $(am_exportfs_OBJECTS)
+am__DEPENDENCIES_1 =
+exportfs_DEPENDENCIES = ../../support/export/libexport.a \
+ ../../support/nfs/libnfs.la ../../support/misc/libmisc.a \
+ ../../support/reexport/libreexport.a $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+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 =
+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)/exportfs-exportfs.Po
+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 = $(exportfs_SOURCES)
+DIST_SOURCES = $(exportfs_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+man5dir = $(mandir)/man5
+man7dir = $(mandir)/man7
+man8dir = $(mandir)/man8
+NROFF = nroff
+MANS = $(man5_MANS) $(man7_MANS) $(man8_MANS)
+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@
+man5_MANS = exports.man
+man7_MANS = nfsd.man
+man8_MANS = exportfs.man
+EXTRA_DIST = $(man5_MANS) $(man7_MANS) $(man8_MANS)
+exportfs_SOURCES = exportfs.c
+exportfs_LDADD = ../../support/export/libexport.a \
+ ../../support/nfs/libnfs.la \
+ ../../support/misc/libmisc.a \
+ ../../support/reexport/libreexport.a \
+ $(LIBWRAP) $(LIBNSL) $(LIBPTHREAD)
+
+exportfs_CPPFLAGS = $(AM_CPPFLAGS) $(CPPFLAGS) -I$(top_srcdir)/support/reexport
+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 utils/exportfs/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu utils/exportfs/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):
+install-sbinPROGRAMS: $(sbin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(sbindir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-sbinPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(sbindir)" && rm -f $$files
+
+clean-sbinPROGRAMS:
+ @list='$(sbin_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+exportfs$(EXEEXT): $(exportfs_OBJECTS) $(exportfs_DEPENDENCIES) $(EXTRA_exportfs_DEPENDENCIES)
+ @rm -f exportfs$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(exportfs_OBJECTS) $(exportfs_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/exportfs-exportfs.Po@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 $@ $<
+
+exportfs-exportfs.o: exportfs.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(exportfs_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT exportfs-exportfs.o -MD -MP -MF $(DEPDIR)/exportfs-exportfs.Tpo -c -o exportfs-exportfs.o `test -f 'exportfs.c' || echo '$(srcdir)/'`exportfs.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/exportfs-exportfs.Tpo $(DEPDIR)/exportfs-exportfs.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='exportfs.c' object='exportfs-exportfs.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(exportfs_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o exportfs-exportfs.o `test -f 'exportfs.c' || echo '$(srcdir)/'`exportfs.c
+
+exportfs-exportfs.obj: exportfs.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(exportfs_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT exportfs-exportfs.obj -MD -MP -MF $(DEPDIR)/exportfs-exportfs.Tpo -c -o exportfs-exportfs.obj `if test -f 'exportfs.c'; then $(CYGPATH_W) 'exportfs.c'; else $(CYGPATH_W) '$(srcdir)/exportfs.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/exportfs-exportfs.Tpo $(DEPDIR)/exportfs-exportfs.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='exportfs.c' object='exportfs-exportfs.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(exportfs_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o exportfs-exportfs.obj `if test -f 'exportfs.c'; then $(CYGPATH_W) 'exportfs.c'; else $(CYGPATH_W) '$(srcdir)/exportfs.c'; fi`
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-man5: $(man5_MANS)
+ @$(NORMAL_INSTALL)
+ @list1='$(man5_MANS)'; \
+ list2=''; \
+ test -n "$(man5dir)" \
+ && test -n "`echo $$list1$$list2`" \
+ || exit 0; \
+ echo " $(MKDIR_P) '$(DESTDIR)$(man5dir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(man5dir)" || exit 1; \
+ { for i in $$list1; do echo "$$i"; done; \
+ if test -n "$$list2"; then \
+ for i in $$list2; do echo "$$i"; done \
+ | sed -n '/\.5[a-z]*$$/p'; \
+ fi; \
+ } | while read p; do \
+ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; echo "$$p"; \
+ done | \
+ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^5][0-9a-z]*$$,5,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \
+ sed 'N;N;s,\n, ,g' | { \
+ list=; while read file base inst; do \
+ if test "$$base" = "$$inst"; then list="$$list $$file"; else \
+ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man5dir)/$$inst'"; \
+ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man5dir)/$$inst" || exit $$?; \
+ fi; \
+ done; \
+ for i in $$list; do echo "$$i"; done | $(am__base_list) | \
+ while read files; do \
+ test -z "$$files" || { \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man5dir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(man5dir)" || exit $$?; }; \
+ done; }
+
+uninstall-man5:
+ @$(NORMAL_UNINSTALL)
+ @list='$(man5_MANS)'; test -n "$(man5dir)" || exit 0; \
+ files=`{ for i in $$list; do echo "$$i"; done; \
+ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^5][0-9a-z]*$$,5,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
+ dir='$(DESTDIR)$(man5dir)'; $(am__uninstall_files_from_dir)
+install-man7: $(man7_MANS)
+ @$(NORMAL_INSTALL)
+ @list1='$(man7_MANS)'; \
+ list2=''; \
+ test -n "$(man7dir)" \
+ && test -n "`echo $$list1$$list2`" \
+ || exit 0; \
+ echo " $(MKDIR_P) '$(DESTDIR)$(man7dir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(man7dir)" || exit 1; \
+ { for i in $$list1; do echo "$$i"; done; \
+ if test -n "$$list2"; then \
+ for i in $$list2; do echo "$$i"; done \
+ | sed -n '/\.7[a-z]*$$/p'; \
+ fi; \
+ } | while read p; do \
+ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; echo "$$p"; \
+ done | \
+ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^7][0-9a-z]*$$,7,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \
+ sed 'N;N;s,\n, ,g' | { \
+ list=; while read file base inst; do \
+ if test "$$base" = "$$inst"; then list="$$list $$file"; else \
+ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man7dir)/$$inst'"; \
+ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man7dir)/$$inst" || exit $$?; \
+ fi; \
+ done; \
+ for i in $$list; do echo "$$i"; done | $(am__base_list) | \
+ while read files; do \
+ test -z "$$files" || { \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man7dir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(man7dir)" || exit $$?; }; \
+ done; }
+
+uninstall-man7:
+ @$(NORMAL_UNINSTALL)
+ @list='$(man7_MANS)'; test -n "$(man7dir)" || exit 0; \
+ files=`{ for i in $$list; do echo "$$i"; done; \
+ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^7][0-9a-z]*$$,7,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
+ dir='$(DESTDIR)$(man7dir)'; $(am__uninstall_files_from_dir)
+install-man8: $(man8_MANS)
+ @$(NORMAL_INSTALL)
+ @list1='$(man8_MANS)'; \
+ list2=''; \
+ test -n "$(man8dir)" \
+ && test -n "`echo $$list1$$list2`" \
+ || exit 0; \
+ echo " $(MKDIR_P) '$(DESTDIR)$(man8dir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(man8dir)" || exit 1; \
+ { for i in $$list1; do echo "$$i"; done; \
+ if test -n "$$list2"; then \
+ for i in $$list2; do echo "$$i"; done \
+ | sed -n '/\.8[a-z]*$$/p'; \
+ fi; \
+ } | while read p; do \
+ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; echo "$$p"; \
+ done | \
+ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \
+ sed 'N;N;s,\n, ,g' | { \
+ list=; while read file base inst; do \
+ if test "$$base" = "$$inst"; then list="$$list $$file"; else \
+ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man8dir)/$$inst'"; \
+ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man8dir)/$$inst" || exit $$?; \
+ fi; \
+ done; \
+ for i in $$list; do echo "$$i"; done | $(am__base_list) | \
+ while read files; do \
+ test -z "$$files" || { \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man8dir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(man8dir)" || exit $$?; }; \
+ done; }
+
+uninstall-man8:
+ @$(NORMAL_UNINSTALL)
+ @list='$(man8_MANS)'; test -n "$(man8dir)" || exit 0; \
+ files=`{ for i in $$list; do echo "$$i"; done; \
+ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
+ dir='$(DESTDIR)$(man8dir)'; $(am__uninstall_files_from_dir)
+
+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 $(PROGRAMS) $(MANS)
+installdirs:
+ for dir in "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man5dir)" "$(DESTDIR)$(man7dir)" "$(DESTDIR)$(man8dir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+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-sbinPROGRAMS \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/exportfs-exportfs.Po
+ -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-man
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-sbinPROGRAMS
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man: install-man5 install-man7 install-man8
+
+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)/exportfs-exportfs.Po
+ -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: uninstall-man uninstall-sbinPROGRAMS
+
+uninstall-man: uninstall-man5 uninstall-man7 uninstall-man8
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-sbinPROGRAMS 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-man5 install-man7 \
+ install-man8 install-pdf install-pdf-am install-ps \
+ install-ps-am install-sbinPROGRAMS 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 uninstall-man \
+ uninstall-man5 uninstall-man7 uninstall-man8 \
+ uninstall-sbinPROGRAMS
+
+.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/utils/exportfs/exportfs.c b/utils/exportfs/exportfs.c
new file mode 100644
index 0000000..b03a047
--- /dev/null
+++ b/utils/exportfs/exportfs.c
@@ -0,0 +1,768 @@
+/*
+ * utils/exportfs/exportfs.c
+ *
+ * Export file systems to knfsd
+ *
+ * Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de>
+ *
+ * Extensive changes, 1999, Neil Brown <neilb@cse.unsw.edu.au>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/vfs.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <unistd.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <getopt.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <errno.h>
+#include <limits.h>
+#include <time.h>
+
+#define INT_TO_LONG_THRESHOLD_SECS (INT_MAX - (60 * 60 * 24))
+
+#include "sockaddr.h"
+#include "misc.h"
+#include "nfsd_path.h"
+#include "nfslib.h"
+#include "exportfs.h"
+#include "xlog.h"
+#include "conffile.h"
+#include "reexport.h"
+
+static void export_all(int verbose);
+static void exportfs(char *arg, char *options, int verbose);
+static void unexportfs(char *arg, int verbose);
+static void dump(int verbose, int export_format);
+static void usage(const char *progname, int n);
+static void validate_export(nfs_export *exp);
+static int matchhostname(const char *hostname1, const char *hostname2);
+static void grab_lockfile(void);
+static void release_lockfile(void);
+
+static const char *lockfile = EXP_LOCKFILE;
+static int _lockfd = -1;
+
+/*
+ * If we aren't careful, changes made by exportfs can be lost
+ * when multiple exports process run at once:
+ *
+ * exportfs process 1 exportfs process 2
+ * ------------------------------------------
+ * reads etab version A reads etab version A
+ * adds new export B adds new export C
+ * writes A+B writes A+C
+ *
+ * The locking in support/export/xtab.c will prevent mountd from
+ * seeing a partially written version of etab, and will prevent
+ * the two writers above from writing simultaneously and
+ * corrupting etab, but to prevent problems like the above we
+ * need these additional lockfile() routines.
+ */
+static void
+grab_lockfile(void)
+{
+ _lockfd = open(lockfile, O_CREAT|O_RDWR, 0666);
+ if (_lockfd != -1)
+ lockf(_lockfd, F_LOCK, 0);
+}
+static void
+release_lockfile(void)
+{
+ if (_lockfd != -1) {
+ lockf(_lockfd, F_ULOCK, 0);
+ close(_lockfd);
+ _lockfd = -1;
+ }
+}
+inline static void
+read_exportfs_conf(char **argv)
+{
+ char *s;
+
+ conf_init_file(NFS_CONFFILE);
+ xlog_set_debug("exportfs");
+
+ /* NOTE: following uses "mountd" section of nfs.conf !!!! */
+ s = conf_get_str("mountd", "state-directory-path");
+ /* Also look in the exportd section */
+ if (s == NULL)
+ s = conf_get_str("exportd", "state-directory-path");
+ if (s && !state_setup_basedir(argv[0], s))
+ exit(1);
+
+}
+int
+main(int argc, char **argv)
+{
+ char *options = NULL;
+ char *progname = NULL;
+ int f_export = 1;
+ int f_all = 0;
+ int f_verbose = 0;
+ int f_export_format = 0;
+ int f_reexport = 0;
+ int f_ignore = 0;
+ int i, c;
+ int force_flush = 0;
+
+ if ((progname = strrchr(argv[0], '/')) != NULL)
+ progname++;
+ else
+ progname = argv[0];
+
+ xlog_open(progname);
+ xlog_stderr(1);
+ xlog_syslog(0);
+
+ /* Read in config setting */
+ read_exportfs_conf(argv);
+
+ nfsd_path_init();
+
+ while ((c = getopt(argc, argv, "ad:fhio:ruvs")) != EOF) {
+ switch(c) {
+ case 'a':
+ f_all = 1;
+ break;
+ case 'd':
+ xlog_sconfig(optarg, 1);
+ break;
+ case 'f':
+ force_flush = 1;
+ break;
+ case 'h':
+ usage(progname, 0);
+ break;
+ case 'i':
+ f_ignore = 1;
+ break;
+ case 'o':
+ options = optarg;
+ break;
+ case 'r':
+ f_reexport = 1;
+ f_all = 1;
+ break;
+ case 'u':
+ f_export = 0;
+ break;
+ case 'v':
+ f_verbose = 1;
+ break;
+ case 's':
+ f_export_format = 1;
+ break;
+ default:
+ usage(progname, 1);
+ break;
+ }
+ }
+
+ if (optind != argc && f_all) {
+ xlog(L_ERROR, "extra arguments are not permitted with -a or -r");
+ return 1;
+ }
+ if (f_ignore && (f_all || ! f_export)) {
+ xlog(L_ERROR, "-i not meaningful with -a, -r or -u");
+ return 1;
+ }
+ if (f_reexport && ! f_export) {
+ xlog(L_ERROR, "-r and -u are incompatible");
+ return 1;
+ }
+
+ if (!setup_state_path_names(progname, ETAB, ETABTMP, ETABLCK, &etab))
+ return 1;
+
+ if (optind == argc && ! f_all) {
+ if (force_flush) {
+ cache_flush();
+ free_state_path_names(&etab);
+ return 0;
+ } else {
+ xtab_export_read();
+ dump(f_verbose, f_export_format);
+ free_state_path_names(&etab);
+ export_freeall();
+ return 0;
+ }
+ }
+
+ /*
+ * Serialize things as best we can
+ */
+ grab_lockfile();
+ atexit(release_lockfile);
+
+ if (f_export && ! f_ignore) {
+ if (! (export_read(_PATH_EXPORTS, 0) +
+ export_d_read(_PATH_EXPORTS_D, 0))) {
+ if (f_verbose)
+ xlog(L_WARNING, "No file systems exported!");
+ }
+ }
+ if (f_export) {
+ if (f_all)
+ export_all(f_verbose);
+ else
+ for (i = optind; i < argc ; i++)
+ exportfs(argv[i], options, f_verbose);
+ }
+ /* If we are unexporting everything, then
+ * don't care about what should be exported, as that
+ * may require DNS lookups..
+ */
+ if (! ( !f_export && f_all)) {
+ /* note: xtab_*_read does not update entries if they already exist,
+ * so this will not lose new options
+ */
+ if (!f_reexport)
+ xtab_export_read();
+ if (!f_export)
+ for (i = optind ; i < argc ; i++)
+ unexportfs(argv[i], f_verbose);
+ }
+ xtab_export_write();
+ cache_flush();
+ free_state_path_names(&etab);
+ export_freeall();
+
+ return export_errno;
+}
+
+/*
+ * export_all finds all entries and
+ * marks them xtabent and mayexport so that they get exported
+ */
+static void
+export_all(int verbose)
+{
+ nfs_export *exp;
+ int i;
+
+ for (i = 0; i < MCL_MAXTYPES; i++) {
+ for (exp = exportlist[i].p_head; exp; exp = exp->m_next) {
+ if (verbose)
+ printf("exporting %s:%s\n",
+ exp->m_client->m_hostname,
+ exp->m_export.e_path);
+ exp->m_xtabent = 1;
+ exp->m_mayexport = 1;
+ exp->m_changed = 1;
+ exp->m_warned = 0;
+ validate_export(exp);
+ }
+ }
+}
+
+
+static void
+exportfs_parsed(char *hname, char *path, char *options, int verbose)
+{
+ struct exportent *eep;
+ nfs_export *exp = NULL;
+ struct addrinfo *ai = NULL;
+ int htype;
+
+ if ((htype = client_gettype(hname)) == MCL_FQDN) {
+ ai = host_addrinfo(hname);
+ if (ai != NULL) {
+ exp = export_find(ai, path);
+ hname = ai->ai_canonname;
+ }
+ } else
+ exp = export_lookup(hname, path, 0);
+
+ if (!exp) {
+ if (!(eep = mkexportent(hname, path, options)) ||
+ !(exp = export_create(eep, 0)))
+ goto out;
+ } else if (!updateexportent(&exp->m_export, options))
+ goto out;
+
+ if (verbose)
+ printf("exporting %s:%s\n", exp->m_client->m_hostname,
+ exp->m_export.e_path);
+ exp->m_xtabent = 1;
+ exp->m_mayexport = 1;
+ exp->m_changed = 1;
+ exp->m_warned = 0;
+ validate_export(exp);
+
+out:
+ nfs_freeaddrinfo(ai);
+}
+
+static int exportfs_generic(char *arg, char *options, int verbose)
+{
+ char *path;
+
+ if ((path = strchr(arg, ':')) != NULL)
+ *path++ = '\0';
+
+ if (!path || *path != '/')
+ return 1;
+
+ exportfs_parsed(arg, path, options, verbose);
+ return 0;
+}
+
+static int exportfs_ipv6(char *arg, char *options, int verbose)
+{
+ char *path, *c;
+
+ arg++;
+ c = strchr(arg, ']');
+ if (c == NULL)
+ return 1;
+
+ /* no colon means this is a wildcarded DNS hostname */
+ if (memchr(arg, ':', c - arg) == NULL)
+ return exportfs_generic(--arg, options, verbose);
+
+ path = strstr(c, ":/");
+ if (path == NULL)
+ return 1;
+ *path++ = '\0';
+
+ /* if there's anything between the closing brace and the
+ * path separator, it's probably a prefix length */
+ memmove(c, c + 1, path - c);
+
+ exportfs_parsed(arg, path, options, verbose);
+ return 0;
+}
+
+static void
+exportfs(char *arg, char *options, int verbose)
+{
+ int failed;
+
+ if (*arg == '[')
+ failed = exportfs_ipv6(arg, options, verbose);
+ else
+ failed = exportfs_generic(arg, options, verbose);
+ if (failed)
+ xlog(L_ERROR, "Invalid export syntax: %s", arg);
+}
+
+static void
+unexportfs_parsed(char *hname, char *path, int verbose)
+{
+ nfs_export *exp;
+ struct addrinfo *ai = NULL;
+ int htype;
+ int success = 0;
+
+ if ((htype = client_gettype(hname)) == MCL_FQDN) {
+ ai = host_addrinfo(hname);
+ if (ai)
+ hname = ai->ai_canonname;
+ }
+
+ /*
+ * It's possible the specified path ends with a '/'. But
+ * the entry from exportlist won't has the trailing '/',
+ * so need to deal with it.
+ */
+ size_t nlen = strlen(path);
+ while ((nlen > 1) && (path[nlen - 1] == '/'))
+ nlen--;
+
+ for (exp = exportlist[htype].p_head; exp; exp = exp->m_next) {
+ if (strlen(exp->m_export.e_path) != nlen)
+ continue;
+ if (path && strncmp(path, exp->m_export.e_path, nlen))
+ continue;
+ if (htype != exp->m_client->m_type)
+ continue;
+ if (htype == MCL_FQDN
+ && !matchhostname(exp->m_export.e_hostname,
+ hname))
+ continue;
+ if (htype != MCL_FQDN
+ && strcasecmp(exp->m_export.e_hostname, hname))
+ continue;
+ if (verbose) {
+#if 0
+ if (exp->m_exported) {
+ printf("unexporting %s:%s from kernel\n",
+ exp->m_client->m_hostname,
+ exp->m_export.e_path);
+ }
+ else
+#endif
+ printf("unexporting %s:%s\n",
+ exp->m_client->m_hostname,
+ exp->m_export.e_path);
+ }
+ exp->m_xtabent = 0;
+ exp->m_mayexport = 0;
+ success = 1;
+ }
+ if (!success)
+ xlog(L_ERROR, "Could not find '%s:%s' to unexport.", hname, path);
+
+ nfs_freeaddrinfo(ai);
+}
+
+static int unexportfs_generic(char *arg, int verbose)
+{
+ char *path;
+
+ if ((path = strchr(arg, ':')) != NULL)
+ *path++ = '\0';
+
+ if (!path || *path != '/')
+ return 1;
+
+ unexportfs_parsed(arg, path, verbose);
+ return 0;
+}
+
+static int unexportfs_ipv6(char *arg, int verbose)
+{
+ char *path, *c;
+
+ arg++;
+ c = strchr(arg, ']');
+ if (c == NULL)
+ return 1;
+
+ /* no colon means this is a wildcarded DNS hostname */
+ if (memchr(arg, ':', c - arg) == NULL)
+ return unexportfs_generic(--arg, verbose);
+
+ path = strstr(c, ":/");
+ if (path == NULL)
+ return 1;
+ *path++ = '\0';
+
+ /* if there's anything between the closing brace and the
+ * path separator, it's probably a prefix length */
+ memmove(c, c + 1, path - c);
+
+ unexportfs_parsed(arg, path, verbose);
+ return 0;
+}
+
+static void
+unexportfs(char *arg, int verbose)
+{
+ int failed;
+
+ if (*arg == '[')
+ failed = unexportfs_ipv6(arg, verbose);
+ else
+ failed = unexportfs_generic(arg, verbose);
+ if (failed)
+ xlog(L_ERROR, "Invalid export syntax: %s", arg);
+}
+
+static int can_test(void)
+{
+ char buf[1024] = { 0 };
+ int fd;
+ int n;
+ size_t bufsiz = sizeof(buf);
+
+ fd = open("/proc/net/rpc/auth.unix.ip/channel", O_WRONLY);
+ if (fd < 0)
+ return 0;
+
+ /*
+ * We introduce tolerance of 1 day to ensure that we use a
+ * LONG_MAX for the expiry timestamp before it is actually
+ * needed. To use LONG_MAX, the kernel code must have
+ * commit 2f74f972 (sunrpc: prepare NFS for 2038).
+ */
+ if (time(NULL) > INT_TO_LONG_THRESHOLD_SECS)
+ snprintf(buf, bufsiz-1, "nfsd 0.0.0.0 %ld -test-client-\n", LONG_MAX);
+ else
+ snprintf(buf, bufsiz-1, "nfsd 0.0.0.0 %d -test-client-\n", INT_MAX);
+
+ n = write(fd, buf, strlen(buf));
+ close(fd);
+ if (n < 0)
+ return 0;
+
+ fd = open("/proc/net/rpc/nfsd.export/channel", O_WRONLY);
+ if (fd < 0)
+ return 0;
+ close(fd);
+ return 1;
+}
+
+static void
+validate_export(nfs_export *exp)
+{
+ /* Check that the given export point is potentially exportable.
+ * We just give warnings here, don't cause anything to fail.
+ * If a path doesn't exist, or is not a dir or file, give an warning
+ * otherwise trial-export to '-test-client-' and check for failure.
+ */
+ struct stat stb;
+ char *path = exportent_realpath(&exp->m_export);
+ struct statfs stf;
+ int fs_has_fsid = 0;
+
+ if (stat(path, &stb) < 0) {
+ xlog(L_ERROR, "Failed to stat %s: %m", path);
+ return;
+ }
+ if (!S_ISDIR(stb.st_mode)) {
+ xlog(L_ERROR, "%s is not a directory. "
+ "Remote access will fail", path);
+ return;
+ }
+ if (!can_test())
+ return;
+
+ if (!statfs(path, &stf) &&
+ (stf.f_fsid.__val[0] || stf.f_fsid.__val[1]))
+ fs_has_fsid = 1;
+
+ if ((exp->m_export.e_flags & NFSEXP_FSID) || exp->m_export.e_uuid ||
+ fs_has_fsid) {
+ if ( !export_test(&exp->m_export, 1)) {
+ xlog(L_ERROR, "%s does not support NFS export", path);
+ return;
+ }
+ } else if ( !export_test(&exp->m_export, 0)) {
+ if (export_test(&exp->m_export, 1))
+ xlog(L_ERROR, "%s requires fsid= for NFS export", path);
+ else
+ xlog(L_ERROR, "%s does not support NFS export", path);
+ return;
+
+ }
+}
+
+static _Bool
+is_hostname(const char *sp)
+{
+ if (*sp == '\0' || *sp == '@')
+ return false;
+
+ for (; *sp != '\0'; sp++) {
+ if (*sp == '*' || *sp == '?' || *sp == '[' || *sp == '/')
+ return false;
+ if (*sp == '\\' && sp[1] != '\0')
+ sp++;
+ }
+
+ return true;
+}
+
+/*
+ * Take care to perform an explicit reverse lookup on presentation
+ * addresses. Otherwise we don't get a real canonical name or a
+ * complete list of addresses.
+ */
+static struct addrinfo *
+address_list(const char *hostname)
+{
+ struct addrinfo *ai;
+ char *cname;
+
+ ai = host_pton(hostname);
+ if (ai != NULL) {
+ /* @hostname was a presentation address */
+ cname = host_canonname(ai->ai_addr);
+ nfs_freeaddrinfo(ai);
+ if (cname != NULL)
+ goto out;
+ }
+ /* @hostname was a hostname or had no reverse mapping */
+ cname = strdup(hostname);
+ if (cname == NULL)
+ return NULL;
+
+out:
+ ai = host_addrinfo(cname);
+ free(cname);
+ return ai;
+}
+
+static int
+matchhostname(const char *hostname1, const char *hostname2)
+{
+ struct addrinfo *results1 = NULL, *results2 = NULL;
+ struct addrinfo *ai1, *ai2;
+ int result = 0;
+
+ if (strcasecmp(hostname1, hostname2) == 0)
+ return 1;
+
+ /*
+ * Don't pass export wildcards or netgroup names to DNS
+ */
+ if (!is_hostname(hostname1) || !is_hostname(hostname2))
+ return 0;
+
+ results1 = address_list(hostname1);
+ if (results1 == NULL)
+ goto out;
+ results2 = address_list(hostname2);
+ if (results2 == NULL)
+ goto out;
+
+ if (strcasecmp(results1->ai_canonname, results2->ai_canonname) == 0) {
+ result = 1;
+ goto out;
+ }
+
+ for (ai1 = results1; ai1 != NULL; ai1 = ai1->ai_next)
+ for (ai2 = results2; ai2 != NULL; ai2 = ai2->ai_next)
+ if (nfs_compare_sockaddr(ai1->ai_addr, ai2->ai_addr)) {
+ result = 1;
+ break;
+ }
+
+out:
+ nfs_freeaddrinfo(results1);
+ nfs_freeaddrinfo(results2);
+ return result;
+}
+
+#ifdef HAVE_FUNC_ATTRIBUTE_FORMAT
+__attribute__((format (printf, 2, 3)))
+#endif
+static char
+dumpopt(char c, char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ printf("%c", c);
+ vprintf(fmt, ap);
+ va_end(ap);
+ return ',';
+}
+
+static void
+dump(int verbose, int export_format)
+{
+ /* buf[] size should >= sizeof(struct exportent->e_path) */
+ char buf[NFS_MAXPATHLEN+1] = { 0 };
+ char *bp;
+ int len;
+ nfs_export *exp;
+ struct exportent *ep;
+ int htype;
+ char *hname, c;
+
+ for (htype = 0; htype < MCL_MAXTYPES; htype++) {
+ for (exp = exportlist[htype].p_head; exp; exp = exp->m_next) {
+ ep = &exp->m_export;
+ if (!exp->m_xtabent)
+ continue; /* neilb */
+ if (htype == MCL_ANONYMOUS)
+ hname = (export_format) ? "*" : "<world>";
+ else
+ hname = ep->e_hostname;
+ if (strlen(ep->e_path) > 14 && !export_format)
+ printf("%-14s\n\t\t%s", ep->e_path, hname);
+ else
+ if (export_format) {
+ bp = buf;
+ len = sizeof(buf) - 1;
+ qword_add(&bp, &len, ep->e_path);
+ *bp = '\0';
+ printf("%s %s", buf, hname);
+ } else {
+ printf("%-14s\t%s", ep->e_path, hname);
+ }
+
+ if (!verbose && !export_format) {
+ printf("\n");
+ continue;
+ }
+ c = '(';
+ if (ep->e_flags & NFSEXP_ASYNC)
+ c = dumpopt(c, "async");
+ else
+ c = dumpopt(c, "sync");
+ if (ep->e_flags & NFSEXP_GATHERED_WRITES)
+ c = dumpopt(c, "wdelay");
+ else
+ c = dumpopt(c, "no_wdelay");
+ if (ep->e_flags & NFSEXP_NOHIDE)
+ c = dumpopt(c, "nohide");
+ else
+ c = dumpopt(c, "hide");
+ if (ep->e_flags & NFSEXP_CROSSMOUNT)
+ c = dumpopt(c, "crossmnt");
+ if (ep->e_flags & NFSEXP_NOSUBTREECHECK)
+ c = dumpopt(c, "no_subtree_check");
+ if (ep->e_flags & NFSEXP_NOAUTHNLM)
+ c = dumpopt(c, "insecure_locks");
+ if (ep->e_flags & NFSEXP_NOREADDIRPLUS)
+ c = dumpopt(c, "nordirplus");
+ if (ep->e_flags & NFSEXP_SECURITY_LABEL)
+ c = dumpopt(c, "security_label");
+ if (ep->e_flags & NFSEXP_NOACL)
+ c = dumpopt(c, "no_acl");
+ if (ep->e_flags & NFSEXP_PNFS)
+ c = dumpopt(c, "pnfs");
+ if (ep->e_flags & NFSEXP_FSID)
+ c = dumpopt(c, "fsid=%d", ep->e_fsid);
+ if (ep->e_uuid)
+ c = dumpopt(c, "fsid=%s", ep->e_uuid);
+ if (ep->e_reexport) {
+ switch (ep->e_reexport) {
+ case REEXP_AUTO_FSIDNUM:
+ c = dumpopt(c, "reexport=%s", "auto-fsidnum");
+ break;
+ case REEXP_PREDEFINED_FSIDNUM:
+ c = dumpopt(c, "reexport=%s", "predefined-fsidnum");
+ break;
+ }
+ }
+ if (ep->e_mountpoint)
+ c = dumpopt(c, "mountpoint%s%s",
+ ep->e_mountpoint[0]?"=":"",
+ ep->e_mountpoint);
+ if (ep->e_anonuid != 65534)
+ c = dumpopt(c, "anonuid=%d", ep->e_anonuid);
+ if (ep->e_anongid != 65534)
+ c = dumpopt(c, "anongid=%d", ep->e_anongid);
+ switch(ep->e_fslocmethod) {
+ case FSLOC_NONE:
+ break;
+ case FSLOC_REFER:
+ c = dumpopt(c, "refer=%s", ep->e_fslocdata);
+ break;
+ case FSLOC_REPLICA:
+ c = dumpopt(c, "replicas=%s", ep->e_fslocdata);
+ break;
+#ifdef DEBUG
+ case FSLOC_STUB:
+ c = dumpopt(c, "fsloc=stub");
+ break;
+#endif
+ }
+ secinfo_show(stdout, ep);
+ xprtsecinfo_show(stdout, ep);
+ printf("%c\n", (c != '(')? ')' : ' ');
+ }
+ }
+}
+
+static void
+usage(const char *progname, int n)
+{
+ fprintf(stderr, "usage: %s [-adfhioruvs] [host:/path]\n", progname);
+ exit(n);
+}
diff --git a/utils/exportfs/exportfs.man b/utils/exportfs/exportfs.man
new file mode 100644
index 0000000..6d417a7
--- /dev/null
+++ b/utils/exportfs/exportfs.man
@@ -0,0 +1,337 @@
+.\"@(#)exportfs.8"
+.\"
+.\" Copyright (C) 1995 Olaf Kirch <okir@monad.swb.de>
+.\" Modifications 1999-2003 Neil Brown <neilb@cse.unsw.edu.au>
+.\"
+.TH exportfs 8 "30 September 2013"
+.SH NAME
+exportfs \- maintain table of exported NFS file systems
+.SH SYNOPSIS
+.BI "/usr/sbin/exportfs [-avi] [-o " "options,.." "] [" "client:/path" " ..]
+.br
+.BI "/usr/sbin/exportfs -r [-v]"
+.br
+.BI "/usr/sbin/exportfs [-av] -u [" "client:/path" " ..]
+.br
+.BI "/usr/sbin/exportfs [-v]
+.br
+.BI "/usr/sbin/exportfs -f"
+.br
+.BI "/usr/sbin/exportfs -s"
+.br
+.SH DESCRIPTION
+An NFS server maintains a table of local physical file systems
+that are accessible to NFS clients.
+Each file system in this table is referred to as an
+.IR "exported file system" ,
+or
+.IR export ,
+for short.
+.PP
+The
+.B exportfs
+command maintains the current table of exports for the NFS server.
+The master export table is kept in a file named
+.IR /var/lib/nfs/etab .
+This file is read by
+.B rpc.mountd
+when a client sends an NFS MOUNT request.
+.PP
+Normally the master export table is initialized with the contents of
+.I /etc/exports
+and files under
+.I /etc/exports.d
+by invoking
+.BR "exportfs -a" .
+However, a system administrator can choose to add or delete
+exports without modifying
+.I /etc/exports
+or files under
+.I /etc/exports.d
+by using the
+.B exportfs
+command.
+.PP
+.B exportfs
+and its partner program
+.B rpc.mountd
+work in one of two modes: a legacy mode which applies to 2.4 and
+earlier versions of the Linux kernel, and a new mode which applies to
+2.6 and later versions, providing the
+.B nfsd
+virtual filesystem has been mounted at
+.I /proc/fs/nfsd
+or
+.IR /proc/fs/nfs .
+On 2.6 kernels, if this filesystem is not mounted, the legacy mode is used.
+.PP
+In the new mode,
+.B exportfs
+does not give any information to the kernel, but provides it only to
+.B rpc.mountd
+through the
+.I /var/lib/nfs/etab
+file.
+.B rpc.mountd
+then manages kernel requests for information about exports, as needed.
+.PP
+In the legacy mode,
+exports which identify a specific host, rather than a subnet or netgroup,
+are entered directly into the kernel's export table,
+as well as being written to
+.IR /var/lib/nfs/etab .
+Further, exports listed in
+.I /var/lib/nfs/rmtab
+which match a non host-specific export request will cause an
+appropriate export entry for the host given in
+.I rmtab
+to be added to the kernel's export table.
+.SH OPTIONS
+.TP
+.B \-d kind " or " \-\-debug kind
+Turn on debugging. Valid kinds are: all, auth, call, general and parse.
+Debugging can also be turned on by setting
+.B debug=
+in the
+.B [exportfs]
+section of
+.IR /etc/nfs.conf .
+
+.TP
+.B -a
+Export or unexport all directories.
+.TP
+.BI "-o " options,...
+Specify a list of export options in the same manner as in
+.BR exports (5).
+.TP
+.B -i
+Ignore the
+.I /etc/exports
+file and files under
+.I /etc/exports.d
+directory. Only default options and options given on the command line are used.
+.TP
+.B -r
+Reexport all directories, synchronizing
+.I /var/lib/nfs/etab
+with
+.IR /etc/exports
+and files under
+.IR /etc/exports.d .
+This option removes entries in
+.I /var/lib/nfs/etab
+which have been deleted from
+.I /etc/exports
+or files under
+.IR /etc/exports.d ,
+and removes any entries from the
+kernel export table which are no longer valid.
+.TP
+.B -u
+Unexport one or more directories.
+.TP
+.B -f
+If
+.I /proc/fs/nfsd
+or
+.I /proc/fs/nfs
+is mounted, flush everything out of the kernel's export table.
+Fresh entries for active clients are added to the kernel's export table by
+.B rpc.mountd
+when they make their next NFS mount request.
+.TP
+.B -v
+Be verbose. When exporting or unexporting, show what's going on. When
+displaying the current export list, also display the list of export
+options.
+.TP
+.B -s
+Display the current export list suitable for /etc/exports.
+
+.SH CONFIGURATION FILE
+The
+.B [exportfs]
+section of the
+.I /etc/nfs.conf
+configuration file can contain a
+.B debug
+value, which can be one or more from the list
+.BR general ,
+.BR call ,
+.BR auth ,
+.BR parse ,
+.BR all .
+When a list is given, the members should be comma-separated.
+
+.B exportfs
+will also recognize the
+.B state-directory-path
+value from both the
+.B [mountd]
+section and the
+.B [exportd]
+section
+
+.SH DISCUSSION
+.SS Exporting Directories
+The first synopsis shows how to invoke
+.B exportfs
+when adding new entries to the export table. When using
+.BR "exportfs -a" ,
+all exports listed in
+.I /etc/exports
+and files under
+.I /etc/exports.d
+are added to
+.IR /var/lib/nfs/etab .
+The kernel's export table is also updated as needed.
+.PP
+The
+.I host:/path
+argument specifies a local directory to export,
+along with the client or clients who are permitted to access it.
+See
+.B exports(5)
+for a description of supported options and access list formats.
+.PP
+IPv6 presentation addresses contain colons, which are already used
+to separate the "host" and "path" command line arguments.
+When specifying a client using a raw IPv6 address,
+enclose the address in square brackets.
+For IPv6 network addresses, place the prefix just after the closing
+bracket.
+.PP
+To export a directory to the world, simply specify
+.IR :/path .
+.PP
+The export options for a particular host/directory pair derive from
+several sources.
+The default export options are
+.BR sync,ro,root_squash,wdelay .
+These can be overridden by entries in
+.IR /etc/exports
+or files under
+.IR /etc/exports.d .
+.PP
+A system administrator may override options from these sources using the
+.B -o
+command-line option on
+.BR exportfs .
+This option takes a comma-separated list of options in the same fashion
+as one would specify them in
+.IR /etc/exports .
+In this way
+.B exportfs
+can be used to modify the export options of an already exported directory.
+.SS Unexporting Directories
+The third synopsis shows how to unexport a currently exported directory.
+When using
+.BR "exportfs -ua" ,
+all entries listed in
+.I /var/lib/nfs/etab
+are removed from the kernel export tables, and the file is cleared. This
+effectively shuts down all NFS activity.
+.PP
+To remove an export, specify a
+.I host:/path
+pair. This deletes the specified entry from
+.I /var/lib/nfs/etab
+and removes the corresponding kernel entry (if any).
+.PP
+.SS Dumping the Export Table
+Invoking
+.B exportfs
+without options shows the current list of exported file systems.
+Adding the
+.B -v
+option causes
+.B exportfs
+to display the export options for each export.
+.SH EXAMPLES
+The following adds all directories listed in
+.I /etc/exports
+and files under
+.I /etc/exports.d
+to
+.I /var/lib/nfs/etab
+and pushes the resulting export entries into the kernel:
+.PP
+.nf
+.B "# exportfs -a
+.fi
+.PP
+To export the
+.I /usr/tmp
+directory to host
+.BR django ,
+allowing insecure file locking requests from clients:
+.PP
+.nf
+.B "# exportfs -o insecure_locks django:/usr/tmp
+.fi
+.PP
+To unexport the
+.I /usr/tmp
+directory:
+.PP
+.nf
+.B "# exportfs -u django:/usr/tmp
+.fi
+.PP
+To unexport all exports listed in
+.IR /etc/exports
+and files under
+.IR /etc/exports.d :
+.PP
+.nf
+.B "# exportfs -au
+.fi
+.PP
+To export the
+.I /usr/tmp
+directory to IPv6 link-local clients:
+.PP
+.nf
+.B "# exportfs [fe80::]/64:/usr/tmp
+.fi
+.SH USAGE NOTES
+Exporting to IP networks or DNS and NIS domains does not enable clients
+from these groups to access NFS immediately.
+Rather, these sorts of exports are hints to
+.BR rpc.mountd (8)
+to grant any mount requests from these clients.
+This is usually not a problem, because any existing mounts are preserved in
+.I rmtab
+across reboots.
+.PP
+When unexporting a network or domain entry, any current exports to members
+of this group will be checked against the remaining valid exports and
+if they themselves are no longer valid they will be removed.
+.SH FILES
+.TP 2.5i
+.I /etc/exports
+input file listing exports, export options, and access control lists
+.TP 2.5i
+.I /etc/exports.d
+directory where extra input files are stored.
+.B Note:
+only files that end with
+.I .exports
+are used.
+.TP 2.5i
+.I /var/lib/nfs/etab
+master table of exports
+.TP 2.5i
+.I /var/lib/nfs/rmtab
+table of clients accessing server's exports
+.SH SEE ALSO
+.BR exports (5),
+.BR nfs.conf (5),
+.BR rpc.mountd (8),
+.BR exportd (8),
+.BR netgroup (5)
+.SH AUTHORS
+Olaf Kirch <okir@monad.swb.de>
+.br
+Neil Brown <neilb@cse.unsw.edu.au>
diff --git a/utils/exportfs/exports.man b/utils/exportfs/exports.man
new file mode 100644
index 0000000..b758277
--- /dev/null
+++ b/utils/exportfs/exports.man
@@ -0,0 +1,678 @@
+.\"@(#)exports.5"
+.\"
+.TH exports 5 "31 December 2009"
+.SH NAME
+exports \- NFS server export table
+.SH DESCRIPTION
+The file
+.I /etc/exports
+contains a table of local physical file systems on an NFS server
+that are accessible to NFS clients.
+The contents of the file are maintained by the server's system
+administrator.
+.PP
+Each file system in this table has a list of options and an
+access control list.
+The table is used by
+.BR exportfs (8)
+to give information to
+.BR mountd (8).
+.PP
+The file format is similar to the SunOS
+.I exports
+file. Each line contains an export point and a whitespace-separated list
+of clients allowed to mount the file system at that point. Each listed
+client may be immediately followed by a parenthesized, comma-separated
+list of export options for that client. No whitespace is permitted
+between a client and its option list.
+.PP
+Also, each line may have one or more specifications for default options
+after the path name, in the form of a dash ("\-") followed by an option
+list. The option list is used for all subsequent exports on that line
+only.
+.PP
+Blank lines are ignored. A pound sign ("#") introduces a comment to the
+end of the line. Entries may be continued across newlines using a
+backslash. If an export name contains spaces it should be quoted using
+double quotes. You can also specify spaces or other unusual character in
+the export name using a backslash followed by the character code as three
+octal digits.
+.PP
+To apply changes to this file, run
+.BR "exportfs \-ra"
+or restart the NFS server.
+.PP
+.SS Machine Name Formats
+NFS clients may be specified in a number of ways:
+.IP "single host
+You may specify a host either by an
+abbreviated name recognized be the resolver, the fully qualified domain
+name, an IPv4 address, or an IPv6 address. IPv6 addresses must not be
+inside square brackets in /etc/exports lest they be confused with
+character-class wildcard matches.
+.IP "IP networks
+You can also export directories to all hosts on an IP (sub-) network
+simultaneously. This is done by specifying an IP address and netmask pair
+as
+.IR address/netmask
+where the netmask can be specified in dotted-decimal format, or as a
+contiguous mask length.
+For example, either `/255.255.252.0' or `/22' appended
+to the network base IPv4 address results in identical subnetworks with 10 bits
+of host. IPv6 addresses must use a contiguous mask length and must not be inside square brackets to avoid confusion with character-class wildcards. Wildcard characters generally do not work on IP addresses, though they
+may work by accident when reverse DNS lookups fail.
+.IP "wildcards
+Machine names may contain the wildcard characters \fI*\fR and \fI?\fR, or may contain character class lists within [square brackets].
+This can be used to make the \fIexports\fR file more compact; for instance,
+\fI*.cs.foo.edu\fR matches all hosts in the domain
+\fIcs.foo.edu\fR. As these characters also match the dots in a domain
+name, the given pattern will also match all hosts within any subdomain
+of \fIcs.foo.edu\fR.
+.IP "netgroups
+NIS netgroups may be given as
+.IR @group .
+Only the host part of each
+netgroup members is consider in checking for membership. Empty host
+parts or those containing a single dash (\-) are ignored.
+.IP "anonymous
+This is specified by a single
+.I *
+character (not to be confused with the
+.I wildcard
+entry above) and will match all clients.
+.\".TP
+.\".B =public
+.\"This is a special ``hostname'' that identifies the given directory name
+.\"as the public root directory (see the section on WebNFS in
+.\".BR nfsd (8)
+.\"for a discussion of WebNFS and the public root handle). When using this
+.\"convention,
+.\".B =public
+.\"must be the only entry on this line, and must have no export options
+.\"associated with it. Note that this does
+.\".I not
+.\"actually export the named directory; you still have to set the exports
+.\"options in a separate entry.
+.\".PP
+.\"The public root path can also be specified by invoking
+.\".I nfsd
+.\"with the
+.\".B \-\-public\-root
+.\"option. Multiple specifications of a public root will be ignored.
+.PP
+If a client matches more than one of the specifications above, then
+the first match from the above list order takes precedence - regardless of
+the order they appear on the export line. However, if a client matches
+more than one of the same type of specification (e.g. two netgroups),
+then the first match from the order they appear on the export line takes
+precedence.
+.SS RPCSEC_GSS security
+You may use the special strings "gss/krb5", "gss/krb5i", or "gss/krb5p"
+to restrict access to clients using rpcsec_gss security. However, this
+syntax is deprecated; on linux kernels since 2.6.23, you should instead
+use the "sec=" export option:
+.TP
+.IR sec=
+The sec= option, followed by a colon-delimited list of security flavors,
+restricts the export to clients using those flavors. Available security
+flavors include sys (the default--no cryptographic security), krb5
+(authentication only), krb5i (integrity protection), and krb5p (privacy
+protection). For the purposes of security flavor negotiation, order
+counts: preferred flavors should be listed first. The order of the sec=
+option with respect to the other options does not matter, unless you
+want some options to be enforced differently depending on flavor.
+In that case you may include multiple sec= options, and following options
+will be enforced only for access using flavors listed in the immediately
+preceding sec= option. The only options that are permitted to vary in
+this way are ro, rw, no_root_squash, root_squash, and all_squash.
+.SS Transport layer security
+The Linux NFS server allows the use of RPC-with-TLS (RFC 9289) to
+protect RPC traffic between itself and its clients.
+Alternately, administrators can secure NFS traffic using a VPN,
+or an ssh tunnel or similar mechanism, in a way that is transparent
+to the server.
+.PP
+To enable the use of RPC-with-TLS, the server's administrator must
+install and configure
+.BR tlshd
+to handle transport layer security handshake requests from the local
+kernel.
+Clients can then choose to use RPC-with-TLS or they may continue
+operating without it.
+.PP
+Administrators may require the use of RPC-with-TLS to protect access
+to individual exports.
+This is particularly useful when using non-cryptographic security
+flavors such as
+.IR sec=sys .
+The
+.I xprtsec=
+option, followed by an unordered colon-delimited list of security policies,
+can restrict access to the export to only clients that have negotiated
+transport-layer security.
+Currently supported transport layer security policies include:
+.TP
+.IR none
+The server permits clients to access the export
+without the use of transport layer security.
+.TP
+.IR tls
+The server permits clients that have negotiated an RPC-with-TLS session
+without peer authentication (confidentiality only) to access the export.
+Clients are not required to offer an x.509 certificate
+when establishing a transport layer security session.
+.TP
+.IR mtls
+The server permits clients that have negotiated an RPC-with-TLS session
+with peer authentication to access the export.
+The server requires clients to offer an x.509 certificate
+when establishing a transport layer security session.
+.PP
+If RPC-with-TLS is configured and enabled and the
+.I xprtsec=
+option is not specified, the default setting for an export is
+.IR xprtsec=none:tls:mtls .
+With this setting, the server permits clients to use any transport
+layer security mechanism or none at all to access the export.
+.SS General Options
+.BR exportfs
+understands the following export options:
+.TP
+.IR secure
+This option requires that requests not using gss originate on an
+Internet port less than IPPORT_RESERVED (1024). This option is on by default.
+To turn it off, specify
+.IR insecure .
+(NOTE: older kernels (before upstream kernel version 4.17) enforced this
+requirement on gss requests as well.)
+.TP
+.IR rw
+Allow both read and write requests on this NFS volume. The
+default is to disallow any request which changes the filesystem.
+This can also be made explicit by using
+the
+.IR ro " option.
+.TP
+.IR async
+This option allows the NFS server to violate the NFS protocol and
+reply to requests before any changes made by that request have been
+committed to stable storage (e.g. disc drive).
+
+Using this option usually improves performance, but at the cost that
+an unclean server restart (i.e. a crash) can cause data to be lost or
+corrupted.
+
+.TP
+.IR sync
+Reply to requests only after the changes have been committed to stable
+storage (see
+.IR async
+above).
+
+In releases of nfs-utils up to and including 1.0.0, the
+.I async
+option was the
+default. In all releases after 1.0.0,
+.I sync
+is the default, and
+.I async
+must be explicitly requested if needed.
+.TP
+.IR no_wdelay
+This option has no effect if
+.I async
+is also set. The NFS server will normally delay committing a write request
+to disc slightly if it suspects that another related write request may be in
+progress or may arrive soon. This allows multiple write requests to
+be committed to disc with the one operation which can improve
+performance. If an NFS server received mainly small unrelated
+requests, this behaviour could actually reduce performance, so
+.IR no_wdelay
+is available to turn it off.
+The default can be explicitly requested with the
+.IR wdelay " option.
+.TP
+.IR nohide
+This option is based on the option of the same name provided in IRIX
+NFS. Normally, if a server exports two filesystems one of which is
+mounted on the other, then the client will have to mount both
+filesystems explicitly to get access to them. If it just mounts the
+parent, it will see an empty directory at the place where the other
+filesystem is mounted. That filesystem is "hidden".
+
+Setting the
+.I nohide
+option on a filesystem causes it not to be hidden, and an
+appropriately authorised client will be able to move from the parent to
+that filesystem without noticing the change.
+
+However, some NFS clients do not cope well with this situation as, for
+instance, it is then possible for two files in the one apparent
+filesystem to have the same inode number.
+
+The
+.I nohide
+option is currently only effective on
+.I "single host
+exports. It does not work reliably with netgroup, subnet, or wildcard
+exports.
+
+This option can be very useful in some situations, but it should be
+used with due care, and only after confirming that the client system
+copes with the situation effectively.
+
+The option can be explicitly disabled for NFSv2 and NFSv3 with
+.IR hide .
+
+This option is not relevant when NFSv4 is use. NFSv4 never hides
+subordinate filesystems. Any filesystem that is exported will be
+visible where expected when using NFSv4.
+.TP
+.I crossmnt
+This option is similar to
+.I nohide
+but it makes it possible for clients to access all filesystems mounted
+on a filesystem marked with
+.IR crossmnt .
+Thus when a child filesystem "B" is mounted on a parent "A", setting
+crossmnt on "A" has a similar effect to setting "nohide" on B.
+
+With
+.I nohide
+the child filesystem needs to be explicitly exported. With
+.I crossmnt
+it need not. If a child of a
+.I crossmnt
+file is not explicitly exported, then it will be implicitly exported
+with the same export options as the parent, except for
+.IR fsid= .
+This makes it impossible to
+.B not
+export a child of a
+.I crossmnt
+filesystem. If some but not all subordinate filesystems of a parent
+are to be exported, then they must be explicitly exported and the
+parent should not have
+.I crossmnt
+set.
+
+The
+.I nocrossmnt
+option can explictly disable
+.I crossmnt
+if it was previously set. This is rarely useful.
+.TP
+.IR no_subtree_check
+This option disables subtree checking, which has mild security
+implications, but can improve reliability in some circumstances.
+
+If a subdirectory of a filesystem is exported, but the whole
+filesystem isn't then whenever a NFS request arrives, the server must
+check not only that the accessed file is in the appropriate filesystem
+(which is easy) but also that it is in the exported tree (which is
+harder). This check is called the
+.IR subtree_check .
+
+In order to perform this check, the server must include some
+information about the location of the file in the "filehandle" that is
+given to the client. This can cause problems with accessing files that
+are renamed while a client has them open (though in many simple cases
+it will still work).
+
+subtree checking is also used to make sure that files inside
+directories to which only root has access can only be accessed if the
+filesystem is exported with
+.I no_root_squash
+(see below), even if the file itself allows more general access.
+
+As a general guide, a home directory filesystem, which is normally
+exported at the root and may see lots of file renames, should be
+exported with subtree checking disabled. A filesystem which is mostly
+readonly, and at least doesn't see many file renames (e.g. /usr or
+/var) and for which subdirectories may be exported, should probably be
+exported with subtree checks enabled.
+
+The default of having subtree checks enabled, can be explicitly
+requested with
+.IR subtree_check .
+
+From release 1.1.0 of nfs-utils onwards, the default will be
+.I no_subtree_check
+as subtree_checking tends to cause more problems than it is worth.
+If you genuinely require subtree checking, you should explicitly put
+that option in the
+.B exports
+file. If you put neither option,
+.B exportfs
+will warn you that the change is pending.
+
+.TP
+.IR insecure_locks
+.TP
+.IR no_auth_nlm
+This option (the two names are synonymous) tells the NFS server not to require authentication of
+locking requests (i.e. requests which use the NLM protocol). Normally
+the NFS server will require a lock request to hold a credential for a
+user who has read access to the file. With this flag no access checks
+will be performed.
+
+Early NFS client implementations did not send credentials with lock
+requests, and many current NFS clients still exist which are based on
+the old implementations. Use this flag if you find that you can only
+lock files which are world readable.
+
+The default behaviour of requiring authentication for NLM requests can
+be explicitly requested with either of the synonymous
+.IR auth_nlm ,
+or
+.IR secure_locks .
+.\".TP
+.\".I noaccess
+.\"This makes everything below the directory inaccessible for the named
+.\"client. This is useful when you want to export a directory hierarchy to
+.\"a client, but exclude certain subdirectories. The client's view of a
+.\"directory flagged with noaccess is very limited; it is allowed to read
+.\"its attributes, and lookup `.' and `..'. These are also the only entries
+.\"returned by a readdir.
+.\".TP
+.\".IR link_relative
+.\"Convert absolute symbolic links (where the link contents start with a
+.\"slash) into relative links by prepending the necessary number of ../'s
+.\"to get from the directory containing the link to the root on the
+.\"server. This has subtle, perhaps questionable, semantics when the file
+.\"hierarchy is not mounted at its root.
+.\".TP
+.\".IR link_absolute
+.\"Leave all symbolic link as they are. This is the default operation.
+
+.TP
+.IR mountpoint= path
+.TP
+.I mp
+This option makes it possible to only export a directory if it has
+successfully been mounted.
+If no path is given (e.g.
+.IR mountpoint " or " mp )
+then the export point must also be a mount point. If it isn't then
+the export point is not exported. This allows you to be sure that the
+directory underneath a mountpoint will never be exported by accident
+if, for example, the filesystem failed to mount due to a disc error.
+
+If a path is given (e.g.
+.IR mountpoint= "/path or " mp= /path)
+then the nominated path must be a mountpoint for the exportpoint to be
+exported.
+
+.TP
+.IR fsid= num|root|uuid
+NFS needs to be able to identify each filesystem that it exports.
+Normally it will use a UUID for the filesystem (if the filesystem has
+such a thing) or the device number of the device holding the
+filesystem (if the filesystem is stored on the device).
+
+As not all filesystems are stored on devices, and not all filesystems
+have UUIDs, it is sometimes necessary to explicitly tell NFS how to
+identify a filesystem. This is done with the
+.I fsid=
+option.
+
+For NFSv4, there is a distinguished filesystem which is the root of
+all exported filesystem. This is specified with
+.I fsid=root
+or
+.I fsid=0
+both of which mean exactly the same thing.
+
+Other filesystems can be identified with a small integer, or a UUID
+which should contain 32 hex digits and arbitrary punctuation.
+
+Linux kernels version 2.6.20 and earlier do not understand the UUID
+setting so a small integer must be used if an fsid option needs to be
+set for such kernels. Setting both a small number and a UUID is
+supported so the same configuration can be made to work on old and new
+kernels alike.
+
+.TP
+.IR nordirplus
+This option will disable READDIRPLUS request handling. When set,
+READDIRPLUS requests from NFS clients return NFS3ERR_NOTSUPP, and
+clients fall back on READDIR. This option affects only NFSv3 clients.
+.TP
+.IR refer= path@host[+host][:path@host[+host]]
+A client referencing the export point will be directed to choose from
+the given list an alternative location for the filesystem.
+(Note that the server must have a mountpoint here, though a different
+filesystem is not required; so, for example,
+.IR "mount --bind" " /path /path"
+is sufficient.)
+.TP
+.IR replicas= path@host[+host][:path@host[+host]]
+If the client asks for alternative locations for the export point, it
+will be given this list of alternatives. (Note that actual replication
+of the filesystem must be handled elsewhere.)
+
+.TP
+.IR pnfs
+This option enables the use of the pNFS extension if the protocol level
+is NFSv4.1 or higher, and the filesystem supports pNFS exports. With
+pNFS clients can bypass the server and perform I/O directly to storage
+devices. The default can be explicitly requested with the
+.I no_pnfs
+option.
+
+.TP
+.IR security_label
+With this option set, clients using NFSv4.2 or higher will be able to
+set and retrieve security labels (such as those used by SELinux). This
+will only work if all clients use a consistent security policy. Note
+that early kernels did not support this export option, and instead
+enabled security labels by default.
+
+.TP
+.IR reexport= auto-fsidnum|predefined-fsidnum
+This option helps when a NFS share is re-exported. Since the NFS server
+needs a unique identifier for each exported filesystem and a NFS share
+cannot provide such, usually a manual fsid is needed.
+As soon
+.IR crossmnt
+is used manually assigning fsid won't work anymore. This is where this
+option becomes handy. It will automatically assign a numerical fsid
+to exported NFS shares. The fsid and path relations are stored in a SQLite
+database. If
+.IR auto-fsidnum
+is selected, the fsid is also autmatically allocated.
+.IR predefined-fsidnum
+assumes pre-allocated fsid numbers and will just look them up.
+This option depends also on the kernel, you will need at least kernel version
+5.19.
+Since
+.IR reexport=
+can automatically allocate and assign numerical fsids, it is no longer possible
+to have numerical fsids in other exports as soon this option is used in at least
+one export entry.
+
+The association between fsid numbers and paths is stored in a SQLite database.
+Don't edit or remove the database unless you know exactly what you're doing.
+.IR predefined-fsidnum
+is useful when you have used
+.IR auto-fsidnum
+before and don't want further entries stored.
+
+
+.SS User ID Mapping
+.PP
+.B nfsd
+bases its access control to files on the server machine on the uid and
+gid provided in each NFS RPC request. The normal behavior a user would
+expect is that she can access her files on the server just as she would
+on a normal file system. This requires that the same uids and gids are
+used on the client and the server machine. This is not always true, nor
+is it always desirable.
+.PP
+Very often, it is not desirable that the root user on a client machine
+is also treated as root when accessing files on the NFS server. To this
+end, uid 0 is normally mapped to a different id: the so-called
+anonymous or
+.I nobody
+uid. This mode of operation (called `root squashing') is the default,
+and can be turned off with
+.IR no_root_squash .
+.PP
+By default,
+.\".B nfsd
+.\"tries to obtain the anonymous uid and gid by looking up user
+.\".I nobody
+.\"in the password file at startup time. If it isn't found, a uid and gid
+.B exportfs
+chooses a uid and gid
+of 65534 for squashed access. These values can also be overridden by
+the
+.IR anonuid " and " anongid
+options.
+.\".PP
+.\"In addition to this,
+.\".B nfsd
+.\"lets you specify arbitrary uids and gids that should be mapped to user
+.\"nobody as well.
+Finally, you can map all user requests to the
+anonymous uid by specifying the
+.IR all_squash " option.
+.PP
+Here's the complete list of mapping options:
+.TP
+.IR root_squash
+Map requests from uid/gid 0 to the anonymous uid/gid. Note that this does
+not apply to any other uids or gids that might be equally sensitive, such as
+user
+.IR bin
+or group
+.IR staff .
+.TP
+.IR no_root_squash
+Turn off root squashing. This option is mainly useful for diskless clients.
+.TP
+.IR all_squash
+Map all uids and gids to the anonymous user. Useful for NFS-exported
+public FTP directories, news spool directories, etc. The opposite option
+is
+.IR no_all_squash ,
+which is the default setting.
+.TP
+.IR anonuid " and " anongid
+These options explicitly set the uid and gid of the anonymous account.
+This option is primarily useful for PC/NFS clients, where you might want
+all requests appear to be from one user. As an example, consider the
+export entry for
+.B /home/joe
+in the example section below, which maps all requests to uid 150 (which
+is supposedly that of user joe).
+
+.SS Subdirectory Exports
+
+Normally you should only export only the root of a filesystem. The NFS
+server will also allow you to export a subdirectory of a filesystem,
+however, this has drawbacks:
+
+First, it may be possible for a malicious user to access files on the
+filesystem outside of the exported subdirectory, by guessing filehandles
+for those other files. The only way to prevent this is by using the
+.IR no_subtree_check
+option, which can cause other problems.
+
+Second, export options may not be enforced in the way that you would
+expect. For example, the
+.IR security_label
+option will not work on subdirectory exports, and if nested subdirectory
+exports change the
+.IR security_label
+or
+.IR sec=
+options, NFSv4 clients will normally see only the options on the parent
+export. Also, where security options differ, a malicious client may use
+filehandle-guessing attacks to access the files from one subdirectory
+using the options from another.
+
+
+.SS Extra Export Tables
+After reading
+.I /etc/exports
+.B exportfs
+reads files in the
+.I /etc/exports.d
+directory as extra export tables. Only files ending in
+.I .exports
+are considered. Files beginning with a dot are ignored.
+The format for extra export tables is the same as
+.I /etc/exports
+.
+.IP
+.SH EXAMPLE
+.PP
+.nf
+.ta +3i
+# sample /etc/exports file
+/ master(rw) trusty(rw,no_root_squash)
+/projects proj*.local.domain(rw)
+/usr *.local.domain(ro) @trusted(rw)
+/home/joe pc001(rw,all_squash,anonuid=150,anongid=100)
+/pub *(ro,insecure,all_squash)
+/srv/www \-sync,rw server @trusted @external(ro)
+/foo 2001:db8:9:e54::/64(rw) 192.0.2.0/24(rw)
+/build buildhost[0-9].local.domain(rw)
+.\"/pub/private (noaccess)
+.fi
+.PP
+The first line exports the entire filesystem to machines master and trusty.
+In addition to write access, all uid squashing is turned off for host
+trusty. The second and third entry show examples for wildcard hostnames
+and netgroups (this is the entry `@trusted'). The fourth line shows the
+entry for the PC/NFS client discussed above. Line 5 exports the
+public FTP directory to every host in the world, executing all requests
+under the nobody account. The
+.I insecure
+option in this entry also allows clients with NFS implementations that
+don't use a reserved port for NFS.
+The sixth line exports a directory read-write to the machine 'server'
+as well as the `@trusted' netgroup, and read-only to netgroup `@external',
+all three mounts with the `sync' option enabled. The seventh line exports
+a directory to both an IPv6 and an IPv4 subnet. The eighth line demonstrates
+a character class wildcard match.
+.\" The last line denies all NFS clients
+.\"access to the private directory.
+.\".SH CAVEATS
+.\"Unlike other NFS server implementations, this
+.\".B nfsd
+.\"allows you to export both a directory and a subdirectory thereof to
+.\"the same host, for instance
+.\".IR /usr " and " /usr/X11R6 .
+.\"In this case, the mount options of the most specific entry apply. For
+.\"instance, when a user on the client host accesses a file in
+.\".IR /usr/X11R6 ,
+.\"the mount options given in the
+.\".I /usr/X11R6
+.\"entry apply. This is also true when the latter is a wildcard or netgroup
+.\"entry.
+.SH FILES
+/etc/exports
+/etc/exports.d
+.SH SEE ALSO
+.BR exportfs (8),
+.BR netgroup (5),
+.BR mountd (8),
+.BR nfsd (8),
+.BR showmount (8),
+.BR tlshd (8).
+.\".SH DIAGNOSTICS
+.\"An error parsing the file is reported using syslogd(8) as level NOTICE from
+.\"a DAEMON whenever
+.\".BR nfsd (8)
+.\"or
+.\".BR mountd (8)
+.\"is started up. Any unknown
+.\"host is reported at that time, but often not all hosts are not yet known
+.\"to
+.\".BR named (8)
+.\"at boot time, thus as hosts are found they are reported
+.\"with the same
+.\".BR syslogd (8)
+.\"parameters.
diff --git a/utils/exportfs/nfsd.man b/utils/exportfs/nfsd.man
new file mode 100644
index 0000000..514153f
--- /dev/null
+++ b/utils/exportfs/nfsd.man
@@ -0,0 +1,214 @@
+.\"
+.\" nfsd(7) - The nfsd filesystem
+.\"
+.\" Copyright (C) 2003 Neil Brown <neilb@cse.unsw.edu.au>
+.\" Licensed for public use under the terms of the FSF
+.\" General Public License (GPL) version 2.
+.TH nfsd 7 "3 July 2003"
+.SH NAME
+nfsd \- special filesystem for controlling Linux NFS server
+.SH SYNPOSIS
+.B "mount -t nfsd nfsd /proc/fs/nfsd"
+.SH DESCRIPTION
+The
+.B nfsd
+filesystem is a special filesystem which provides access to the Linux
+NFS server. Writing to files in this filesystem can affect the server.
+Reading from them can provide information about the server.
+.P
+As well as this filesystem, there are a collection of files in the
+.B procfs
+filesystem (normally mounted at
+.BR /proc )
+which are used to control the NFS server.
+This manual page describes all of these files.
+.P
+The
+.I exportfs
+and
+.I mountd
+programs (part of the nfs-utils package) expect to find this
+filesystem mounted at
+.B /proc/fs/nfsd
+or
+.BR /proc/fs/nfs .
+.SH DETAILS
+Files in the
+.B nfsd
+filesystem include:
+.TP
+.B exports
+This file contains a list of filesystems that are currently exported
+and clients that each filesystem is exported to, together with a list
+of export options for that client/filesystem pair. This is similar
+to the
+.B /proc/fs/nfs/exports
+file in 2.4.
+One difference is that a client doesn't necessarily correspond to just
+one host. It can respond to a large collection of hosts that are
+being treated identically.
+
+Each line of the file contains a path name, a client name, and a
+number of options in parentheses. Any space, tab, newline or
+back-slash character in the path name or client name will be replaced
+by a backslash followed by the octal ASCII code for that character.
+
+.TP
+.B threads
+This file represents the number of
+.B nfsd
+thread currently running. Reading it will show the number of
+threads. Writing an ASCII decimal number will cause the number of
+threads to be changed (increased or decreased as necessary) to achieve
+that number.
+
+.TP
+.B filehandle
+This is a somewhat unusual file in that what is read from it depends
+on what was just written to it. It provides a transactional interface
+where a program can open the file, write a request, and read a
+response. If two separate programs open, write, and read at the same
+time, their requests will not be mixed up.
+
+The request written to
+.B filehandle
+should be a client name, a path name, and a number of bytes. This
+should be followed by a newline, with white-space separating the
+fields, and octal quoting of special characters.
+
+On writing this, the program will be able to read back a filehandle
+for that path as exported to the given client. The filehandle's length
+will be at most the number of bytes given.
+
+The filehandle will be represented in hex with a leading '\ex'.
+
+.TP
+.B clients/
+This directory contains a subdirectory for each NFSv4 client. Each file
+under that subdirectory gives some details about the client in YAML
+format. In addition, writing "expire\\n" to the
+.B ctl
+file will force the server to immediately revoke all state held by that
+client.
+
+.PP
+The directory
+.B /proc/net/rpc
+in the
+.B procfs
+filesystem contains a number of files and directories.
+The files contain statistics that can be display using the
+.I nfsstat
+program.
+The directories contain information about various caches that the NFS
+server maintains to keep track of access permissions that different
+clients have for different filesystems.
+The caches are:
+
+.TP
+.B auth.unix.ip
+This cache contains a mapping from IP address to the name of the
+authentication domain that the ipaddress should be treated as part of.
+
+.TP
+.B nfsd.export
+This cache contains a mapping from directory and domain to export
+options.
+
+.TP
+.B nfsd.fh
+This cache contains a mapping from domain and a filesystem identifier
+to a directory. The filesystem identifier is stored in the
+filehandles and consists of a number indicating the type of identifier
+and a number of hex bytes indicating the content of the identifier.
+
+.PP
+Each directory representing a cache can hold from 1 to 3 files. They
+are:
+.TP
+.B flush
+When a number of seconds since epoch (1 Jan 1970) is written to this
+file, all entries in the cache that were last updated before that file
+become invalidated and will be flushed out. Writing a time in the
+future (in seconds since epoch) will flush
+everything. This is the only file that will always be present.
+
+.TP
+.B content
+This file, if present, contains a textual representation of ever entry
+in the cache, one per line. If an entry is still in the cache
+(because it is actively being used) but has expired or is otherwise
+invalid, it will be presented as a comment (with a leading hash
+character).
+
+.TP
+.B channel
+This file, if present, acts a channel for request from the kernel-based
+nfs server to be passed to a user-space program for handling.
+
+When the kernel needs some information which isn't in the cache, it
+makes a line appear in the
+.B channel
+file giving the key for the information. A user-space program should
+read this, find the answer, and write a line containing the key, an
+expiry time, and the content.
+For example the kernel might make
+.ti +5
+nfsd 127.0.0.1
+.br
+appear in the
+.B auth.unix.ip/content
+file. The user-space program might then write
+.ti +5
+nfsd 127.0.0.1 1057206953 localhost
+.br
+to indicate that 127.0.0.1 should map to localhost, at least for now.
+
+If the program uses select(2) or poll(2) to discover if it can read
+from the
+.B channel
+then it will never see and end-of-file but when all requests have been
+answered, it will block until another request appears.
+
+.PP
+In the
+.B /proc
+filesystem there are 4 files that can be used to enabled extra tracing
+of nfsd and related code. They are:
+.in +5
+.B /proc/sys/sunrpc/nfs_debug
+.br
+.B /proc/sys/sunrpc/nfsd_debug
+.br
+.B /proc/sys/sunrpc/nlm_debug
+.br
+.B /proc/sys/sunrpc/rpc_debug
+.br
+.in -5
+They control tracing for the NFS client, the NFS server, the Network
+Lock Manager (lockd) and the underlying RPC layer respectively.
+Decimal numbers can be read from or written to these files. Each
+number represents a bit-pattern where bits that are set cause certain
+classes of tracing to be enabled. Consult the kernel header files to
+find out what number correspond to what tracing.
+
+.SH NOTES
+This file system is only available in Linux 2.6 and later series
+kernels (and in the later parts of the 2.5 development series leading
+up to 2.6). This man page does not apply to 2.4 and earlier.
+.P
+Previously the nfsctl systemcall was used for communication between nfsd
+and user utilities. That systemcall was removed in kernel version 3.1.
+Older nfs-utils versions were able to fall back to nfsctl if necessary;
+that was removed from nfs-utils 1.3.5.
+
+.SH SEE ALSO
+.BR nfsd (8),
+.BR rpc.nfsd (8),
+.BR exports (5),
+.BR nfsstat (8),
+.BR mountd (8)
+.BR exportfs (8).
+
+.SH AUTHOR
+NeilBrown
diff --git a/utils/gssd/.gitignore b/utils/gssd/.gitignore
new file mode 100644
index 0000000..aba7b13
--- /dev/null
+++ b/utils/gssd/.gitignore
@@ -0,0 +1,3 @@
+gss_clnt_send_err
+gssd
+svcgssd
diff --git a/utils/gssd/Makefile.am b/utils/gssd/Makefile.am
new file mode 100644
index 0000000..21d3bb8
--- /dev/null
+++ b/utils/gssd/Makefile.am
@@ -0,0 +1,130 @@
+## Process this file with automake to produce Makefile.in
+
+man8_MANS = gssd.man
+if CONFIG_SVCGSS
+man8_MANS += svcgssd.man
+endif
+
+AM_CPPFLAGS += -I ../../support/nfsidmap
+
+RPCPREFIX = rpc.
+KPREFIX = @kprefix@
+sbin_PREFIXED = gssd
+if CONFIG_SVCGSS
+sbin_PREFIXED += svcgssd
+endif
+
+sbin_PROGRAMS = $(sbin_PREFIXED)
+
+EXTRA_DIST = \
+ $(man8_MANS)
+
+COMMON_SRCS = \
+ context.c \
+ context_mit.c \
+ context_heimdal.c \
+ context_lucid.c \
+ gss_util.c \
+ gss_oids.c \
+ gss_names.c \
+ err_util.c \
+ \
+ context.h \
+ err_util.h \
+ gss_oids.h \
+ gss_names.h \
+ gss_util.h
+
+gssd_SOURCES = \
+ $(COMMON_SRCS) \
+ gssd.c \
+ gssd_proc.c \
+ krb5_util.c \
+ \
+ gssd.h \
+ krb5_util.h \
+ write_bytes.h
+
+gssd_LDADD = \
+ ../../support/nfs/libnfs.la \
+ $(LIBEVENT) \
+ $(RPCSECGSS_LIBS) \
+ $(KRBLIBS) \
+ $(GSSAPI_LIBS) \
+ $(LIBTIRPC) \
+ $(LIBPTHREAD)
+
+gssd_LDFLAGS = \
+ $(KRBLDFLAGS)
+
+gssd_CFLAGS = \
+ $(AM_CFLAGS) \
+ $(CFLAGS) \
+ $(RPCSECGSS_CFLAGS) \
+ $(KRBCFLAGS) \
+ $(GSSAPI_CFLAGS)
+
+svcgssd_SOURCES = \
+ $(COMMON_SRCS) \
+ svcgssd.c \
+ svcgssd_mech2file.c \
+ svcgssd_proc.c \
+ svcgssd_krb5.c \
+ \
+ svcgssd_krb5.h \
+ svcgssd.h
+
+svcgssd_LDADD = \
+ ../../support/nfs/libnfs.la \
+ ../../support/nfsidmap/libnfsidmap.la \
+ $(LIBEVENT) \
+ $(RPCSECGSS_LIBS) \
+ $(KRBLIBS) $(GSSAPI_LIBS) $(LIBTIRPC)
+
+svcgssd_LDFLAGS = $(KRBLDFLAGS)
+
+svcgssd_CFLAGS = $(AM_CFLAGS) $(CFLAGS) \
+ $(RPCSECGSS_CFLAGS) $(KRBCFLAGS) $(GSSAPI_CFLAGS)
+
+MAINTAINERCLEANFILES = Makefile.in
+
+#######################################################################
+# The following allows the current practice of having
+# daemons renamed during the install to include RPCPREFIX
+# and the KPREFIX
+# This could all be done much easier with program_transform_name
+# ( program_transform_name = s/^/$(RPCPREFIX)$(KPREFIX)/ )
+# but that also renames the man pages, which the current
+# practice does not do.
+install-exec-hook:
+ (cd $(DESTDIR)$(sbindir) && \
+ for p in $(sbin_PREFIXED); do \
+ mv -f $$p$(EXEEXT) $(RPCPREFIX)$(KPREFIX)$$p$(EXEEXT) ;\
+ done)
+uninstall-hook:
+ (cd $(DESTDIR)$(sbindir) && \
+ for p in $(sbin_PREFIXED); do \
+ rm -f $(RPCPREFIX)$(KPREFIX)$$p$(EXEEXT) ;\
+ done)
+
+
+# XXX This makes some assumptions about what automake does.
+# XXX But there is no install-man-hook or install-man-local.
+install-man: install-man8 install-man-links
+uninstall-man: uninstall-man8 uninstall-man-links
+
+install-man-links:
+ (cd $(DESTDIR)$(man8dir) && \
+ for m in $(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS); do \
+ inst=`echo $$m | sed -e 's/man$$/8/'`; \
+ rm -f $(RPCPREFIX)$$inst ; \
+ $(LN_S) $$inst $(RPCPREFIX)$$inst ; \
+ done)
+
+uninstall-man-links:
+ (cd $(DESTDIR)$(man8dir) && \
+ for m in $(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS); do \
+ inst=`echo $$m | sed -e 's/man$$/8/'`; \
+ rm -f $(RPCPREFIX)$$inst ; \
+ done)
+
diff --git a/utils/gssd/Makefile.in b/utils/gssd/Makefile.in
new file mode 100644
index 0000000..75d69c3
--- /dev/null
+++ b/utils/gssd/Makefile.in
@@ -0,0 +1,1356 @@
+# 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@
+@CONFIG_SVCGSS_TRUE@am__append_1 = svcgssd.man
+@CONFIG_SVCGSS_TRUE@am__append_2 = svcgssd
+sbin_PROGRAMS = $(am__EXEEXT_2)
+subdir = utils/gssd
+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 =
+@CONFIG_SVCGSS_TRUE@am__EXEEXT_1 = svcgssd$(EXEEXT)
+am__EXEEXT_2 = gssd$(EXEEXT) $(am__EXEEXT_1)
+am__installdirs = "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man8dir)"
+PROGRAMS = $(sbin_PROGRAMS)
+am__objects_1 = gssd-context.$(OBJEXT) gssd-context_mit.$(OBJEXT) \
+ gssd-context_heimdal.$(OBJEXT) gssd-context_lucid.$(OBJEXT) \
+ gssd-gss_util.$(OBJEXT) gssd-gss_oids.$(OBJEXT) \
+ gssd-gss_names.$(OBJEXT) gssd-err_util.$(OBJEXT)
+am_gssd_OBJECTS = $(am__objects_1) gssd-gssd.$(OBJEXT) \
+ gssd-gssd_proc.$(OBJEXT) gssd-krb5_util.$(OBJEXT)
+gssd_OBJECTS = $(am_gssd_OBJECTS)
+am__DEPENDENCIES_1 =
+gssd_DEPENDENCIES = ../../support/nfs/libnfs.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+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 =
+gssd_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(gssd_CFLAGS) $(CFLAGS) \
+ $(gssd_LDFLAGS) $(LDFLAGS) -o $@
+am__objects_2 = svcgssd-context.$(OBJEXT) \
+ svcgssd-context_mit.$(OBJEXT) \
+ svcgssd-context_heimdal.$(OBJEXT) \
+ svcgssd-context_lucid.$(OBJEXT) svcgssd-gss_util.$(OBJEXT) \
+ svcgssd-gss_oids.$(OBJEXT) svcgssd-gss_names.$(OBJEXT) \
+ svcgssd-err_util.$(OBJEXT)
+am_svcgssd_OBJECTS = $(am__objects_2) svcgssd-svcgssd.$(OBJEXT) \
+ svcgssd-svcgssd_mech2file.$(OBJEXT) \
+ svcgssd-svcgssd_proc.$(OBJEXT) svcgssd-svcgssd_krb5.$(OBJEXT)
+svcgssd_OBJECTS = $(am_svcgssd_OBJECTS)
+svcgssd_DEPENDENCIES = ../../support/nfs/libnfs.la \
+ ../../support/nfsidmap/libnfsidmap.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+svcgssd_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(svcgssd_CFLAGS) \
+ $(CFLAGS) $(svcgssd_LDFLAGS) $(LDFLAGS) -o $@
+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)/gssd-context.Po \
+ ./$(DEPDIR)/gssd-context_heimdal.Po \
+ ./$(DEPDIR)/gssd-context_lucid.Po \
+ ./$(DEPDIR)/gssd-context_mit.Po ./$(DEPDIR)/gssd-err_util.Po \
+ ./$(DEPDIR)/gssd-gss_names.Po ./$(DEPDIR)/gssd-gss_oids.Po \
+ ./$(DEPDIR)/gssd-gss_util.Po ./$(DEPDIR)/gssd-gssd.Po \
+ ./$(DEPDIR)/gssd-gssd_proc.Po ./$(DEPDIR)/gssd-krb5_util.Po \
+ ./$(DEPDIR)/svcgssd-context.Po \
+ ./$(DEPDIR)/svcgssd-context_heimdal.Po \
+ ./$(DEPDIR)/svcgssd-context_lucid.Po \
+ ./$(DEPDIR)/svcgssd-context_mit.Po \
+ ./$(DEPDIR)/svcgssd-err_util.Po \
+ ./$(DEPDIR)/svcgssd-gss_names.Po \
+ ./$(DEPDIR)/svcgssd-gss_oids.Po \
+ ./$(DEPDIR)/svcgssd-gss_util.Po ./$(DEPDIR)/svcgssd-svcgssd.Po \
+ ./$(DEPDIR)/svcgssd-svcgssd_krb5.Po \
+ ./$(DEPDIR)/svcgssd-svcgssd_mech2file.Po \
+ ./$(DEPDIR)/svcgssd-svcgssd_proc.Po
+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 = $(gssd_SOURCES) $(svcgssd_SOURCES)
+DIST_SOURCES = $(gssd_SOURCES) $(svcgssd_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+man8dir = $(mandir)/man8
+NROFF = nroff
+MANS = $(man8_MANS)
+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@ -I ../../support/nfsidmap
+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@
+man8_MANS = gssd.man $(am__append_1)
+RPCPREFIX = rpc.
+KPREFIX = @kprefix@
+sbin_PREFIXED = gssd $(am__append_2)
+EXTRA_DIST = \
+ $(man8_MANS)
+
+COMMON_SRCS = \
+ context.c \
+ context_mit.c \
+ context_heimdal.c \
+ context_lucid.c \
+ gss_util.c \
+ gss_oids.c \
+ gss_names.c \
+ err_util.c \
+ \
+ context.h \
+ err_util.h \
+ gss_oids.h \
+ gss_names.h \
+ gss_util.h
+
+gssd_SOURCES = \
+ $(COMMON_SRCS) \
+ gssd.c \
+ gssd_proc.c \
+ krb5_util.c \
+ \
+ gssd.h \
+ krb5_util.h \
+ write_bytes.h
+
+gssd_LDADD = \
+ ../../support/nfs/libnfs.la \
+ $(LIBEVENT) \
+ $(RPCSECGSS_LIBS) \
+ $(KRBLIBS) \
+ $(GSSAPI_LIBS) \
+ $(LIBTIRPC) \
+ $(LIBPTHREAD)
+
+gssd_LDFLAGS = \
+ $(KRBLDFLAGS)
+
+gssd_CFLAGS = \
+ $(AM_CFLAGS) \
+ $(CFLAGS) \
+ $(RPCSECGSS_CFLAGS) \
+ $(KRBCFLAGS) \
+ $(GSSAPI_CFLAGS)
+
+svcgssd_SOURCES = \
+ $(COMMON_SRCS) \
+ svcgssd.c \
+ svcgssd_mech2file.c \
+ svcgssd_proc.c \
+ svcgssd_krb5.c \
+ \
+ svcgssd_krb5.h \
+ svcgssd.h
+
+svcgssd_LDADD = \
+ ../../support/nfs/libnfs.la \
+ ../../support/nfsidmap/libnfsidmap.la \
+ $(LIBEVENT) \
+ $(RPCSECGSS_LIBS) \
+ $(KRBLIBS) $(GSSAPI_LIBS) $(LIBTIRPC)
+
+svcgssd_LDFLAGS = $(KRBLDFLAGS)
+svcgssd_CFLAGS = $(AM_CFLAGS) $(CFLAGS) \
+ $(RPCSECGSS_CFLAGS) $(KRBCFLAGS) $(GSSAPI_CFLAGS)
+
+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 utils/gssd/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu utils/gssd/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):
+install-sbinPROGRAMS: $(sbin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(sbindir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-sbinPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(sbindir)" && rm -f $$files
+
+clean-sbinPROGRAMS:
+ @list='$(sbin_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+gssd$(EXEEXT): $(gssd_OBJECTS) $(gssd_DEPENDENCIES) $(EXTRA_gssd_DEPENDENCIES)
+ @rm -f gssd$(EXEEXT)
+ $(AM_V_CCLD)$(gssd_LINK) $(gssd_OBJECTS) $(gssd_LDADD) $(LIBS)
+
+svcgssd$(EXEEXT): $(svcgssd_OBJECTS) $(svcgssd_DEPENDENCIES) $(EXTRA_svcgssd_DEPENDENCIES)
+ @rm -f svcgssd$(EXEEXT)
+ $(AM_V_CCLD)$(svcgssd_LINK) $(svcgssd_OBJECTS) $(svcgssd_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gssd-context.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gssd-context_heimdal.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gssd-context_lucid.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gssd-context_mit.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gssd-err_util.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gssd-gss_names.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gssd-gss_oids.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gssd-gss_util.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gssd-gssd.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gssd-gssd_proc.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gssd-krb5_util.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/svcgssd-context.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/svcgssd-context_heimdal.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/svcgssd-context_lucid.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/svcgssd-context_mit.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/svcgssd-err_util.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/svcgssd-gss_names.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/svcgssd-gss_oids.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/svcgssd-gss_util.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/svcgssd-svcgssd.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/svcgssd-svcgssd_krb5.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/svcgssd-svcgssd_mech2file.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/svcgssd-svcgssd_proc.Po@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 $@ $<
+
+gssd-context.o: context.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -MT gssd-context.o -MD -MP -MF $(DEPDIR)/gssd-context.Tpo -c -o gssd-context.o `test -f 'context.c' || echo '$(srcdir)/'`context.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gssd-context.Tpo $(DEPDIR)/gssd-context.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='context.c' object='gssd-context.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -c -o gssd-context.o `test -f 'context.c' || echo '$(srcdir)/'`context.c
+
+gssd-context.obj: context.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -MT gssd-context.obj -MD -MP -MF $(DEPDIR)/gssd-context.Tpo -c -o gssd-context.obj `if test -f 'context.c'; then $(CYGPATH_W) 'context.c'; else $(CYGPATH_W) '$(srcdir)/context.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gssd-context.Tpo $(DEPDIR)/gssd-context.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='context.c' object='gssd-context.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -c -o gssd-context.obj `if test -f 'context.c'; then $(CYGPATH_W) 'context.c'; else $(CYGPATH_W) '$(srcdir)/context.c'; fi`
+
+gssd-context_mit.o: context_mit.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -MT gssd-context_mit.o -MD -MP -MF $(DEPDIR)/gssd-context_mit.Tpo -c -o gssd-context_mit.o `test -f 'context_mit.c' || echo '$(srcdir)/'`context_mit.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gssd-context_mit.Tpo $(DEPDIR)/gssd-context_mit.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='context_mit.c' object='gssd-context_mit.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -c -o gssd-context_mit.o `test -f 'context_mit.c' || echo '$(srcdir)/'`context_mit.c
+
+gssd-context_mit.obj: context_mit.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -MT gssd-context_mit.obj -MD -MP -MF $(DEPDIR)/gssd-context_mit.Tpo -c -o gssd-context_mit.obj `if test -f 'context_mit.c'; then $(CYGPATH_W) 'context_mit.c'; else $(CYGPATH_W) '$(srcdir)/context_mit.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gssd-context_mit.Tpo $(DEPDIR)/gssd-context_mit.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='context_mit.c' object='gssd-context_mit.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -c -o gssd-context_mit.obj `if test -f 'context_mit.c'; then $(CYGPATH_W) 'context_mit.c'; else $(CYGPATH_W) '$(srcdir)/context_mit.c'; fi`
+
+gssd-context_heimdal.o: context_heimdal.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -MT gssd-context_heimdal.o -MD -MP -MF $(DEPDIR)/gssd-context_heimdal.Tpo -c -o gssd-context_heimdal.o `test -f 'context_heimdal.c' || echo '$(srcdir)/'`context_heimdal.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gssd-context_heimdal.Tpo $(DEPDIR)/gssd-context_heimdal.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='context_heimdal.c' object='gssd-context_heimdal.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -c -o gssd-context_heimdal.o `test -f 'context_heimdal.c' || echo '$(srcdir)/'`context_heimdal.c
+
+gssd-context_heimdal.obj: context_heimdal.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -MT gssd-context_heimdal.obj -MD -MP -MF $(DEPDIR)/gssd-context_heimdal.Tpo -c -o gssd-context_heimdal.obj `if test -f 'context_heimdal.c'; then $(CYGPATH_W) 'context_heimdal.c'; else $(CYGPATH_W) '$(srcdir)/context_heimdal.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gssd-context_heimdal.Tpo $(DEPDIR)/gssd-context_heimdal.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='context_heimdal.c' object='gssd-context_heimdal.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -c -o gssd-context_heimdal.obj `if test -f 'context_heimdal.c'; then $(CYGPATH_W) 'context_heimdal.c'; else $(CYGPATH_W) '$(srcdir)/context_heimdal.c'; fi`
+
+gssd-context_lucid.o: context_lucid.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -MT gssd-context_lucid.o -MD -MP -MF $(DEPDIR)/gssd-context_lucid.Tpo -c -o gssd-context_lucid.o `test -f 'context_lucid.c' || echo '$(srcdir)/'`context_lucid.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gssd-context_lucid.Tpo $(DEPDIR)/gssd-context_lucid.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='context_lucid.c' object='gssd-context_lucid.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -c -o gssd-context_lucid.o `test -f 'context_lucid.c' || echo '$(srcdir)/'`context_lucid.c
+
+gssd-context_lucid.obj: context_lucid.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -MT gssd-context_lucid.obj -MD -MP -MF $(DEPDIR)/gssd-context_lucid.Tpo -c -o gssd-context_lucid.obj `if test -f 'context_lucid.c'; then $(CYGPATH_W) 'context_lucid.c'; else $(CYGPATH_W) '$(srcdir)/context_lucid.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gssd-context_lucid.Tpo $(DEPDIR)/gssd-context_lucid.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='context_lucid.c' object='gssd-context_lucid.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -c -o gssd-context_lucid.obj `if test -f 'context_lucid.c'; then $(CYGPATH_W) 'context_lucid.c'; else $(CYGPATH_W) '$(srcdir)/context_lucid.c'; fi`
+
+gssd-gss_util.o: gss_util.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -MT gssd-gss_util.o -MD -MP -MF $(DEPDIR)/gssd-gss_util.Tpo -c -o gssd-gss_util.o `test -f 'gss_util.c' || echo '$(srcdir)/'`gss_util.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gssd-gss_util.Tpo $(DEPDIR)/gssd-gss_util.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gss_util.c' object='gssd-gss_util.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -c -o gssd-gss_util.o `test -f 'gss_util.c' || echo '$(srcdir)/'`gss_util.c
+
+gssd-gss_util.obj: gss_util.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -MT gssd-gss_util.obj -MD -MP -MF $(DEPDIR)/gssd-gss_util.Tpo -c -o gssd-gss_util.obj `if test -f 'gss_util.c'; then $(CYGPATH_W) 'gss_util.c'; else $(CYGPATH_W) '$(srcdir)/gss_util.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gssd-gss_util.Tpo $(DEPDIR)/gssd-gss_util.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gss_util.c' object='gssd-gss_util.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -c -o gssd-gss_util.obj `if test -f 'gss_util.c'; then $(CYGPATH_W) 'gss_util.c'; else $(CYGPATH_W) '$(srcdir)/gss_util.c'; fi`
+
+gssd-gss_oids.o: gss_oids.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -MT gssd-gss_oids.o -MD -MP -MF $(DEPDIR)/gssd-gss_oids.Tpo -c -o gssd-gss_oids.o `test -f 'gss_oids.c' || echo '$(srcdir)/'`gss_oids.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gssd-gss_oids.Tpo $(DEPDIR)/gssd-gss_oids.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gss_oids.c' object='gssd-gss_oids.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -c -o gssd-gss_oids.o `test -f 'gss_oids.c' || echo '$(srcdir)/'`gss_oids.c
+
+gssd-gss_oids.obj: gss_oids.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -MT gssd-gss_oids.obj -MD -MP -MF $(DEPDIR)/gssd-gss_oids.Tpo -c -o gssd-gss_oids.obj `if test -f 'gss_oids.c'; then $(CYGPATH_W) 'gss_oids.c'; else $(CYGPATH_W) '$(srcdir)/gss_oids.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gssd-gss_oids.Tpo $(DEPDIR)/gssd-gss_oids.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gss_oids.c' object='gssd-gss_oids.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -c -o gssd-gss_oids.obj `if test -f 'gss_oids.c'; then $(CYGPATH_W) 'gss_oids.c'; else $(CYGPATH_W) '$(srcdir)/gss_oids.c'; fi`
+
+gssd-gss_names.o: gss_names.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -MT gssd-gss_names.o -MD -MP -MF $(DEPDIR)/gssd-gss_names.Tpo -c -o gssd-gss_names.o `test -f 'gss_names.c' || echo '$(srcdir)/'`gss_names.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gssd-gss_names.Tpo $(DEPDIR)/gssd-gss_names.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gss_names.c' object='gssd-gss_names.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -c -o gssd-gss_names.o `test -f 'gss_names.c' || echo '$(srcdir)/'`gss_names.c
+
+gssd-gss_names.obj: gss_names.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -MT gssd-gss_names.obj -MD -MP -MF $(DEPDIR)/gssd-gss_names.Tpo -c -o gssd-gss_names.obj `if test -f 'gss_names.c'; then $(CYGPATH_W) 'gss_names.c'; else $(CYGPATH_W) '$(srcdir)/gss_names.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gssd-gss_names.Tpo $(DEPDIR)/gssd-gss_names.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gss_names.c' object='gssd-gss_names.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -c -o gssd-gss_names.obj `if test -f 'gss_names.c'; then $(CYGPATH_W) 'gss_names.c'; else $(CYGPATH_W) '$(srcdir)/gss_names.c'; fi`
+
+gssd-err_util.o: err_util.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -MT gssd-err_util.o -MD -MP -MF $(DEPDIR)/gssd-err_util.Tpo -c -o gssd-err_util.o `test -f 'err_util.c' || echo '$(srcdir)/'`err_util.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gssd-err_util.Tpo $(DEPDIR)/gssd-err_util.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='err_util.c' object='gssd-err_util.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -c -o gssd-err_util.o `test -f 'err_util.c' || echo '$(srcdir)/'`err_util.c
+
+gssd-err_util.obj: err_util.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -MT gssd-err_util.obj -MD -MP -MF $(DEPDIR)/gssd-err_util.Tpo -c -o gssd-err_util.obj `if test -f 'err_util.c'; then $(CYGPATH_W) 'err_util.c'; else $(CYGPATH_W) '$(srcdir)/err_util.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gssd-err_util.Tpo $(DEPDIR)/gssd-err_util.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='err_util.c' object='gssd-err_util.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -c -o gssd-err_util.obj `if test -f 'err_util.c'; then $(CYGPATH_W) 'err_util.c'; else $(CYGPATH_W) '$(srcdir)/err_util.c'; fi`
+
+gssd-gssd.o: gssd.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -MT gssd-gssd.o -MD -MP -MF $(DEPDIR)/gssd-gssd.Tpo -c -o gssd-gssd.o `test -f 'gssd.c' || echo '$(srcdir)/'`gssd.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gssd-gssd.Tpo $(DEPDIR)/gssd-gssd.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gssd.c' object='gssd-gssd.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -c -o gssd-gssd.o `test -f 'gssd.c' || echo '$(srcdir)/'`gssd.c
+
+gssd-gssd.obj: gssd.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -MT gssd-gssd.obj -MD -MP -MF $(DEPDIR)/gssd-gssd.Tpo -c -o gssd-gssd.obj `if test -f 'gssd.c'; then $(CYGPATH_W) 'gssd.c'; else $(CYGPATH_W) '$(srcdir)/gssd.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gssd-gssd.Tpo $(DEPDIR)/gssd-gssd.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gssd.c' object='gssd-gssd.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -c -o gssd-gssd.obj `if test -f 'gssd.c'; then $(CYGPATH_W) 'gssd.c'; else $(CYGPATH_W) '$(srcdir)/gssd.c'; fi`
+
+gssd-gssd_proc.o: gssd_proc.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -MT gssd-gssd_proc.o -MD -MP -MF $(DEPDIR)/gssd-gssd_proc.Tpo -c -o gssd-gssd_proc.o `test -f 'gssd_proc.c' || echo '$(srcdir)/'`gssd_proc.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gssd-gssd_proc.Tpo $(DEPDIR)/gssd-gssd_proc.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gssd_proc.c' object='gssd-gssd_proc.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -c -o gssd-gssd_proc.o `test -f 'gssd_proc.c' || echo '$(srcdir)/'`gssd_proc.c
+
+gssd-gssd_proc.obj: gssd_proc.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -MT gssd-gssd_proc.obj -MD -MP -MF $(DEPDIR)/gssd-gssd_proc.Tpo -c -o gssd-gssd_proc.obj `if test -f 'gssd_proc.c'; then $(CYGPATH_W) 'gssd_proc.c'; else $(CYGPATH_W) '$(srcdir)/gssd_proc.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gssd-gssd_proc.Tpo $(DEPDIR)/gssd-gssd_proc.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gssd_proc.c' object='gssd-gssd_proc.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -c -o gssd-gssd_proc.obj `if test -f 'gssd_proc.c'; then $(CYGPATH_W) 'gssd_proc.c'; else $(CYGPATH_W) '$(srcdir)/gssd_proc.c'; fi`
+
+gssd-krb5_util.o: krb5_util.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -MT gssd-krb5_util.o -MD -MP -MF $(DEPDIR)/gssd-krb5_util.Tpo -c -o gssd-krb5_util.o `test -f 'krb5_util.c' || echo '$(srcdir)/'`krb5_util.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gssd-krb5_util.Tpo $(DEPDIR)/gssd-krb5_util.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='krb5_util.c' object='gssd-krb5_util.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -c -o gssd-krb5_util.o `test -f 'krb5_util.c' || echo '$(srcdir)/'`krb5_util.c
+
+gssd-krb5_util.obj: krb5_util.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -MT gssd-krb5_util.obj -MD -MP -MF $(DEPDIR)/gssd-krb5_util.Tpo -c -o gssd-krb5_util.obj `if test -f 'krb5_util.c'; then $(CYGPATH_W) 'krb5_util.c'; else $(CYGPATH_W) '$(srcdir)/krb5_util.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gssd-krb5_util.Tpo $(DEPDIR)/gssd-krb5_util.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='krb5_util.c' object='gssd-krb5_util.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gssd_CFLAGS) $(CFLAGS) -c -o gssd-krb5_util.obj `if test -f 'krb5_util.c'; then $(CYGPATH_W) 'krb5_util.c'; else $(CYGPATH_W) '$(srcdir)/krb5_util.c'; fi`
+
+svcgssd-context.o: context.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -MT svcgssd-context.o -MD -MP -MF $(DEPDIR)/svcgssd-context.Tpo -c -o svcgssd-context.o `test -f 'context.c' || echo '$(srcdir)/'`context.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/svcgssd-context.Tpo $(DEPDIR)/svcgssd-context.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='context.c' object='svcgssd-context.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -c -o svcgssd-context.o `test -f 'context.c' || echo '$(srcdir)/'`context.c
+
+svcgssd-context.obj: context.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -MT svcgssd-context.obj -MD -MP -MF $(DEPDIR)/svcgssd-context.Tpo -c -o svcgssd-context.obj `if test -f 'context.c'; then $(CYGPATH_W) 'context.c'; else $(CYGPATH_W) '$(srcdir)/context.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/svcgssd-context.Tpo $(DEPDIR)/svcgssd-context.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='context.c' object='svcgssd-context.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -c -o svcgssd-context.obj `if test -f 'context.c'; then $(CYGPATH_W) 'context.c'; else $(CYGPATH_W) '$(srcdir)/context.c'; fi`
+
+svcgssd-context_mit.o: context_mit.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -MT svcgssd-context_mit.o -MD -MP -MF $(DEPDIR)/svcgssd-context_mit.Tpo -c -o svcgssd-context_mit.o `test -f 'context_mit.c' || echo '$(srcdir)/'`context_mit.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/svcgssd-context_mit.Tpo $(DEPDIR)/svcgssd-context_mit.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='context_mit.c' object='svcgssd-context_mit.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -c -o svcgssd-context_mit.o `test -f 'context_mit.c' || echo '$(srcdir)/'`context_mit.c
+
+svcgssd-context_mit.obj: context_mit.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -MT svcgssd-context_mit.obj -MD -MP -MF $(DEPDIR)/svcgssd-context_mit.Tpo -c -o svcgssd-context_mit.obj `if test -f 'context_mit.c'; then $(CYGPATH_W) 'context_mit.c'; else $(CYGPATH_W) '$(srcdir)/context_mit.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/svcgssd-context_mit.Tpo $(DEPDIR)/svcgssd-context_mit.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='context_mit.c' object='svcgssd-context_mit.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -c -o svcgssd-context_mit.obj `if test -f 'context_mit.c'; then $(CYGPATH_W) 'context_mit.c'; else $(CYGPATH_W) '$(srcdir)/context_mit.c'; fi`
+
+svcgssd-context_heimdal.o: context_heimdal.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -MT svcgssd-context_heimdal.o -MD -MP -MF $(DEPDIR)/svcgssd-context_heimdal.Tpo -c -o svcgssd-context_heimdal.o `test -f 'context_heimdal.c' || echo '$(srcdir)/'`context_heimdal.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/svcgssd-context_heimdal.Tpo $(DEPDIR)/svcgssd-context_heimdal.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='context_heimdal.c' object='svcgssd-context_heimdal.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -c -o svcgssd-context_heimdal.o `test -f 'context_heimdal.c' || echo '$(srcdir)/'`context_heimdal.c
+
+svcgssd-context_heimdal.obj: context_heimdal.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -MT svcgssd-context_heimdal.obj -MD -MP -MF $(DEPDIR)/svcgssd-context_heimdal.Tpo -c -o svcgssd-context_heimdal.obj `if test -f 'context_heimdal.c'; then $(CYGPATH_W) 'context_heimdal.c'; else $(CYGPATH_W) '$(srcdir)/context_heimdal.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/svcgssd-context_heimdal.Tpo $(DEPDIR)/svcgssd-context_heimdal.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='context_heimdal.c' object='svcgssd-context_heimdal.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -c -o svcgssd-context_heimdal.obj `if test -f 'context_heimdal.c'; then $(CYGPATH_W) 'context_heimdal.c'; else $(CYGPATH_W) '$(srcdir)/context_heimdal.c'; fi`
+
+svcgssd-context_lucid.o: context_lucid.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -MT svcgssd-context_lucid.o -MD -MP -MF $(DEPDIR)/svcgssd-context_lucid.Tpo -c -o svcgssd-context_lucid.o `test -f 'context_lucid.c' || echo '$(srcdir)/'`context_lucid.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/svcgssd-context_lucid.Tpo $(DEPDIR)/svcgssd-context_lucid.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='context_lucid.c' object='svcgssd-context_lucid.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -c -o svcgssd-context_lucid.o `test -f 'context_lucid.c' || echo '$(srcdir)/'`context_lucid.c
+
+svcgssd-context_lucid.obj: context_lucid.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -MT svcgssd-context_lucid.obj -MD -MP -MF $(DEPDIR)/svcgssd-context_lucid.Tpo -c -o svcgssd-context_lucid.obj `if test -f 'context_lucid.c'; then $(CYGPATH_W) 'context_lucid.c'; else $(CYGPATH_W) '$(srcdir)/context_lucid.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/svcgssd-context_lucid.Tpo $(DEPDIR)/svcgssd-context_lucid.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='context_lucid.c' object='svcgssd-context_lucid.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -c -o svcgssd-context_lucid.obj `if test -f 'context_lucid.c'; then $(CYGPATH_W) 'context_lucid.c'; else $(CYGPATH_W) '$(srcdir)/context_lucid.c'; fi`
+
+svcgssd-gss_util.o: gss_util.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -MT svcgssd-gss_util.o -MD -MP -MF $(DEPDIR)/svcgssd-gss_util.Tpo -c -o svcgssd-gss_util.o `test -f 'gss_util.c' || echo '$(srcdir)/'`gss_util.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/svcgssd-gss_util.Tpo $(DEPDIR)/svcgssd-gss_util.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gss_util.c' object='svcgssd-gss_util.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -c -o svcgssd-gss_util.o `test -f 'gss_util.c' || echo '$(srcdir)/'`gss_util.c
+
+svcgssd-gss_util.obj: gss_util.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -MT svcgssd-gss_util.obj -MD -MP -MF $(DEPDIR)/svcgssd-gss_util.Tpo -c -o svcgssd-gss_util.obj `if test -f 'gss_util.c'; then $(CYGPATH_W) 'gss_util.c'; else $(CYGPATH_W) '$(srcdir)/gss_util.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/svcgssd-gss_util.Tpo $(DEPDIR)/svcgssd-gss_util.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gss_util.c' object='svcgssd-gss_util.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -c -o svcgssd-gss_util.obj `if test -f 'gss_util.c'; then $(CYGPATH_W) 'gss_util.c'; else $(CYGPATH_W) '$(srcdir)/gss_util.c'; fi`
+
+svcgssd-gss_oids.o: gss_oids.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -MT svcgssd-gss_oids.o -MD -MP -MF $(DEPDIR)/svcgssd-gss_oids.Tpo -c -o svcgssd-gss_oids.o `test -f 'gss_oids.c' || echo '$(srcdir)/'`gss_oids.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/svcgssd-gss_oids.Tpo $(DEPDIR)/svcgssd-gss_oids.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gss_oids.c' object='svcgssd-gss_oids.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -c -o svcgssd-gss_oids.o `test -f 'gss_oids.c' || echo '$(srcdir)/'`gss_oids.c
+
+svcgssd-gss_oids.obj: gss_oids.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -MT svcgssd-gss_oids.obj -MD -MP -MF $(DEPDIR)/svcgssd-gss_oids.Tpo -c -o svcgssd-gss_oids.obj `if test -f 'gss_oids.c'; then $(CYGPATH_W) 'gss_oids.c'; else $(CYGPATH_W) '$(srcdir)/gss_oids.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/svcgssd-gss_oids.Tpo $(DEPDIR)/svcgssd-gss_oids.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gss_oids.c' object='svcgssd-gss_oids.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -c -o svcgssd-gss_oids.obj `if test -f 'gss_oids.c'; then $(CYGPATH_W) 'gss_oids.c'; else $(CYGPATH_W) '$(srcdir)/gss_oids.c'; fi`
+
+svcgssd-gss_names.o: gss_names.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -MT svcgssd-gss_names.o -MD -MP -MF $(DEPDIR)/svcgssd-gss_names.Tpo -c -o svcgssd-gss_names.o `test -f 'gss_names.c' || echo '$(srcdir)/'`gss_names.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/svcgssd-gss_names.Tpo $(DEPDIR)/svcgssd-gss_names.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gss_names.c' object='svcgssd-gss_names.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -c -o svcgssd-gss_names.o `test -f 'gss_names.c' || echo '$(srcdir)/'`gss_names.c
+
+svcgssd-gss_names.obj: gss_names.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -MT svcgssd-gss_names.obj -MD -MP -MF $(DEPDIR)/svcgssd-gss_names.Tpo -c -o svcgssd-gss_names.obj `if test -f 'gss_names.c'; then $(CYGPATH_W) 'gss_names.c'; else $(CYGPATH_W) '$(srcdir)/gss_names.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/svcgssd-gss_names.Tpo $(DEPDIR)/svcgssd-gss_names.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gss_names.c' object='svcgssd-gss_names.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -c -o svcgssd-gss_names.obj `if test -f 'gss_names.c'; then $(CYGPATH_W) 'gss_names.c'; else $(CYGPATH_W) '$(srcdir)/gss_names.c'; fi`
+
+svcgssd-err_util.o: err_util.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -MT svcgssd-err_util.o -MD -MP -MF $(DEPDIR)/svcgssd-err_util.Tpo -c -o svcgssd-err_util.o `test -f 'err_util.c' || echo '$(srcdir)/'`err_util.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/svcgssd-err_util.Tpo $(DEPDIR)/svcgssd-err_util.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='err_util.c' object='svcgssd-err_util.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -c -o svcgssd-err_util.o `test -f 'err_util.c' || echo '$(srcdir)/'`err_util.c
+
+svcgssd-err_util.obj: err_util.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -MT svcgssd-err_util.obj -MD -MP -MF $(DEPDIR)/svcgssd-err_util.Tpo -c -o svcgssd-err_util.obj `if test -f 'err_util.c'; then $(CYGPATH_W) 'err_util.c'; else $(CYGPATH_W) '$(srcdir)/err_util.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/svcgssd-err_util.Tpo $(DEPDIR)/svcgssd-err_util.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='err_util.c' object='svcgssd-err_util.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -c -o svcgssd-err_util.obj `if test -f 'err_util.c'; then $(CYGPATH_W) 'err_util.c'; else $(CYGPATH_W) '$(srcdir)/err_util.c'; fi`
+
+svcgssd-svcgssd.o: svcgssd.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -MT svcgssd-svcgssd.o -MD -MP -MF $(DEPDIR)/svcgssd-svcgssd.Tpo -c -o svcgssd-svcgssd.o `test -f 'svcgssd.c' || echo '$(srcdir)/'`svcgssd.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/svcgssd-svcgssd.Tpo $(DEPDIR)/svcgssd-svcgssd.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='svcgssd.c' object='svcgssd-svcgssd.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -c -o svcgssd-svcgssd.o `test -f 'svcgssd.c' || echo '$(srcdir)/'`svcgssd.c
+
+svcgssd-svcgssd.obj: svcgssd.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -MT svcgssd-svcgssd.obj -MD -MP -MF $(DEPDIR)/svcgssd-svcgssd.Tpo -c -o svcgssd-svcgssd.obj `if test -f 'svcgssd.c'; then $(CYGPATH_W) 'svcgssd.c'; else $(CYGPATH_W) '$(srcdir)/svcgssd.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/svcgssd-svcgssd.Tpo $(DEPDIR)/svcgssd-svcgssd.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='svcgssd.c' object='svcgssd-svcgssd.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -c -o svcgssd-svcgssd.obj `if test -f 'svcgssd.c'; then $(CYGPATH_W) 'svcgssd.c'; else $(CYGPATH_W) '$(srcdir)/svcgssd.c'; fi`
+
+svcgssd-svcgssd_mech2file.o: svcgssd_mech2file.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -MT svcgssd-svcgssd_mech2file.o -MD -MP -MF $(DEPDIR)/svcgssd-svcgssd_mech2file.Tpo -c -o svcgssd-svcgssd_mech2file.o `test -f 'svcgssd_mech2file.c' || echo '$(srcdir)/'`svcgssd_mech2file.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/svcgssd-svcgssd_mech2file.Tpo $(DEPDIR)/svcgssd-svcgssd_mech2file.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='svcgssd_mech2file.c' object='svcgssd-svcgssd_mech2file.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -c -o svcgssd-svcgssd_mech2file.o `test -f 'svcgssd_mech2file.c' || echo '$(srcdir)/'`svcgssd_mech2file.c
+
+svcgssd-svcgssd_mech2file.obj: svcgssd_mech2file.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -MT svcgssd-svcgssd_mech2file.obj -MD -MP -MF $(DEPDIR)/svcgssd-svcgssd_mech2file.Tpo -c -o svcgssd-svcgssd_mech2file.obj `if test -f 'svcgssd_mech2file.c'; then $(CYGPATH_W) 'svcgssd_mech2file.c'; else $(CYGPATH_W) '$(srcdir)/svcgssd_mech2file.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/svcgssd-svcgssd_mech2file.Tpo $(DEPDIR)/svcgssd-svcgssd_mech2file.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='svcgssd_mech2file.c' object='svcgssd-svcgssd_mech2file.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -c -o svcgssd-svcgssd_mech2file.obj `if test -f 'svcgssd_mech2file.c'; then $(CYGPATH_W) 'svcgssd_mech2file.c'; else $(CYGPATH_W) '$(srcdir)/svcgssd_mech2file.c'; fi`
+
+svcgssd-svcgssd_proc.o: svcgssd_proc.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -MT svcgssd-svcgssd_proc.o -MD -MP -MF $(DEPDIR)/svcgssd-svcgssd_proc.Tpo -c -o svcgssd-svcgssd_proc.o `test -f 'svcgssd_proc.c' || echo '$(srcdir)/'`svcgssd_proc.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/svcgssd-svcgssd_proc.Tpo $(DEPDIR)/svcgssd-svcgssd_proc.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='svcgssd_proc.c' object='svcgssd-svcgssd_proc.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -c -o svcgssd-svcgssd_proc.o `test -f 'svcgssd_proc.c' || echo '$(srcdir)/'`svcgssd_proc.c
+
+svcgssd-svcgssd_proc.obj: svcgssd_proc.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -MT svcgssd-svcgssd_proc.obj -MD -MP -MF $(DEPDIR)/svcgssd-svcgssd_proc.Tpo -c -o svcgssd-svcgssd_proc.obj `if test -f 'svcgssd_proc.c'; then $(CYGPATH_W) 'svcgssd_proc.c'; else $(CYGPATH_W) '$(srcdir)/svcgssd_proc.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/svcgssd-svcgssd_proc.Tpo $(DEPDIR)/svcgssd-svcgssd_proc.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='svcgssd_proc.c' object='svcgssd-svcgssd_proc.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -c -o svcgssd-svcgssd_proc.obj `if test -f 'svcgssd_proc.c'; then $(CYGPATH_W) 'svcgssd_proc.c'; else $(CYGPATH_W) '$(srcdir)/svcgssd_proc.c'; fi`
+
+svcgssd-svcgssd_krb5.o: svcgssd_krb5.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -MT svcgssd-svcgssd_krb5.o -MD -MP -MF $(DEPDIR)/svcgssd-svcgssd_krb5.Tpo -c -o svcgssd-svcgssd_krb5.o `test -f 'svcgssd_krb5.c' || echo '$(srcdir)/'`svcgssd_krb5.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/svcgssd-svcgssd_krb5.Tpo $(DEPDIR)/svcgssd-svcgssd_krb5.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='svcgssd_krb5.c' object='svcgssd-svcgssd_krb5.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -c -o svcgssd-svcgssd_krb5.o `test -f 'svcgssd_krb5.c' || echo '$(srcdir)/'`svcgssd_krb5.c
+
+svcgssd-svcgssd_krb5.obj: svcgssd_krb5.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -MT svcgssd-svcgssd_krb5.obj -MD -MP -MF $(DEPDIR)/svcgssd-svcgssd_krb5.Tpo -c -o svcgssd-svcgssd_krb5.obj `if test -f 'svcgssd_krb5.c'; then $(CYGPATH_W) 'svcgssd_krb5.c'; else $(CYGPATH_W) '$(srcdir)/svcgssd_krb5.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/svcgssd-svcgssd_krb5.Tpo $(DEPDIR)/svcgssd-svcgssd_krb5.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='svcgssd_krb5.c' object='svcgssd-svcgssd_krb5.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(svcgssd_CFLAGS) $(CFLAGS) -c -o svcgssd-svcgssd_krb5.obj `if test -f 'svcgssd_krb5.c'; then $(CYGPATH_W) 'svcgssd_krb5.c'; else $(CYGPATH_W) '$(srcdir)/svcgssd_krb5.c'; fi`
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-man8: $(man8_MANS)
+ @$(NORMAL_INSTALL)
+ @list1='$(man8_MANS)'; \
+ list2=''; \
+ test -n "$(man8dir)" \
+ && test -n "`echo $$list1$$list2`" \
+ || exit 0; \
+ echo " $(MKDIR_P) '$(DESTDIR)$(man8dir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(man8dir)" || exit 1; \
+ { for i in $$list1; do echo "$$i"; done; \
+ if test -n "$$list2"; then \
+ for i in $$list2; do echo "$$i"; done \
+ | sed -n '/\.8[a-z]*$$/p'; \
+ fi; \
+ } | while read p; do \
+ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; echo "$$p"; \
+ done | \
+ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \
+ sed 'N;N;s,\n, ,g' | { \
+ list=; while read file base inst; do \
+ if test "$$base" = "$$inst"; then list="$$list $$file"; else \
+ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man8dir)/$$inst'"; \
+ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man8dir)/$$inst" || exit $$?; \
+ fi; \
+ done; \
+ for i in $$list; do echo "$$i"; done | $(am__base_list) | \
+ while read files; do \
+ test -z "$$files" || { \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man8dir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(man8dir)" || exit $$?; }; \
+ done; }
+
+uninstall-man8:
+ @$(NORMAL_UNINSTALL)
+ @list='$(man8_MANS)'; test -n "$(man8dir)" || exit 0; \
+ files=`{ for i in $$list; do echo "$$i"; done; \
+ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
+ dir='$(DESTDIR)$(man8dir)'; $(am__uninstall_files_from_dir)
+
+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 $(PROGRAMS) $(MANS)
+installdirs:
+ for dir in "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man8dir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+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-sbinPROGRAMS \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/gssd-context.Po
+ -rm -f ./$(DEPDIR)/gssd-context_heimdal.Po
+ -rm -f ./$(DEPDIR)/gssd-context_lucid.Po
+ -rm -f ./$(DEPDIR)/gssd-context_mit.Po
+ -rm -f ./$(DEPDIR)/gssd-err_util.Po
+ -rm -f ./$(DEPDIR)/gssd-gss_names.Po
+ -rm -f ./$(DEPDIR)/gssd-gss_oids.Po
+ -rm -f ./$(DEPDIR)/gssd-gss_util.Po
+ -rm -f ./$(DEPDIR)/gssd-gssd.Po
+ -rm -f ./$(DEPDIR)/gssd-gssd_proc.Po
+ -rm -f ./$(DEPDIR)/gssd-krb5_util.Po
+ -rm -f ./$(DEPDIR)/svcgssd-context.Po
+ -rm -f ./$(DEPDIR)/svcgssd-context_heimdal.Po
+ -rm -f ./$(DEPDIR)/svcgssd-context_lucid.Po
+ -rm -f ./$(DEPDIR)/svcgssd-context_mit.Po
+ -rm -f ./$(DEPDIR)/svcgssd-err_util.Po
+ -rm -f ./$(DEPDIR)/svcgssd-gss_names.Po
+ -rm -f ./$(DEPDIR)/svcgssd-gss_oids.Po
+ -rm -f ./$(DEPDIR)/svcgssd-gss_util.Po
+ -rm -f ./$(DEPDIR)/svcgssd-svcgssd.Po
+ -rm -f ./$(DEPDIR)/svcgssd-svcgssd_krb5.Po
+ -rm -f ./$(DEPDIR)/svcgssd-svcgssd_mech2file.Po
+ -rm -f ./$(DEPDIR)/svcgssd-svcgssd_proc.Po
+ -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-man
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-sbinPROGRAMS
+ @$(NORMAL_INSTALL)
+ $(MAKE) $(AM_MAKEFLAGS) install-exec-hook
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+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)/gssd-context.Po
+ -rm -f ./$(DEPDIR)/gssd-context_heimdal.Po
+ -rm -f ./$(DEPDIR)/gssd-context_lucid.Po
+ -rm -f ./$(DEPDIR)/gssd-context_mit.Po
+ -rm -f ./$(DEPDIR)/gssd-err_util.Po
+ -rm -f ./$(DEPDIR)/gssd-gss_names.Po
+ -rm -f ./$(DEPDIR)/gssd-gss_oids.Po
+ -rm -f ./$(DEPDIR)/gssd-gss_util.Po
+ -rm -f ./$(DEPDIR)/gssd-gssd.Po
+ -rm -f ./$(DEPDIR)/gssd-gssd_proc.Po
+ -rm -f ./$(DEPDIR)/gssd-krb5_util.Po
+ -rm -f ./$(DEPDIR)/svcgssd-context.Po
+ -rm -f ./$(DEPDIR)/svcgssd-context_heimdal.Po
+ -rm -f ./$(DEPDIR)/svcgssd-context_lucid.Po
+ -rm -f ./$(DEPDIR)/svcgssd-context_mit.Po
+ -rm -f ./$(DEPDIR)/svcgssd-err_util.Po
+ -rm -f ./$(DEPDIR)/svcgssd-gss_names.Po
+ -rm -f ./$(DEPDIR)/svcgssd-gss_oids.Po
+ -rm -f ./$(DEPDIR)/svcgssd-gss_util.Po
+ -rm -f ./$(DEPDIR)/svcgssd-svcgssd.Po
+ -rm -f ./$(DEPDIR)/svcgssd-svcgssd_krb5.Po
+ -rm -f ./$(DEPDIR)/svcgssd-svcgssd_mech2file.Po
+ -rm -f ./$(DEPDIR)/svcgssd-svcgssd_proc.Po
+ -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: uninstall-man uninstall-sbinPROGRAMS
+ @$(NORMAL_INSTALL)
+ $(MAKE) $(AM_MAKEFLAGS) uninstall-hook
+.MAKE: install-am install-exec-am install-strip uninstall-am
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-sbinPROGRAMS 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-exec-hook install-html install-html-am \
+ install-info install-info-am install-man install-man8 \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-sbinPROGRAMS 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 uninstall-hook \
+ uninstall-man uninstall-man8 uninstall-sbinPROGRAMS
+
+.PRECIOUS: Makefile
+
+
+#######################################################################
+# The following allows the current practice of having
+# daemons renamed during the install to include RPCPREFIX
+# and the KPREFIX
+# This could all be done much easier with program_transform_name
+# ( program_transform_name = s/^/$(RPCPREFIX)$(KPREFIX)/ )
+# but that also renames the man pages, which the current
+# practice does not do.
+install-exec-hook:
+ (cd $(DESTDIR)$(sbindir) && \
+ for p in $(sbin_PREFIXED); do \
+ mv -f $$p$(EXEEXT) $(RPCPREFIX)$(KPREFIX)$$p$(EXEEXT) ;\
+ done)
+uninstall-hook:
+ (cd $(DESTDIR)$(sbindir) && \
+ for p in $(sbin_PREFIXED); do \
+ rm -f $(RPCPREFIX)$(KPREFIX)$$p$(EXEEXT) ;\
+ done)
+
+# XXX This makes some assumptions about what automake does.
+# XXX But there is no install-man-hook or install-man-local.
+install-man: install-man8 install-man-links
+uninstall-man: uninstall-man8 uninstall-man-links
+
+install-man-links:
+ (cd $(DESTDIR)$(man8dir) && \
+ for m in $(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS); do \
+ inst=`echo $$m | sed -e 's/man$$/8/'`; \
+ rm -f $(RPCPREFIX)$$inst ; \
+ $(LN_S) $$inst $(RPCPREFIX)$$inst ; \
+ done)
+
+uninstall-man-links:
+ (cd $(DESTDIR)$(man8dir) && \
+ for m in $(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS); do \
+ inst=`echo $$m | sed -e 's/man$$/8/'`; \
+ rm -f $(RPCPREFIX)$$inst ; \
+ done)
+
+# 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/utils/gssd/context.c b/utils/gssd/context.c
new file mode 100644
index 0000000..7757a77
--- /dev/null
+++ b/utils/gssd/context.c
@@ -0,0 +1,59 @@
+/*
+ Copyright (c) 2004 The Regents of the University of Michigan.
+ 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. 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.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <stdio.h>
+#include <syslog.h>
+#include <string.h>
+#include <gssapi/gssapi.h>
+#include <rpc/rpc.h>
+#include <rpc/auth_gss.h>
+#include "gss_util.h"
+#include "gss_oids.h"
+#include "err_util.h"
+#include "context.h"
+
+int
+serialize_context_for_kernel(gss_ctx_id_t *ctx,
+ gss_buffer_desc *buf,
+ gss_OID mech,
+ int32_t *endtime)
+{
+ if (g_OID_equal(&krb5oid, mech))
+ return serialize_krb5_ctx(ctx, buf, endtime);
+ else {
+ printerr(0, "ERROR: attempting to serialize context with "
+ "unknown/unsupported mechanism oid\n");
+ return -1;
+ }
+}
diff --git a/utils/gssd/context.h b/utils/gssd/context.h
new file mode 100644
index 0000000..3b55c8e
--- /dev/null
+++ b/utils/gssd/context.h
@@ -0,0 +1,49 @@
+/*
+ Copyright (c) 2004,2008 The Regents of the University of Michigan.
+ 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. 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.
+*/
+
+#ifndef _CONTEXT_H_
+#define _CONTEXT_H_
+
+#include <rpc/rpc.h>
+
+/* Hopefully big enough to hold any serialized context */
+#define MAX_CTX_LEN 4096
+
+/* New context format flag values */
+#define KRB5_CTX_FLAG_INITIATOR 0x00000001
+#define KRB5_CTX_FLAG_CFX 0x00000002
+#define KRB5_CTX_FLAG_ACCEPTOR_SUBKEY 0x00000004
+
+int serialize_context_for_kernel(gss_ctx_id_t *ctx, gss_buffer_desc *buf,
+ gss_OID mech, int32_t *endtime);
+int serialize_krb5_ctx(gss_ctx_id_t *ctx, gss_buffer_desc *buf,
+ int32_t *endtime);
+
+#endif /* _CONTEXT_H_ */
diff --git a/utils/gssd/context_heimdal.c b/utils/gssd/context_heimdal.c
new file mode 100644
index 0000000..d07103b
--- /dev/null
+++ b/utils/gssd/context_heimdal.c
@@ -0,0 +1,275 @@
+/*
+ Copyright (c) 2004-2006 The Regents of the University of Michigan.
+ 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. 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.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#ifndef HAVE_LUCID_CONTEXT_SUPPORT
+#ifdef HAVE_HEIMDAL
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <string.h>
+#include <errno.h>
+#include <krb5.h>
+#include <gssapi.h> /* Must use the heimdal copy! */
+#ifdef HAVE_COM_ERR_H
+#include <com_err.h>
+#endif
+#include "err_util.h"
+#include "gss_oids.h"
+#include "write_bytes.h"
+
+int write_heimdal_keyblock(char **p, char *end, krb5_keyblock *key)
+{
+ gss_buffer_desc tmp;
+ int code = -1;
+
+ if (WRITE_BYTES(p, end, key->keytype)) goto out_err;
+ tmp.length = key->keyvalue.length;
+ tmp.value = key->keyvalue.data;
+ if (write_buffer(p, end, &tmp)) goto out_err;
+ code = 0;
+ out_err:
+ return(code);
+}
+
+int write_heimdal_enc_key(char **p, char *end, gss_ctx_id_t ctx)
+{
+ krb5_keyblock enc_key, *key;
+ krb5_context context;
+ krb5_error_code ret;
+ int i;
+ char *skd, *dkd, *k5err = NULL;
+ int code = -1;
+
+ if ((ret = krb5_init_context(&context))) {
+ k5err = gssd_k5_err_msg(NULL, ret);
+ printerr(0, "ERROR: initializing krb5_context: %s\n", k5err);
+ goto out_err;
+ }
+
+ if ((ret = krb5_auth_con_getlocalsubkey(context,
+ ctx->auth_context, &key))){
+ k5err = gssd_k5_err_msg(context, ret);
+ printerr(0, "ERROR: getting auth_context key: %s\n", k5err);
+ goto out_err_free_context;
+ }
+
+ memset(&enc_key, 0, sizeof(enc_key));
+ enc_key.keytype = key->keytype;
+ /* XXX current kernel code only handles des-cbc-raw (4) */
+ if (enc_key.keytype != 4) {
+ printerr(1, "WARN: write_heimdal_enc_key: "
+ "overriding heimdal keytype (%d => %d)\n",
+ enc_key.keytype, 4);
+ enc_key.keytype = 4;
+ }
+ enc_key.keyvalue.length = key->keyvalue.length;
+ if ((enc_key.keyvalue.data =
+ calloc(1, enc_key.keyvalue.length)) == NULL) {
+ k5err = gssd_k5_err_msg(context, ENOMEM);
+ printerr(0, "ERROR: allocating memory for enc key: %s\n",
+ k5err);
+ goto out_err_free_key;
+ }
+ skd = (char *) key->keyvalue.data;
+ dkd = (char *) enc_key.keyvalue.data;
+ for (i = 0; i < enc_key.keyvalue.length; i++)
+ dkd[i] = skd[i] ^ 0xf0;
+ if (write_heimdal_keyblock(p, end, &enc_key)) {
+ goto out_err_free_enckey;
+ }
+
+ code = 0;
+
+ out_err_free_enckey:
+ krb5_free_keyblock_contents(context, &enc_key);
+ out_err_free_key:
+ krb5_free_keyblock(context, key);
+ out_err_free_context:
+ krb5_free_context(context);
+ out_err:
+ free(k5err);
+ printerr(2, "write_heimdal_enc_key: %s\n", code ? "FAILED" : "SUCCESS");
+ return(code);
+}
+
+int write_heimdal_seq_key(char **p, char *end, gss_ctx_id_t ctx)
+{
+ krb5_keyblock *key;
+ krb5_context context;
+ krb5_error_code ret;
+ char *k5err = NULL;
+ int code = -1;
+
+ if ((ret = krb5_init_context(&context))) {
+ k5err = gssd_k5_err_msg(NULL, ret);
+ printerr(0, "ERROR: initializing krb5_context: %s\n", k5err);
+ goto out_err;
+ }
+
+ if ((ret = krb5_auth_con_getlocalsubkey(context,
+ ctx->auth_context, &key))){
+ k5err = gssd_k5_err_msg(context, ret);
+ printerr(0, "ERROR: getting auth_context key: %s\n", k5err);
+ goto out_err_free_context;
+ }
+
+ /* XXX current kernel code only handles des-cbc-raw (4) */
+ if (key->keytype != 4) {
+ printerr(1, "WARN: write_heimdal_seq_key: "
+ "overriding heimdal keytype (%d => %d)\n",
+ key->keytype, 4);
+ key->keytype = 4;
+ }
+
+ if (write_heimdal_keyblock(p, end, key)) {
+ goto out_err_free_key;
+ }
+
+ code = 0;
+
+ out_err_free_key:
+ krb5_free_keyblock(context, key);
+ out_err_free_context:
+ krb5_free_context(context);
+ out_err:
+ free(k5err);
+ printerr(2, "write_heimdal_seq_key: %s\n", code ? "FAILED" : "SUCCESS");
+ return(code);
+}
+
+/*
+ * The following is the kernel structure that we are filling in:
+ *
+ * struct krb5_ctx {
+ * int initiate;
+ * int seed_init;
+ * unsigned char seed[16];
+ * int signalg;
+ * int sealalg;
+ * struct crypto_tfm *enc;
+ * struct crypto_tfm *seq;
+ * s32 endtime;
+ * u32 seq_send;
+ * struct xdr_netobj mech_used;
+ * };
+ *
+ * However, note that we do not send the data fields in the
+ * order they appear in the structure. The order they are
+ * sent down in is:
+ *
+ * initiate
+ * seed_init
+ * seed
+ * signalg
+ * sealalg
+ * endtime
+ * seq_send
+ * mech_used
+ * enc key
+ * seq key
+ *
+ */
+
+int
+serialize_krb5_ctx(gss_ctx_id_t *_ctx, gss_buffer_desc *buf, int32_t *endtime)
+{
+ gss_ctx_id_t ctx = *_ctx;
+ char *p, *end;
+ static int constant_one = 1;
+ static int constant_zero = 0;
+ unsigned char fakeseed[16];
+ uint32_t algorithm;
+
+ if (!(buf->value = calloc(1, MAX_CTX_LEN)))
+ goto out_err;
+ p = buf->value;
+ end = buf->value + MAX_CTX_LEN;
+
+
+ /* initiate: 1 => initiating 0 => accepting */
+ if (ctx->more_flags & LOCAL) {
+ if (WRITE_BYTES(&p, end, constant_one)) goto out_err;
+ }
+ else {
+ if (WRITE_BYTES(&p, end, constant_zero)) goto out_err;
+ }
+
+ /* seed_init: not used by kernel code */
+ if (WRITE_BYTES(&p, end, constant_zero)) goto out_err;
+
+ /* seed: not used by kernel code */
+ memset(&fakeseed, 0, sizeof(fakeseed));
+ if (write_bytes(&p, end, &fakeseed, 16)) goto out_err;
+
+ /* signalg */
+ algorithm = 0; /* SGN_ALG_DES_MAC_MD5 XXX */
+ if (WRITE_BYTES(&p, end, algorithm)) goto out_err;
+
+ /* sealalg */
+ algorithm = 0; /* SEAL_ALG_DES XXX */
+ if (WRITE_BYTES(&p, end, algorithm)) goto out_err;
+
+ /* endtime */
+ if (WRITE_BYTES(&p, end, ctx->lifetime)) goto out_err;
+
+ if (endtime)
+ *endtime = ctx->lifetime;
+
+ /* seq_send */
+ if (WRITE_BYTES(&p, end, ctx->auth_context->local_seqnumber))
+ goto out_err;
+ /* mech_used */
+ if (write_buffer(&p, end, (gss_buffer_desc*)&krb5oid)) goto out_err;
+
+ /* enc: derive the encryption key and copy it into buffer */
+ if (write_heimdal_enc_key(&p, end, ctx)) goto out_err;
+
+ /* seq: get the sequence number key and copy it into buffer */
+ if (write_heimdal_seq_key(&p, end, ctx)) goto out_err;
+
+ buf->length = p - (char *)buf->value;
+ printerr(4, "serialize_krb5_ctx: returning buffer "
+ "with %d bytes\n", buf->length);
+
+ return 0;
+out_err:
+ printerr(0, "ERROR: failed exporting Heimdal krb5 ctx to kernel\n");
+ if (buf->value) free(buf->value);
+ buf->length = 0;
+ return -1;
+}
+
+#endif /* HAVE_HEIMDAL */
+#endif /* HAVE_LUCID_CONTEXT_SUPPORT */
diff --git a/utils/gssd/context_lucid.c b/utils/gssd/context_lucid.c
new file mode 100644
index 0000000..5d77c21
--- /dev/null
+++ b/utils/gssd/context_lucid.c
@@ -0,0 +1,327 @@
+/*
+ * COPYRIGHT (c) 2006
+ * The Regents of the University of Michigan
+ * ALL RIGHTS RESERVED
+ *
+ * Permission is granted to use, copy, create derivative works
+ * and redistribute this software and such derivative works
+ * for any purpose, so long as the name of The University of
+ * Michigan is not used in any advertising or publicity
+ * pertaining to the use of distribution of this software
+ * without specific, written prior authorization. If the
+ * above copyright notice or any other identification of the
+ * University of Michigan is included in any copy of any
+ * portion of this software, then the disclaimer below must
+ * also be included.
+ *
+ * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION
+ * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY
+ * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF
+ * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
+ * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE
+ * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN
+ * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGES.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#ifdef HAVE_LUCID_CONTEXT_SUPPORT
+
+/*
+ * Newer versions of MIT and Heimdal have lucid context support.
+ * We can use common code if it is supported.
+ */
+
+#include <stdio.h>
+#include <syslog.h>
+#include <string.h>
+#include <errno.h>
+
+#include <gssapi/gssapi_krb5.h>
+
+#include "gss_util.h"
+#include "gss_oids.h"
+#include "err_util.h"
+#include "context.h"
+
+#ifndef OM_uint64
+typedef uint64_t OM_uint64;
+#endif
+
+static int
+write_lucid_keyblock(char **p, char *end, gss_krb5_lucid_key_t *key)
+{
+ gss_buffer_desc tmp;
+
+ if (WRITE_BYTES(p, end, key->type)) return -1;
+ tmp.length = key->length;
+ tmp.value = key->data;
+ if (write_buffer(p, end, &tmp)) return -1;
+ return 0;
+}
+
+static int
+prepare_krb5_rfc1964_buffer(gss_krb5_lucid_context_v1_t *lctx,
+ gss_buffer_desc *buf, int32_t *endtime)
+{
+#define FAKESEED_SIZE 16
+ char *p, *end;
+ static int constant_zero = 0;
+ unsigned char fakeseed[FAKESEED_SIZE];
+ uint32_t word_send_seq;
+ gss_krb5_lucid_key_t enc_key;
+ uint32_t i;
+ char *skd, *dkd;
+ gss_buffer_desc fakeoid;
+ int err;
+
+ /*
+ * The new Kerberos interface to get the gss context
+ * does not include the seed or seed_init fields
+ * because we never really use them. But for now,
+ * send down a fake buffer so we can use the same
+ * interface to the kernel.
+ */
+ memset(&enc_key, 0, sizeof(enc_key));
+ memset(&fakeoid, 0, sizeof(fakeoid));
+ memset(fakeseed, 0, FAKESEED_SIZE);
+
+ if (!(buf->value = calloc(1, MAX_CTX_LEN)))
+ goto out_err;
+ p = buf->value;
+ end = buf->value + MAX_CTX_LEN;
+
+ if (WRITE_BYTES(&p, end, lctx->initiate)) goto out_err;
+
+ /* seed_init and seed not used by kernel anyway */
+ if (WRITE_BYTES(&p, end, constant_zero)) goto out_err;
+ if (write_bytes(&p, end, &fakeseed, FAKESEED_SIZE)) goto out_err;
+
+ if (WRITE_BYTES(&p, end, lctx->rfc1964_kd.sign_alg)) goto out_err;
+ if (WRITE_BYTES(&p, end, lctx->rfc1964_kd.seal_alg)) goto out_err;
+ if (WRITE_BYTES(&p, end, lctx->endtime)) goto out_err;
+ if (endtime)
+ *endtime = lctx->endtime;
+ word_send_seq = lctx->send_seq; /* XXX send_seq is 64-bit */
+ if (WRITE_BYTES(&p, end, word_send_seq)) goto out_err;
+ if (write_oid(&p, end, &krb5oid)) goto out_err;
+
+#ifdef HAVE_HEIMDAL
+ /*
+ * The kernel gss code expects des-cbc-raw for all flavors of des.
+ * The keytype from MIT has this type, but Heimdal does not.
+ * Force the Heimdal keytype to 4 (des-cbc-raw).
+ * Note that the rfc1964 version only supports DES enctypes.
+ */
+ if (lctx->rfc1964_kd.ctx_key.type != 4) {
+ printerr(2, "%s: overriding heimdal keytype (%d => %d)\n",
+ __FUNCTION__, lctx->rfc1964_kd.ctx_key.type, 4);
+ lctx->rfc1964_kd.ctx_key.type = 4;
+ }
+#endif
+ printerr(2, "%s: serializing keys with enctype %d and length %d\n",
+ __FUNCTION__, lctx->rfc1964_kd.ctx_key.type,
+ lctx->rfc1964_kd.ctx_key.length);
+
+ /* derive the encryption key and copy it into buffer */
+ enc_key.type = lctx->rfc1964_kd.ctx_key.type;
+ enc_key.length = lctx->rfc1964_kd.ctx_key.length;
+ if ((enc_key.data = calloc(1, enc_key.length)) == NULL)
+ goto out_err;
+ skd = (char *) lctx->rfc1964_kd.ctx_key.data;
+ dkd = (char *) enc_key.data;
+ for (i = 0; i < enc_key.length; i++)
+ dkd[i] = skd[i] ^ 0xf0;
+ err = write_lucid_keyblock(&p, end, &enc_key);
+ free(enc_key.data);
+ if (err)
+ goto out_err;
+
+ if (write_lucid_keyblock(&p, end, &lctx->rfc1964_kd.ctx_key))
+ goto out_err;
+
+ buf->length = p - (char *)buf->value;
+ return 0;
+out_err:
+ printerr(0, "ERROR: failed serializing krb5 context for kernel\n");
+ if (buf->value) free(buf->value);
+ buf->length = 0;
+ return -1;
+}
+
+/* Flags for version 2 context flags */
+#define KRB5_CTX_FLAG_INITIATOR 0x00000001
+#define KRB5_CTX_FLAG_CFX 0x00000002
+#define KRB5_CTX_FLAG_ACCEPTOR_SUBKEY 0x00000004
+
+/*
+ * Prepare a new-style buffer, as defined in rfc4121 (a.k.a. cfx),
+ * to send to the kernel for newer encryption types -- or for DES3.
+ *
+ * The new format is:
+ *
+ * u32 flags;
+ * #define KRB5_CTX_FLAG_INITIATOR 0x00000001
+ * #define KRB5_CTX_FLAG_CFX 0x00000002
+ * #define KRB5_CTX_FLAG_ACCEPTOR_SUBKEY 0x00000004
+ * s32 endtime;
+ * u64 seq_send;
+ * u32 enctype; ( encrption type of key )
+ * raw key; ( raw key bytes (kernel will derive))
+ *
+ */
+static int
+prepare_krb5_rfc4121_buffer(gss_krb5_lucid_context_v1_t *lctx,
+ gss_buffer_desc *buf, int32_t *endtime)
+{
+ char *p, *end;
+ uint32_t v2_flags = 0;
+ uint32_t enctype;
+ uint32_t keysize;
+
+ if (!(buf->value = calloc(1, MAX_CTX_LEN)))
+ goto out_err;
+ p = buf->value;
+ end = buf->value + MAX_CTX_LEN;
+
+ /* Version 2 */
+ if (lctx->initiate)
+ v2_flags |= KRB5_CTX_FLAG_INITIATOR;
+ if (lctx->protocol != 0)
+ v2_flags |= KRB5_CTX_FLAG_CFX;
+ if (lctx->protocol != 0 && lctx->cfx_kd.have_acceptor_subkey == 1)
+ v2_flags |= KRB5_CTX_FLAG_ACCEPTOR_SUBKEY;
+
+ if (WRITE_BYTES(&p, end, v2_flags)) goto out_err;
+ if (WRITE_BYTES(&p, end, lctx->endtime)) goto out_err;
+ if (endtime)
+ *endtime = lctx->endtime;
+ if (WRITE_BYTES(&p, end, lctx->send_seq)) goto out_err;
+
+ /* Protocol 0 here implies DES3 or RC4 */
+ printerr(4, "%s: protocol %d\n", __FUNCTION__, lctx->protocol);
+ if (lctx->protocol == 0) {
+ enctype = lctx->rfc1964_kd.ctx_key.type;
+ keysize = lctx->rfc1964_kd.ctx_key.length;
+ } else {
+ if (lctx->cfx_kd.have_acceptor_subkey) {
+ enctype = lctx->cfx_kd.acceptor_subkey.type;
+ keysize = lctx->cfx_kd.acceptor_subkey.length;
+ } else {
+ enctype = lctx->cfx_kd.ctx_key.type;
+ keysize = lctx->cfx_kd.ctx_key.length;
+ }
+ }
+ printerr(4, "%s: serializing key with enctype %d and size %d\n",
+ __FUNCTION__, enctype, keysize);
+
+ if (WRITE_BYTES(&p, end, enctype)) goto out_err;
+
+ if (lctx->protocol == 0) {
+ if (write_bytes(&p, end, lctx->rfc1964_kd.ctx_key.data,
+ lctx->rfc1964_kd.ctx_key.length))
+ goto out_err;
+ } else {
+ if (lctx->cfx_kd.have_acceptor_subkey) {
+ if (write_bytes(&p, end,
+ lctx->cfx_kd.acceptor_subkey.data,
+ lctx->cfx_kd.acceptor_subkey.length))
+ goto out_err;
+ } else {
+ if (write_bytes(&p, end, lctx->cfx_kd.ctx_key.data,
+ lctx->cfx_kd.ctx_key.length))
+ goto out_err;
+ }
+ }
+
+ buf->length = p - (char *)buf->value;
+ return 0;
+
+out_err:
+ printerr(0, "ERROR: %s: failed serializing krb5 context for kernel\n",
+ __FUNCTION__);
+ if (buf->value) {
+ free(buf->value);
+ buf->value = NULL;
+ }
+ buf->length = 0;
+ return -1;
+}
+
+
+int
+serialize_krb5_ctx(gss_ctx_id_t *ctx, gss_buffer_desc *buf, int32_t *endtime)
+{
+ OM_uint32 maj_stat, min_stat;
+ void *return_ctx = 0;
+ OM_uint32 vers;
+ gss_krb5_lucid_context_v1_t *lctx = 0;
+ int retcode = 0;
+
+ printerr(4, "DEBUG: %s: lucid version!\n", __FUNCTION__);
+ maj_stat = gss_export_lucid_sec_context(&min_stat, ctx,
+ 1, &return_ctx);
+ if (maj_stat != GSS_S_COMPLETE) {
+ pgsserr("gss_export_lucid_sec_context",
+ maj_stat, min_stat, &krb5oid);
+ goto out_err;
+ }
+
+ /* Check the version returned, we only support v1 right now */
+ vers = ((gss_krb5_lucid_context_version_t *)return_ctx)->version;
+ switch (vers) {
+ case 1:
+ lctx = (gss_krb5_lucid_context_v1_t *) return_ctx;
+ break;
+ default:
+ printerr(0, "ERROR: unsupported lucid sec context version %d\n",
+ vers);
+ goto out_err;
+ break;
+ }
+
+ /*
+ * Now lctx points to a lucid context that we can send down to kernel
+ *
+ * Note: we send down different information to the kernel depending
+ * on the protocol version and the enctyption type.
+ * For protocol version 0 with all enctypes besides DES3, we use
+ * the original format. For protocol version != 0 or DES3, we
+ * send down the new style information.
+ */
+
+ if (lctx->protocol == 0 && lctx->rfc1964_kd.ctx_key.type <= 4)
+ retcode = prepare_krb5_rfc1964_buffer(lctx, buf, endtime);
+ else
+ retcode = prepare_krb5_rfc4121_buffer(lctx, buf, endtime);
+
+ maj_stat = gss_free_lucid_sec_context(&min_stat, ctx, return_ctx);
+ if (maj_stat != GSS_S_COMPLETE) {
+ pgsserr("gss_free_lucid_sec_context",
+ maj_stat, min_stat, &krb5oid);
+ printerr(0, "WARN: failed to free lucid sec context\n");
+ }
+
+ if (retcode) {
+ printerr(1, "%s: prepare_krb5_*_buffer failed (retcode = %d)\n",
+ __FUNCTION__, retcode);
+ goto out_err;
+ }
+
+ return 0;
+
+out_err:
+ printerr(0, "ERROR: failed serializing krb5 context for kernel\n");
+ return -1;
+}
+
+
+
+#endif /* HAVE_LUCID_CONTEXT_SUPPORT */
diff --git a/utils/gssd/context_mit.c b/utils/gssd/context_mit.c
new file mode 100644
index 0000000..fad6756
--- /dev/null
+++ b/utils/gssd/context_mit.c
@@ -0,0 +1,280 @@
+/*
+ Copyright (c) 2004-2006 The Regents of the University of Michigan.
+ 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. 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.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#ifndef HAVE_LUCID_CONTEXT_SUPPORT
+#ifdef HAVE_KRB5
+
+#include <stdio.h>
+#include <syslog.h>
+#include <string.h>
+#include <errno.h>
+#include <gssapi/gssapi.h>
+#include <rpc/rpc.h>
+#include <rpc/auth_gss.h>
+#include "gss_util.h"
+#include "gss_oids.h"
+#include "err_util.h"
+#include "context.h"
+
+#include <krb5.h>
+
+#if (KRB5_VERSION > 131)
+/* XXX argggg, there's gotta be a better way than just duplicating this
+ * whole struct. Unfortunately, this is in a "private" header file,
+ * so this is our best choice at this point :-/
+ */
+
+typedef struct _krb5_gss_ctx_id_rec {
+ unsigned int initiate : 1; /* nonzero if initiating, zero if accepting */
+ unsigned int established : 1;
+ unsigned int big_endian : 1;
+ unsigned int have_acceptor_subkey : 1;
+ unsigned int seed_init : 1; /* XXX tested but never actually set */
+#ifdef CFX_EXERCISE
+ unsigned int testing_unknown_tokid : 1; /* for testing only */
+#endif
+ OM_uint32 gss_flags;
+ unsigned char seed[16];
+ krb5_principal here;
+ krb5_principal there;
+ krb5_keyblock *subkey;
+ int signalg;
+ size_t cksum_size;
+ int sealalg;
+ krb5_keyblock *enc;
+ krb5_keyblock *seq;
+ krb5_timestamp endtime;
+ krb5_flags krb_flags;
+ /* XXX these used to be signed. the old spec is inspecific, and
+ the new spec specifies unsigned. I don't believe that the change
+ affects the wire encoding. */
+ uint64_t seq_send; /* gssint_uint64 */
+ uint64_t seq_recv; /* gssint_uint64 */
+ void *seqstate;
+ krb5_auth_context auth_context;
+ gss_OID_desc *mech_used; /* gss_OID_desc */
+ /* Protocol spec revision
+ 0 => RFC 1964 with 3DES and RC4 enhancements
+ 1 => draft-ietf-krb-wg-gssapi-cfx-01
+ No others defined so far. */
+ int proto;
+ krb5_cksumtype cksumtype; /* for "main" subkey */
+ krb5_keyblock *acceptor_subkey; /* CFX only */
+ krb5_cksumtype acceptor_subkey_cksumtype;
+#ifdef CFX_EXERCISE
+ gss_buffer_desc init_token;
+#endif
+} krb5_gss_ctx_id_rec, *krb5_gss_ctx_id_t;
+
+#else /* KRB5_VERSION > 131 */
+
+typedef struct _krb5_gss_ctx_id_rec {
+ int initiate;
+ u_int32_t gss_flags;
+ int seed_init;
+ unsigned char seed[16];
+ krb5_principal here;
+ krb5_principal there;
+ krb5_keyblock *subkey;
+ int signalg;
+ int cksum_size;
+ int sealalg;
+ krb5_keyblock *enc;
+ krb5_keyblock *seq;
+ krb5_timestamp endtime;
+ krb5_flags krb_flags;
+ krb5_ui_4 seq_send;
+ krb5_ui_4 seq_recv;
+ void *seqstate;
+ int established;
+ int big_endian;
+ krb5_auth_context auth_context;
+ gss_OID_desc *mech_used;
+ int nctypes;
+ krb5_cksumtype *ctypes;
+} krb5_gss_ctx_id_rec, *krb5_gss_ctx_id_t;
+
+#endif /* KRB5_VERSION */
+
+
+static int
+write_keyblock(char **p, char *end, struct _krb5_keyblock *arg)
+{
+ gss_buffer_desc tmp;
+
+ if (WRITE_BYTES(p, end, arg->enctype)) return -1;
+ tmp.length = arg->length;
+ tmp.value = arg->contents;
+ if (write_buffer(p, end, &tmp)) return -1;
+ return 0;
+}
+
+/*
+ * We really shouldn't know about glue-layer context structure, but
+ * we need to get at the real krb5 context pointer. This should be
+ * removed as soon as we say there is no support for MIT Kerberos
+ * prior to 1.4 -- which gives us "legal" access to the context info.
+ */
+typedef struct gss_union_ctx_id_t {
+ gss_OID mech_type;
+ gss_ctx_id_t internal_ctx_id;
+} gss_union_ctx_id_desc, *gss_union_ctx_id_t;
+
+int
+serialize_krb5_ctx(gss_ctx_id_t *ctx, gss_buffer_desc *buf, int32_t *endtime)
+{
+ krb5_gss_ctx_id_t kctx = ((gss_union_ctx_id_t)(*ctx))->internal_ctx_id;
+ char *p, *end;
+ static int constant_zero = 0;
+ static int constant_one = 1;
+ static int constant_two = 2;
+ uint32_t word_seq_send;
+ u_int64_t seq_send_64bit;
+ uint32_t v2_flags = 0;
+
+ if (!(buf->value = calloc(1, MAX_CTX_LEN)))
+ goto out_err;
+ p = buf->value;
+ end = buf->value + MAX_CTX_LEN;
+
+ switch (kctx->enc->enctype) {
+ case ENCTYPE_DES_CBC_CRC:
+ case ENCTYPE_DES_CBC_MD4:
+ case ENCTYPE_DES_CBC_MD5:
+ case ENCTYPE_DES_CBC_RAW:
+ /* Old format of context to the kernel */
+ if (kctx->initiate) {
+ if (WRITE_BYTES(&p, end, constant_one)) goto out_err;
+ }
+ else {
+ if (WRITE_BYTES(&p, end, constant_zero)) goto out_err;
+ }
+ if (kctx->seed_init) {
+ if (WRITE_BYTES(&p, end, constant_one)) goto out_err;
+ }
+ else {
+ if (WRITE_BYTES(&p, end, constant_zero)) goto out_err;
+ }
+ if (write_bytes(&p, end, &kctx->seed, sizeof(kctx->seed)))
+ goto out_err;
+ if (WRITE_BYTES(&p, end, kctx->signalg)) goto out_err;
+ if (WRITE_BYTES(&p, end, kctx->sealalg)) goto out_err;
+ if (WRITE_BYTES(&p, end, kctx->endtime)) goto out_err;
+ if (endtime)
+ *endtime = kctx->endtime;
+ word_seq_send = kctx->seq_send;
+ if (WRITE_BYTES(&p, end, word_seq_send)) goto out_err;
+ if (write_oid(&p, end, kctx->mech_used)) goto out_err;
+
+ printerr(2, "serialize_krb5_ctx: serializing keys with "
+ "enctype %d and length %d\n",
+ kctx->enc->enctype, kctx->enc->length);
+
+ if (write_keyblock(&p, end, kctx->enc)) goto out_err;
+ if (write_keyblock(&p, end, kctx->seq)) goto out_err;
+ break;
+ case ENCTYPE_DES3_CBC_RAW:
+ case ENCTYPE_DES3_CBC_SHA1:
+ case ENCTYPE_ARCFOUR_HMAC:
+ case ENCTYPE_ARCFOUR_HMAC_EXP:
+ case ENCTYPE_AES128_CTS_HMAC_SHA1_96:
+ case ENCTYPE_AES256_CTS_HMAC_SHA1_96:
+ /* New format of context to the kernel */
+ /* u32 flags;
+ * #define KRB5_CTX_FLAG_INITIATOR 0x00000001
+ * #define KRB5_CTX_FLAG_CFX 0x00000002
+ * #define KRB5_CTX_FLAG_ACCEPTOR_SUBKEY 0x00000004
+ * s32 endtime;
+ * u64 seq_send;
+ * u32 enctype;
+ * rawkey data
+ */
+
+ if (kctx->initiate)
+ v2_flags |= KRB5_CTX_FLAG_INITIATOR;
+ if (kctx->proto == 1)
+ v2_flags |= KRB5_CTX_FLAG_CFX;
+ if (kctx->have_acceptor_subkey)
+ v2_flags |= KRB5_CTX_FLAG_ACCEPTOR_SUBKEY;
+ if (WRITE_BYTES(&p, end, v2_flags)) goto out_err;
+ if (WRITE_BYTES(&p, end, kctx->endtime)) goto out_err;
+
+ seq_send_64bit = kctx->seq_send;
+ if (WRITE_BYTES(&p, end, seq_send_64bit)) goto out_err;
+
+ if (kctx->have_acceptor_subkey) {
+ if (WRITE_BYTES(&p, end, kctx->acceptor_subkey->enctype))
+ goto out_err;
+ printerr(2, "serialize_krb5_ctx: serializing subkey "
+ "with enctype %d and size %d\n",
+ kctx->acceptor_subkey->enctype,
+ kctx->acceptor_subkey->length);
+
+ if (write_bytes(&p, end,
+ kctx->acceptor_subkey->contents,
+ kctx->acceptor_subkey->length))
+ goto out_err;
+ } else {
+ if (WRITE_BYTES(&p, end, kctx->enc->enctype))
+ goto out_err;
+ printerr(2, "serialize_krb5_ctx: serializing key "
+ "with enctype %d and size %d\n",
+ kctx->enc->enctype, kctx->enc->length);
+
+ if (write_bytes(&p, end, kctx->enc->contents,
+ kctx->enc->length))
+ goto out_err;
+ }
+ break;
+ default:
+ printerr(0, "ERROR: serialize_krb5_ctx: unsupported encryption "
+ "algorithm %d\n", kctx->enc->enctype);
+ goto out_err;
+ }
+
+ buf->length = p - (char *)buf->value;
+ return 0;
+
+out_err:
+ printerr(0, "ERROR: failed serializing krb5 context for kernel\n");
+ if (buf->value) {
+ free(buf->value);
+ }
+ buf->value = NULL;
+ buf->length = 0;
+ return -1;
+}
+
+#endif /* HAVE_KRB5 */
+#endif /* HAVE_LUCID_CONTEXT_SUPPORT */
diff --git a/utils/gssd/err_util.c b/utils/gssd/err_util.c
new file mode 100644
index 0000000..27abd23
--- /dev/null
+++ b/utils/gssd/err_util.c
@@ -0,0 +1,86 @@
+/*
+ Copyright (c) 2004 The Regents of the University of Michigan.
+ 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. 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.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include "xlog.h"
+#include "err_util.h"
+
+static int verbosity = 0;
+static int fg = 0;
+
+void initerr(char *progname, int set_verbosity, int set_fg)
+{
+ verbosity = set_verbosity;
+ fg = set_fg;
+ if (!fg)
+ xlog_open(progname);
+}
+
+
+void printerr(int priority, char *format, ...)
+{
+ va_list args;
+
+ /* Don't bother formatting a message we're never going to print! */
+ if (priority > verbosity)
+ return;
+
+ va_start(args, format);
+ if (fg)
+ vfprintf(stderr, format, args);
+ else
+ xlog_backend(L_ERROR, format, args);
+ va_end(args);
+}
+
+int get_verbosity(void)
+{
+ return verbosity;
+}
+
+char *
+sec2time(int value)
+{
+ static char buf[BUFSIZ];
+ int hr, min, sec;
+
+ hr = (value / 3600);
+ min = (value - (3600*hr))/60;
+ sec = (value - (3600*hr) - (min*60));
+ sprintf(buf, "%dh:%dm:%ds", hr, min, sec);
+ return(buf);
+}
+
diff --git a/utils/gssd/err_util.h b/utils/gssd/err_util.h
new file mode 100644
index 0000000..6fa9d3d
--- /dev/null
+++ b/utils/gssd/err_util.h
@@ -0,0 +1,39 @@
+/*
+ Copyright (c) 2004 The Regents of the University of Michigan.
+ 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. 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.
+*/
+
+#ifndef _ERR_UTIL_H_
+#define _ERR_UTIL_H_
+
+void initerr(char *progname, int verbosity, int fg);
+void printerr(int priority, char *format, ...);
+int get_verbosity(void);
+char * sec2time(int);
+
+#endif /* _ERR_UTIL_H_ */
diff --git a/utils/gssd/gss_names.c b/utils/gssd/gss_names.c
new file mode 100644
index 0000000..982b96f
--- /dev/null
+++ b/utils/gssd/gss_names.c
@@ -0,0 +1,141 @@
+/*
+ Copyright (c) 2000 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Copyright (c) 2002 Bruce Fields <bfields@UMICH.EDU>
+
+ 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.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <rpc/rpc.h>
+
+#include <pwd.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <nfsidmap.h>
+#include <nfslib.h>
+#include <time.h>
+
+#include "svcgssd.h"
+#include "gss_util.h"
+#include "gss_names.h"
+#include "err_util.h"
+#include "context.h"
+#include "misc.h"
+#include "gss_oids.h"
+#include "svcgssd_krb5.h"
+
+static int
+get_krb5_hostbased_name(gss_buffer_desc *name, char **hostbased_name)
+{
+ char *p, *sname = NULL;
+ if (strchr(name->value, '@') && strchr(name->value, '/')) {
+ if ((sname = calloc(name->length, 1)) == NULL) {
+ printerr(0, "ERROR: get_krb5_hostbased_name failed "
+ "to allocate %d bytes\n", name->length);
+ return -1;
+ }
+ /* read in name and instance and replace '/' with '@' */
+ sscanf(name->value, "%[^@]", sname);
+ p = strrchr(sname, '/');
+ if (p == NULL) { /* The '@' preceeded the '/' */
+ free(sname);
+ return -1;
+ }
+ *p = '@';
+ }
+ *hostbased_name = sname;
+ return 0;
+}
+
+int
+get_hostbased_client_name(gss_name_t client_name, gss_OID mech,
+ char **hostbased_name)
+{
+ u_int32_t maj_stat, min_stat;
+ gss_buffer_desc name;
+ gss_OID name_type = GSS_C_NO_OID;
+ char *cname;
+ int res = -1;
+
+ *hostbased_name = NULL; /* preset in case we fail */
+
+ /* Get the client's gss authenticated name */
+ maj_stat = gss_display_name(&min_stat, client_name, &name, &name_type);
+ if (maj_stat != GSS_S_COMPLETE) {
+ pgsserr("get_hostbased_client_name: gss_display_name",
+ maj_stat, min_stat, mech);
+ goto out_err;
+ }
+ if (name.length >= 0xffff) { /* don't overflow */
+ printerr(0, "ERROR: get_hostbased_client_name: "
+ "received gss_name is too long (%d bytes)\n",
+ name.length);
+ goto out_rel_buf;
+ }
+
+ /* For Kerberos, transform the NT_KRB5_PRINCIPAL name to
+ * an NT_HOSTBASED_SERVICE name */
+ if (g_OID_equal(&krb5oid, mech)) {
+ if (get_krb5_hostbased_name(&name, &cname) != 0)
+ goto out_rel_buf;
+ *hostbased_name = cname;
+ } else {
+ printerr(1, "WARNING: unknown/unsupport mech OID\n");
+ goto out_rel_buf;
+ }
+
+ res = 0;
+out_rel_buf:
+ gss_release_buffer(&min_stat, &name);
+out_err:
+ return res;
+}
+
+void
+get_hostbased_client_buffer(gss_name_t client_name, gss_OID mech,
+ gss_buffer_t buf)
+{
+ char *hname;
+
+ if (!get_hostbased_client_name(client_name, mech, &hname)) {
+ buf->length = strlen(hname) + 1;
+ buf->value = hname;
+ } else {
+ buf->length = 0;
+ buf->value = NULL;
+ }
+}
diff --git a/utils/gssd/gss_names.h b/utils/gssd/gss_names.h
new file mode 100644
index 0000000..ce182f7
--- /dev/null
+++ b/utils/gssd/gss_names.h
@@ -0,0 +1,36 @@
+/*
+ Copyright (c) 2000 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Copyright (c) 2002 Bruce Fields <bfields@UMICH.EDU>
+
+ 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.
+*/
+
+extern int get_hostbased_client_name(gss_name_t client_name, gss_OID mech,
+ char **hostbased_name);
+extern void get_hostbased_client_buffer(gss_name_t client_name,
+ gss_OID mech, gss_buffer_t buf);
diff --git a/utils/gssd/gss_oids.c b/utils/gssd/gss_oids.c
new file mode 100644
index 0000000..4362de2
--- /dev/null
+++ b/utils/gssd/gss_oids.c
@@ -0,0 +1,40 @@
+/*
+ Copyright (c) 2004 The Regents of the University of Michigan.
+ 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. 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.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <sys/types.h>
+#include <gssapi/gssapi.h>
+
+/* from kerberos source, gssapi_krb5.c */
+gss_OID_desc krb5oid =
+ {9, "\052\206\110\206\367\022\001\002\002"};
diff --git a/utils/gssd/gss_oids.h b/utils/gssd/gss_oids.h
new file mode 100644
index 0000000..fde8532
--- /dev/null
+++ b/utils/gssd/gss_oids.h
@@ -0,0 +1,44 @@
+/*
+ Copyright (c) 2004 The Regents of the University of Michigan.
+ 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. 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.
+*/
+
+#ifndef _GSS_OIDS_H_
+#define _GSS_OIDS_H_
+
+#include <sys/types.h>
+
+extern gss_OID_desc krb5oid;
+
+#ifndef g_OID_equal
+#define g_OID_equal(o1,o2) \
+ (((o1)->length == (o2)->length) && \
+ (memcmp((o1)->elements,(o2)->elements,(unsigned int) (o1)->length) == 0))
+#endif
+
+#endif /* _GSS_OIDS_H_ */
diff --git a/utils/gssd/gss_util.c b/utils/gssd/gss_util.c
new file mode 100644
index 0000000..a4b2777
--- /dev/null
+++ b/utils/gssd/gss_util.c
@@ -0,0 +1,347 @@
+/*
+ * Adapted in part from MIT Kerberos 5-1.2.1 slave/kprop.c and from
+ * http://docs.sun.com/?p=/doc/816-1331/6m7oo9sms&a=view
+ *
+ * Copyright (c) 2002 The Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Andy Adamson <andros@umich.edu>
+ * J. Bruce Fields <bfields@umich.edu>
+ * Marius Aamodt Eriksen <marius@umich.edu>
+ */
+
+/*
+ * slave/kprop.c
+ *
+ * Copyright 1990,1991 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+
+/*
+ * Copyright 1994 by OpenVision Technologies, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without fee,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear in
+ * supporting documentation, and that the name of OpenVision not be used
+ * in advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission. OpenVision makes no
+ * representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied warranty.
+ *
+ * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
+ * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <errno.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/file.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/param.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <gssapi/gssapi.h>
+#if defined(HAVE_KRB5) && !defined(GSS_C_NT_HOSTBASED_SERVICE)
+#include <gssapi/gssapi_generic.h>
+#define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name
+#endif
+#include "gss_util.h"
+#include "err_util.h"
+#include "gssd.h"
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <stdlib.h>
+#ifdef HAVE_COM_ERR_H
+#include <com_err.h>
+#endif
+
+/* Global gssd_credentials handle */
+gss_cred_id_t gssd_creds;
+
+gss_OID g_mechOid = GSS_C_NULL_OID;
+
+#if 0
+static void
+display_status_1(char *m, u_int32_t code, int type, const gss_OID mech)
+{
+ u_int32_t maj_stat, min_stat;
+ gss_buffer_desc msg = GSS_C_EMPTY_BUFFER;
+ u_int32_t msg_ctx = 0;
+ char *typestr;
+
+ switch (type) {
+ case GSS_C_GSS_CODE:
+ typestr = "GSS";
+ break;
+ case GSS_C_MECH_CODE:
+ typestr = "mechanism";
+ break;
+ default:
+ return;
+ /* NOTREACHED */
+ }
+
+ for (;;) {
+ maj_stat = gss_display_status(&min_stat, code,
+ type, mech, &msg_ctx, &msg);
+ if (maj_stat != GSS_S_COMPLETE) {
+ printerr(0, "ERROR: in call to "
+ "gss_display_status called from %s\n", m);
+ break;
+ } else {
+ printerr(0, "ERROR: GSS-API: (%s) error in %s(): %s\n",
+ typestr, m, (char *)msg.value);
+ }
+
+ if (msg.length != 0)
+ (void) gss_release_buffer(&min_stat, &msg);
+
+ if (msg_ctx == 0)
+ break;
+ }
+}
+#endif
+static char *
+gss_display_error(OM_uint32 status)
+{
+ char *error = NULL;
+
+ switch(status) {
+ case GSS_S_COMPLETE:
+ error = "GSS_S_COMPLETE";
+ break;
+ case GSS_S_CALL_INACCESSIBLE_READ:
+ error = "GSS_S_CALL_INACCESSIBLE_READ";
+ break;
+ case GSS_S_CALL_INACCESSIBLE_WRITE:
+ error = "GSS_S_CALL_INACCESSIBLE_WRITE";
+ break;
+ case GSS_S_CALL_BAD_STRUCTURE:
+ error = "GSS_S_CALL_BAD_STRUCTURE";
+ break;
+ case GSS_S_BAD_MECH:
+ error = "GSS_S_BAD_MECH";
+ break;
+ case GSS_S_BAD_NAME:
+ error = "GSS_S_BAD_NAME";
+ break;
+ case GSS_S_BAD_NAMETYPE:
+ error = "GSS_S_BAD_NAMETYPE";
+ break;
+ case GSS_S_BAD_BINDINGS:
+ error = "GSS_S_BAD_BINDINGS";
+ break;
+ case GSS_S_BAD_STATUS:
+ error = "GSS_S_BAD_STATUS";
+ break;
+ case GSS_S_BAD_SIG:
+ error = "GSS_S_BAD_SIG";
+ break;
+ case GSS_S_NO_CRED:
+ error = "GSS_S_NO_CRED";
+ break;
+ case GSS_S_NO_CONTEXT:
+ error = "GSS_S_NO_CONTEXT";
+ break;
+ case GSS_S_DEFECTIVE_TOKEN:
+ error = "GSS_S_DEFECTIVE_TOKEN";
+ break;
+ case GSS_S_DEFECTIVE_CREDENTIAL:
+ error = "GSS_S_DEFECTIVE_CREDENTIAL";
+ break;
+ case GSS_S_CREDENTIALS_EXPIRED:
+ error = "GSS_S_CREDENTIALS_EXPIRED";
+ break;
+ case GSS_S_CONTEXT_EXPIRED:
+ error = "GSS_S_CONTEXT_EXPIRED";
+ break;
+ case GSS_S_FAILURE:
+ error = "GSS_S_FAILURE";
+ break;
+ case GSS_S_BAD_QOP:
+ error = "GSS_S_BAD_QOP";
+ break;
+ case GSS_S_UNAUTHORIZED:
+ error = "GSS_S_UNAUTHORIZED";
+ break;
+ case GSS_S_UNAVAILABLE:
+ error = "GSS_S_UNAVAILABLE";
+ break;
+ case GSS_S_DUPLICATE_ELEMENT:
+ error = "GSS_S_DUPLICATE_ELEMENT";
+ break;
+ case GSS_S_NAME_NOT_MN:
+ error = "GSS_S_NAME_NOT_MN";
+ break;
+ default:
+ error = "Not defined";
+ }
+ return error;
+}
+
+static void
+display_status_2(char *m, u_int32_t major, u_int32_t minor, const gss_OID mech)
+{
+ u_int32_t maj_stat1, min_stat1;
+ u_int32_t maj_stat2, min_stat2;
+ gss_buffer_desc maj_gss_buf = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc min_gss_buf = GSS_C_EMPTY_BUFFER;
+ char maj_buf[30], min_buf[30];
+ char *maj, *min;
+ u_int32_t msg_ctx = 0;
+ int msg_verbosity = 0;
+
+ /* Get major status message */
+ maj_stat1 = gss_display_status(&min_stat1, major,
+ GSS_C_GSS_CODE, mech, &msg_ctx, &maj_gss_buf);
+
+ if (maj_stat1 != GSS_S_COMPLETE) {
+ snprintf(maj_buf, sizeof(maj_buf), "(0x%08x)", major);
+ maj = &maj_buf[0];
+ } else {
+ maj = maj_gss_buf.value;
+ }
+
+ /* Get minor status message */
+ maj_stat2 = gss_display_status(&min_stat2, minor,
+ GSS_C_MECH_CODE, mech, &msg_ctx, &min_gss_buf);
+
+ if (maj_stat2 != GSS_S_COMPLETE) {
+ snprintf(min_buf, sizeof(min_buf), "(0x%08x)", minor);
+ min = &min_buf[0];
+ } else {
+ min = min_gss_buf.value;
+ }
+
+ if (major == GSS_S_CREDENTIALS_EXPIRED)
+ msg_verbosity = 1;
+
+ printerr(msg_verbosity, "ERROR: GSS-API: error in %s(): %s (%s) - %s\n",
+ m, gss_display_error(major), maj, min);
+
+ if (maj_gss_buf.length != 0)
+ (void) gss_release_buffer(&min_stat1, &maj_gss_buf);
+ if (min_gss_buf.length != 0)
+ (void) gss_release_buffer(&min_stat2, &min_gss_buf);
+}
+
+void
+pgsserr(char *msg, u_int32_t maj_stat, u_int32_t min_stat, const gss_OID mech)
+{
+ display_status_2(msg, maj_stat, min_stat, mech);
+}
+
+int
+gssd_acquire_cred(char *server_name, const gss_OID oid)
+{
+ gss_buffer_desc name;
+ gss_name_t target_name;
+ u_int32_t maj_stat, min_stat;
+ u_int32_t ignore_maj_stat, ignore_min_stat;
+ gss_buffer_desc pbuf;
+
+ /* If server_name is NULL, get cred for GSS_C_NO_NAME */
+ if (server_name == NULL) {
+ target_name = GSS_C_NO_NAME;
+ } else {
+ name.value = (void *)server_name;
+ name.length = strlen(server_name);
+
+ maj_stat = gss_import_name(&min_stat, &name,
+ oid,
+ &target_name);
+
+ if (maj_stat != GSS_S_COMPLETE) {
+ pgsserr("gss_import_name", maj_stat, min_stat, g_mechOid);
+ return (FALSE);
+ }
+ }
+
+ maj_stat = gss_acquire_cred(&min_stat, target_name, GSS_C_INDEFINITE,
+ GSS_C_NO_OID_SET, GSS_C_ACCEPT,
+ &gssd_creds, NULL, NULL);
+
+ if (maj_stat != GSS_S_COMPLETE) {
+ pgsserr("gss_acquire_cred", maj_stat, min_stat, g_mechOid);
+ ignore_maj_stat = gss_display_name(&ignore_min_stat,
+ target_name, &pbuf, NULL);
+ if (ignore_maj_stat == GSS_S_COMPLETE) {
+ printerr(1, "Unable to obtain credentials for '%.*s'\n",
+ pbuf.length, pbuf.value);
+ ignore_maj_stat = gss_release_buffer(&ignore_min_stat,
+ &pbuf);
+ }
+ }
+
+ ignore_maj_stat = gss_release_name(&ignore_min_stat, &target_name);
+
+ return (maj_stat == GSS_S_COMPLETE);
+}
+
+int gssd_check_mechs(void)
+{
+ u_int32_t maj_stat, min_stat;
+ gss_OID_set supported_mechs = GSS_C_NO_OID_SET;
+ int retval = -1;
+
+ maj_stat = gss_indicate_mechs(&min_stat, &supported_mechs);
+ if (maj_stat != GSS_S_COMPLETE) {
+ printerr(0, "Unable to obtain list of supported mechanisms. "
+ "Check that gss library is properly configured.\n");
+ goto out;
+ }
+ if (supported_mechs == GSS_C_NO_OID_SET ||
+ supported_mechs->count == 0) {
+ printerr(0, "Unable to obtain list of supported mechanisms. "
+ "Check that gss library is properly configured.\n");
+ goto out;
+ }
+ maj_stat = gss_release_oid_set(&min_stat, &supported_mechs);
+ retval = 0;
+out:
+ return retval;
+}
+
+void
+gssd_cleanup(void)
+{
+ u_int32_t min_stat;
+ gss_release_cred(&min_stat, &gssd_creds);
+}
diff --git a/utils/gssd/gss_util.h b/utils/gssd/gss_util.h
new file mode 100644
index 0000000..4da64e3
--- /dev/null
+++ b/utils/gssd/gss_util.h
@@ -0,0 +1,56 @@
+/*
+ Copyright (c) 2004 The Regents of the University of Michigan.
+ 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. 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.
+*/
+
+#ifndef _GSS_UTIL_H_
+#define _GSS_UTIL_H_
+
+#include <stdlib.h>
+#include <rpc/rpc.h>
+#include "write_bytes.h"
+
+extern gss_cred_id_t gssd_creds;
+
+int gssd_acquire_cred(char *server_name, const gss_OID oid);
+void pgsserr(char *msg, u_int32_t maj_stat, u_int32_t min_stat,
+ const gss_OID mech);
+int gssd_check_mechs(void);
+void gssd_cleanup(void);
+
+#ifndef HAVE_LIBGSSGLUE
+#include <gssapi/gssapi_krb5.h>
+#define gss_free_lucid_sec_context(min, ctx, ret) \
+ gss_krb5_free_lucid_sec_context(min, ret)
+
+#define gss_export_lucid_sec_context gss_krb5_export_lucid_sec_context
+#define gss_set_allowable_enctypes(min, cred, oid, num, types) \
+ gss_krb5_set_allowable_enctypes(min, cred, num, types)
+#endif
+
+#endif /* _GSS_UTIL_H_ */
diff --git a/utils/gssd/gssd.c b/utils/gssd/gssd.c
new file mode 100644
index 0000000..ca9b326
--- /dev/null
+++ b/utils/gssd/gssd.c
@@ -0,0 +1,1320 @@
+/*
+ gssd.c
+
+ Copyright (c) 2000, 2004 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>.
+ 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.
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/inotify.h>
+#include <rpc/rpc.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <unistd.h>
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <memory.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <netdb.h>
+#include <event2/event.h>
+
+#include "gssd.h"
+#include "err_util.h"
+#include "gss_util.h"
+#include "krb5_util.h"
+#include "nfslib.h"
+#include "conffile.h"
+
+static char *pipefs_path = GSSD_PIPEFS_DIR;
+static DIR *pipefs_dir;
+static int pipefs_fd;
+static int inotify_fd;
+struct event *inotify_ev;
+
+char *keytabfile = GSSD_DEFAULT_KEYTAB_FILE;
+char **ccachesearch;
+int use_memcache = 0;
+int root_uses_machine_creds = 1;
+unsigned int context_timeout = 0;
+unsigned int rpc_timeout = 5;
+char *preferred_realm = NULL;
+char *ccachedir = NULL;
+/* set $HOME to "/" by default */
+static bool set_home = true;
+/* Avoid DNS reverse lookups on server names */
+static bool avoid_dns = true;
+static bool use_gssproxy = false;
+pthread_mutex_t clp_lock = PTHREAD_MUTEX_INITIALIZER;
+static bool signal_received = false;
+static struct event_base *evbase = NULL;
+
+int upcall_timeout = DEF_UPCALL_TIMEOUT;
+static bool cancel_timed_out_upcalls = false;
+
+TAILQ_HEAD(topdir_list_head, topdir) topdir_list;
+
+/*
+ * active_thread_list:
+ *
+ * used to track upcalls for timeout purposes.
+ *
+ * protected by the active_thread_list_lock mutex.
+ *
+ * upcall_thread_info structures are added to the tail of the list
+ * by start_upcall_thread(), so entries closer to the head of the list
+ * will be closer to hitting the upcall timeout.
+ *
+ * upcall_thread_info structures are removed from the list upon a
+ * sucessful join of the upcall thread by the watchdog thread (via
+ * scan_active_thread_list().
+ */
+TAILQ_HEAD(active_thread_list_head, upcall_thread_info) active_thread_list;
+pthread_mutex_t active_thread_list_lock = PTHREAD_MUTEX_INITIALIZER;
+
+struct topdir {
+ TAILQ_ENTRY(topdir) list;
+ TAILQ_HEAD(clnt_list_head, clnt_info) clnt_list;
+ int wd;
+ char name[];
+};
+
+/*
+ * topdir_list:
+ * linked list of struct topdir with basic data about a topdir.
+ *
+ * clnt_list:
+ * linked list of struct clnt_info with basic data about a clntXXX dir,
+ * one per topdir.
+ *
+ * Directory structure: created by the kernel
+ * {rpc_pipefs}/{topdir}/clntXX : one per rpc_clnt struct in the kernel
+ * {rpc_pipefs}/{topdir}/clntXX/krb5 : read uid for which kernel wants
+ * a context, write the resulting context
+ * {rpc_pipefs}/{topdir}/clntXX/info : stores info such as server name
+ * {rpc_pipefs}/{topdir}/clntXX/gssd : pipe for all gss mechanisms using
+ * a text-based string of parameters
+ *
+ * Algorithm:
+ * Poll all {rpc_pipefs}/{topdir}/clntXX/YYYY files. When data is ready,
+ * read and process; performs rpcsec_gss context initialization protocol to
+ * get a cred for that user. Writes result to corresponding krb5 file
+ * in a form the kernel code will understand.
+ * In addition, we make sure we are notified whenever anything is
+ * created or destroyed in {rpc_pipefs} or in any of the clntXX directories,
+ * and rescan the whole {rpc_pipefs} when this happens.
+ */
+
+/*
+ * convert a presentation address string to a sockaddr_storage struct. Returns
+ * true on success or false on failure.
+ *
+ * Note that we do not populate the sin6_scope_id field here for IPv6 addrs.
+ * gssd nececessarily relies on hostname resolution and DNS AAAA records
+ * do not generally contain scope-id's. This means that GSSAPI auth really
+ * can't work with IPv6 link-local addresses.
+ *
+ * We *could* consider changing this if we did something like adopt the
+ * Microsoft "standard" of using the ipv6-literal.net domainname, but it's
+ * not really feasible at present.
+ */
+static bool
+gssd_addrstr_to_sockaddr(struct sockaddr *sa, const char *node, const char *port)
+{
+ int rc;
+ struct addrinfo *res;
+ struct addrinfo hints = { .ai_flags = AI_NUMERICHOST | AI_NUMERICSERV };
+
+#ifndef IPV6_SUPPORTED
+ hints.ai_family = AF_INET;
+#endif /* IPV6_SUPPORTED */
+
+ rc = getaddrinfo(node, port, &hints, &res);
+ if (rc) {
+ printerr(0, "ERROR: unable to convert %s|%s to sockaddr: %s\n",
+ node, port,
+ rc == EAI_SYSTEM ? strerror(errno) : gai_strerror(rc));
+ return false;
+ }
+
+#ifdef IPV6_SUPPORTED
+ /*
+ * getnameinfo ignores the scopeid. If the address turns out to have
+ * a non-zero scopeid, we can't use it -- the resolved host might be
+ * completely different from the one intended.
+ */
+ if (res->ai_addr->sa_family == AF_INET6) {
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)res->ai_addr;
+ if (sin6->sin6_scope_id) {
+ printerr(0, "ERROR: address %s has non-zero "
+ "sin6_scope_id!\n", node);
+ nfs_freeaddrinfo(res);
+ return false;
+ }
+ }
+#endif /* IPV6_SUPPORTED */
+
+ memcpy(sa, res->ai_addr, res->ai_addrlen);
+ nfs_freeaddrinfo(res);
+ return true;
+}
+
+/*
+ * convert a sockaddr to a hostname
+ */
+static char *
+gssd_get_servername(const char *name, const struct sockaddr *sa, const char *addr)
+{
+ socklen_t addrlen;
+ int err;
+ char hbuf[NI_MAXHOST];
+ unsigned char buf[sizeof(struct in6_addr)];
+
+ while (avoid_dns) {
+ /*
+ * Determine if this is a server name, or an IP address.
+ * If it is an IP address, do the DNS lookup otherwise
+ * skip the DNS lookup.
+ */
+ if (strchr(name, '.') == NULL)
+ break; /* local name */
+ else if (inet_pton(AF_INET, name, buf) == 1)
+ break; /* IPv4 address */
+ else if (inet_pton(AF_INET6, name, buf) == 1)
+ break; /* IPv6 addrss */
+
+ return strdup(name);
+ }
+
+ switch (sa->sa_family) {
+ case AF_INET:
+ addrlen = sizeof(struct sockaddr_in);
+ break;
+#ifdef IPV6_SUPPORTED
+ case AF_INET6:
+ addrlen = sizeof(struct sockaddr_in6);
+ break;
+#endif /* IPV6_SUPPORTED */
+ default:
+ printerr(0, "ERROR: unrecognized addr family %d\n",
+ sa->sa_family);
+ return NULL;
+ }
+
+ err = getnameinfo(sa, addrlen, hbuf, sizeof(hbuf), NULL, 0,
+ NI_NAMEREQD);
+ if (err) {
+ printerr(0, "ERROR: unable to resolve %s to hostname: %s\n",
+ addr, err == EAI_SYSTEM ? strerror(errno) :
+ gai_strerror(err));
+ return NULL;
+ }
+
+ return strdup(hbuf);
+}
+
+static void
+gssd_read_service_info(int dirfd, struct clnt_info *clp)
+{
+ int fd;
+ FILE *info = NULL;
+ int numfields;
+ char *server = NULL;
+ char *service = NULL;
+ int program;
+ int version;
+ char *address = NULL;
+ char *protoname = NULL;
+ char *port = NULL;
+ char *servername = NULL;
+
+ fd = openat(dirfd, "info", O_RDONLY);
+ if (fd < 0) {
+ printerr(0, "ERROR: can't open %s/info: %s\n",
+ clp->relpath, strerror(errno));
+ goto fail;
+ }
+
+ info = fdopen(fd, "r");
+ if (!info) {
+ printerr(0, "ERROR: can't fdopen %s/info: %s\n",
+ clp->relpath, strerror(errno));
+ close(fd);
+ goto fail;
+ }
+
+ /*
+ * Some history:
+ *
+ * The first three lines were added with rpc_pipefs in 2003-01-13.
+ * (commit af2f003391786fb632889c02142c941b212ba4ff)
+ *
+ * The 'protocol' line was added in 2003-06-11.
+ * (commit 9bd741ae48785d0c0e75cf906ff66f893d600c2d)
+ *
+ * The 'port' line was added in 2007-09-26.
+ * (commit bf19aacecbeebccb2c3d150a8bd9416b7dba81fe)
+ */
+ numfields = fscanf(info,
+ "RPC server: %ms\n"
+ "service: %ms (%d) version %d\n"
+ "address: %ms\n"
+ "protocol: %ms\n"
+ "port: %ms\n",
+ &server,
+ &service, &program, &version,
+ &address,
+ &protoname,
+ &port);
+
+
+ switch (numfields) {
+ case 5:
+ protoname = strdup("tcp");
+ if (!protoname)
+ goto fail;
+ /* fall through */
+ case 6:
+ /* fall through */
+ case 7:
+ break;
+ default:
+ goto fail;
+ }
+
+ /*
+ * The user space RPC library has no support for
+ * RPC-over-RDMA at this time, so change 'rdma'
+ * to 'tcp', and '20049' to '2049'.
+ */
+ if (strcmp(protoname, "rdma") == 0) {
+ free(protoname);
+ protoname = strdup("tcp");
+ if (!protoname)
+ goto fail;
+ free(port);
+ port = strdup("2049");
+ if (!port)
+ goto fail;
+ }
+
+ if (!gssd_addrstr_to_sockaddr((struct sockaddr *)&clp->addr,
+ address, port ? port : ""))
+ goto fail;
+
+ servername = gssd_get_servername(server, (struct sockaddr *)&clp->addr, address);
+ if (!servername)
+ goto fail;
+
+ if (asprintf(&clp->servicename, "%s@%s", service, servername) < 0)
+ goto fail;
+
+ clp->servername = servername;
+ clp->prog = program;
+ clp->vers = version;
+ clp->protocol = protoname;
+
+ goto out;
+
+fail:
+ printerr(0, "ERROR: failed to parse %s/info\n", clp->relpath);
+ clp->upcall_address = strdup(address);
+ clp->upcall_port = strdup(port);
+ clp->upcall_program = program;
+ clp->upcall_vers = version;
+ clp->upcall_protoname = strdup(protoname);
+ clp->upcall_service = strdup(service);
+ free(servername);
+ free(protoname);
+ clp->servicename = NULL;
+ clp->servername = NULL;
+ clp->prog = 0;
+ clp->vers = 0;
+ clp->protocol = NULL;
+out:
+ if (info)
+ fclose(info);
+
+ free(server);
+ free(service);
+ free(address);
+ free(port);
+}
+
+/* Actually frees clp and fields that might be used from other
+ * threads if was last reference.
+ */
+void
+gssd_free_client(struct clnt_info *clp)
+{
+ int refcnt;
+
+ pthread_mutex_lock(&clp_lock);
+ refcnt = --clp->refcount;
+ pthread_mutex_unlock(&clp_lock);
+ if (refcnt > 0)
+ return;
+
+ printerr(4, "freeing client %s\n", clp->relpath);
+
+ if (clp->krb5_fd >= 0)
+ close(clp->krb5_fd);
+
+ if (clp->gssd_fd >= 0)
+ close(clp->gssd_fd);
+
+ free(clp->relpath);
+ free(clp->servicename);
+ free(clp->servername);
+ free(clp->protocol);
+ if (!clp->servername) {
+ if (clp->upcall_address)
+ free(clp->upcall_address);
+ if (clp->upcall_port)
+ free(clp->upcall_port);
+ if (clp->upcall_protoname)
+ free(clp->upcall_protoname);
+ if (clp->upcall_service)
+ free(clp->upcall_service);
+ }
+ free(clp);
+}
+
+/* Called when removing from clnt_list to tear down event handling.
+ * Will then free clp if was last reference.
+ */
+static void
+gssd_destroy_client(struct clnt_info *clp)
+{
+ printerr(4, "destroying client %s\n", clp->relpath);
+
+ if (clp->krb5_ev) {
+ event_del(clp->krb5_ev);
+ event_free(clp->krb5_ev);
+ clp->krb5_ev = NULL;
+ }
+
+ if (clp->gssd_ev) {
+ event_del(clp->gssd_ev);
+ event_free(clp->gssd_ev);
+ clp->gssd_ev = NULL;
+ }
+
+ inotify_rm_watch(inotify_fd, clp->wd);
+ gssd_free_client(clp);
+}
+
+static void gssd_scan(void);
+
+/* For each upcall read the upcall info into the buffer, then create a
+ * thread in a detached state so that resources are released back into
+ * the system without the need for a join.
+ */
+static void
+gssd_clnt_gssd_cb(int UNUSED(fd), short UNUSED(which), void *data)
+{
+ struct clnt_info *clp = data;
+
+ /* if there was a failure to translate IP to name for this server,
+ * try again
+ */
+ if (!clp->servername) {
+ if (!gssd_addrstr_to_sockaddr((struct sockaddr *)&clp->addr,
+ clp->upcall_address, clp->upcall_port ?
+ clp->upcall_port : "")) {
+ goto do_upcall;
+ }
+ clp->servername = gssd_get_servername(clp->upcall_address,
+ (struct sockaddr *)&clp->addr, clp->upcall_address);
+ if (!clp->servername)
+ goto do_upcall;
+
+ if (asprintf(&clp->servicename, "%s@%s", clp->upcall_service,
+ clp->servername) < 0) {
+ free(clp->servername);
+ clp->servername = NULL;
+ goto do_upcall;
+ }
+ clp->prog = clp->upcall_program;
+ clp->vers = clp->upcall_vers;
+ clp->protocol = strdup(clp->upcall_protoname);
+ }
+do_upcall:
+ handle_gssd_upcall(clp);
+}
+
+static void
+gssd_clnt_krb5_cb(int UNUSED(fd), short UNUSED(which), void *data)
+{
+ struct clnt_info *clp = data;
+
+ handle_krb5_upcall(clp);
+}
+
+/*
+ * scan_active_thread_list:
+ *
+ * Walks the active_thread_list, trying to join as many upcall threads as
+ * possible. For threads that have terminated, the corresponding
+ * upcall_thread_info will be removed from the list and freed. Threads that
+ * are still busy and have exceeded the upcall_timeout will cause an error to
+ * be logged and may be canceled (depending on the value of
+ * cancel_timed_out_upcalls).
+ *
+ * Returns the number of seconds that the watchdog thread should wait before
+ * calling scan_active_thread_list() again.
+ */
+static int
+scan_active_thread_list(void)
+{
+ struct upcall_thread_info *info;
+ struct timespec now;
+ unsigned int sleeptime;
+ bool sleeptime_set = false;
+ int err;
+ void *tret, *saveprev;
+
+ sleeptime = upcall_timeout;
+ pthread_mutex_lock(&active_thread_list_lock);
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ TAILQ_FOREACH(info, &active_thread_list, list) {
+ err = pthread_tryjoin_np(info->tid, &tret);
+ switch (err) {
+ case 0:
+ /*
+ * The upcall thread has either completed successfully, or
+ * has been canceled _and_ has acted on the cancellation request
+ * (i.e. has hit a cancellation point). We can now remove the
+ * upcall_thread_info from the list and free it.
+ */
+ if (tret == PTHREAD_CANCELED)
+ printerr(2, "watchdog: thread id 0x%lx cancelled successfully\n",
+ info->tid);
+ saveprev = info->list.tqe_prev;
+ TAILQ_REMOVE(&active_thread_list, info, list);
+ free(info);
+ info = saveprev;
+ break;
+ case EBUSY:
+ /*
+ * The upcall thread is still running. If the timeout has expired
+ * then we either cancel the thread, log an error, and do an error
+ * downcall to the kernel (cancel_timed_out_upcalls=true) or simply
+ * log an error (cancel_timed_out_upcalls=false). In either case,
+ * the error is logged only once.
+ */
+ if (now.tv_sec >= info->timeout.tv_sec) {
+ if (cancel_timed_out_upcalls && !(info->flags & UPCALL_THREAD_CANCELED)) {
+ printerr(0, "watchdog: thread id 0x%lx timed out\n",
+ info->tid);
+ pthread_cancel(info->tid);
+ info->flags |= (UPCALL_THREAD_CANCELED|UPCALL_THREAD_WARNED);
+ do_error_downcall(info->fd, info->uid, -ETIMEDOUT);
+ } else {
+ if (!(info->flags & UPCALL_THREAD_WARNED)) {
+ printerr(0, "watchdog: thread id 0x%lx running for %ld seconds\n",
+ info->tid,
+ now.tv_sec - info->timeout.tv_sec + upcall_timeout);
+ info->flags |= UPCALL_THREAD_WARNED;
+ }
+ }
+ } else if (!sleeptime_set) {
+ /*
+ * The upcall thread is still running, but the timeout has not yet
+ * expired. Calculate the time remaining until the timeout will
+ * expire. This is the amount of time the watchdog thread will
+ * wait before running again. We only need to do this for the busy
+ * thread closest to the head of the list - entries appearing later
+ * in the list will time out later.
+ */
+ sleeptime = info->timeout.tv_sec - now.tv_sec;
+ sleeptime_set = true;
+ }
+ break;
+ default:
+ /* EDEADLK, EINVAL, and ESRCH... none of which should happen! */
+ printerr(0, "watchdog: attempt to join thread id 0x%lx returned %d (%s)!\n",
+ info->tid, err, strerror(err));
+ break;
+ }
+ }
+ pthread_mutex_unlock(&active_thread_list_lock);
+
+ return sleeptime;
+}
+
+static void *
+watchdog_thread_fn(void *UNUSED(arg))
+{
+ unsigned int sleeptime;
+
+ for (;;) {
+ sleeptime = scan_active_thread_list();
+ printerr(4, "watchdog: sleeping %u secs\n", sleeptime);
+ sleep(sleeptime);
+ }
+ return (void *)0;
+}
+
+static int
+start_watchdog_thread(void)
+{
+ pthread_attr_t attr;
+ pthread_t th;
+ int ret;
+
+ ret = pthread_attr_init(&attr);
+ if (ret != 0) {
+ printerr(0, "ERROR: failed to init pthread attr: ret %d: %s\n",
+ ret, strerror(errno));
+ return ret;
+ }
+ ret = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ if (ret != 0) {
+ printerr(0, "ERROR: failed to create pthread attr: ret %d: %s\n",
+ ret, strerror(errno));
+ return ret;
+ }
+ ret = pthread_create(&th, &attr, watchdog_thread_fn, NULL);
+ if (ret != 0) {
+ printerr(0, "ERROR: pthread_create failed: ret %d: %s\n",
+ ret, strerror(errno));
+ }
+ return ret;
+}
+
+static struct clnt_info *
+gssd_get_clnt(struct topdir *tdi, const char *name)
+{
+ struct clnt_info *clp;
+
+ TAILQ_FOREACH(clp, &tdi->clnt_list, list)
+ if (!strcmp(clp->name, name))
+ return clp;
+
+ printerr(4, "creating client %s/%s\n", tdi->name, name);
+
+ clp = calloc(1, sizeof(struct clnt_info));
+ if (!clp) {
+ printerr(0, "ERROR: can't malloc clnt_info: %s\n",
+ strerror(errno));
+ return NULL;
+ }
+
+ if (asprintf(&clp->relpath, "%s/%s", tdi->name, name) < 0) {
+ clp->relpath = NULL;
+ goto out;
+ }
+
+ clp->wd = inotify_add_watch(inotify_fd, clp->relpath, IN_CREATE | IN_DELETE);
+ if (clp->wd < 0) {
+ if (errno != ENOENT)
+ printerr(0, "ERROR: %s: inotify_add_watch failed for %s: %s\n",
+ __FUNCTION__, clp->relpath, strerror(errno));
+ goto out;
+ }
+
+ clp->name = clp->relpath + strlen(tdi->name) + 1;
+ clp->krb5_fd = -1;
+ clp->gssd_fd = -1;
+ clp->refcount = 1;
+
+ TAILQ_INSERT_HEAD(&tdi->clnt_list, clp, list);
+ return clp;
+
+out:
+ free(clp->relpath);
+ free(clp);
+ return NULL;
+}
+
+static int
+gssd_scan_clnt(struct clnt_info *clp)
+{
+ int clntfd;
+
+ printerr(4, "scanning client %s\n", clp->relpath);
+
+ clntfd = openat(pipefs_fd, clp->relpath, O_RDONLY);
+ if (clntfd < 0) {
+ if (errno != ENOENT)
+ printerr(0, "ERROR: %s: can't openat %s: %s\n",
+ __FUNCTION__, clp->relpath, strerror(errno));
+ return -1;
+ }
+
+ if (clp->gssd_fd == -1)
+ clp->gssd_fd = openat(clntfd, "gssd", O_RDWR | O_NONBLOCK);
+
+ if (clp->gssd_fd == -1 && clp->krb5_fd == -1)
+ clp->krb5_fd = openat(clntfd, "krb5", O_RDWR | O_NONBLOCK);
+
+ if (!clp->gssd_ev && clp->gssd_fd >= 0) {
+ clp->gssd_ev = event_new(evbase, clp->gssd_fd, EV_READ | EV_PERSIST,
+ gssd_clnt_gssd_cb, clp);
+ if (!clp->gssd_ev) {
+ printerr(0, "ERROR: %s: can't create gssd event for %s: %s\n",
+ __FUNCTION__, clp->relpath, strerror(errno));
+ close(clp->gssd_fd);
+ clp->gssd_fd = -1;
+ } else {
+ event_add(clp->gssd_ev, NULL);
+ }
+ }
+
+ if (!clp->krb5_ev && clp->krb5_fd >= 0) {
+ clp->krb5_ev = event_new(evbase, clp->krb5_fd, EV_READ | EV_PERSIST,
+ gssd_clnt_krb5_cb, clp);
+ if (!clp->krb5_ev) {
+ printerr(0, "ERROR: %s: can't create krb5 event for %s: %s\n",
+ __FUNCTION__, clp->relpath, strerror(errno));
+ close(clp->krb5_fd);
+ clp->krb5_fd = -1;
+ } else {
+ event_add(clp->krb5_ev, NULL);
+ }
+ }
+
+ if (clp->krb5_fd == -1 && clp->gssd_fd == -1)
+ /* not fatal, files might appear later */
+ goto out;
+
+ if (clp->prog == 0)
+ gssd_read_service_info(clntfd, clp);
+
+out:
+ close(clntfd);
+ clp->scanned = true;
+ return 0;
+}
+
+static int
+gssd_create_clnt(struct topdir *tdi, const char *name)
+{
+ struct clnt_info *clp;
+
+ clp = gssd_get_clnt(tdi, name);
+ if (!clp)
+ return -1;
+
+ return gssd_scan_clnt(clp);
+}
+
+static struct topdir *
+gssd_get_topdir(const char *name)
+{
+ struct topdir *tdi;
+
+ TAILQ_FOREACH(tdi, &topdir_list, list)
+ if (!strcmp(tdi->name, name))
+ return tdi;
+
+ tdi = malloc(sizeof(*tdi) + strlen(name) + 1);
+ if (!tdi) {
+ printerr(0, "ERROR: Couldn't allocate struct topdir\n");
+ return NULL;
+ }
+
+ tdi->wd = inotify_add_watch(inotify_fd, name, IN_CREATE);
+ if (tdi->wd < 0) {
+ printerr(0, "ERROR: %s: inotify_add_watch failed for top dir %s: %s\n",
+ __FUNCTION__, tdi->name, strerror(errno));
+ free(tdi);
+ return NULL;
+ }
+
+ strcpy(tdi->name, name);
+ TAILQ_INIT(&tdi->clnt_list);
+
+ TAILQ_INSERT_HEAD(&topdir_list, tdi, list);
+ return tdi;
+}
+
+static void
+gssd_scan_topdir(const char *name)
+{
+ struct topdir *tdi;
+ int dfd;
+ DIR *dir;
+ struct clnt_info *clp;
+ struct dirent *d;
+
+ tdi = gssd_get_topdir(name);
+ if (!tdi)
+ return;
+
+ dfd = openat(pipefs_fd, tdi->name, O_RDONLY);
+ if (dfd < 0) {
+ if (errno != ENOENT)
+ printerr(0, "ERROR: %s: can't openat %s: %s\n",
+ __FUNCTION__, tdi->name, strerror(errno));
+ return;
+ }
+
+ dir = fdopendir(dfd);
+ if (!dir) {
+ printerr(0, "ERROR: can't fdopendir %s: %s\n",
+ tdi->name, strerror(errno));
+ return;
+ }
+
+ TAILQ_FOREACH(clp, &tdi->clnt_list, list)
+ clp->scanned = false;
+
+ while ((d = readdir(dir))) {
+ if (d->d_type != DT_DIR)
+ continue;
+
+ if (strncmp(d->d_name, "clnt", strlen("clnt")))
+ continue;
+
+ gssd_create_clnt(tdi, d->d_name);
+ }
+
+ closedir(dir);
+
+ TAILQ_FOREACH(clp, &tdi->clnt_list, list) {
+ void *saveprev;
+
+ if (clp->scanned)
+ continue;
+
+ printerr(3, "orphaned client %s\n", clp->relpath);
+ saveprev = clp->list.tqe_prev;
+ TAILQ_REMOVE(&tdi->clnt_list, clp, list);
+ gssd_destroy_client(clp);
+ clp = saveprev;
+ }
+}
+
+static void
+gssd_scan(void)
+{
+ struct dirent *d;
+
+ printerr(4, "doing a full rescan\n");
+ rewinddir(pipefs_dir);
+
+ while ((d = readdir(pipefs_dir))) {
+ if (d->d_type != DT_DIR)
+ continue;
+
+ if (d->d_name[0] == '.')
+ continue;
+
+ gssd_scan_topdir(d->d_name);
+ }
+
+ if (TAILQ_EMPTY(&topdir_list)) {
+ printerr(0, "ERROR: the rpc_pipefs directory is empty!\n");
+ exit(EXIT_FAILURE);
+ }
+}
+
+static void
+gssd_scan_cb(int UNUSED(fd), short UNUSED(which), void *UNUSED(data))
+{
+ gssd_scan();
+}
+
+static bool
+gssd_inotify_topdir(struct topdir *tdi, const struct inotify_event *ev)
+{
+ printerr(5, "inotify event for topdir (%s) - "
+ "ev->wd (%d) ev->name (%s) ev->mask (0x%08x)\n",
+ tdi->name, ev->wd, ev->len > 0 ? ev->name : "<?>", ev->mask);
+
+ if (ev->mask & IN_IGNORED) {
+ printerr(0, "ERROR: topdir disappeared!\n");
+ return false;
+ }
+
+ if (ev->len == 0)
+ return false;
+
+ if (ev->mask & IN_CREATE) {
+ if (!(ev->mask & IN_ISDIR))
+ return true;
+
+ if (strncmp(ev->name, "clnt", strlen("clnt")))
+ return true;
+
+ if (gssd_create_clnt(tdi, ev->name))
+ return false;
+
+ return true;
+ }
+
+ return false;
+}
+
+static bool
+gssd_inotify_clnt(struct topdir *tdi, struct clnt_info *clp, const struct inotify_event *ev)
+{
+ printerr(5, "inotify event for clntdir (%s) - "
+ "ev->wd (%d) ev->name (%s) ev->mask (0x%08x)\n",
+ clp->relpath, ev->wd, ev->len > 0 ? ev->name : "<?>", ev->mask);
+
+ if (ev->mask & IN_IGNORED) {
+ TAILQ_REMOVE(&tdi->clnt_list, clp, list);
+ gssd_destroy_client(clp);
+ return true;
+ }
+
+ if (ev->len == 0)
+ return false;
+
+ if (ev->mask & IN_CREATE) {
+ if (!strcmp(ev->name, "gssd") ||
+ !strcmp(ev->name, "krb5") ||
+ !strcmp(ev->name, "info"))
+ if (gssd_scan_clnt(clp))
+ return false;
+
+ return true;
+
+ } else if (ev->mask & IN_DELETE) {
+ if (!strcmp(ev->name, "gssd") && clp->gssd_fd >= 0) {
+ close(clp->gssd_fd);
+ event_del(clp->gssd_ev);
+ event_free(clp->gssd_ev);
+ clp->gssd_ev = NULL;
+ clp->gssd_fd = -1;
+
+ } else if (!strcmp(ev->name, "krb5") && clp->krb5_fd >= 0) {
+ close(clp->krb5_fd);
+ event_del(clp->krb5_ev);
+ event_free(clp->krb5_ev);
+ clp->krb5_ev = NULL;
+ clp->krb5_fd = -1;
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+static void
+gssd_inotify_cb(int ifd, short UNUSED(which), void *UNUSED(data))
+{
+ bool rescan = false;
+ struct topdir *tdi;
+ struct clnt_info *clp;
+
+ while (true) {
+ char buf[4096] __attribute__ ((aligned(__alignof__(struct inotify_event))));
+ const struct inotify_event *ev;
+ ssize_t len;
+ char *ptr;
+
+ len = read(ifd, buf, sizeof(buf));
+ if (len == -1 && errno == EINTR)
+ continue;
+
+ if (len <= 0)
+ break;
+
+ for (ptr = buf; ptr < buf + len;
+ ptr += sizeof(struct inotify_event) + ev->len) {
+ ev = (const struct inotify_event *)ptr;
+
+ if (ev->mask & IN_Q_OVERFLOW) {
+ printerr(0, "ERROR: inotify queue overflow\n");
+ rescan = true;
+ break;
+ }
+
+ TAILQ_FOREACH(tdi, &topdir_list, list) {
+ if (tdi->wd == ev->wd) {
+ if (!gssd_inotify_topdir(tdi, ev))
+ rescan = true;
+ goto found;
+ }
+
+ TAILQ_FOREACH(clp, &tdi->clnt_list, list) {
+ if (clp->wd == ev->wd) {
+ if (!gssd_inotify_clnt(tdi, clp, ev))
+ rescan = true;
+ goto found;
+ }
+ }
+ }
+
+found:
+ if (!tdi) {
+ printerr(5, "inotify event for unknown wd!!! - "
+ "ev->wd (%d) ev->name (%s) ev->mask (0x%08x)\n",
+ ev->wd, ev->len > 0 ? ev->name : "<?>", ev->mask);
+ rescan = true;
+ }
+ }
+ }
+
+ if (rescan)
+ gssd_scan();
+}
+
+static void
+sig_die(int signal)
+{
+ if (signal_received) {
+ gssd_destroy_krb5_principals(root_uses_machine_creds);
+ printerr(1, "forced exiting on signal %d\n", signal);
+ exit(0);
+ }
+
+ signal_received = true;
+ printerr(1, "exiting on signal %d\n", signal);
+ event_base_loopexit(evbase, NULL);
+}
+
+static void
+usage(char *progname)
+{
+ fprintf(stderr, "usage: %s [-f] [-l] [-M] [-n] [-v] [-r] [-p pipefsdir] [-k keytab] [-d ccachedir] [-t timeout] [-R preferred realm] [-D] [-H] [-U upcall timeout] [-C]\n",
+ progname);
+ exit(1);
+}
+
+inline static void
+read_gss_conf(void)
+{
+ char *s;
+
+ conf_init_file(NFS_CONFFILE);
+ use_memcache = conf_get_bool("gssd", "use-memcache", use_memcache);
+ root_uses_machine_creds = conf_get_bool("gssd", "use-machine-creds",
+ root_uses_machine_creds);
+ avoid_dns = conf_get_bool("gssd", "avoid-dns", avoid_dns);
+#ifdef HAVE_SET_ALLOWABLE_ENCTYPES
+ limit_to_legacy_enctypes = conf_get_bool("gssd", "limit-to-legacy-enctypes",
+ limit_to_legacy_enctypes);
+#endif
+ context_timeout = conf_get_num("gssd", "context-timeout", context_timeout);
+ rpc_timeout = conf_get_num("gssd", "rpc-timeout", rpc_timeout);
+ upcall_timeout = conf_get_num("gssd", "upcall-timeout", upcall_timeout);
+ cancel_timed_out_upcalls = conf_get_bool("gssd", "cancel-timed-out-upcalls",
+ cancel_timed_out_upcalls);
+ s = conf_get_str("gssd", "pipefs-directory");
+ if (!s)
+ s = conf_get_str("general", "pipefs-directory");
+ else
+ printerr(0, "WARNING: Specifying pipefs-directory in the [gssd] "
+ "section of %s is deprecated. Use the [general] "
+ "section instead.", NFS_CONFFILE);
+ if (s)
+ pipefs_path = s;
+ s = conf_get_str("gssd", "keytab-file");
+ if (s)
+ keytabfile = s;
+ s = conf_get_str("gssd", "cred-cache-directory");
+ if (s)
+ ccachedir = strdup(s);
+ s = conf_get_str("gssd", "preferred-realm");
+ if (s)
+ preferred_realm = s;
+
+ use_gssproxy = conf_get_bool("gssd", "use-gss-proxy", use_gssproxy);
+ set_home = conf_get_bool("gssd", "set-home", set_home);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int fg = 0;
+ int verbosity = 0;
+ int rpc_verbosity = 0;
+ int opt;
+ int i;
+ int rc;
+ extern char *optarg;
+ char *progname;
+ struct event *sighup_ev;
+
+ read_gss_conf();
+
+ verbosity = conf_get_num("gssd", "verbosity", verbosity);
+ rpc_verbosity = conf_get_num("gssd", "rpc-verbosity", rpc_verbosity);
+
+ while ((opt = getopt(argc, argv, "HDfvrlmnMp:k:d:t:T:R:U:C")) != -1) {
+ switch (opt) {
+ case 'f':
+ fg = 1;
+ break;
+ case 'm':
+ /* Accept but ignore this. Now the default. */
+ break;
+ case 'M':
+ use_memcache = 1;
+ break;
+ case 'n':
+ root_uses_machine_creds = 0;
+ break;
+ case 'v':
+ verbosity++;
+ break;
+ case 'r':
+ rpc_verbosity++;
+ break;
+ case 'p':
+ pipefs_path = optarg;
+ break;
+ case 'k':
+ keytabfile = optarg;
+ break;
+ case 'd':
+ free(ccachedir);
+ ccachedir = strdup(optarg);
+ break;
+ case 't':
+ context_timeout = atoi(optarg);
+ break;
+ case 'T':
+ rpc_timeout = atoi(optarg);
+ break;
+ case 'R':
+ preferred_realm = strdup(optarg);
+ break;
+ case 'l':
+#ifdef HAVE_SET_ALLOWABLE_ENCTYPES
+ limit_to_legacy_enctypes = 1;
+#else
+ errx(1, "Encryption type limits not supported by Kerberos libraries.");
+#endif
+ break;
+ case 'D':
+ avoid_dns = false;
+ break;
+ case 'H':
+ set_home = false;
+ break;
+ case 'U':
+ upcall_timeout = atoi(optarg);
+ break;
+ case 'C':
+ cancel_timed_out_upcalls = true;
+ break;
+ default:
+ usage(argv[0]);
+ break;
+ }
+ }
+
+ /*
+ * Some krb5 routines try to scrape info out of files in the user's
+ * home directory. This can easily deadlock when that homedir is on a
+ * kerberized NFS mount. By setting $HOME to "/" by default, we prevent
+ * this behavior in routines that use $HOME in preference to the results
+ * of getpw*.
+ *
+ * Some users do not use Kerberized home dirs and need $HOME to remain
+ * unchanged. Those users can leave $HOME unchanged by setting set_home
+ * to false.
+ */
+ if (set_home) {
+ if (setenv("HOME", "/", 1)) {
+ printerr(0, "gssd: Unable to set $HOME: %s\n", strerror(errno));
+ exit(1);
+ }
+ }
+
+ if (use_gssproxy) {
+ if (setenv("GSS_USE_PROXY", "yes", 1) < 0) {
+ printerr(0, "gssd: Unable to set $GSS_USE_PROXY: %s\n",
+ strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if (ccachedir) {
+ char *ptr;
+
+ for (ptr = ccachedir, i = 2; *ptr; ptr++)
+ if (*ptr == ':')
+ i++;
+
+ ccachesearch = malloc(i * sizeof(char *));
+ if (!ccachesearch) {
+ printerr(0, "malloc failure\n");
+ exit(EXIT_FAILURE);
+ }
+
+ i = 0;
+ ccachesearch[i++] = strtok(ccachedir, ":");
+ while(ccachesearch[i - 1])
+ ccachesearch[i++] = strtok(NULL, ":");
+
+ } else {
+ ccachesearch = malloc(3 * sizeof(char *));
+ if (!ccachesearch) {
+ printerr(0, "malloc failure\n");
+ exit(EXIT_FAILURE);
+ }
+
+ ccachesearch[0] = GSSD_DEFAULT_CRED_DIR;
+ ccachesearch[1] = GSSD_USER_CRED_DIR;
+ ccachesearch[2] = NULL;
+ }
+
+ if (preferred_realm == NULL)
+ gssd_k5_get_default_realm(&preferred_realm);
+
+ if ((progname = strrchr(argv[0], '/')))
+ progname++;
+ else
+ progname = argv[0];
+
+ if (upcall_timeout > MAX_UPCALL_TIMEOUT)
+ upcall_timeout = MAX_UPCALL_TIMEOUT;
+ else if (upcall_timeout < MIN_UPCALL_TIMEOUT)
+ upcall_timeout = MIN_UPCALL_TIMEOUT;
+
+ initerr(progname, verbosity, fg);
+#ifdef HAVE_LIBTIRPC_SET_DEBUG
+ /*
+ * Only set the libtirpc debug level if explicitly requested via -r.
+ */
+ if (rpc_verbosity > 0)
+ libtirpc_set_debug(progname, rpc_verbosity, fg);
+#else
+ if (rpc_verbosity > 0)
+ printerr(0, "Warning: libtirpc does not "
+ "support setting debug levels\n");
+#endif
+
+ daemon_init(fg);
+
+ if (gssd_check_mechs() != 0)
+ errx(1, "Problem with gssapi library");
+
+ evbase = event_base_new();
+ if (!evbase) {
+ printerr(0, "ERROR: failed to create event base: %s\n", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ pipefs_dir = opendir(pipefs_path);
+ if (!pipefs_dir) {
+ printerr(0, "ERROR: opendir(%s) failed: %s\n", pipefs_path, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ pipefs_fd = dirfd(pipefs_dir);
+ if (fchdir(pipefs_fd)) {
+ printerr(0, "ERROR: fchdir(%s) failed: %s\n", pipefs_path, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ inotify_fd = inotify_init1(IN_NONBLOCK);
+ if (inotify_fd == -1) {
+ printerr(0, "ERROR: inotify_init1 failed: %s\n", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ signal(SIGINT, sig_die);
+ signal(SIGTERM, sig_die);
+ sighup_ev = evsignal_new(evbase, SIGHUP, gssd_scan_cb, NULL);
+ if (!sighup_ev) {
+ printerr(0, "ERROR: failed to create SIGHUP event: %s\n", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ evsignal_add(sighup_ev, NULL);
+ inotify_ev = event_new(evbase, inotify_fd, EV_READ | EV_PERSIST,
+ gssd_inotify_cb, NULL);
+ if (!inotify_ev) {
+ printerr(0, "ERROR: failed to create inotify event: %s\n", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ event_add(inotify_ev, NULL);
+
+ TAILQ_INIT(&active_thread_list);
+
+ rc = start_watchdog_thread();
+ if (rc != 0) {
+ printerr(0, "ERROR: failed to start watchdog thread: %d\n", rc);
+ exit(EXIT_FAILURE);
+ }
+
+ TAILQ_INIT(&topdir_list);
+ gssd_scan();
+ daemon_ready();
+
+ rc = event_base_dispatch(evbase);
+
+ printerr(0, "event_dispatch() returned %i!\n", rc);
+
+ gssd_destroy_krb5_principals(root_uses_machine_creds);
+
+ while (!TAILQ_EMPTY(&topdir_list)) {
+ struct topdir *tdi = TAILQ_FIRST(&topdir_list);
+ TAILQ_REMOVE(&topdir_list, tdi, list);
+ while (!TAILQ_EMPTY(&tdi->clnt_list)) {
+ struct clnt_info *clp = TAILQ_FIRST(&tdi->clnt_list);
+ TAILQ_REMOVE(&tdi->clnt_list, clp, list);
+ gssd_destroy_client(clp);
+ }
+ free(tdi);
+ }
+
+ event_free(inotify_ev);
+ event_free(sighup_ev);
+ event_base_free(evbase);
+
+ close(inotify_fd);
+ close(pipefs_fd);
+ closedir(pipefs_dir);
+
+ free(preferred_realm);
+ free(ccachesearch);
+ free(ccachedir);
+
+ return rc < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/utils/gssd/gssd.h b/utils/gssd/gssd.h
new file mode 100644
index 0000000..4e070ed
--- /dev/null
+++ b/utils/gssd/gssd.h
@@ -0,0 +1,124 @@
+/*
+ Copyright (c) 2004 The Regents of the University of Michigan.
+ 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. 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.
+*/
+
+#ifndef _RPC_GSSD_H_
+#define _RPC_GSSD_H_
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <gssapi/gssapi.h>
+#include <event.h>
+#include <stdbool.h>
+#include <pthread.h>
+
+#ifndef GSSD_PIPEFS_DIR
+#define GSSD_PIPEFS_DIR NFS_STATEDIR "/rpc_pipefs"
+#endif
+#define DNOTIFY_SIGNAL (SIGRTMIN + 3)
+
+#define GSSD_DEFAULT_CRED_DIR "/tmp"
+#define GSSD_USER_CRED_DIR "/run/user/%U"
+#define GSSD_DEFAULT_CRED_PREFIX "krb5cc"
+#define GSSD_DEFAULT_MACHINE_CRED_SUFFIX "machine"
+#define GSSD_DEFAULT_KEYTAB_FILE "/etc/krb5.keytab"
+#define GSSD_SERVICE_NAME "nfs"
+#define RPC_CHAN_BUF_SIZE 32768
+
+/* timeouts are in seconds */
+#define MIN_UPCALL_TIMEOUT 5
+#define DEF_UPCALL_TIMEOUT 30
+#define MAX_UPCALL_TIMEOUT 600
+
+/*
+ * The gss mechanisms that we can handle
+ */
+enum {AUTHTYPE_KRB5, AUTHTYPE_LIPKEY};
+
+extern char *keytabfile;
+extern char **ccachesearch;
+extern int use_memcache;
+extern int root_uses_machine_creds;
+extern unsigned int context_timeout;
+extern unsigned int rpc_timeout;
+extern char *preferred_realm;
+
+struct clnt_info {
+ TAILQ_ENTRY(clnt_info) list;
+ int refcount;
+ int wd;
+ bool scanned;
+ char *name;
+ char *relpath;
+ char *servicename;
+ char *servername;
+ int prog;
+ int vers;
+ char *protocol;
+ int krb5_fd;
+ struct event *krb5_ev;
+ int gssd_fd;
+ struct event *gssd_ev;
+ struct sockaddr_storage addr;
+ char *upcall_address;
+ char *upcall_port;
+ int upcall_program;
+ int upcall_vers;
+ char *upcall_protoname;
+ char *upcall_service;
+};
+
+struct clnt_upcall_info {
+ struct clnt_info *clp;
+ uid_t uid;
+ int fd;
+ char *srchost;
+ char *target;
+ char *service;
+};
+
+struct upcall_thread_info {
+ TAILQ_ENTRY(upcall_thread_info) list;
+ pthread_t tid;
+ struct timespec timeout;
+ uid_t uid;
+ int fd;
+ unsigned short flags;
+#define UPCALL_THREAD_CANCELED 0x0001
+#define UPCALL_THREAD_WARNED 0x0002
+};
+
+void handle_krb5_upcall(struct clnt_info *clp);
+void handle_gssd_upcall(struct clnt_info *clp);
+void free_upcall_info(struct clnt_upcall_info *info);
+void gssd_free_client(struct clnt_info *clp);
+int do_error_downcall(int k5_fd, uid_t uid, int err);
+
+
+#endif /* _RPC_GSSD_H_ */
diff --git a/utils/gssd/gssd.man b/utils/gssd/gssd.man
new file mode 100644
index 0000000..2a5384d
--- /dev/null
+++ b/utils/gssd/gssd.man
@@ -0,0 +1,408 @@
+.\"
+.\" rpc.gssd(8)
+.\"
+.\" Copyright (C) 2003 J. Bruce Fields <bfields@umich.edu>
+.\"
+.TH rpc.gssd 8 "20 Feb 2013"
+.SH NAME
+rpc.gssd \- RPCSEC_GSS daemon
+.SH SYNOPSIS
+.B rpc.gssd
+.RB [ \-DfMnlvrHC ]
+.RB [ \-k
+.IR keytab ]
+.RB [ \-p
+.IR pipefsdir ]
+.RB [ \-d
+.IR ccachedir ]
+.RB [ \-t
+.IR timeout ]
+.RB [ \-T
+.IR timeout ]
+.RB [ \-U
+.IR timeout ]
+.RB [ \-R
+.IR realm ]
+.SH INTRODUCTION
+The RPCSEC_GSS protocol, defined in RFC 5403, is used to provide
+strong security for RPC-based protocols such as NFS.
+.P
+Before exchanging RPC requests using RPCSEC_GSS, an RPC client must
+establish a GSS
+.IR "security context" .
+A security context is shared state on each
+end of a network transport that enables GSS-API security services.
+.P
+Security contexts are established using
+.IR "security credentials" .
+A credential grants temporary access to a secure network service,
+much as a railway ticket grants temporary access to use a rail service.
+.P
+A user typically obtains a credential by providing a password to the
+.BR kinit (1)
+command, or via a PAM library at login time.
+A credential acquired with a
+.I user principal
+is known as a
+.I user credential
+(see
+.BR kerberos (1)
+for more on principals).
+.P
+Certain operations require a credential that
+represents no particular user
+or
+represents the host itself.
+This kind of credential is called a
+.IR "machine credential" .
+.P
+A host establishes its machine credential using a
+.I "service principal"
+whose encrypted password is stored in a local file known as a
+.IR keytab .
+A machine credential remains effective
+without user intervention
+as long as the host can renew it.
+.P
+Once obtained, credentials are typically stored in local temporary files
+with well-known pathnames.
+.SH DESCRIPTION
+To establish GSS security contexts using these credential files,
+the Linux kernel RPC client depends on a userspace daemon called
+.BR rpc.gssd .
+The
+.B rpc.gssd
+daemon uses the rpc_pipefs filesystem to communicate with the kernel.
+.SS User Credentials
+When a user authenticates using a command such as
+.BR kinit (1),
+the resulting credential is stored in a file with a well-known name
+constructed using the user's UID.
+.P
+To interact with an NFS server
+on behalf of a particular Kerberos-authenticated user,
+the Linux kernel RPC client requests that
+.B rpc.gssd
+initialize a security context with the credential
+in that user's credential file.
+.P
+Typically, credential files are placed in
+.IR /tmp .
+However,
+.B rpc.gssd
+can search for credential files in more than one directory.
+See the description of the
+.B -d
+option for details.
+.SS Machine Credentials
+.B rpc.gssd
+searches the default keytab,
+.IR /etc/krb5.keytab ,
+in the following order for a principal and password to use
+when establishing the machine credential.
+For the search, rpc.gssd replaces <hostname> and <REALM> with the local
+system's hostname and Kerberos realm.
+.sp
+ <HOSTNAME>$@<REALM>
+.br
+ root/<hostname>@<REALM>
+.br
+ nfs/<hostname>@<REALM>
+.br
+ host/<hostname>@<REALM>
+.br
+ root/<anyname>@<REALM>
+.br
+ nfs/<anyname>@<REALM>
+.br
+ host/<anyname>@<REALM>
+.sp
+rpc.gssd selects one of the <anyname> entries if it does not find
+a service principal matching the local hostname,
+e.g. if DHCP assigns the local hostname dynamically.
+The <anyname> facility enables the use of the same keytab on multiple systems.
+However, using the same service principal to establish a machine credential
+on multiple hosts can create unwanted security exposures
+and is therefore not recommended.
+.P
+Note that <HOSTNAME>$@<REALM> is a user principal
+that enables Kerberized NFS when the local system is joined
+to an Active Directory domain using Samba.
+The keytab provides the password for this principal.
+.P
+You can specify a different keytab by using the
+.B -k
+option if
+.I /etc/krb5.keytab
+does not exist or does not provide one of these principals.
+.SS Credentials for UID 0
+UID 0 is a special case.
+By default
+.B rpc.gssd
+uses the system's machine credentials for UID 0 accesses
+that require GSS authentication.
+This limits the privileges of the root user
+when accessing network resources that require authentication.
+.P
+Specify the
+.B -n
+option when starting
+.B rpc.gssd
+if you'd like to force the root user to obtain a user credential
+rather than use the local system's machine credential.
+.P
+When
+.B -n
+is specified,
+the kernel continues to request a GSS context established
+with a machine credential for NFSv4 operations,
+such as SETCLIENTID or RENEW, that manage state.
+If
+.B rpc.gssd
+cannot obtain a machine credential (say, the local system has
+no keytab), NFSv4 operations that require machine credentials will fail.
+.SS Encryption types
+A realm administrator can choose to add keys encoded in a number of different
+encryption types to the local system's keytab.
+For instance, a host/ principal might have keys for the
+.BR aes256-cts-hmac-sha1-96 ,
+.BR aes128-cts-hmac-sha1-96 ,
+.BR des3-cbc-sha1 ", and"
+.BR arcfour-hmac " encryption types."
+This permits
+.B rpc.gssd
+to choose an appropriate encryption type that the target NFS server
+supports.
+.P
+These encryption types are stronger than legacy single-DES encryption types.
+To interoperate in environments where servers support
+only weak encryption types,
+you can restrict your client to use only single-DES encryption types
+by specifying the
+.B -l
+option when starting
+.BR rpc.gssd .
+.SH OPTIONS
+.TP
+.B \-D
+The server name passed to GSSAPI for authentication is normally the
+name exactly as requested. e.g. for NFS
+it is the server name in the "servername:/path" mount request. Only if this
+servername appears to be an IP address (IPv4 or IPv6) or an
+unqualified name (no dots) will a reverse DNS lookup
+will be performed to get the canoncial server name.
+
+If
+.B \-D
+is present, a reverse DNS lookup will
+.I always
+be used, even if the server name looks like a canonical name. So it
+is needed if partially qualified, or non canonical names are regularly
+used.
+
+Using
+.B \-D
+can introduce a security vulnerability, so it is recommended that
+.B \-D
+not be used, and that canonical names always be used when requesting
+services.
+.TP
+.B -f
+Runs
+.B rpc.gssd
+in the foreground and sends output to stderr (as opposed to syslogd)
+.TP
+.B -n
+When specified, UID 0 is forced to obtain user credentials
+which are used instead of the local system's machine credentials.
+.TP
+.BI "-k " keytab
+Tells
+.B rpc.gssd
+to use the keys found in
+.I keytab
+to obtain machine credentials.
+The default value is
+.IR /etc/krb5.keytab .
+.TP
+.B -l
+When specified, restricts
+.B rpc.gssd
+to sessions to weak encryption types such as
+.BR des-cbc-crc .
+This option is available only when the local system's Kerberos library
+supports settable encryption types.
+.TP
+.BI "-p " path
+Tells
+.B rpc.gssd
+where to look for the rpc_pipefs filesystem. The default value is
+.IR /var/lib/nfs/rpc_pipefs .
+.TP
+.BI "-d " search-path
+This option specifies a colon separated list of directories that
+.B rpc.gssd
+searches for credential files. The default value is
+.IR /tmp:/run/user/%U .
+The literal sequence "%U" can be specified to substitue the UID
+of the user for whom credentials are being searched.
+.TP
+.B -M
+By default, machine credentials are stored in files in the first
+directory in the credential directory search path (see the
+.B -d
+option). When
+.B -M
+is set,
+.B rpc.gssd
+stores machine credentials in memory instead.
+.TP
+.B -v
+Increases the verbosity of the output (can be specified multiple times).
+.TP
+.B -r
+If the RPCSEC_GSS library supports setting debug level,
+increases the verbosity of the output (can be specified multiple times).
+.TP
+.BI "-R " realm
+Kerberos tickets from this
+.I realm
+will be preferred when scanning available credentials cache files to be
+used to create a context. By default, the default realm, as configured
+in the Kerberos configuration file, is preferred.
+.TP
+.BI "-t " timeout
+Timeout, in seconds, for kernel GSS contexts. This option allows you to force
+new kernel contexts to be negotiated after
+.I timeout
+seconds, which allows changing Kerberos tickets and identities frequently.
+The default is no explicit timeout, which means the kernel context will live
+the lifetime of the Kerberos service ticket used in its creation.
+.TP
+.BI "-T " timeout
+Timeout, in seconds, to create an RPC connection with a server while
+establishing an authenticated gss context for a user.
+The default timeout is set to 5 seconds.
+If you get messages like "WARNING: can't create tcp rpc_clnt to server
+%servername% for user with uid %uid%: RPC: Remote system error -
+Connection timed out", you should consider an increase of this timeout.
+.TP
+.BI "-U " timeout
+Timeout, in seconds, for upcall threads. Threads executing longer than
+.I timeout
+seconds will cause an error message to be logged. The default
+.I timeout
+is 30 seconds. The minimum is 5 seconds. The maximum is 600 seconds.
+.TP
+.B -C
+In addition to logging an error message for threads that have timed out,
+the thread will be canceled and an error of -ETIMEDOUT will be reported
+to the kernel.
+.TP
+.B -H
+Avoids setting $HOME to "/". This allows rpc.gssd to read per user k5identity
+files versus trying to read /.k5identity for each user.
+
+If
+.B \-H
+is not set, rpc.gssd will use the first match found in
+/var/kerberos/krb5/user/$EUID/client.keytab and will not use a principal based on
+host and/or service parameters listed in $HOME/.k5identity.
+.SH CONFIGURATION FILE
+Many of the options that can be set on the command line can also be
+controlled through values set in the
+.B [gssd]
+section of the
+.I /etc/nfs.conf
+configuration file. Values recognized include:
+.TP
+.B verbosity
+Value which is equivalent to the number of
+.BR -v .
+.TP
+.B rpc-verbosity
+Value which is equivalent to the number of
+.BR -r .
+.TP
+.B use-memcache
+A Boolean flag equivalent to
+.BR -M .
+.TP
+.B use-machine-creds
+A Boolean flag. Setting to
+.B false
+is equivalent to giving the
+.B -n
+flag.
+.TP
+.B avoid-dns
+Setting to
+.B false
+is equivalent to providing the
+.B -D
+flag.
+.TP
+.B limit-to-legacy-enctypes
+Equivalent to
+.BR -l .
+.TP
+.B context-timeout
+Equivalent to
+.BR -t .
+.TP
+.B rpc-timeout
+Equivalent to
+.BR -T .
+.TP
+.B keytab-file
+Equivalent to
+.BR -k .
+.TP
+.BR cred-cache-directory
+Equivalent to
+.BR -d .
+.TP
+.B preferred-realm
+Equivalent to
+.BR -R .
+.TP
+.B upcall-timeout
+Equivalent to
+.BR -U .
+.TP
+.B cancel-timed-out-upcalls
+Setting to
+.B true
+is equivalent to providing the
+.B -C
+flag.
+.TP
+.B set-home
+Setting to
+.B false
+is equivalent to providing the
+.B -H
+flag.
+.P
+In addtion, the following value is recognized from the
+.B [general]
+section:
+.TP
+.B pipefs-directory
+Equivalent to
+.BR -p .
+
+.SH SEE ALSO
+.BR rpc.svcgssd (8),
+.BR kerberos (1),
+.BR kinit (1),
+.BR krb5.conf (5)
+.SH AUTHORS
+.br
+Dug Song <dugsong@umich.edu>
+.br
+Andy Adamson <andros@umich.edu>
+.br
+Marius Aamodt Eriksen <marius@umich.edu>
+.br
+J. Bruce Fields <bfields@umich.edu>
diff --git a/utils/gssd/gssd_proc.c b/utils/gssd/gssd_proc.c
new file mode 100644
index 0000000..a96647d
--- /dev/null
+++ b/utils/gssd/gssd_proc.c
@@ -0,0 +1,1075 @@
+/*
+ gssd_proc.c
+
+ Copyright (c) 2000-2004 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>.
+ Copyright (c) 2001 Andy Adamson <andros@UMICH.EDU>.
+ Copyright (c) 2002 Marius Aamodt Eriksen <marius@UMICH.EDU>.
+ Copyright (c) 2002 Bruce Fields <bfields@UMICH.EDU>
+ Copyright (c) 2004 Kevin Coffman <kwc@umich.edu>
+ Copyright (c) 2014 David H?rdeman <david@hardeman.nu>
+ 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.
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <sys/param.h>
+#include <rpc/rpc.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <sys/fsuid.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <pwd.h>
+#include <grp.h>
+#include <string.h>
+#include <dirent.h>
+#include <poll.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <unistd.h>
+#include <errno.h>
+#include <gssapi/gssapi.h>
+#include <netdb.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <syscall.h>
+
+#include "gssd.h"
+#include "err_util.h"
+#include "gss_util.h"
+#include "krb5_util.h"
+#include "context.h"
+#include "nfsrpc.h"
+#include "nfslib.h"
+#include "gss_names.h"
+
+extern pthread_mutex_t clp_lock;
+extern pthread_mutex_t active_thread_list_lock;
+extern int upcall_timeout;
+extern TAILQ_HEAD(active_thread_list_head, upcall_thread_info) active_thread_list;
+
+/* Encryption types supported by the kernel rpcsec_gss code */
+int num_krb5_enctypes = 0;
+krb5_enctype *krb5_enctypes = NULL;
+
+/* Args for the cleanup_handler() */
+struct cleanup_args {
+ OM_uint32 *min_stat;
+ gss_buffer_t acceptor;
+ gss_buffer_t token;
+ struct authgss_private_data *pd;
+ AUTH **auth;
+ CLIENT **rpc_clnt;
+};
+
+/*
+ * Parse the supported encryption type information
+ */
+static int
+parse_enctypes(char *enctypes)
+{
+ int n = 0;
+ char *curr, *comma;
+ int i;
+ static char *cached_types;
+
+ if (cached_types && strcmp(cached_types, enctypes) == 0)
+ return 0;
+ free(cached_types);
+
+ if (krb5_enctypes != NULL) {
+ free(krb5_enctypes);
+ krb5_enctypes = NULL;
+ num_krb5_enctypes = 0;
+ }
+
+ /* count the number of commas */
+ for (curr = enctypes; curr && *curr != '\0'; curr = ++comma) {
+ comma = strchr(curr, ',');
+ if (comma != NULL)
+ n++;
+ else
+ break;
+ }
+ /* If no more commas and we're not at the end, there's one more value */
+ if (*curr != '\0')
+ n++;
+
+ /* Empty string, return an error */
+ if (n == 0)
+ return ENOENT;
+
+ /* Allocate space for enctypes array */
+ if ((krb5_enctypes = (int *) calloc(n, sizeof(int))) == NULL) {
+ return ENOMEM;
+ }
+
+ /* Now parse each value into the array */
+ for (curr = enctypes, i = 0; curr && *curr != '\0'; curr = ++comma) {
+ krb5_enctypes[i++] = atoi(curr);
+ comma = strchr(curr, ',');
+ if (comma == NULL)
+ break;
+ }
+
+ num_krb5_enctypes = n;
+ if ((cached_types = malloc(strlen(enctypes)+1)))
+ strcpy(cached_types, enctypes);
+
+ return 0;
+}
+
+static void
+do_downcall(int k5_fd, uid_t uid, struct authgss_private_data *pd,
+ gss_buffer_desc *context_token, OM_uint32 lifetime_rec,
+ gss_buffer_desc *acceptor)
+{
+ char *buf = NULL, *p = NULL, *end = NULL;
+ unsigned int timeout = context_timeout;
+ unsigned int buf_size = 0;
+ pthread_t tid = pthread_self();
+
+ if (get_verbosity() > 1)
+ printerr(2, "do_downcall(0x%lx): lifetime_rec=%s acceptor=%.*s\n",
+ tid, sec2time(lifetime_rec), acceptor->length, acceptor->value);
+ buf_size = sizeof(uid) + sizeof(timeout) + sizeof(pd->pd_seq_win) +
+ sizeof(pd->pd_ctx_hndl.length) + pd->pd_ctx_hndl.length +
+ sizeof(context_token->length) + context_token->length +
+ sizeof(acceptor->length) + acceptor->length;
+ p = buf = malloc(buf_size);
+ if (!buf)
+ goto out_err;
+
+ end = buf + buf_size;
+
+ /* context_timeout set by -t option overrides context lifetime */
+ if (timeout == 0)
+ timeout = lifetime_rec;
+ if (WRITE_BYTES(&p, end, uid)) goto out_err;
+ if (WRITE_BYTES(&p, end, timeout)) goto out_err;
+ if (WRITE_BYTES(&p, end, pd->pd_seq_win)) goto out_err;
+ if (write_buffer(&p, end, &pd->pd_ctx_hndl)) goto out_err;
+ if (write_buffer(&p, end, context_token)) goto out_err;
+ if (write_buffer(&p, end, acceptor)) goto out_err;
+
+ if (write(k5_fd, buf, p - buf) < p - buf) goto out_err;
+ free(buf);
+ return;
+out_err:
+ free(buf);
+ printerr(1, "do_downcall(0x%lx): Failed to write downcall!\n", tid);
+ return;
+}
+
+int
+do_error_downcall(int k5_fd, uid_t uid, int err)
+{
+ char buf[1024];
+ char *p = buf, *end = buf + 1024;
+ unsigned int timeout = 0;
+ int zero = 0;
+ pthread_t tid = pthread_self();
+
+ printerr(2, "do_error_downcall(0x%lx): uid %d err %d\n", tid, uid, err);
+
+ if (WRITE_BYTES(&p, end, uid)) goto out_err;
+ if (WRITE_BYTES(&p, end, timeout)) goto out_err;
+ /* use seq_win = 0 to indicate an error: */
+ if (WRITE_BYTES(&p, end, zero)) goto out_err;
+ if (WRITE_BYTES(&p, end, err)) goto out_err;
+
+ if (write(k5_fd, buf, p - buf) < p - buf) goto out_err;
+ return 0;
+out_err:
+ printerr(1, "Failed to write error downcall!\n");
+ return -1;
+}
+
+/*
+ * If the port isn't already set, do an rpcbind query to the remote server
+ * using the program and version and get the port.
+ *
+ * Newer kernels send the value of the port= mount option in the "info"
+ * file for the upcall or '0' for NFSv2/3. For NFSv4 it sends the value
+ * of the port= option or '2049'. The port field in a new sockaddr should
+ * reflect the value that was sent by the kernel.
+ */
+static int
+populate_port(struct sockaddr *sa, const socklen_t salen,
+ const rpcprog_t program, const rpcvers_t version,
+ const unsigned short protocol)
+{
+ struct sockaddr_in *s4 = (struct sockaddr_in *) sa;
+#ifdef IPV6_SUPPORTED
+ struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) sa;
+#endif /* IPV6_SUPPORTED */
+ unsigned short port;
+
+ /*
+ * Newer kernels send the port in the upcall. If we already have
+ * the port, there's no need to look it up.
+ */
+ switch (sa->sa_family) {
+ case AF_INET:
+ if (s4->sin_port != 0) {
+ printerr(4, "DEBUG: port already set to %d\n",
+ ntohs(s4->sin_port));
+ return 1;
+ }
+ break;
+#ifdef IPV6_SUPPORTED
+ case AF_INET6:
+ if (s6->sin6_port != 0) {
+ printerr(4, "DEBUG: port already set to %d\n",
+ ntohs(s6->sin6_port));
+ return 1;
+ }
+ break;
+#endif /* IPV6_SUPPORTED */
+ default:
+ printerr(0, "ERROR: unsupported address family %d\n",
+ sa->sa_family);
+ return 0;
+ }
+
+ /*
+ * Newer kernels that send the port in the upcall set the value to
+ * 2049 for NFSv4 mounts when one isn't specified. The check below is
+ * only for kernels that don't send the port in the upcall. For those
+ * we either have to do an rpcbind query or set it to the standard
+ * port. Doing a query could be problematic (firewalls, etc), so take
+ * the latter approach.
+ */
+ if (program == 100003 && version == 4) {
+ port = 2049;
+ goto set_port;
+ }
+
+ port = nfs_getport(sa, salen, program, version, protocol);
+ if (!port) {
+ printerr(0, "ERROR: unable to obtain port for prog %ld "
+ "vers %ld\n", program, version);
+ return 0;
+ }
+
+set_port:
+ printerr(2, "DEBUG: setting port to %hu for prog %lu vers %lu\n", port,
+ program, version);
+
+ switch (sa->sa_family) {
+ case AF_INET:
+ s4->sin_port = htons(port);
+ break;
+#ifdef IPV6_SUPPORTED
+ case AF_INET6:
+ s6->sin6_port = htons(port);
+ break;
+#endif /* IPV6_SUPPORTED */
+ }
+
+ return 1;
+}
+
+/*
+ * Create an RPC connection and establish an authenticated
+ * gss context with a server.
+ */
+static int
+create_auth_rpc_client(struct clnt_info *clp,
+ char *tgtname,
+ CLIENT **clnt_return,
+ AUTH **auth_return,
+ uid_t uid,
+ int authtype,
+ gss_cred_id_t cred)
+{
+ CLIENT *rpc_clnt = NULL;
+ struct rpc_gss_sec sec;
+ AUTH *auth = NULL;
+ int retval = -1;
+ OM_uint32 min_stat;
+ char rpc_errmsg[1024];
+ int protocol;
+ struct timeval timeout;
+ struct sockaddr *addr = (struct sockaddr *) &clp->addr;
+ socklen_t salen;
+ pthread_t tid = pthread_self();
+
+ sec.qop = GSS_C_QOP_DEFAULT;
+ sec.svc = RPCSEC_GSS_SVC_NONE;
+ sec.cred = cred;
+ sec.req_flags = 0;
+ if (authtype == AUTHTYPE_KRB5) {
+ sec.mech = (gss_OID)&krb5oid;
+ sec.req_flags = GSS_C_MUTUAL_FLAG;
+ }
+ else {
+ printerr(0, "ERROR: Invalid authentication type (%d) "
+ "in create_auth_rpc_client\n", authtype);
+ goto out_fail;
+ }
+
+
+ if (authtype == AUTHTYPE_KRB5) {
+#ifdef HAVE_SET_ALLOWABLE_ENCTYPES
+ /*
+ * Do this before creating rpc connection since we won't need
+ * rpc connection if it fails!
+ */
+ if (limit_krb5_enctypes(&sec)) {
+ printerr(1, "WARNING: Failed while limiting krb5 "
+ "encryption types for user with uid %d\n",
+ uid);
+ goto out_fail;
+ }
+#endif
+ }
+
+ /* create an rpc connection to the nfs server */
+
+ printerr(3, "create_auth_rpc_client(0x%lx): creating %s client for server %s\n",
+ tid, clp->protocol, clp->servername);
+
+ protocol = IPPROTO_TCP;
+ if ((strcmp(clp->protocol, "udp")) == 0)
+ protocol = IPPROTO_UDP;
+
+ switch (addr->sa_family) {
+ case AF_INET:
+ salen = sizeof(struct sockaddr_in);
+ break;
+#ifdef IPV6_SUPPORTED
+ case AF_INET6:
+ salen = sizeof(struct sockaddr_in6);
+ break;
+#endif /* IPV6_SUPPORTED */
+ default:
+ printerr(1, "ERROR: Unknown address family %d\n",
+ addr->sa_family);
+ goto out_fail;
+ }
+
+ if (!populate_port(addr, salen, clp->prog, clp->vers, protocol))
+ goto out_fail;
+
+ /* set the timeout according to the requested valued */
+ timeout.tv_sec = (long) rpc_timeout;
+ timeout.tv_usec = (long) 0;
+
+ rpc_clnt = nfs_get_rpcclient(addr, salen, protocol, clp->prog,
+ clp->vers, &timeout);
+ if (!rpc_clnt) {
+ snprintf(rpc_errmsg, sizeof(rpc_errmsg),
+ "WARNING: can't create %s rpc_clnt to server %s for "
+ "user with uid %d",
+ protocol == IPPROTO_TCP ? "tcp" : "udp",
+ clp->servername, uid);
+ printerr(0, "%s\n",
+ clnt_spcreateerror(rpc_errmsg));
+ goto out_fail;
+ }
+ if (!tgtname)
+ tgtname = clp->servicename;
+
+ printerr(3, "create_auth_rpc_client(0x%lx): creating context with server %s\n",
+ tid, tgtname);
+ auth = authgss_create_default(rpc_clnt, tgtname, &sec);
+ if (!auth) {
+ if (sec.minor_status == KRB5KRB_AP_ERR_BAD_INTEGRITY) {
+ printerr(2, "WARNING: server=%s failed context "
+ "creation with KRB5_AP_ERR_BAD_INTEGRITY\n",
+ clp->servername);
+ if (cred == GSS_C_NO_CREDENTIAL)
+ retval = gssd_refresh_krb5_machine_credential(clp->servername,
+ "*", NULL, 1);
+ else
+ retval = gssd_k5_remove_bad_service_cred(clp->servername);
+ if (!retval) {
+ auth = authgss_create_default(rpc_clnt, tgtname,
+ &sec);
+ if (auth)
+ goto success;
+ }
+ }
+ /* Our caller should print appropriate message */
+ printerr(2, "WARNING: Failed to create krb5 context for "
+ "user with uid %d for server %s\n",
+ uid, tgtname);
+ goto out_fail;
+ }
+success:
+ /* Success !!! */
+ rpc_clnt->cl_auth = auth;
+ *clnt_return = rpc_clnt;
+ *auth_return = auth;
+ retval = 0;
+
+ out:
+ if (sec.cred != GSS_C_NO_CREDENTIAL)
+ gss_release_cred(&min_stat, &sec.cred);
+ return retval;
+
+ out_fail:
+ /* Only destroy here if failure. Otherwise, caller is responsible */
+ if (rpc_clnt) clnt_destroy(rpc_clnt);
+
+ goto out;
+}
+
+/*
+ * Create the context as the user (not as root).
+ *
+ * Note that we change the *real* uid here, as changing the effective uid is
+ * not sufficient. This is due to an unfortunate historical error in the MIT
+ * krb5 libs, where they used %{uid} in the default_ccache_name. Changing that
+ * now might break some applications so we're sort of stuck with it.
+ *
+ * Unfortunately, doing this leaves the forked child vulnerable to signals and
+ * renicing, but this is the best we can do. In the event that a child is
+ * signalled before downcalling, the kernel will just eventually time out the
+ * upcall attempt.
+ */
+static int
+change_identity(uid_t uid)
+{
+ struct passwd *pw;
+ int res;
+
+ /* drop list of supplimentary groups first */
+#ifdef __NR_setgroups32
+ if (syscall(SYS_setgroups32, 0, 0) != 0) {
+#else
+ if (syscall(SYS_setgroups, 0, 0) != 0) {
+#endif
+ printerr(0, "WARNING: unable to drop supplimentary groups!");
+ return errno;
+ }
+
+ /* try to get pwent for user */
+ pw = getpwuid(uid);
+ if (!pw) {
+ /* if that doesn't work, try to get one for "nobody" */
+ errno = 0;
+ pw = getpwnam("nobody");
+ if (!pw) {
+ printerr(0, "WARNING: unable to determine gid for uid %u\n", uid);
+ return errno ? errno : ENOENT;
+ }
+ }
+
+ /* Switch the UIDs and GIDs. */
+ /* For the threaded version we have to set uid,gid per thread instead
+ * of per process. glibc setresuid() when called from a thread, it'll
+ * send a signal to all other threads to synchronize the uid in all
+ * other threads. To bypass this, we have to call syscall() directly.
+ */
+#ifdef __NR_setresgid32
+ res = syscall(SYS_setresgid32, pw->pw_gid, pw->pw_gid, pw->pw_gid);
+#else
+ res = syscall(SYS_setresgid, pw->pw_gid, pw->pw_gid, pw->pw_gid);
+#endif
+ if (res != 0) {
+ printerr(0, "WARNING: failed to set gid to %u!\n", pw->pw_gid);
+ return errno;
+ }
+
+#ifdef __NR_setresuid32
+ res = syscall(SYS_setresuid32, uid, uid, uid);
+#else
+ res = syscall(SYS_setresuid, uid, uid, uid);
+#endif
+ if (res != 0) {
+ printerr(0, "WARNING: Failed to setuid for user with uid %u\n", uid);
+ return errno;
+ }
+
+ return 0;
+}
+
+static AUTH *
+krb5_not_machine_creds(struct clnt_info *clp, uid_t uid, char *tgtname,
+ int *downcall_err, int *chg_err, CLIENT **rpc_clnt)
+{
+ AUTH *auth = NULL;
+ gss_cred_id_t gss_cred;
+ char **dname;
+ int err, resp = -1;
+ pthread_t tid = pthread_self();
+
+ printerr(2, "krb5_not_machine_creds(0x%lx): uid %d tgtname %s\n",
+ tid, uid, tgtname);
+
+ *chg_err = change_identity(uid);
+ if (*chg_err) {
+ printerr(0, "WARNING: failed to change identity: %s",
+ strerror(*chg_err));
+ goto out;
+ }
+
+ /** Tell krb5 gss which credentials cache to use.
+ * Try first to acquire credentials directly via GSSAPI
+ */
+ err = gssd_acquire_user_cred(&gss_cred);
+ if (err == 0)
+ resp = create_auth_rpc_client(clp, tgtname, rpc_clnt,
+ &auth, uid,
+ AUTHTYPE_KRB5, gss_cred);
+
+ /** if create_auth_rplc_client fails try the traditional
+ * method of trolling for credentials
+ */
+ for (dname = ccachesearch; resp != 0 && *dname != NULL; dname++) {
+ err = gssd_setup_krb5_user_gss_ccache(uid, clp->servername,
+ *dname);
+ if (err == -EKEYEXPIRED)
+ *downcall_err = -EKEYEXPIRED;
+ else if (err == 0)
+ resp = create_auth_rpc_client(clp, tgtname, rpc_clnt,
+ &auth, uid,AUTHTYPE_KRB5,
+ GSS_C_NO_CREDENTIAL);
+ }
+
+out:
+ return auth;
+}
+
+static AUTH *
+krb5_use_machine_creds(struct clnt_info *clp, uid_t uid,
+ char *srchost, char *tgtname, char *service,
+ CLIENT **rpc_clnt)
+{
+ AUTH *auth = NULL;
+ char **credlist = NULL;
+ char **ccname;
+ int nocache = 0;
+ int success = 0;
+ pthread_t tid = pthread_self();
+
+ printerr(2, "krb5_use_machine_creds(0x%lx): uid %d tgtname %s\n",
+ tid, uid, tgtname);
+
+ do {
+ gssd_refresh_krb5_machine_credential(clp->servername,
+ service, srchost, 0);
+ /*
+ * Get a list of credential cache names and try each
+ * of them until one works or we've tried them all
+ */
+ if (gssd_get_krb5_machine_cred_list(&credlist)) {
+ printerr(0, "ERROR: No credentials found "
+ "for connection to server %s\n",
+ clp->servername);
+ goto out;
+ }
+ for (ccname = credlist; ccname && *ccname; ccname++) {
+ u_int min_stat;
+
+ if (gss_krb5_ccache_name(&min_stat, *ccname, NULL) !=
+ GSS_S_COMPLETE) {
+ printerr(1, "WARNING: gss_krb5_ccache_name "
+ "with name '%s' failed (%s)\n",
+ *ccname, error_message(min_stat));
+ continue;
+ }
+ if ((create_auth_rpc_client(clp, tgtname, rpc_clnt,
+ &auth, uid,
+ AUTHTYPE_KRB5,
+ GSS_C_NO_CREDENTIAL)) == 0) {
+ /* Success! */
+ success++;
+ break;
+ }
+ printerr(2, "WARNING: Failed to create machine krb5 "
+ "context with cred cache %s for server %s\n",
+ *ccname, clp->servername);
+ }
+ gssd_free_krb5_machine_cred_list(credlist);
+ if (!success) {
+ if(nocache == 0) {
+ nocache++;
+ printerr(2, "WARNING: Machine cache prematurely "
+ "expired or corrupted trying to "
+ "recreate cache for server %s\n",
+ clp->servername);
+ } else {
+ printerr(1, "ERROR: Failed to create machine "
+ "krb5 context with any credentials "
+ "cache for server %s\n",
+ clp->servername);
+ goto out;
+ }
+ }
+ } while(!success);
+
+out:
+ return auth;
+}
+
+/*
+ * cleanup_handler:
+ *
+ * Free any resources allocated by process_krb5_upcall().
+ *
+ * Runs upon normal termination of process_krb5_upcall as well as if the
+ * thread is canceled.
+ */
+static void
+cleanup_handler(void *arg)
+{
+ struct cleanup_args *args = (struct cleanup_args *)arg;
+
+ gss_release_buffer(args->min_stat, args->acceptor);
+ if (args->token->value)
+ free(args->token->value);
+#ifdef HAVE_AUTHGSS_FREE_PRIVATE_DATA
+ if (args->pd->pd_ctx_hndl.length != 0 || args->pd->pd_ctx != 0)
+ authgss_free_private_data(args->pd);
+#endif
+ if (*args->auth)
+ AUTH_DESTROY(*args->auth);
+ if (*args->rpc_clnt)
+ clnt_destroy(*args->rpc_clnt);
+}
+
+/*
+ * process_krb5_upcall:
+ *
+ * this code uses the userland rpcsec gss library to create a krb5
+ * context on behalf of the kernel
+ *
+ * This is the meat of the upcall thread. Note that cancelability is disabled
+ * and enabled at various points to ensure that any resources reserved by the
+ * lower level libraries are released safely.
+ */
+static void
+process_krb5_upcall(struct clnt_upcall_info *info)
+{
+ struct clnt_info *clp = info->clp;
+ uid_t uid = info->uid;
+ int fd = info->fd;
+ char *srchost = info->srchost;
+ char *tgtname = info->target;
+ char *service = info->service;
+ CLIENT *rpc_clnt = NULL;
+ AUTH *auth = NULL;
+ struct authgss_private_data pd;
+ gss_buffer_desc token;
+ int err, downcall_err;
+ OM_uint32 maj_stat, min_stat, lifetime_rec;
+ gss_name_t gacceptor = GSS_C_NO_NAME;
+ gss_OID mech;
+ gss_buffer_desc acceptor = {0};
+ struct cleanup_args cleanup_args = {&min_stat, &acceptor, &token, &pd, &auth, &rpc_clnt};
+
+ token.length = 0;
+ token.value = NULL;
+ memset(&pd, 0, sizeof(struct authgss_private_data));
+
+ pthread_cleanup_push(cleanup_handler, &cleanup_args);
+ /*
+ * If "service" is specified, then the kernel is indicating that
+ * we must use machine credentials for this request. (Regardless
+ * of the uid value or the setting of root_uses_machine_creds.)
+ * If the service value is "*", then any service name can be used.
+ * Otherwise, it specifies the service name that should be used.
+ * (For now, the values of service will only be "*" or "nfs".)
+ *
+ * Restricting gssd to use "nfs" service name is needed for when
+ * the NFS server is doing a callback to the NFS client. In this
+ * case, the NFS server has to authenticate itself as "nfs" --
+ * even if there are other service keys such as "host" or "root"
+ * in the keytab.
+ *
+ * Another case when the kernel may specify the service attribute
+ * is when gssd is being asked to create the context for a
+ * SETCLIENT_ID operation. In this case, machine credentials
+ * must be used for the authentication. However, the service name
+ * used for this case is not important.
+ *
+ */
+ downcall_err = -EACCES;
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
+ if (uid != 0 || (uid == 0 && root_uses_machine_creds == 0 &&
+ service == NULL)) {
+
+ auth = krb5_not_machine_creds(clp, uid, tgtname, &downcall_err,
+ &err, &rpc_clnt);
+ if (err)
+ goto out_return_error;
+ }
+ if (auth == NULL) {
+ if (uid == 0 && (root_uses_machine_creds == 1 ||
+ service != NULL)) {
+ auth = krb5_use_machine_creds(clp, uid, srchost, tgtname,
+ service, &rpc_clnt);
+ if (auth == NULL)
+ goto out_return_error;
+ } else {
+ /* krb5_not_machine_creds logs the error */
+ goto out_return_error;
+ }
+ }
+ pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
+ pthread_testcancel();
+
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
+ if (!authgss_get_private_data(auth, &pd)) {
+ printerr(1, "WARNING: Failed to obtain authentication "
+ "data for user with uid %d for server %s\n",
+ uid, clp->servername);
+ goto out_return_error;
+ }
+ pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
+ pthread_testcancel();
+
+ /* Grab the context lifetime and acceptor name out of the ctx. */
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
+ maj_stat = gss_inquire_context(&min_stat, pd.pd_ctx, NULL, &gacceptor,
+ &lifetime_rec, &mech, NULL, NULL, NULL);
+
+ if (maj_stat != GSS_S_COMPLETE) {
+ printerr(1, "WARNING: Failed to inquire context "
+ "maj_stat (0x%x)\n", maj_stat);
+ lifetime_rec = 0;
+ } else {
+ get_hostbased_client_buffer(gacceptor, mech, &acceptor);
+ gss_release_name(&min_stat, &gacceptor);
+ }
+ pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
+ pthread_testcancel();
+
+ /*
+ * The serialization can mean turning pd.pd_ctx into a lucid context. If
+ * that happens then the pd.pd_ctx will be unusable, so we must never
+ * try to use it after this point.
+ */
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
+ if (serialize_context_for_kernel(&pd.pd_ctx, &token, &krb5oid, NULL)) {
+ printerr(1, "WARNING: Failed to serialize krb5 context for "
+ "user with uid %d for server %s\n",
+ uid, clp->servername);
+ goto out_return_error;
+ }
+ pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
+ pthread_testcancel();
+
+ do_downcall(fd, uid, &pd, &token, lifetime_rec, &acceptor);
+
+out:
+ pthread_cleanup_pop(1);
+
+ return;
+
+out_return_error:
+ pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
+ pthread_testcancel();
+
+ do_error_downcall(fd, uid, downcall_err);
+ goto out;
+}
+
+static struct clnt_upcall_info *
+alloc_upcall_info(struct clnt_info *clp, uid_t uid, int fd, char *srchost,
+ char *target, char *service)
+{
+ struct clnt_upcall_info *info;
+
+ info = malloc(sizeof(struct clnt_upcall_info));
+ if (info == NULL)
+ return NULL;
+
+ memset(info, 0, sizeof(*info));
+ pthread_mutex_lock(&clp_lock);
+ clp->refcount++;
+ pthread_mutex_unlock(&clp_lock);
+ info->clp = clp;
+ info->uid = uid;
+ info->fd = fd;
+ if (srchost) {
+ info->srchost = strdup(srchost);
+ if (info->srchost == NULL)
+ goto out_info;
+ }
+ if (target) {
+ info->target = strdup(target);
+ if (info->target == NULL)
+ goto out_srchost;
+ }
+ if (service) {
+ info->service = strdup(service);
+ if (info->service == NULL)
+ goto out_target;
+ }
+
+out:
+ return info;
+
+out_target:
+ if (info->target)
+ free(info->target);
+out_srchost:
+ if (info->srchost)
+ free(info->srchost);
+out_info:
+ free(info);
+ info = NULL;
+ goto out;
+}
+
+void free_upcall_info(struct clnt_upcall_info *info)
+{
+ gssd_free_client(info->clp);
+ if (info->service)
+ free(info->service);
+ if (info->target)
+ free(info->target);
+ if (info->srchost)
+ free(info->srchost);
+ free(info);
+}
+
+static void
+cleanup_clnt_upcall_info(void *arg)
+{
+ struct clnt_upcall_info *info = (struct clnt_upcall_info *)arg;
+
+ free_upcall_info(info);
+}
+
+static void
+gssd_work_thread_fn(struct clnt_upcall_info *info)
+{
+ pthread_cleanup_push(cleanup_clnt_upcall_info, info);
+ process_krb5_upcall(info);
+ pthread_cleanup_pop(1);
+}
+
+static struct upcall_thread_info *
+alloc_upcall_thread_info(void)
+{
+ struct upcall_thread_info *info;
+
+ info = malloc(sizeof(struct upcall_thread_info));
+ if (info == NULL)
+ return NULL;
+ memset(info, 0, sizeof(*info));
+ return info;
+}
+
+static int
+start_upcall_thread(void (*func)(struct clnt_upcall_info *), struct clnt_upcall_info *info)
+{
+ pthread_attr_t attr;
+ pthread_t th;
+ struct upcall_thread_info *tinfo;
+ int ret;
+ pthread_t tid = pthread_self();
+
+ tinfo = alloc_upcall_thread_info();
+ if (!tinfo)
+ return -ENOMEM;
+ tinfo->fd = info->fd;
+ tinfo->uid = info->uid;
+
+ ret = pthread_attr_init(&attr);
+ if (ret != 0) {
+ printerr(0, "ERROR: failed to init pthread attr: ret %d: %s\n",
+ ret, strerror(errno));
+ free(tinfo);
+ return ret;
+ }
+
+ ret = pthread_create(&th, &attr, (void *)func, (void *)info);
+ if (ret != 0) {
+ printerr(0, "ERROR: pthread_create failed: ret %d: %s\n",
+ ret, strerror(errno));
+ free(tinfo);
+ return ret;
+ }
+ printerr(2, "start_upcall_thread(0x%lx): created thread id 0x%lx\n",
+ tid, th);
+
+ tinfo->tid = th;
+ pthread_mutex_lock(&active_thread_list_lock);
+ clock_gettime(CLOCK_MONOTONIC, &tinfo->timeout);
+ tinfo->timeout.tv_sec += upcall_timeout;
+ TAILQ_INSERT_TAIL(&active_thread_list, tinfo, list);
+ pthread_mutex_unlock(&active_thread_list_lock);
+
+ return ret;
+}
+
+void
+handle_krb5_upcall(struct clnt_info *clp)
+{
+ uid_t uid;
+ struct clnt_upcall_info *info;
+ int err;
+
+ if (read(clp->krb5_fd, &uid, sizeof(uid)) < (ssize_t)sizeof(uid)) {
+ printerr(0, "WARNING: failed reading uid from krb5 "
+ "upcall pipe: %s\n", strerror(errno));
+ return;
+ }
+ printerr(2, "\n%s: uid %d (%s)\n", __func__, uid, clp->relpath);
+
+ info = alloc_upcall_info(clp, uid, clp->krb5_fd, NULL, NULL, NULL);
+ if (info == NULL) {
+ printerr(0, "%s: failed to allocate clnt_upcall_info\n", __func__);
+ do_error_downcall(clp->krb5_fd, uid, -EACCES);
+ return;
+ }
+ err = start_upcall_thread(gssd_work_thread_fn, info);
+ if (err != 0) {
+ do_error_downcall(clp->krb5_fd, uid, -EACCES);
+ free_upcall_info(info);
+ }
+}
+
+void
+handle_gssd_upcall(struct clnt_info *clp)
+{
+ uid_t uid;
+ char lbuf[RPC_CHAN_BUF_SIZE];
+ int lbuflen = 0;
+ char *p;
+ char *mech = NULL;
+ char *uidstr = NULL;
+ char *target = NULL;
+ char *service = NULL;
+ char *srchost = NULL;
+ char *enctypes = NULL;
+ pthread_t tid = pthread_self();
+ struct clnt_upcall_info *info;
+ int err;
+
+ lbuflen = read(clp->gssd_fd, lbuf, sizeof(lbuf));
+ if (lbuflen <= 0 || lbuf[lbuflen-1] != '\n') {
+ printerr(0, "WARNING: handle_gssd_upcall: "
+ "failed reading request\n");
+ return;
+ }
+ lbuf[lbuflen-1] = 0;
+
+ printerr(2, "\n%s(0x%lx): '%s' (%s)\n", __func__, tid,
+ lbuf, clp->relpath);
+
+ for (p = strtok(lbuf, " "); p; p = strtok(NULL, " ")) {
+ if (!strncmp(p, "mech=", strlen("mech=")))
+ mech = p + strlen("mech=");
+ else if (!strncmp(p, "uid=", strlen("uid=")))
+ uidstr = p + strlen("uid=");
+ else if (!strncmp(p, "enctypes=", strlen("enctypes=")))
+ enctypes = p + strlen("enctypes=");
+ else if (!strncmp(p, "target=", strlen("target=")))
+ target = p + strlen("target=");
+ else if (!strncmp(p, "service=", strlen("service=")))
+ service = p + strlen("service=");
+ else if (!strncmp(p, "srchost=", strlen("srchost=")))
+ srchost = p + strlen("srchost=");
+ }
+
+ if (!mech || strlen(mech) < 1) {
+ printerr(0, "WARNING: handle_gssd_upcall: "
+ "failed to find gss mechanism name "
+ "in upcall string '%s'\n", lbuf);
+ return;
+ }
+
+ if (uidstr) {
+ uid = (uid_t)strtol(uidstr, &p, 10);
+ if (p == uidstr || *p != '\0')
+ uidstr = NULL;
+ }
+
+ if (!uidstr) {
+ printerr(0, "WARNING: handle_gssd_upcall: "
+ "failed to find uid "
+ "in upcall string '%s'\n", lbuf);
+ return;
+ }
+
+ if (enctypes && parse_enctypes(enctypes) != 0) {
+ printerr(0, "WARNING: handle_gssd_upcall: "
+ "parsing encryption types failed: errno %d\n", errno);
+ return;
+ }
+
+ if (target && strlen(target) < 1) {
+ printerr(0, "WARNING: handle_gssd_upcall: "
+ "failed to parse target name "
+ "in upcall string '%s'\n", lbuf);
+ return;
+ }
+
+ /*
+ * The presence of attribute "service=" indicates that machine
+ * credentials should be used for this request. If the value
+ * is "*", then any machine credentials available can be used.
+ * If the value is anything else, then machine credentials for
+ * the specified service name (always "nfs" for now) should be
+ * used.
+ */
+ if (service && strlen(service) < 1) {
+ printerr(0, "WARNING: handle_gssd_upcall: "
+ "failed to parse service type "
+ "in upcall string '%s'\n", lbuf);
+ return;
+ }
+
+ if (strcmp(mech, "krb5") == 0 && clp->servername) {
+ info = alloc_upcall_info(clp, uid, clp->gssd_fd, srchost, target, service);
+ if (info == NULL) {
+ printerr(0, "%s: failed to allocate clnt_upcall_info\n", __func__);
+ do_error_downcall(clp->gssd_fd, uid, -EACCES);
+ return;
+ }
+ err = start_upcall_thread(gssd_work_thread_fn, info);
+ if (err != 0) {
+ do_error_downcall(clp->gssd_fd, uid, -EACCES);
+ free_upcall_info(info);
+ }
+ } else {
+ if (clp->servername)
+ printerr(0, "WARNING: handle_gssd_upcall: "
+ "received unknown gss mech '%s'\n", mech);
+ do_error_downcall(clp->gssd_fd, uid, -EACCES);
+ }
+}
diff --git a/utils/gssd/krb5_util.c b/utils/gssd/krb5_util.c
new file mode 100644
index 0000000..6f66ef4
--- /dev/null
+++ b/utils/gssd/krb5_util.c
@@ -0,0 +1,1649 @@
+/*
+ * Adapted in part from MIT Kerberos 5-1.2.1 slave/kprop.c and from
+ * http://docs.sun.com/?p=/doc/816-1331/6m7oo9sms&a=view
+ *
+ * Copyright (c) 2002-2004 The Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Andy Adamson <andros@umich.edu>
+ * J. Bruce Fields <bfields@umich.edu>
+ * Marius Aamodt Eriksen <marius@umich.edu>
+ * Kevin Coffman <kwc@umich.edu>
+ */
+
+/*
+ * slave/kprop.c
+ *
+ * Copyright 1990,1991 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+
+/*
+ * Copyright 1994 by OpenVision Technologies, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without fee,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear in
+ * supporting documentation, and that the name of OpenVision not be used
+ * in advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission. OpenVision makes no
+ * representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied warranty.
+ *
+ * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
+ * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+/*
+ krb5_util.c
+
+ Copyright (c) 2004 The Regents of the University of Michigan.
+ 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. 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.
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <sys/param.h>
+#include <rpc/rpc.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <dirent.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+#include <time.h>
+#include <gssapi/gssapi.h>
+#ifdef USE_PRIVATE_KRB5_FUNCTIONS
+#include <gssapi/gssapi_krb5.h>
+#endif
+#include <krb5.h>
+#include <rpc/auth_gss.h>
+
+#include <sys/types.h>
+#include <fcntl.h>
+
+#include "nfslib.h"
+#include "gssd.h"
+#include "err_util.h"
+#include "gss_util.h"
+#include "krb5_util.h"
+
+/*
+ * List of principals from our keytab that we
+ * will try to use to obtain credentials
+ * (known as a principal list entry (ple))
+ */
+struct gssd_k5_kt_princ {
+ struct gssd_k5_kt_princ *next;
+ // Only protect against deletion, not modification
+ int refcount;
+ // Only set during creation in new_ple()
+ krb5_principal princ;
+ char *realm;
+ // Modified during usage by gssd_get_single_krb5_cred()
+ char *ccname;
+ krb5_timestamp endtime;
+};
+
+
+/* Global list of principals/cache file names for machine credentials */
+static struct gssd_k5_kt_princ *gssd_k5_kt_princ_list = NULL;
+/* This mutex protects list modification & ple->ccname */
+static pthread_mutex_t ple_lock = PTHREAD_MUTEX_INITIALIZER;
+
+#ifdef HAVE_SET_ALLOWABLE_ENCTYPES
+int limit_to_legacy_enctypes = 0;
+#endif
+
+/*==========================*/
+/*=== Internal routines ===*/
+/*==========================*/
+
+static int select_krb5_ccache(const struct dirent *d);
+static int gssd_find_existing_krb5_ccache(uid_t uid, char *dirname,
+ const char **cctype, struct dirent **d);
+static int gssd_get_single_krb5_cred(krb5_context context,
+ krb5_keytab kt, struct gssd_k5_kt_princ *ple, int force_renew);
+static int query_krb5_ccache(const char* cred_cache, char **ret_princname,
+ char **ret_realm);
+
+static void release_ple_locked(krb5_context context,
+ struct gssd_k5_kt_princ *ple)
+{
+ if (--ple->refcount)
+ return;
+
+ printerr(3, "freeing cached principal (ccname=%s, realm=%s)\n",
+ ple->ccname, ple->realm);
+ krb5_free_principal(context, ple->princ);
+ free(ple->ccname);
+ free(ple->realm);
+ free(ple);
+}
+
+static void release_ple(krb5_context context, struct gssd_k5_kt_princ *ple)
+{
+ pthread_mutex_lock(&ple_lock);
+ release_ple_locked(context, ple);
+ pthread_mutex_unlock(&ple_lock);
+}
+
+
+/*
+ * Called from the scandir function to weed out potential krb5
+ * credentials cache files
+ *
+ * Returns:
+ * 0 => don't select this one
+ * 1 => select this one
+ */
+static int
+select_krb5_ccache(const struct dirent *d)
+{
+ /*
+ * Note: We used to check d->d_type for DT_REG here,
+ * but apparenlty reiser4 always has DT_UNKNOWN.
+ * Check for IS_REG after stat() call instead.
+ */
+ if (strstr(d->d_name, GSSD_DEFAULT_CRED_PREFIX))
+ return 1;
+ else
+ return 0;
+}
+
+/*
+ * Look in directory "dirname" for files that look like they
+ * are Kerberos Credential Cache files for a given UID.
+ *
+ * Returns 0 if a valid-looking entry is found. "*cctype" is
+ * set to the name of the cache type. A pointer to the dirent
+ * is planted in "*d". Caller must free "*d" with free(3).
+ *
+ * Otherwise, a negative errno is returned.
+ */
+static int
+gssd_find_existing_krb5_ccache(uid_t uid, char *dirname,
+ const char **cctype, struct dirent **d)
+{
+ struct dirent **namelist;
+ int n;
+ int i;
+ int found = 0;
+ struct dirent *best_match_dir = NULL;
+ struct stat best_match_stat, tmp_stat;
+ /* dirname + cctype + d_name + NULL */
+ char buf[PATH_MAX+5+256+1];
+ char *princname = NULL;
+ char *realm = NULL;
+ int score, best_match_score = 0, err = -EACCES;
+
+ memset(&best_match_stat, 0, sizeof(best_match_stat));
+ *cctype = NULL;
+ *d = NULL;
+ n = scandir(dirname, &namelist, select_krb5_ccache, 0);
+ if (n < 0) {
+ printerr(1, "Error doing scandir on directory '%s': %s\n",
+ dirname, strerror(errno));
+ }
+ else if (n > 0) {
+ for (i = 0; i < n; i++) {
+ snprintf(buf, sizeof(buf),
+ "%s/%s", dirname, namelist[i]->d_name);
+ printerr(3, "CC '%s' being considered, "
+ "with preferred realm '%s'\n",
+ buf, preferred_realm ?
+ preferred_realm : "<none selected>");
+ if (lstat(buf, &tmp_stat)) {
+ printerr(0, "Error doing stat on '%s'\n", buf);
+ free(namelist[i]);
+ continue;
+ }
+ /* Only pick caches owned by the user (uid) */
+ if (tmp_stat.st_uid != uid) {
+ printerr(3, "CC '%s' owned by %u, not %u\n",
+ buf, tmp_stat.st_uid, uid);
+ free(namelist[i]);
+ continue;
+ }
+ if (!S_ISREG(tmp_stat.st_mode) &&
+ !S_ISDIR(tmp_stat.st_mode)) {
+ printerr(3, "CC '%s' is not a regular "
+ "file or directory\n", buf);
+ free(namelist[i]);
+ continue;
+ }
+ if (uid == 0 && !root_uses_machine_creds &&
+ strstr(namelist[i]->d_name, "machine_")) {
+ printerr(3, "CC '%s' not available to root\n", buf);
+ free(namelist[i]);
+ continue;
+ }
+ if (S_ISDIR(tmp_stat.st_mode)) {
+ *cctype = "DIR";
+ } else
+ if (S_ISREG(tmp_stat.st_mode)) {
+ *cctype = "FILE";
+ } else {
+ continue;
+ }
+ snprintf(buf, sizeof(buf), "%s:%s/%s", *cctype,
+ dirname, namelist[i]->d_name);
+ if (!query_krb5_ccache(buf, &princname, &realm)) {
+ printerr(3, "CC '%s' is expired or corrupt\n",
+ buf);
+ free(namelist[i]);
+ err = -EKEYEXPIRED;
+ continue;
+ }
+
+ score = 0;
+ if (preferred_realm &&
+ strcmp(realm, preferred_realm) == 0)
+ score++;
+
+ printerr(3, "CC '%s'(%s@%s) passed all checks and"
+ " has mtime of %u\n",
+ buf, princname, realm,
+ tmp_stat.st_mtime);
+ /*
+ * if more than one match is found, return the most
+ * recent (the one with the latest mtime), and
+ * don't free the dirent
+ */
+ if (!found) {
+ best_match_dir = namelist[i];
+ best_match_stat = tmp_stat;
+ best_match_score = score;
+ found++;
+ }
+ else {
+ /*
+ * If current score is higher than best match
+ * score, we use the current match. Otherwise,
+ * if the current match has an mtime later
+ * than the one we are looking at, then use
+ * the current match. Otherwise, we still
+ * have the best match.
+ */
+ if (best_match_score < score ||
+ (best_match_score == score &&
+ tmp_stat.st_mtime >
+ best_match_stat.st_mtime)) {
+ free(best_match_dir);
+ best_match_dir = namelist[i];
+ best_match_stat = tmp_stat;
+ best_match_score = score;
+ }
+ else {
+ free(namelist[i]);
+ }
+ printerr(3, "CC '%s:%s/%s' is our "
+ "current best match "
+ "with mtime of %u\n",
+ cctype, dirname,
+ best_match_dir->d_name,
+ best_match_stat.st_mtime);
+ }
+ free(princname);
+ free(realm);
+ }
+ free(namelist);
+ }
+ if (found) {
+ *d = best_match_dir;
+ return 0;
+ }
+
+ return err;
+}
+
+/* check if the ticket cache exists, if not set nocache=1 so that new
+ * tgt is gotten
+ */
+static int
+gssd_check_if_cc_exists(struct gssd_k5_kt_princ *ple)
+{
+ int fd;
+ char cc_name[BUFSIZ];
+
+ snprintf(cc_name, sizeof(cc_name), "%s/%s%s_%s",
+ ccachesearch[0], GSSD_DEFAULT_CRED_PREFIX,
+ GSSD_DEFAULT_MACHINE_CRED_SUFFIX, ple->realm);
+ fd = open(cc_name, O_RDONLY);
+ if (fd < 0)
+ return 1;
+ close(fd);
+ return 0;
+}
+
+/*
+ * Obtain credentials via a key in the keytab given
+ * a keytab handle and a gssd_k5_kt_princ structure.
+ * Checks to see if current credentials are expired,
+ * if not, uses the keytab to obtain new credentials.
+ *
+ * Returns:
+ * 0 => success (or credentials have not expired)
+ * nonzero => error
+ */
+static int
+gssd_get_single_krb5_cred(krb5_context context,
+ krb5_keytab kt,
+ struct gssd_k5_kt_princ *ple,
+ int force_renew)
+{
+#ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_ADDRESSLESS
+ krb5_get_init_creds_opt *init_opts = NULL;
+#else
+ krb5_get_init_creds_opt options;
+#endif
+ krb5_get_init_creds_opt *opts;
+ krb5_creds my_creds;
+ krb5_ccache ccache = NULL;
+ char kt_name[BUFSIZ];
+ char cc_name[BUFSIZ];
+ int code;
+ time_t now = time(0);
+ char *cache_type;
+ char *pname = NULL;
+ char *k5err = NULL;
+ int nocache = 0;
+ pthread_t tid = pthread_self();
+
+ memset(&my_creds, 0, sizeof(my_creds));
+
+ if (!use_memcache)
+ nocache = gssd_check_if_cc_exists(ple);
+ /*
+ * Workaround for clock skew among NFS server, NFS client and KDC
+ * 300 because clock skew must be within 300sec for kerberos
+ */
+ now += 300;
+ pthread_mutex_lock(&ple_lock);
+ if (ple->ccname && ple->endtime > now && !nocache && !force_renew) {
+ printerr(3, "%s(0x%lx): Credentials in CC '%s' are good until %s",
+ __func__, tid, ple->ccname, ctime((time_t *)&ple->endtime));
+ code = 0;
+ pthread_mutex_unlock(&ple_lock);
+ goto out;
+ }
+ pthread_mutex_unlock(&ple_lock);
+
+ if ((code = krb5_kt_get_name(context, kt, kt_name, BUFSIZ))) {
+ printerr(0, "ERROR: Unable to get keytab name in "
+ "gssd_get_single_krb5_cred\n");
+ goto out;
+ }
+
+ if ((krb5_unparse_name(context, ple->princ, &pname)))
+ pname = NULL;
+
+#ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_ADDRESSLESS
+ code = krb5_get_init_creds_opt_alloc(context, &init_opts);
+ if (code) {
+ k5err = gssd_k5_err_msg(context, code);
+ printerr(0, "ERROR: %s allocating gic options\n", k5err);
+ goto out;
+ }
+ if (krb5_get_init_creds_opt_set_addressless(context, init_opts, 1))
+ printerr(1, "WARNING: Unable to set option for addressless "
+ "tickets. May have problems behind a NAT.\n");
+#ifdef TEST_SHORT_LIFETIME
+ /* set a short lifetime (for debugging only!) */
+ printerr(1, "WARNING: Using (debug) short machine cred lifetime!\n");
+ krb5_get_init_creds_opt_set_tkt_life(init_opts, 5*60);
+#endif
+ opts = init_opts;
+
+#else /* HAVE_KRB5_GET_INIT_CREDS_OPT_SET_ADDRESSLESS */
+
+ krb5_get_init_creds_opt_init(&options);
+ krb5_get_init_creds_opt_set_address_list(&options, NULL);
+#ifdef TEST_SHORT_LIFETIME
+ /* set a short lifetime (for debugging only!) */
+ printerr(0, "WARNING: Using (debug) short machine cred lifetime!\n");
+ krb5_get_init_creds_opt_set_tkt_life(&options, 5*60);
+#endif
+ opts = &options;
+#endif
+
+ if ((code = krb5_get_init_creds_keytab(context, &my_creds, ple->princ,
+ kt, 0, NULL, opts))) {
+ k5err = gssd_k5_err_msg(context, code);
+ printerr(1, "WARNING: %s while getting initial ticket for "
+ "principal '%s' using keytab '%s'\n", k5err,
+ pname ? pname : "<unparsable>", kt_name);
+ goto out;
+ }
+
+ /*
+ * Initialize cache file which we're going to be using
+ */
+
+ pthread_mutex_lock(&ple_lock);
+ if (use_memcache)
+ cache_type = "MEMORY";
+ else
+ cache_type = "FILE";
+ snprintf(cc_name, sizeof(cc_name), "%s:%s/%s%s_%s",
+ cache_type,
+ ccachesearch[0], GSSD_DEFAULT_CRED_PREFIX,
+ GSSD_DEFAULT_MACHINE_CRED_SUFFIX, ple->realm);
+ ple->endtime = my_creds.times.endtime;
+ if (ple->ccname == NULL || strcmp(ple->ccname, cc_name) != 0) {
+ free(ple->ccname);
+ ple->ccname = strdup(cc_name);
+ if (ple->ccname == NULL) {
+ printerr(0, "ERROR: no storage to duplicate credentials "
+ "cache name '%s'\n", cc_name);
+ code = ENOMEM;
+ pthread_mutex_unlock(&ple_lock);
+ goto out;
+ }
+ }
+ pthread_mutex_unlock(&ple_lock);
+ if ((code = krb5_cc_resolve(context, cc_name, &ccache))) {
+ k5err = gssd_k5_err_msg(context, code);
+ printerr(0, "ERROR: %s while opening credential cache '%s'\n",
+ k5err, cc_name);
+ goto out;
+ }
+ if ((code = krb5_cc_initialize(context, ccache, ple->princ))) {
+ k5err = gssd_k5_err_msg(context, code);
+ printerr(0, "ERROR: %s while initializing credential "
+ "cache '%s'\n", k5err, cc_name);
+ goto out;
+ }
+ if ((code = krb5_cc_store_cred(context, ccache, &my_creds))) {
+ k5err = gssd_k5_err_msg(context, code);
+ printerr(0, "ERROR: %s while storing credentials in '%s'\n",
+ k5err, cc_name);
+ goto out;
+ }
+
+ code = 0;
+ printerr(2, "%s(0x%lx): principal '%s' ccache:'%s'\n",
+ __func__, tid, pname, cc_name);
+ out:
+#ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_ADDRESSLESS
+ if (init_opts)
+ krb5_get_init_creds_opt_free(context, init_opts);
+#endif
+ if (pname)
+ k5_free_unparsed_name(context, pname);
+ if (ccache)
+ krb5_cc_close(context, ccache);
+ krb5_free_cred_contents(context, &my_creds);
+ free(k5err);
+ return (code);
+}
+
+/*
+ * Given a principal, find a matching ple structure
+ * Called with mutex held
+ */
+static struct gssd_k5_kt_princ *
+find_ple_by_princ(krb5_context context, krb5_principal princ)
+{
+ struct gssd_k5_kt_princ *ple;
+
+ for (ple = gssd_k5_kt_princ_list; ple != NULL; ple = ple->next) {
+ if (krb5_principal_compare(context, ple->princ, princ))
+ return ple;
+ }
+ /* no match found */
+ return NULL;
+}
+
+/*
+ * Create, initialize, and add a new ple structure to the global list
+ * Called with mutex held
+ */
+static struct gssd_k5_kt_princ *
+new_ple(krb5_context context, krb5_principal princ)
+{
+ struct gssd_k5_kt_princ *ple = NULL, *p;
+ krb5_error_code code;
+ char *default_realm;
+ int is_default_realm = 0;
+
+ ple = malloc(sizeof(struct gssd_k5_kt_princ));
+ if (ple == NULL)
+ goto outerr;
+ memset(ple, 0, sizeof(*ple));
+
+#ifdef HAVE_KRB5
+ ple->realm = strndup(princ->realm.data,
+ princ->realm.length);
+#else
+ ple->realm = strdup(princ->realm);
+#endif
+ if (ple->realm == NULL)
+ goto outerr;
+ code = krb5_copy_principal(context, princ, &ple->princ);
+ if (code)
+ goto outerr;
+
+ /*
+ * Add new entry onto the list (if this is the default
+ * realm, always add to the front of the list)
+ */
+
+ code = krb5_get_default_realm(context, &default_realm);
+ if (code == 0) {
+ if (strcmp(ple->realm, default_realm) == 0)
+ is_default_realm = 1;
+ k5_free_default_realm(context, default_realm);
+ }
+
+ if (is_default_realm) {
+ ple->next = gssd_k5_kt_princ_list;
+ gssd_k5_kt_princ_list = ple;
+ } else {
+ p = gssd_k5_kt_princ_list;
+ while (p != NULL && p->next != NULL)
+ p = p->next;
+ if (p == NULL)
+ gssd_k5_kt_princ_list = ple;
+ else
+ p->next = ple;
+ }
+
+ ple->refcount = 1;
+ return ple;
+outerr:
+ if (ple) {
+ if (ple->realm)
+ free(ple->realm);
+ free(ple);
+ }
+ return NULL;
+}
+
+/*
+ * Given a principal, find an existing ple structure, or create one
+ */
+static struct gssd_k5_kt_princ *
+get_ple_by_princ(krb5_context context, krb5_principal princ)
+{
+ struct gssd_k5_kt_princ *ple;
+
+ pthread_mutex_lock(&ple_lock);
+ ple = find_ple_by_princ(context, princ);
+ if (ple == NULL) {
+ ple = new_ple(context, princ);
+ }
+ if (ple != NULL) {
+ ple->refcount++;
+ }
+ pthread_mutex_unlock(&ple_lock);
+
+ return ple;
+}
+
+/*
+ * Given a (possibly unqualified) hostname,
+ * return the fully qualified (lower-case!) hostname
+ */
+static int
+get_full_hostname(const char *inhost, char *outhost, int outhostlen)
+{
+ struct addrinfo *addrs = NULL;
+ struct addrinfo hints;
+ int retval;
+ char *c;
+ pthread_t tid = pthread_self();
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_flags = AI_CANONNAME;
+
+ /* Get full target hostname */
+ retval = getaddrinfo(inhost, NULL, &hints, &addrs);
+ if (retval) {
+ printerr(1, "%s(0x%lx): getaddrinfo(%s) failed: %s\n",
+ __func__, tid, inhost, gai_strerror(retval));
+ goto out;
+ }
+ strncpy(outhost, addrs->ai_canonname, outhostlen);
+ nfs_freeaddrinfo(addrs);
+ for (c = outhost; *c != '\0'; c++)
+ *c = tolower(*c);
+
+ if (get_verbosity() && strcmp(inhost, outhost))
+ printerr(1, "%s(0x%0lx): inhost '%s' different than outhost '%s'\n",
+ __func__, tid, inhost, outhost);
+
+ retval = 0;
+out:
+ return retval;
+}
+
+/*
+ * If principal matches the given realm and service name,
+ * and has *any* instance (hostname), return 1.
+ * Otherwise return 0, indicating no match.
+ */
+#ifdef HAVE_KRB5
+static int
+realm_and_service_match(krb5_principal p, const char *realm, const char *service)
+{
+ /* Must have two components */
+ if (p->length != 2)
+ return 0;
+
+ if ((strlen(realm) == p->realm.length)
+ && (strncmp(realm, p->realm.data, p->realm.length) == 0)
+ && (strlen(service) == p->data[0].length)
+ && (strncmp(service, p->data[0].data, p->data[0].length) == 0))
+ return 1;
+
+ return 0;
+}
+#else
+static int
+realm_and_service_match(krb5_context context, krb5_principal p,
+ const char *realm, const char *service)
+{
+ const char *name, *inst;
+
+ if (p->name.name_string.len != 2)
+ return 0;
+
+ name = krb5_principal_get_comp_string(context, p, 0);
+ inst = krb5_principal_get_comp_string(context, p, 1);
+ if (name == NULL || inst == NULL)
+ return 0;
+ if ((strcmp(realm, p->realm) == 0)
+ && (strcmp(service, name) == 0))
+ return 1;
+
+ return 0;
+}
+#endif
+
+/*
+ * Search the given keytab file looking for an entry with the given
+ * service name and realm, ignoring hostname (instance).
+ *
+ * Returns:
+ * 0 => No error
+ * non-zero => An error occurred
+ *
+ * If a keytab entry is found, "found" is set to one, and the keytab
+ * entry is returned in "kte". Otherwise, "found" is zero, and the
+ * value of "kte" is unpredictable.
+ */
+static int
+gssd_search_krb5_keytab(krb5_context context, krb5_keytab kt,
+ const char *realm, const char *service,
+ int *found, krb5_keytab_entry *kte)
+{
+ krb5_kt_cursor cursor;
+ krb5_error_code code;
+ struct gssd_k5_kt_princ *ple;
+ int retval = -1, status;
+ char kt_name[BUFSIZ];
+ char *pname;
+ char *k5err = NULL;
+
+ if (found == NULL) {
+ retval = EINVAL;
+ goto out;
+ }
+ *found = 0;
+
+ /*
+ * Look through each entry in the keytab file and determine
+ * if we might want to use it as machine credentials. If so,
+ * save info in the global principal list (gssd_k5_kt_princ_list).
+ */
+ if ((code = krb5_kt_get_name(context, kt, kt_name, BUFSIZ))) {
+ k5err = gssd_k5_err_msg(context, code);
+ printerr(0, "ERROR: %s attempting to get keytab name\n", k5err);
+ retval = code;
+ goto out;
+ }
+ if ((code = krb5_kt_start_seq_get(context, kt, &cursor))) {
+ k5err = gssd_k5_err_msg(context, code);
+ printerr(0, "ERROR: %s while beginning keytab scan "
+ "for keytab '%s'\n", k5err, kt_name);
+ retval = code;
+ goto out;
+ }
+
+ printerr(4, "Scanning keytab for %s/*@%s\n", service, realm);
+ while ((code = krb5_kt_next_entry(context, kt, kte, &cursor)) == 0) {
+ if ((code = krb5_unparse_name(context, kte->principal,
+ &pname))) {
+ k5err = gssd_k5_err_msg(context, code);
+ printerr(0, "WARNING: Skipping keytab entry because "
+ "we failed to unparse principal name: %s\n",
+ k5err);
+ k5_free_kt_entry(context, kte);
+ free(k5err);
+ k5err = NULL;
+ continue;
+ }
+ printerr(4, "Processing keytab entry for principal '%s'\n",
+ pname);
+ /* Use the first matching keytab entry found */
+#ifdef HAVE_KRB5
+ status = realm_and_service_match(kte->principal, realm, service);
+#else
+ status = realm_and_service_match(context, kte->principal, realm, service);
+#endif
+ if (status) {
+ printerr(4, "We WILL use this entry (%s)\n", pname);
+ ple = get_ple_by_princ(context, kte->principal);
+ /*
+ * Return, don't free, keytab entry if
+ * we were successful!
+ */
+ if (ple == NULL) {
+ retval = ENOMEM;
+ k5_free_kt_entry(context, kte);
+ } else {
+ release_ple(context, ple);
+ ple = NULL;
+ retval = 0;
+ *found = 1;
+ }
+ k5_free_unparsed_name(context, pname);
+ break;
+ }
+ else {
+ printerr(4, "We will NOT use this entry (%s)\n",
+ pname);
+ }
+ k5_free_unparsed_name(context, pname);
+ k5_free_kt_entry(context, kte);
+ }
+
+ if ((code = krb5_kt_end_seq_get(context, kt, &cursor))) {
+ k5err = gssd_k5_err_msg(context, code);
+ printerr(0, "WARNING: %s while ending keytab scan for "
+ "keytab '%s'\n", k5err, kt_name);
+ }
+
+ /* Only clear the retval if has not been set */
+ if (retval < 0)
+ retval = 0;
+ out:
+ free(k5err);
+ return retval;
+}
+
+/*
+ * Find a keytab entry to use for a given target realm.
+ * Tries to find the most appropriate keytab to use given the
+ * name of the host we are trying to connect with.
+ *
+ * Note: the tgtname contains a hostname in the realm that we
+ * are authenticating to. It may, or may not be the same as
+ * the server hostname.
+ */
+static int
+find_keytab_entry(krb5_context context, krb5_keytab kt,
+ const char *srchost, const char *tgtname,
+ krb5_keytab_entry *kte, const char **svcnames)
+{
+ krb5_error_code code;
+ char **realmnames = NULL;
+ char myhostname[NI_MAXHOST], targethostname[NI_MAXHOST];
+ char myhostad[NI_MAXHOST+1];
+ int i, j, k, retval;
+ char *default_realm = NULL;
+ char *realm;
+ char *k5err = NULL;
+ int tried_all = 0, tried_default = 0, tried_upper = 0;
+ krb5_principal princ;
+ const char *notsetstr = "not set";
+ char *adhostoverride = NULL;
+ pthread_t tid = pthread_self();
+
+
+ /* Get full target hostname */
+ retval = get_full_hostname(tgtname, targethostname,
+ sizeof(targethostname));
+ if (retval)
+ goto out;
+
+ /* Get full local hostname */
+ if (srchost) {
+ strcpy(myhostname, srchost);
+ strcpy(myhostad, myhostname);
+ } else {
+ /* Borrow myhostad for gethostname(), we need it later anyways */
+ if (gethostname(myhostad, sizeof(myhostad)-1) == -1) {
+ retval = errno;
+ k5err = gssd_k5_err_msg(context, retval);
+ printerr(1, "%s while getting local hostname\n", k5err);
+ goto out;
+ }
+ retval = get_full_hostname(myhostad, myhostname, sizeof(myhostname));
+ if (retval) {
+ /* Don't use myhostname */
+ myhostname[0] = 0;
+ }
+ }
+
+ /* Compute the active directory machine name HOST$ */
+ krb5_appdefault_string(context, "nfs", NULL, "ad_principal_name",
+ notsetstr, &adhostoverride);
+ if (adhostoverride && strcmp(adhostoverride, notsetstr) != 0) {
+ printerr(1,
+ "AD host string overridden with \"%s\" from appdefaults\n",
+ adhostoverride);
+ /* No overflow: Windows cannot handle strings longer than 19 chars */
+ strcpy(myhostad, adhostoverride);
+ } else {
+ /* In this case, it's been pre-filled above */
+ for (i = 0; myhostad[i] != 0; ++i) {
+ if (myhostad[i] == '.') break;
+ }
+ myhostad[i] = '$';
+ myhostad[i+1] = 0;
+ }
+ if (adhostoverride)
+ krb5_free_string(context, adhostoverride);
+
+ code = krb5_get_default_realm(context, &default_realm);
+ if (code) {
+ retval = code;
+ k5err = gssd_k5_err_msg(context, code);
+ printerr(1, "%s while getting default realm name\n", k5err);
+ goto out;
+ }
+
+ /*
+ * Get the realm name(s) for the target hostname.
+ * In reality, this function currently only returns a
+ * single realm, but we code with the assumption that
+ * someday it may actually return a list.
+ */
+ code = krb5_get_host_realm(context, targethostname, &realmnames);
+ if (code) {
+ k5err = gssd_k5_err_msg(context, code);
+ printerr(0, "ERROR: %s while getting realm(s) for host '%s'\n",
+ k5err, targethostname);
+ retval = code;
+ goto out;
+ }
+
+ /*
+ * Make sure the preferred_realm, which may have been explicitly set
+ * on the command line, is tried first. If nothing is found go on with
+ * the host and local default realm (if that hasn't already been tried).
+ */
+ i = 0;
+ realm = realmnames[i];
+
+ if (preferred_realm && strcmp (realm, preferred_realm) != 0) {
+ realm = preferred_realm;
+ /* resetting the realmnames index */
+ i = -1;
+ }
+
+ while (1) {
+ if (realm == NULL) {
+ tried_all = 1;
+ if (!tried_default)
+ realm = default_realm;
+ }
+ if (tried_all && tried_default)
+ break;
+ if (strcmp(realm, default_realm) == 0)
+ tried_default = 1;
+ for (j = 0; svcnames[j] != NULL; j++) {
+ char spn[NI_MAXHOST+2];
+
+ /*
+ * The special svcname "$" means 'try the active
+ * directory machine account'
+ */
+ if (strcmp(svcnames[j],"$") == 0) {
+ snprintf(spn, sizeof(spn), "%s@%s", myhostad, realm);
+ code = krb5_build_principal_ext(context, &princ,
+ strlen(realm),
+ realm,
+ strlen(myhostad),
+ myhostad,
+ NULL);
+ } else {
+ if (!myhostname[0])
+ continue;
+ snprintf(spn, sizeof(spn), "%s/%s@%s",
+ svcnames[j], myhostname, realm);
+ code = krb5_build_principal_ext(context, &princ,
+ strlen(realm),
+ realm,
+ strlen(svcnames[j]),
+ svcnames[j],
+ strlen(myhostname),
+ myhostname,
+ NULL);
+ }
+
+ if (code) {
+ k5err = gssd_k5_err_msg(context, code);
+ printerr(1, "%s while building principal for '%s'\n",
+ k5err, spn);
+ free(k5err);
+ k5err = NULL;
+ continue;
+ }
+ code = krb5_kt_get_entry(context, kt, princ, 0, 0, kte);
+ krb5_free_principal(context, princ);
+ if (code) {
+ k5err = gssd_k5_err_msg(context, code);
+ printerr(3, "%s while getting keytab entry for '%s'\n",
+ k5err, spn);
+ free(k5err);
+ k5err = NULL;
+ /*
+ * We tried the active directory machine account
+ * with the hostname part as-is and failed...
+ * convert it to uppercase and try again before
+ * moving on to the svcname
+ */
+ if (strcmp(svcnames[j],"$") == 0 && !tried_upper) {
+ for (k = 0; myhostad[k] != '$'; ++k) {
+ myhostad[k] = toupper(myhostad[k]);
+ }
+ j--;
+ tried_upper = 1;
+ }
+ } else {
+ printerr(2, "find_keytab_entry(0x%lx): Success getting keytab entry for '%s'\n",tid, spn);
+ retval = 0;
+ goto out;
+ }
+ retval = code;
+ }
+ /*
+ * Nothing found with our hostname instance, now look for
+ * names with any instance (they must have an instance)
+ */
+ for (j = 0; svcnames[j] != NULL; j++) {
+ int found = 0;
+ if (strcmp(svcnames[j],"$") == 0)
+ continue;
+ code = gssd_search_krb5_keytab(context, kt, realm,
+ svcnames[j], &found, kte);
+ if (!code && found) {
+ printerr(3, "Success getting keytab entry for "
+ "%s/*@%s\n", svcnames[j], realm);
+ retval = 0;
+ goto out;
+ }
+ }
+ if (!tried_all) {
+ i++;
+ realm = realmnames[i];
+ }
+ }
+out:
+ if (default_realm)
+ k5_free_default_realm(context, default_realm);
+ if (realmnames)
+ krb5_free_host_realm(context, realmnames);
+ free(k5err);
+ return retval;
+}
+
+
+static inline int data_is_equal(krb5_data d1, krb5_data d2)
+{
+ return (d1.length == d2.length
+ && memcmp(d1.data, d2.data, d1.length) == 0);
+}
+
+static int
+check_for_tgt(krb5_context context, krb5_ccache ccache,
+ krb5_principal principal)
+{
+ krb5_error_code ret;
+ krb5_creds creds;
+ krb5_cc_cursor cur;
+ int found = 0;
+
+ ret = krb5_cc_start_seq_get(context, ccache, &cur);
+ if (ret)
+ return 0;
+
+ while (!found &&
+ (ret = krb5_cc_next_cred(context, ccache, &cur, &creds)) == 0) {
+ if (creds.server->length == 2 &&
+ data_is_equal(creds.server->realm,
+ principal->realm) &&
+ creds.server->data[0].length == 6 &&
+ memcmp(creds.server->data[0].data,
+ "krbtgt", 6) == 0 &&
+ data_is_equal(creds.server->data[1],
+ principal->realm) &&
+ creds.times.endtime > time(NULL))
+ found = 1;
+ krb5_free_cred_contents(context, &creds);
+ }
+ krb5_cc_end_seq_get(context, ccache, &cur);
+
+ return found;
+}
+
+static int
+query_krb5_ccache(const char* cred_cache, char **ret_princname,
+ char **ret_realm)
+{
+ krb5_error_code ret;
+ krb5_context context;
+ krb5_ccache ccache;
+ krb5_principal principal;
+ int found = 0;
+ char *str = NULL;
+ char *princstring;
+
+ *ret_princname = *ret_realm = NULL;
+
+ ret = krb5_init_context(&context);
+ if (ret)
+ return 0;
+
+ if(!cred_cache || krb5_cc_resolve(context, cred_cache, &ccache))
+ goto err_cache;
+
+ if (krb5_cc_set_flags(context, ccache, 0))
+ goto err_princ;
+
+ ret = krb5_cc_get_principal(context, ccache, &principal);
+ if (ret)
+ goto err_princ;
+
+ found = check_for_tgt(context, ccache, principal);
+ if (found) {
+ ret = krb5_unparse_name(context, principal, &princstring);
+ if (ret == 0) {
+ if ((str = strchr(princstring, '@')) != NULL) {
+ *str = '\0';
+ *ret_princname = strdup(princstring);
+ *ret_realm = strdup(str+1);
+ if (!*ret_princname || !*ret_realm) {
+ free(*ret_princname);
+ free(*ret_realm);
+ *ret_princname = NULL;
+ *ret_realm = NULL;
+ }
+ }
+ k5_free_unparsed_name(context, princstring);
+ }
+ }
+ krb5_free_principal(context, principal);
+err_princ:
+ krb5_cc_set_flags(context, ccache, KRB5_TC_OPENCLOSE);
+ krb5_cc_close(context, ccache);
+err_cache:
+ krb5_free_context(context);
+ return (*ret_princname && *ret_realm);
+}
+
+/*
+ * Obtain (or refresh if necessary) Kerberos machine credentials
+ * If a ple is passed in, it's reference will be released
+ */
+static int
+gssd_refresh_krb5_machine_credential_internal(char *hostname,
+ struct gssd_k5_kt_princ *ple,
+ char *service, char *srchost,
+ int force_renew)
+{
+ krb5_error_code code = 0;
+ krb5_context context;
+ krb5_keytab kt = NULL;;
+ int retval = 0;
+ char *k5err = NULL;
+ const char *svcnames[] = { "$", "root", "nfs", "host", NULL };
+
+ /*
+ * If a specific service name was specified, use it.
+ * Otherwise, use the default list.
+ */
+ if (service != NULL && strcmp(service, "*") != 0) {
+ svcnames[0] = service;
+ svcnames[1] = NULL;
+ }
+ if (hostname == NULL && ple == NULL) {
+ printerr(0, "ERROR: %s: Invalid args\n", __func__);
+ return EINVAL;
+ }
+ code = krb5_init_context(&context);
+ if (code) {
+ k5err = gssd_k5_err_msg(NULL, code);
+ printerr(0, "ERROR: %s: %s while initializing krb5 context\n",
+ __func__, k5err);
+ retval = code;
+ goto out;
+ }
+
+ if ((code = krb5_kt_resolve(context, keytabfile, &kt))) {
+ k5err = gssd_k5_err_msg(context, code);
+ printerr(0, "ERROR: %s: %s while resolving keytab '%s'\n",
+ __func__, k5err, keytabfile);
+ goto out_free_context;
+ }
+
+ if (ple == NULL) {
+ krb5_keytab_entry kte;
+
+ code = find_keytab_entry(context, kt, srchost, hostname,
+ &kte, svcnames);
+ if (code) {
+ printerr(0, "ERROR: %s: no usable keytab entry found "
+ "in keytab %s for connection with host %s\n",
+ __FUNCTION__, keytabfile, hostname);
+ retval = code;
+ goto out_free_kt;
+ }
+
+ ple = get_ple_by_princ(context, kte.principal);
+ k5_free_kt_entry(context, &kte);
+ if (ple == NULL) {
+ char *pname;
+ if ((krb5_unparse_name(context, kte.principal, &pname))) {
+ pname = NULL;
+ }
+ printerr(0, "ERROR: %s: Could not locate or create "
+ "ple struct for principal %s for connection "
+ "with host %s\n",
+ __FUNCTION__, pname ? pname : "<unparsable>",
+ hostname);
+ if (pname) k5_free_unparsed_name(context, pname);
+ goto out_free_kt;
+ }
+ }
+ retval = gssd_get_single_krb5_cred(context, kt, ple, force_renew);
+out_free_kt:
+ krb5_kt_close(context, kt);
+out_free_context:
+ if (ple)
+ release_ple(context, ple);
+ krb5_free_context(context);
+out:
+ free(k5err);
+ return retval;
+}
+
+/*==========================*/
+/*=== External routines ===*/
+/*==========================*/
+
+/*
+ * Attempt to find the best match for a credentials cache file
+ * given only a UID. We really need more information, but we
+ * do the best we can.
+ *
+ * Returns 0 if a ccache was found, or a negative errno otherwise.
+ */
+int
+gssd_setup_krb5_user_gss_ccache(uid_t uid, char *servername, char *dirpattern)
+{
+ /* dirname + cctype + d_name + NULL */
+ char buf[PATH_MAX+5+256+1], dirname[PATH_MAX];
+ const char *cctype;
+ struct dirent *d;
+ int err, i, j;
+ u_int maj_stat, min_stat;
+
+ printerr(3, "looking for client creds with uid %u for "
+ "server %s in %s\n", uid, servername, dirpattern);
+
+ for (i = 0, j = 0; dirpattern[i] != '\0'; i++) {
+ switch (dirpattern[i]) {
+ case '%':
+ switch (dirpattern[i + 1]) {
+ case '%':
+ dirname[j++] = dirpattern[i];
+ i++;
+ break;
+ case 'U':
+ j += sprintf(dirname + j, "%lu",
+ (unsigned long) uid);
+ i++;
+ break;
+ }
+ break;
+ default:
+ dirname[j++] = dirpattern[i];
+ break;
+ }
+ }
+ dirname[j] = '\0';
+
+ err = gssd_find_existing_krb5_ccache(uid, dirname, &cctype, &d);
+ if (err)
+ return err;
+
+ snprintf(buf, sizeof(buf), "%s:%s/%s", cctype, dirname, d->d_name);
+ free(d);
+
+ printerr(2, "using %s as credentials cache for client with "
+ "uid %u for server %s\n", buf, uid, servername);
+
+ printerr(3, "using gss_krb5_ccache_name to select krb5 ccache %s\n",
+ buf);
+ maj_stat = gss_krb5_ccache_name(&min_stat, buf, NULL);
+ if (maj_stat != GSS_S_COMPLETE) {
+ printerr(0, "ERROR: unable to get user cred cache '%s' "
+ "failed (%s)\n", buf, error_message(min_stat));
+ return maj_stat;
+ }
+ return 0;
+}
+
+/*
+ * Return an array of pointers to names of credential cache files
+ * which can be used to try to create gss contexts with a server.
+ *
+ * Returns:
+ * 0 => list is attached
+ * nonzero => error
+ */
+int
+gssd_get_krb5_machine_cred_list(char ***list)
+{
+ char **l;
+ int listinc = 10;
+ int listsize = listinc;
+ int i = 0;
+ int retval;
+ struct gssd_k5_kt_princ *ple;
+
+ /* Assume failure */
+ retval = -1;
+ *list = (char **) NULL;
+
+ if ((l = (char **) malloc(listsize * sizeof(char *))) == NULL) {
+ retval = ENOMEM;
+ goto out;
+ }
+
+ pthread_mutex_lock(&ple_lock);
+ for (ple = gssd_k5_kt_princ_list; ple; ple = ple->next) {
+ if (!ple->ccname)
+ continue;
+
+ /* Take advantage of the fact we only remove the ple
+ * from the list during shutdown. If it's modified
+ * concurrently at worst we'll just miss a new entry
+ * before the current ple
+ *
+ * gssd_refresh_krb5_machine_credential_internal() will
+ * release the ple refcount
+ */
+ ple->refcount++;
+ pthread_mutex_unlock(&ple_lock);
+ /* Make sure cred is up-to-date before returning it */
+ retval = gssd_refresh_krb5_machine_credential_internal(NULL, ple,
+ NULL, NULL, 0);
+ pthread_mutex_lock(&ple_lock);
+ if (gssd_k5_kt_princ_list == NULL) {
+ /* Looks like we did shutdown... abort */
+ l[i] = NULL;
+ gssd_free_krb5_machine_cred_list(l);
+ retval = ENOMEM;
+ goto out_lock;
+ }
+ if (retval)
+ continue;
+ if (i + 1 > listsize) {
+ char **tmplist;
+ listsize += listinc;
+ tmplist = (char **)
+ realloc(l, listsize * sizeof(char *));
+ if (tmplist == NULL) {
+ gssd_free_krb5_machine_cred_list(l);
+ retval = ENOMEM;
+ goto out_lock;
+ }
+ l = tmplist;
+ }
+ if ((l[i++] = strdup(ple->ccname)) == NULL) {
+ gssd_free_krb5_machine_cred_list(l);
+ retval = ENOMEM;
+ goto out_lock;
+ }
+ }
+ if (i > 0) {
+ l[i] = NULL;
+ *list = l;
+ retval = 0;
+ } else
+ free((void *)l);
+out_lock:
+ pthread_mutex_unlock(&ple_lock);
+ out:
+ return retval;
+}
+
+/*
+ * Frees the list of names returned in get_krb5_machine_cred_list()
+ */
+void
+gssd_free_krb5_machine_cred_list(char **list)
+{
+ char **n;
+
+ if (list == NULL)
+ return;
+ for (n = list; n && *n; n++) {
+ free(*n);
+ }
+ free(list);
+}
+
+/*
+ * Called upon exit. Destroys machine credentials.
+ */
+void
+gssd_destroy_krb5_principals(int destroy_machine_creds)
+{
+ krb5_context context;
+ krb5_error_code code = 0;
+ krb5_ccache ccache;
+ struct gssd_k5_kt_princ *ple;
+ char *k5err = NULL;
+
+ code = krb5_init_context(&context);
+ if (code) {
+ k5err = gssd_k5_err_msg(NULL, code);
+ printerr(0, "ERROR: %s while initializing krb5\n", k5err);
+ free(k5err);
+ return;
+ }
+
+ pthread_mutex_lock(&ple_lock);
+ while (gssd_k5_kt_princ_list) {
+ ple = gssd_k5_kt_princ_list;
+ gssd_k5_kt_princ_list = ple->next;
+
+ if (destroy_machine_creds && ple->ccname) {
+ if ((code = krb5_cc_resolve(context, ple->ccname, &ccache))) {
+ k5err = gssd_k5_err_msg(context, code);
+ printerr(0, "WARNING: %s while resolving credential "
+ "cache '%s' for destruction\n", k5err,
+ ple->ccname);
+ free(k5err);
+ k5err = NULL;
+ }
+
+ if (!code && (code = krb5_cc_destroy(context, ccache))) {
+ k5err = gssd_k5_err_msg(context, code);
+ printerr(0, "WARNING: %s while destroying credential "
+ "cache '%s'\n", k5err, ple->ccname);
+ free(k5err);
+ k5err = NULL;
+ }
+ }
+
+ release_ple_locked(context, ple);
+ }
+ pthread_mutex_unlock(&ple_lock);
+ krb5_free_context(context);
+}
+
+/*
+ * Obtain (or refresh if necessary) Kerberos machine credentials
+ */
+int
+gssd_refresh_krb5_machine_credential(char *hostname,
+ char *service, char *srchost,
+ int force_renew)
+{
+ return gssd_refresh_krb5_machine_credential_internal(hostname, NULL,
+ service, srchost,
+ force_renew);
+}
+
+/*
+ * A common routine for getting the Kerberos error message
+ */
+char *
+gssd_k5_err_msg(krb5_context context, krb5_error_code code)
+{
+ const char *origmsg;
+ char *msg = NULL;
+
+#if HAVE_KRB5_GET_ERROR_MESSAGE
+ if (context != NULL) {
+ origmsg = krb5_get_error_message(context, code);
+ msg = strdup(origmsg);
+ krb5_free_error_message(context, origmsg);
+ }
+#endif
+ if (msg != NULL)
+ return msg;
+#if HAVE_KRB5
+ return strdup(error_message(code));
+#else
+ if (context != NULL)
+ return strdup(krb5_get_err_text(context, code));
+ else
+ return strdup(error_message(code));
+#endif
+}
+
+/*
+ * Return default Kerberos realm
+ */
+void
+gssd_k5_get_default_realm(char **def_realm)
+{
+ krb5_context context;
+
+ if (krb5_init_context(&context))
+ return;
+
+ krb5_get_default_realm(context, def_realm);
+
+ krb5_free_context(context);
+}
+
+static int
+gssd_acquire_krb5_cred(gss_cred_id_t *gss_cred)
+{
+ OM_uint32 maj_stat, min_stat;
+ gss_OID_set_desc desired_mechs = { 1, &krb5oid };
+
+ maj_stat = gss_acquire_cred(&min_stat, GSS_C_NO_NAME, GSS_C_INDEFINITE,
+ &desired_mechs, GSS_C_INITIATE,
+ gss_cred, NULL, NULL);
+
+ if (maj_stat != GSS_S_COMPLETE) {
+ if (get_verbosity() > 0)
+ pgsserr("gss_acquire_cred",
+ maj_stat, min_stat, &krb5oid);
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+gssd_acquire_user_cred(gss_cred_id_t *gss_cred)
+{
+ OM_uint32 maj_stat, min_stat;
+ int ret;
+
+ ret = gssd_acquire_krb5_cred(gss_cred);
+ if (ret)
+ return ret;
+
+ /* force validation of cred to check for expiry */
+ maj_stat = gss_inquire_cred(&min_stat, *gss_cred,
+ NULL, NULL, NULL, NULL);
+ if (maj_stat != GSS_S_COMPLETE) {
+ if (get_verbosity() > 0)
+ pgsserr("gss_inquire_cred",
+ maj_stat, min_stat, &krb5oid);
+ ret = -1;
+ }
+
+ return ret;
+}
+
+/* Removed a service ticket for nfs/<name> from the ticket cache
+ */
+int
+gssd_k5_remove_bad_service_cred(char *name)
+{
+ krb5_creds in_creds, out_creds;
+ krb5_error_code ret;
+ krb5_context context;
+ krb5_ccache cache;
+ krb5_principal principal;
+ int retflags = KRB5_TC_MATCH_SRV_NAMEONLY;
+ char srvname[1024];
+
+ ret = krb5_init_context(&context);
+ if (ret)
+ goto out_cred;
+ ret = krb5_cc_default(context, &cache);
+ if (ret)
+ goto out_free_context;
+ ret = krb5_cc_get_principal(context, cache, &principal);
+ if (ret)
+ goto out_close_cache;
+ memset(&in_creds, 0, sizeof(in_creds));
+ in_creds.client = principal;
+ sprintf(srvname, "nfs/%s", name);
+ ret = krb5_parse_name(context, srvname, &in_creds.server);
+ if (ret)
+ goto out_free_principal;
+ ret = krb5_cc_retrieve_cred(context, cache, retflags, &in_creds, &out_creds);
+ if (ret)
+ goto out_free_principal;
+ ret = krb5_cc_remove_cred(context, cache, 0, &out_creds);
+out_free_principal:
+ krb5_free_principal(context, principal);
+out_close_cache:
+ krb5_cc_close(context, cache);
+out_free_context:
+ krb5_free_context(context);
+out_cred:
+ return ret;
+}
+
+#ifdef HAVE_SET_ALLOWABLE_ENCTYPES
+/*
+ * this routine obtains a credentials handle via gss_acquire_cred()
+ * then calls gss_krb5_set_allowable_enctypes() to limit the encryption
+ * types negotiated.
+ *
+ * XXX Should call some function to determine the enctypes supported
+ * by the kernel. (Only need to do that once!)
+ *
+ * Returns:
+ * 0 => all went well
+ * -1 => there was an error
+ */
+
+int
+limit_krb5_enctypes(struct rpc_gss_sec *sec)
+{
+ u_int maj_stat, min_stat;
+ krb5_enctype enctypes[] = { ENCTYPE_DES_CBC_CRC,
+ ENCTYPE_DES_CBC_MD5,
+ ENCTYPE_DES_CBC_MD4 };
+ int num_enctypes = sizeof(enctypes) / sizeof(enctypes[0]);
+ extern int num_krb5_enctypes;
+ extern krb5_enctype *krb5_enctypes;
+ int err = -1;
+
+ if (sec->cred == GSS_C_NO_CREDENTIAL) {
+ err = gssd_acquire_krb5_cred(&sec->cred);
+ if (err)
+ return -1;
+ }
+
+ /*
+ * If we failed for any reason to produce global
+ * list of supported enctypes, use local default here.
+ */
+ if (krb5_enctypes == NULL || limit_to_legacy_enctypes)
+ maj_stat = gss_set_allowable_enctypes(&min_stat, sec->cred,
+ &krb5oid, num_enctypes, enctypes);
+ else
+ maj_stat = gss_set_allowable_enctypes(&min_stat, sec->cred,
+ &krb5oid, num_krb5_enctypes, krb5_enctypes);
+
+ if (maj_stat != GSS_S_COMPLETE) {
+ pgsserr("gss_set_allowable_enctypes",
+ maj_stat, min_stat, &krb5oid);
+ return -1;
+ }
+
+ return 0;
+}
+#endif /* HAVE_SET_ALLOWABLE_ENCTYPES */
diff --git a/utils/gssd/krb5_util.h b/utils/gssd/krb5_util.h
new file mode 100644
index 0000000..7ef8701
--- /dev/null
+++ b/utils/gssd/krb5_util.h
@@ -0,0 +1,47 @@
+#ifndef KRB5_UTIL_H
+#define KRB5_UTIL_H
+
+#include <krb5.h>
+
+#ifdef HAVE_LIBTIRPC
+#include <rpc/auth_gss.h>
+#else
+#include "gss_oids.h"
+#endif
+
+
+int gssd_setup_krb5_user_gss_ccache(uid_t uid, char *servername,
+ char *dirname);
+int gssd_get_krb5_machine_cred_list(char ***list);
+void gssd_free_krb5_machine_cred_list(char **list);
+void gssd_destroy_krb5_principals(int destroy_machine_creds);
+int gssd_refresh_krb5_machine_credential(char *hostname,
+ char *service, char *srchost,
+ int force_renew);
+char *gssd_k5_err_msg(krb5_context context, krb5_error_code code);
+void gssd_k5_get_default_realm(char **def_realm);
+
+int gssd_acquire_user_cred(gss_cred_id_t *gss_cred);
+int gssd_k5_remove_bad_service_cred(char *srvname);
+
+#ifdef HAVE_SET_ALLOWABLE_ENCTYPES
+extern int limit_to_legacy_enctypes;
+int limit_krb5_enctypes(struct rpc_gss_sec *sec);
+#endif
+
+/*
+ * Hide away some of the MIT vs. Heimdal differences
+ * here with macros...
+ */
+
+#ifdef HAVE_KRB5
+#define k5_free_unparsed_name(ctx, name) krb5_free_unparsed_name((ctx), (name))
+#define k5_free_default_realm(ctx, realm) krb5_free_default_realm((ctx), (realm))
+#define k5_free_kt_entry(ctx, kte) krb5_free_keytab_entry_contents((ctx),(kte))
+#else /* Heimdal */
+#define k5_free_unparsed_name(ctx, name) free(name)
+#define k5_free_default_realm(ctx, realm) free(realm)
+#define k5_free_kt_entry(ctx, kte) krb5_kt_free_entry((ctx),(kte))
+#endif
+
+#endif /* KRB5_UTIL_H */
diff --git a/utils/gssd/svcgssd.c b/utils/gssd/svcgssd.c
new file mode 100644
index 0000000..ce78d8f
--- /dev/null
+++ b/utils/gssd/svcgssd.c
@@ -0,0 +1,348 @@
+/*
+ gssd.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>.
+ 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.
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <rpc/rpc.h>
+#include <fcntl.h>
+#include <errno.h>
+
+
+#include <unistd.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <nfsidmap.h>
+#include <event2/event.h>
+
+#include "nfslib.h"
+#include "svcgssd.h"
+#include "gss_util.h"
+#include "err_util.h"
+#include "conffile.h"
+#include "misc.h"
+#include "svcgssd_krb5.h"
+
+static bool signal_received = false;
+static struct event_base *evbase = NULL;
+static int nullrpc_fd = -1;
+static struct event *nullrpc_event = NULL;
+static struct event *wait_event = NULL;
+
+#define NULLRPC_FILE "/proc/net/rpc/auth.rpcsec.init/channel"
+
+static void
+sig_die(int signal)
+{
+ if (signal_received) {
+ /* destroy krb5 machine creds */
+ printerr(1, "forced exiting on signal %d\n", signal);
+ exit(0);
+ }
+ signal_received = true;
+ printerr(1, "exiting on signal %d\n", signal);
+ event_base_loopexit(evbase, NULL);
+}
+
+static void
+sig_hup(int signal)
+{
+ /* don't exit on SIGHUP */
+ printerr(1, "Received SIGHUP(%d)... Ignoring.\n", signal);
+ return;
+}
+
+static void
+usage(char *progname)
+{
+ fprintf(stderr, "usage: %s [-n] [-f] [-v] [-r] [-i] [-p principal]\n",
+ progname);
+ exit(1);
+}
+
+static void
+svcgssd_nullrpc_cb(int fd, short UNUSED(which), void *UNUSED(data))
+{
+ char lbuf[RPC_CHAN_BUF_SIZE];
+ int lbuflen = 0;
+
+ printerr(1, "reading null request\n");
+
+ lbuflen = read(fd, lbuf, sizeof(lbuf));
+ if (lbuflen <= 0 || lbuf[lbuflen-1] != '\n') {
+ printerr(0, "WARNING: handle_nullreq: failed reading request\n");
+ return;
+ }
+ lbuf[lbuflen-1] = 0;
+
+ handle_nullreq(lbuf);
+}
+
+static void
+svcgssd_nullrpc_close(void)
+{
+ if (nullrpc_event) {
+ printerr(2, "closing nullrpc channel %s\n", NULLRPC_FILE);
+ event_free(nullrpc_event);
+ nullrpc_event = NULL;
+ }
+ if (nullrpc_fd != -1) {
+ close(nullrpc_fd);
+ nullrpc_fd = -1;
+ }
+}
+
+static void
+svcgssd_nullrpc_open(void)
+{
+ nullrpc_fd = open(NULLRPC_FILE, O_RDWR);
+ if (nullrpc_fd < 0) {
+ printerr(0, "failed to open %s: %s\n",
+ NULLRPC_FILE, strerror(errno));
+ return;
+ }
+ nullrpc_event = event_new(evbase, nullrpc_fd, EV_READ | EV_PERSIST,
+ svcgssd_nullrpc_cb, NULL);
+ if (!nullrpc_event) {
+ printerr(0, "failed to create event for %s: %s\n",
+ NULLRPC_FILE, strerror(errno));
+ close(nullrpc_fd);
+ nullrpc_fd = -1;
+ return;
+ }
+ event_add(nullrpc_event, NULL);
+ printerr(2, "opened nullrpc channel %s\n", NULLRPC_FILE);
+}
+
+static void
+svcgssd_wait_cb(int UNUSED(fd), short UNUSED(which), void *UNUSED(data))
+{
+ static int times = 0;
+ int rc;
+
+ rc = access(NULLRPC_FILE, R_OK | W_OK);
+ if (rc != 0) {
+ struct timeval t = {times < 10 ? 1 : 10, 0};
+ times++;
+ if (times % 30 == 0)
+ printerr(2, "still waiting for nullrpc channel: %s\n",
+ NULLRPC_FILE);
+ evtimer_add(wait_event, &t);
+ return;
+ }
+
+ svcgssd_nullrpc_open();
+ event_free(wait_event);
+ wait_event = NULL;
+}
+
+
+
+int
+main(int argc, char *argv[])
+{
+ int get_creds = 1;
+ int fg = 0;
+ int verbosity = 0;
+ int rpc_verbosity = 0;
+ int idmap_verbosity = 0;
+ int opt, status;
+ extern char *optarg;
+ char *progname;
+ char *principal = NULL;
+ char *s;
+ int rc;
+
+ conf_init_file(NFS_CONFFILE);
+
+ s = conf_get_str("svcgssd", "principal");
+ if (!s)
+ ;
+ else if (strcmp(s, "system")== 0)
+ get_creds = 0;
+ else
+ principal = s;
+
+ verbosity = conf_get_num("svcgssd", "Verbosity", verbosity);
+ rpc_verbosity = conf_get_num("svcgssd", "RPC-Verbosity", rpc_verbosity);
+ idmap_verbosity = conf_get_num("svcgssd", "IDMAP-Verbosity", idmap_verbosity);
+
+ while ((opt = getopt(argc, argv, "fivrnp:")) != -1) {
+ switch (opt) {
+ case 'f':
+ fg = 1;
+ break;
+ case 'i':
+ idmap_verbosity++;
+ break;
+ case 'n':
+ get_creds = 0;
+ break;
+ case 'v':
+ verbosity++;
+ break;
+ case 'r':
+ rpc_verbosity++;
+ break;
+ case 'p':
+ principal = optarg;
+ break;
+ default:
+ usage(argv[0]);
+ break;
+ }
+ }
+
+ if ((progname = strrchr(argv[0], '/')))
+ progname++;
+ else
+ progname = argv[0];
+
+ initerr(progname, verbosity, fg);
+#ifdef HAVE_AUTHGSS_SET_DEBUG_LEVEL
+ if (verbosity && rpc_verbosity == 0)
+ rpc_verbosity = verbosity;
+ authgss_set_debug_level(rpc_verbosity);
+#elif HAVE_LIBTIRPC_SET_DEBUG
+ /*
+ * Only set the libtirpc debug level if explicitly requested via -r...
+ * svcgssd is chatty enough as it is.
+ */
+ if (rpc_verbosity > 0)
+ libtirpc_set_debug(progname, rpc_verbosity, fg);
+#else
+ if (rpc_verbosity > 0)
+ printerr(0, "Warning: rpcsec_gss library does not "
+ "support setting debug level\n");
+#endif
+#ifdef HAVE_NFS4_SET_DEBUG
+ if (verbosity && idmap_verbosity == 0)
+ idmap_verbosity = verbosity;
+ nfs4_set_debug(idmap_verbosity, NULL);
+#else
+ if (idmap_verbosity > 0)
+ printerr(0, "Warning: your nfsidmap library does not "
+ "support setting debug level\n");
+#endif
+
+ if (gssd_check_mechs() != 0) {
+ printerr(0, "ERROR: Problem with gssapi library\n");
+ exit(1);
+ }
+
+ daemon_init(fg);
+
+ evbase = event_base_new();
+ if (!evbase) {
+ printerr(0, "ERROR: failed to create event base: %s\n", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ signal(SIGINT, sig_die);
+ signal(SIGTERM, sig_die);
+ signal(SIGHUP, sig_hup);
+
+ if (get_creds) {
+ if (principal)
+ status = gssd_acquire_cred(principal,
+ ((const gss_OID)GSS_C_NT_USER_NAME));
+ else
+ status = gssd_acquire_cred(GSSD_SERVICE_NAME,
+ (const gss_OID)GSS_C_NT_HOSTBASED_SERVICE);
+ if (status == FALSE) {
+ printerr(0, "unable to obtain root (machine) credentials\n");
+ printerr(0, "do you have a keytab entry for %s in"
+ "/etc/krb5.keytab?\n",
+ principal ? principal : "nfs/<your.host>@<YOUR.REALM>");
+ exit(1);
+ }
+ } else {
+ status = gssd_acquire_cred(NULL,
+ (const gss_OID)GSS_C_NT_HOSTBASED_SERVICE);
+ if (status == FALSE) {
+ printerr(0, "unable to obtain nameless credentials\n");
+ exit(1);
+ }
+ }
+
+ svcgssd_nullrpc_open();
+ if (!nullrpc_event) {
+ struct timeval t = {1, 0};
+
+ printerr(2, "waiting for nullrpc channel to appear\n");
+ wait_event = evtimer_new(evbase, svcgssd_wait_cb, NULL);
+ if (!wait_event) {
+ printerr(0, "ERROR: failed to create wait event: %s\n",
+ strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ evtimer_add(wait_event, &t);
+ }
+
+ daemon_ready();
+
+ /* We don't need the config anymore */
+ conf_cleanup();
+
+ nfs4_init_name_mapping(NULL); /* XXX: should only do this once */
+
+ rc = event_base_dispatch(evbase);
+ if (rc < 0)
+ printerr(0, "event_base_dispatch() returned %i!\n", rc);
+
+ svcgssd_nullrpc_close();
+ if (wait_event)
+ event_free(wait_event);
+
+ event_base_free(evbase);
+
+ nfs4_term_name_mapping();
+ svcgssd_free_enctypes();
+ gssd_cleanup();
+
+ return EXIT_SUCCESS;
+}
diff --git a/utils/gssd/svcgssd.h b/utils/gssd/svcgssd.h
new file mode 100644
index 0000000..e229b98
--- /dev/null
+++ b/utils/gssd/svcgssd.h
@@ -0,0 +1,42 @@
+/*
+ Copyright (c) 2004 The Regents of the University of Michigan.
+ 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. 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.
+*/
+
+#ifndef _RPC_SVCGSSD_H_
+#define _RPC_SVCGSSD_H_
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <gssapi/gssapi.h>
+
+void handle_nullreq(char *cp);
+
+#define GSSD_SERVICE_NAME "nfs"
+
+#endif /* _RPC_SVCGSSD_H_ */
diff --git a/utils/gssd/svcgssd.man b/utils/gssd/svcgssd.man
new file mode 100644
index 0000000..8771c03
--- /dev/null
+++ b/utils/gssd/svcgssd.man
@@ -0,0 +1,88 @@
+.\"
+.\" rpc.svcgssd(8)
+.\"
+.\" Copyright (C) 2003 J. Bruce Fields <bfields@umich.edu>
+.TH rpc.svcgssd 8 "12 Jan 2007"
+.SH NAME
+rpc.svcgssd \- server-side rpcsec_gss daemon
+.SH SYNOPSIS
+.B "rpc.svcgssd [-n] [-v] [-r] [-i] [-f] [-p principal]"
+.SH DESCRIPTION
+The rpcsec_gss protocol gives a means of using the gss-api generic security
+api to provide security for protocols using rpc (in particular, nfs). Before
+exchanging any rpc requests using rpcsec_gss, the rpc client must first
+establish a security context with the rpc server. The linux kernel's
+implementation of rpcsec_gss depends on the userspace daemon
+.B rpc.svcgssd
+to handle context establishment on the rpc server. The
+daemon uses files in the proc filesystem to communicate with
+the kernel.
+
+.SH OPTIONS
+.TP
+.B -f
+Runs
+.B rpc.svcgssd
+in the foreground and sends output to stderr (as opposed to syslogd)
+.TP
+.B -v
+Increases the verbosity of the output (can be specified multiple times).
+.TP
+.B -r
+If the rpcsec_gss library supports setting debug level,
+increases the verbosity of the output (can be specified multiple times).
+.TP
+.B -i
+If the nfsidmap library supports setting debug level,
+increases the verbosity of the output (can be specified multiple times).
+.TP
+.B -p
+Use \fIprincipal\fR instead of the default
+.RI nfs/ FQDN @ REALM .
+.TP
+.B -n
+Use the system default credentials
+.RI (host/ FQDN @ REALM )
+rather than the default
+.RI nfs/ FQDN @ REALM .
+.SH CONFIGURATION FILE
+Some of the options that can be set on the command line can also be
+controlled through values set in the
+.B [svcgssd]
+section of the
+.I /etc/nfs.conf
+configuration file. Values recognized include:
+.TP
+.B principal
+If set to
+.B system
+this is equivalent to the
+.B -n
+option. If set to any other value, that is used like the
+.B -p
+option.
+.TP
+.B verbosity
+Value which is equivalent to the number of
+.BR -v .
+.TP
+.B rpc-verbosity
+Value which is equivalent to the number of
+.BR -r .
+.TP
+.B idmap-verbosity
+Value which is equivalent to the number of
+.BR -i .
+
+
+.SH SEE ALSO
+.BR rpc.gssd(8),
+.SH AUTHORS
+.br
+Dug Song <dugsong@umich.edu>
+.br
+Andy Adamson <andros@umich.edu>
+.br
+Marius Aamodt Eriksen <marius@umich.edu>
+.br
+J. Bruce Fields <bfields@umich.edu>
diff --git a/utils/gssd/svcgssd_krb5.c b/utils/gssd/svcgssd_krb5.c
new file mode 100644
index 0000000..2503c38
--- /dev/null
+++ b/utils/gssd/svcgssd_krb5.c
@@ -0,0 +1,237 @@
+/*
+ * COPYRIGHT (c) 2011
+ * The Regents of the University of Michigan
+ * ALL RIGHTS RESERVED
+ *
+ * Permission is granted to use, copy, create derivative works
+ * and redistribute this software and such derivative works
+ * for any purpose, so long as the name of The University of
+ * Michigan is not used in any advertising or publicity
+ * pertaining to the use of distribution of this software
+ * without specific, written prior authorization. If the
+ * above copyright notice or any other identification of the
+ * University of Michigan is included in any copy of any
+ * portion of this software, then the disclaimer below must
+ * also be included.
+ *
+ * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION
+ * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY
+ * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF
+ * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
+ * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE
+ * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN
+ * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGES.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+#include <gssapi/gssapi.h>
+#include <krb5.h>
+
+#include "gss_util.h"
+#include "gss_oids.h"
+#include "err_util.h"
+#include "svcgssd_krb5.h"
+#include "version.h"
+
+#define MYBUFLEN 1024
+
+char *supported_enctypes_filename = "/proc/fs/nfsd/supported_krb5_enctypes";
+int parsed_num_enctypes = 0;
+krb5_enctype *parsed_enctypes = NULL;
+char *cached_enctypes = NULL;
+
+/*==========================*/
+/*=== Internal routines ===*/
+/*==========================*/
+
+/*
+ * Parse the supported encryption type information
+ */
+static int
+parse_enctypes(char *enctypes)
+{
+ int n = 0;
+ char *curr, *comma;
+ int i;
+
+ /* Don't parse the same string over and over... */
+ if (cached_enctypes && strcmp(cached_enctypes, enctypes) == 0)
+ return 0;
+
+ /* Free any existing cached_enctypes */
+ svcgssd_free_enctypes();
+
+ /* count the number of commas */
+ for (curr = enctypes; curr && *curr != '\0'; curr = ++comma) {
+ comma = strchr(curr, ',');
+ if (comma != NULL)
+ n++;
+ else
+ break;
+ }
+
+ /* If no more commas and we're not at the end, there's one more value */
+ if (*curr != '\0')
+ n++;
+
+ /* Empty string, return an error */
+ if (n == 0)
+ return ENOENT;
+
+ /* Skip pass any non digits */
+ while (*enctypes && isdigit(*enctypes) == 0)
+ enctypes++;
+ if (*enctypes == '\0')
+ return EINVAL;
+
+ /* Allocate space for enctypes array */
+ if ((parsed_enctypes = (int *) calloc(n, sizeof(int))) == NULL) {
+ return ENOMEM;
+ }
+
+ /* Now parse each value into the array */
+ for (curr = enctypes, i = 0; curr && *curr != '\0'; curr = ++comma) {
+ parsed_enctypes[i++] = atoi(curr);
+ comma = strchr(curr, ',');
+ if (comma == NULL)
+ break;
+ }
+
+ parsed_num_enctypes = n;
+ if ((cached_enctypes = malloc(strlen(enctypes)+1)))
+ strcpy(cached_enctypes, enctypes);
+
+ return 0;
+}
+
+static void
+get_kernel_supported_enctypes(void)
+{
+ FILE *s_e;
+ int ret;
+ char buffer[MYBUFLEN + 1];
+
+ memset(buffer, '\0', sizeof(buffer));
+
+ s_e = fopen(supported_enctypes_filename, "r");
+ if (s_e == NULL)
+ goto out_clean_parsed;
+
+ ret = fread(buffer, 1, MYBUFLEN, s_e);
+ if (ret < 0) {
+ fclose(s_e);
+ goto out_clean_parsed;
+ }
+ fclose(s_e);
+ if (parse_enctypes(buffer)) {
+ goto out_clean_parsed;
+ }
+out:
+ return;
+
+out_clean_parsed:
+ if (parsed_enctypes != NULL) {
+ free(parsed_enctypes);
+ parsed_num_enctypes = 0;
+ }
+ goto out;
+}
+
+/*==========================*/
+/*=== External routines ===*/
+/*==========================*/
+
+void
+svcgssd_free_enctypes(void)
+{
+ free(cached_enctypes);
+ cached_enctypes = NULL;
+
+ if (parsed_enctypes != NULL) {
+ free(parsed_enctypes);
+ parsed_enctypes = NULL;
+ parsed_num_enctypes = 0;
+ }
+}
+
+/*
+ * Get encryption types supported by the kernel, and then
+ * call gss_krb5_set_allowable_enctypes() to limit the
+ * encryption types negotiated.
+ *
+ * Returns:
+ * 0 => all went well
+ * -1 => there was an error
+ */
+
+int
+svcgssd_limit_krb5_enctypes(void)
+{
+#ifdef HAVE_SET_ALLOWABLE_ENCTYPES
+ u_int maj_stat, min_stat;
+ krb5_enctype old_kernel_enctypes[] = {
+ ENCTYPE_DES_CBC_CRC,
+ ENCTYPE_DES_CBC_MD5,
+ ENCTYPE_DES_CBC_MD4 };
+ krb5_enctype new_kernel_enctypes[] = {
+ ENCTYPE_AES256_CTS_HMAC_SHA1_96,
+ ENCTYPE_AES128_CTS_HMAC_SHA1_96,
+ ENCTYPE_DES3_CBC_SHA1,
+ ENCTYPE_ARCFOUR_HMAC,
+ ENCTYPE_DES_CBC_CRC,
+ ENCTYPE_DES_CBC_MD5,
+ ENCTYPE_DES_CBC_MD4 };
+ krb5_enctype *default_enctypes, *enctypes;
+ int default_num_enctypes, num_enctypes;
+
+
+ if (linux_version_code() < MAKE_VERSION(2, 6, 35)) {
+ default_enctypes = old_kernel_enctypes;
+ default_num_enctypes =
+ sizeof(old_kernel_enctypes) / sizeof(old_kernel_enctypes[0]);
+ } else {
+ default_enctypes = new_kernel_enctypes;
+ default_num_enctypes =
+ sizeof(new_kernel_enctypes) / sizeof(new_kernel_enctypes[0]);
+ }
+
+ get_kernel_supported_enctypes();
+
+ if (parsed_enctypes != NULL) {
+ enctypes = parsed_enctypes;
+ num_enctypes = parsed_num_enctypes;
+ printerr(2, "%s: Calling gss_set_allowable_enctypes with %d "
+ "enctypes from the kernel\n", __func__, num_enctypes);
+ } else {
+ enctypes = default_enctypes;
+ num_enctypes = default_num_enctypes;
+ printerr(2, "%s: Calling gss_set_allowable_enctypes with %d "
+ "enctypes from defaults\n", __func__, num_enctypes);
+ }
+
+ maj_stat = gss_set_allowable_enctypes(&min_stat, gssd_creds,
+ &krb5oid, num_enctypes, enctypes);
+ if (maj_stat != GSS_S_COMPLETE) {
+ printerr(1, "WARNING: gss_set_allowable_enctypes failed\n");
+ pgsserr("svcgssd_limit_krb5_enctypes: gss_set_allowable_enctypes",
+ maj_stat, min_stat, &krb5oid);
+ return -1;
+ }
+#endif
+ return 0;
+}
diff --git a/utils/gssd/svcgssd_krb5.h b/utils/gssd/svcgssd_krb5.h
new file mode 100644
index 0000000..78a90e9
--- /dev/null
+++ b/utils/gssd/svcgssd_krb5.h
@@ -0,0 +1,37 @@
+/*
+ * COPYRIGHT (c) 2011
+ * The Regents of the University of Michigan
+ * ALL RIGHTS RESERVED
+ *
+ * Permission is granted to use, copy, create derivative works
+ * and redistribute this software and such derivative works
+ * for any purpose, so long as the name of The University of
+ * Michigan is not used in any advertising or publicity
+ * pertaining to the use of distribution of this software
+ * without specific, written prior authorization. If the
+ * above copyright notice or any other identification of the
+ * University of Michigan is included in any copy of any
+ * portion of this software, then the disclaimer below must
+ * also be included.
+ *
+ * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION
+ * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY
+ * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF
+ * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
+ * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE
+ * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN
+ * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGES.
+ */
+
+#ifndef SVCGSSD_KRB5_H
+#define SVCGSSD_KRB5_H
+
+int svcgssd_limit_krb5_enctypes(void);
+void svcgssd_free_enctypes(void);
+
+#endif /* SVCGSSD_KRB5_H */
diff --git a/utils/gssd/svcgssd_mech2file.c b/utils/gssd/svcgssd_mech2file.c
new file mode 100644
index 0000000..c26b435
--- /dev/null
+++ b/utils/gssd/svcgssd_mech2file.c
@@ -0,0 +1,74 @@
+/*
+ linux_downcall.c
+
+ Copyright (c) 2000 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Copyright (c) 2004 Andy Adamson <andros@UMICH.EDU>.
+ 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.
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <gssapi/gssapi.h>
+#include <string.h>
+
+char * mech2file(gss_OID mech);
+
+#define g_OID_equal(o1,o2) \
+ (((o1)->length == (o2)->length) && \
+ (memcmp((o1)->elements,(o2)->elements,(int) (o1)->length) == 0))
+
+struct mech2file {
+ gss_OID_desc mech;
+ char filename[8];
+};
+
+struct mech2file m2f[] = {
+ {{9, "\052\206\110\206\367\022\001\002\002"}, "krb5"},
+ {{0,0},""},
+};
+
+/*
+ * Find the Linux svcgssd downcall file name given the mechanism
+ */
+char *
+mech2file(gss_OID mech)
+{
+ struct mech2file *m2fp = m2f;
+
+ while(m2fp->mech.length != 0) {
+ if (g_OID_equal(mech,&m2fp->mech))
+ return(m2fp->filename);
+ m2fp++;
+ }
+ return NULL;
+}
diff --git a/utils/gssd/svcgssd_proc.c b/utils/gssd/svcgssd_proc.c
new file mode 100644
index 0000000..b403143
--- /dev/null
+++ b/utils/gssd/svcgssd_proc.c
@@ -0,0 +1,444 @@
+/*
+ svc_in_gssd_proc.c
+
+ Copyright (c) 2000 The Regents of the University of Michigan.
+ All rights reserved.
+
+ Copyright (c) 2002 Bruce Fields <bfields@UMICH.EDU>
+
+ 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.
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <rpc/rpc.h>
+
+#include <pwd.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <nfsidmap.h>
+#include <nfslib.h>
+#include <time.h>
+
+#include "svcgssd.h"
+#include "gss_util.h"
+#include "err_util.h"
+#include "context.h"
+#include "misc.h"
+#include "gss_oids.h"
+#include "svcgssd_krb5.h"
+#include "gss_names.h"
+
+extern char * mech2file(gss_OID mech);
+#define SVCGSSD_CONTEXT_CHANNEL "/proc/net/rpc/auth.rpcsec.context/channel"
+#define SVCGSSD_INIT_CHANNEL "/proc/net/rpc/auth.rpcsec.init/channel"
+
+#define TOKEN_BUF_SIZE 8192
+
+struct svc_cred {
+ uid_t cr_uid;
+ gid_t cr_gid;
+ int cr_ngroups;
+ gid_t cr_groups[NGROUPS];
+};
+
+static int
+do_svc_downcall(gss_buffer_desc *out_handle, struct svc_cred *cred,
+ gss_OID mech, gss_buffer_desc *context_token,
+ int32_t endtime, char *client_name)
+{
+ char buf[RPC_CHAN_BUF_SIZE], *bp;
+ int i, f, err, blen;
+ char *fname = NULL;
+
+ printerr(1, "doing downcall\n");
+ if ((fname = mech2file(mech)) == NULL)
+ goto out_err;
+
+ f = open(SVCGSSD_CONTEXT_CHANNEL, O_WRONLY);
+ if (f < 0) {
+ printerr(0, "WARNING: unable to open downcall channel "
+ "%s: %s\n",
+ SVCGSSD_CONTEXT_CHANNEL, strerror(errno));
+ goto out_err;
+ }
+ bp = buf, blen = sizeof(buf);
+ qword_addhex(&bp, &blen, out_handle->value, out_handle->length);
+ /* XXX are types OK for the rest of this? */
+ /* For context cache, use the actual context endtime */
+ qword_addint(&bp, &blen, endtime);
+ qword_addint(&bp, &blen, cred->cr_uid);
+ qword_addint(&bp, &blen, cred->cr_gid);
+ qword_addint(&bp, &blen, cred->cr_ngroups);
+ printerr(2, "mech: %s, hndl len: %d, ctx len %d, timeout: %d (%d from now), "
+ "clnt: %s, uid: %d, gid: %d, num aux grps: %d:\n",
+ fname, out_handle->length, context_token->length,
+ endtime, endtime - time(0),
+ client_name ? client_name : "<null>",
+ cred->cr_uid, cred->cr_gid, cred->cr_ngroups);
+ for (i=0; i < cred->cr_ngroups; i++) {
+ qword_addint(&bp, &blen, cred->cr_groups[i]);
+ printerr(2, " (%4d) %d\n", i+1, cred->cr_groups[i]);
+ }
+ qword_add(&bp, &blen, fname);
+ qword_addhex(&bp, &blen, context_token->value, context_token->length);
+ if (client_name)
+ qword_add(&bp, &blen, client_name);
+ qword_addeol(&bp, &blen);
+ err = 0;
+ if (blen <= 0 || write(f, buf, bp - buf) != bp - buf) {
+ printerr(1, "WARNING: error writing to downcall channel "
+ "%s: %s\n", SVCGSSD_CONTEXT_CHANNEL, strerror(errno));
+ err = -1;
+ }
+ close(f);
+ return err;
+out_err:
+ printerr(1, "WARNING: downcall failed\n");
+ return -1;
+}
+
+struct gss_verifier {
+ u_int32_t flav;
+ gss_buffer_desc body;
+};
+
+#define RPCSEC_GSS_SEQ_WIN 5
+
+static int
+send_response(gss_buffer_desc *in_handle, gss_buffer_desc *in_token,
+ u_int32_t maj_stat, u_int32_t min_stat,
+ gss_buffer_desc *out_handle, gss_buffer_desc *out_token)
+{
+ char buf[2 * TOKEN_BUF_SIZE];
+ char *bp = buf;
+ int blen = sizeof(buf);
+ /* XXXARG: */
+ int g;
+
+ printerr(1, "sending null reply\n");
+
+ qword_addhex(&bp, &blen, in_handle->value, in_handle->length);
+ qword_addhex(&bp, &blen, in_token->value, in_token->length);
+ /* For init cache, only needed for a short time */
+ qword_addint(&bp, &blen, time(0) + 60);
+ qword_adduint(&bp, &blen, maj_stat);
+ qword_adduint(&bp, &blen, min_stat);
+ qword_addhex(&bp, &blen, out_handle->value, out_handle->length);
+ qword_addhex(&bp, &blen, out_token->value, out_token->length);
+ qword_addeol(&bp, &blen);
+ if (blen <= 0) {
+ printerr(0, "WARNING: send_respsonse: message too long\n");
+ return -1;
+ }
+ g = open(SVCGSSD_INIT_CHANNEL, O_WRONLY);
+ if (g == -1) {
+ printerr(0, "WARNING: open %s failed: %s\n",
+ SVCGSSD_INIT_CHANNEL, strerror(errno));
+ return -1;
+ }
+ *bp = '\0';
+ printerr(3, "writing message: %s", buf);
+ if (write(g, buf, bp - buf) == -1) {
+ printerr(0, "WARNING: failed to write message\n");
+ close(g);
+ return -1;
+ }
+ close(g);
+ return 0;
+}
+
+#define rpc_auth_ok 0
+#define rpc_autherr_badcred 1
+#define rpc_autherr_rejectedcred 2
+#define rpc_autherr_badverf 3
+#define rpc_autherr_rejectedverf 4
+#define rpc_autherr_tooweak 5
+#define rpcsec_gsserr_credproblem 13
+#define rpcsec_gsserr_ctxproblem 14
+
+static void
+add_supplementary_groups(char *secname, char *name, struct svc_cred *cred)
+{
+ int ret;
+ static gid_t *groups = NULL;
+
+ cred->cr_ngroups = NGROUPS;
+ ret = nfs4_gss_princ_to_grouplist(secname, name,
+ cred->cr_groups, &cred->cr_ngroups);
+ if (ret < 0) {
+ groups = realloc(groups, cred->cr_ngroups*sizeof(gid_t));
+ ret = nfs4_gss_princ_to_grouplist(secname, name,
+ groups, &cred->cr_ngroups);
+ if (ret < 0)
+ cred->cr_ngroups = 0;
+ else {
+ if (cred->cr_ngroups > NGROUPS)
+ cred->cr_ngroups = NGROUPS;
+ memcpy(cred->cr_groups, groups,
+ cred->cr_ngroups*sizeof(gid_t));
+ }
+ }
+}
+
+static int
+get_ids(gss_name_t client_name, gss_OID mech, struct svc_cred *cred)
+{
+ u_int32_t maj_stat, min_stat;
+ gss_buffer_desc name;
+ char *sname;
+ int res = -1;
+ uid_t uid, gid;
+ gss_OID name_type = GSS_C_NO_OID;
+ char *secname;
+
+ maj_stat = gss_display_name(&min_stat, client_name, &name, &name_type);
+ if (maj_stat != GSS_S_COMPLETE) {
+ pgsserr("get_ids: gss_display_name",
+ maj_stat, min_stat, mech);
+ goto out;
+ }
+ if (name.length >= 0xffff || /* be certain name.length+1 doesn't overflow */
+ !(sname = calloc(name.length + 1, 1))) {
+ printerr(0, "WARNING: get_ids: error allocating %d bytes "
+ "for sname\n", name.length + 1);
+ gss_release_buffer(&min_stat, &name);
+ goto out;
+ }
+ memcpy(sname, name.value, name.length);
+ printerr(1, "sname = %s\n", sname);
+ gss_release_buffer(&min_stat, &name);
+
+ res = -EINVAL;
+ if ((secname = mech2file(mech)) == NULL) {
+ printerr(0, "WARNING: get_ids: error mapping mech to "
+ "file for name '%s'\n", sname);
+ goto out_free;
+ }
+
+ res = nfs4_gss_princ_to_ids(secname, sname, &uid, &gid);
+ if (res < 0) {
+ /*
+ * -ENOENT means there was no mapping, any other error
+ * value means there was an error trying to do the
+ * mapping.
+ * If there was no mapping, we send down the value -1
+ * to indicate that the anonuid/anongid for the export
+ * should be used.
+ */
+ if (res == -ENOENT) {
+ cred->cr_uid = -1;
+ cred->cr_gid = -1;
+ cred->cr_ngroups = 0;
+ res = 0;
+ goto out_free;
+ }
+ printerr(1, "WARNING: get_ids: failed to map name '%s' "
+ "to uid/gid: %s\n", sname, strerror(-res));
+ goto out_free;
+ }
+ cred->cr_uid = uid;
+ cred->cr_gid = gid;
+ add_supplementary_groups(secname, sname, cred);
+ res = 0;
+out_free:
+ free(sname);
+out:
+ return res;
+}
+
+#ifdef DEBUG
+void
+print_hexl(const char *description, unsigned char *cp, int length)
+{
+ int i, j, jm;
+ unsigned char c;
+
+ printf("%s (length %d)\n", description, length);
+
+ for (i = 0; i < length; i += 0x10) {
+ printf(" %04x: ", (u_int)i);
+ jm = length - i;
+ jm = jm > 16 ? 16 : jm;
+
+ for (j = 0; j < jm; j++) {
+ if ((j % 2) == 1)
+ printf("%02x ", (u_int)cp[i+j]);
+ else
+ printf("%02x", (u_int)cp[i+j]);
+ }
+ for (; j < 16; j++) {
+ if ((j % 2) == 1)
+ printf(" ");
+ else
+ printf(" ");
+ }
+ printf(" ");
+
+ for (j = 0; j < jm; j++) {
+ c = cp[i+j];
+ c = isprint(c) ? c : '.';
+ printf("%c", c);
+ }
+ printf("\n");
+ }
+}
+#endif
+
+void
+handle_nullreq(char *cp) {
+ /* XXX initialize to a random integer to reduce chances of unnecessary
+ * invalidation of existing ctx's on restarting svcgssd. */
+ static u_int32_t handle_seq = 0;
+ char in_tok_buf[TOKEN_BUF_SIZE];
+ char in_handle_buf[15];
+ char out_handle_buf[15];
+ gss_buffer_desc in_tok = {.value = in_tok_buf},
+ out_tok = {.value = NULL},
+ in_handle = {.value = in_handle_buf},
+ out_handle = {.value = out_handle_buf},
+ ctx_token = {.value = NULL},
+ ignore_out_tok = {.value = NULL},
+ /* XXX isn't there a define for this?: */
+ null_token = {.value = NULL};
+ u_int32_t ret_flags;
+ gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
+ gss_name_t client_name = NULL;
+ gss_OID mech = GSS_C_NO_OID;
+ u_int32_t maj_stat = GSS_S_FAILURE, min_stat = 0;
+ u_int32_t ignore_min_stat;
+ struct svc_cred cred;
+ int32_t ctx_endtime;
+ char *hostbased_name = NULL;
+
+ printerr(1, "handling null request\n");
+
+ in_handle.length = (size_t) qword_get(&cp, in_handle.value,
+ sizeof(in_handle_buf));
+#ifdef DEBUG
+ print_hexl("in_handle", in_handle.value, in_handle.length);
+#endif
+
+ in_tok.length = (size_t) qword_get(&cp, in_tok.value,
+ sizeof(in_tok_buf));
+#ifdef DEBUG
+ print_hexl("in_tok", in_tok.value, in_tok.length);
+#endif
+
+ if (in_handle.length != 0) { /* CONTINUE_INIT case */
+ if (in_handle.length != sizeof(ctx)) {
+ printerr(0, "WARNING: handle_nullreq: "
+ "input handle has unexpected length %d\n",
+ in_handle.length);
+ goto out_err;
+ }
+ /* in_handle is the context id stored in the out_handle
+ * for the GSS_S_CONTINUE_NEEDED case below. */
+ memcpy(&ctx, in_handle.value, in_handle.length);
+ }
+
+ if (svcgssd_limit_krb5_enctypes()) {
+ goto out_err;
+ }
+
+ maj_stat = gss_accept_sec_context(&min_stat, &ctx, gssd_creds,
+ &in_tok, GSS_C_NO_CHANNEL_BINDINGS, &client_name,
+ &mech, &out_tok, &ret_flags, NULL, NULL);
+
+ if (maj_stat == GSS_S_CONTINUE_NEEDED) {
+ printerr(1, "gss_accept_sec_context GSS_S_CONTINUE_NEEDED\n");
+
+ /* Save the context handle for future calls */
+ out_handle.length = sizeof(ctx);
+ memcpy(out_handle.value, &ctx, sizeof(ctx));
+ goto continue_needed;
+ }
+ else if (maj_stat != GSS_S_COMPLETE) {
+ printerr(1, "WARNING: gss_accept_sec_context failed\n");
+ pgsserr("handle_nullreq: gss_accept_sec_context",
+ maj_stat, min_stat, mech);
+ goto out_err;
+ }
+ if (get_ids(client_name, mech, &cred)) {
+ /* get_ids() prints error msg */
+ maj_stat = GSS_S_BAD_NAME; /* XXX ? */
+ goto out_err;
+ }
+ if (get_hostbased_client_name(client_name, mech, &hostbased_name)) {
+ /* get_hostbased_client_name() prints error msg */
+ maj_stat = GSS_S_BAD_NAME; /* XXX ? */
+ goto out_err;
+ }
+
+ /* Context complete. Pass handle_seq in out_handle to use
+ * for context lookup in the kernel. */
+ handle_seq++;
+ out_handle.length = sizeof(handle_seq);
+ memcpy(out_handle.value, &handle_seq, sizeof(handle_seq));
+
+ /* kernel needs ctx to calculate verifier on null response, so
+ * must give it context before doing null call: */
+ if (serialize_context_for_kernel(&ctx, &ctx_token, mech, &ctx_endtime)) {
+ printerr(0, "WARNING: handle_nullreq: "
+ "serialize_context_for_kernel failed\n");
+ maj_stat = GSS_S_FAILURE;
+ goto out_err;
+ }
+ /* We no longer need the gss context */
+ gss_delete_sec_context(&ignore_min_stat, &ctx, &ignore_out_tok);
+
+ do_svc_downcall(&out_handle, &cred, mech, &ctx_token, ctx_endtime,
+ hostbased_name);
+continue_needed:
+ send_response(&in_handle, &in_tok, maj_stat, min_stat,
+ &out_handle, &out_tok);
+out:
+ if (ctx_token.value != NULL)
+ free(ctx_token.value);
+ if (out_tok.value != NULL)
+ gss_release_buffer(&ignore_min_stat, &out_tok);
+ if (client_name)
+ gss_release_name(&ignore_min_stat, &client_name);
+ free(hostbased_name);
+ printerr(1, "finished handling null request\n");
+ return;
+
+out_err:
+ if (ctx != GSS_C_NO_CONTEXT)
+ gss_delete_sec_context(&ignore_min_stat, &ctx, &ignore_out_tok);
+ send_response(&in_handle, &in_tok, maj_stat, min_stat,
+ &null_token, &null_token);
+ goto out;
+}
diff --git a/utils/gssd/write_bytes.h b/utils/gssd/write_bytes.h
new file mode 100644
index 0000000..b3f342b
--- /dev/null
+++ b/utils/gssd/write_bytes.h
@@ -0,0 +1,159 @@
+/*
+ Copyright (c) 2004 The Regents of the University of Michigan.
+ 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. 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.
+*/
+
+#ifndef _WRITE_BYTES_H_
+#define _WRITE_BYTES_H_
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <netinet/in.h> /* for ntohl */
+
+inline static int
+write_bytes(char **ptr, const char *end, const void *arg, int arg_len)
+{
+ char *p = *ptr, *arg_end;
+
+ arg_end = p + arg_len;
+ if (arg_end > end || arg_end < p)
+ return -1;
+ memcpy(p, arg, arg_len);
+ *ptr = arg_end;
+ return 0;
+}
+
+#define WRITE_BYTES(p, end, arg) write_bytes(p, end, &arg, sizeof(arg))
+
+inline static int
+write_buffer(char **p, char *end, gss_buffer_desc *arg)
+{
+ int len = (int)arg->length; /* make an int out of size_t */
+ if (WRITE_BYTES(p, end, len))
+ return -1;
+ if (*p + len > end)
+ return -1;
+ memcpy(*p, arg->value, len);
+ *p += len;
+ return 0;
+}
+
+inline static int
+write_oid(char **p, char *end, gss_OID_desc *arg)
+{
+ int len = (int)arg->length; /* make an int out of size_t */
+ if (WRITE_BYTES(p, end, len))
+ return -1;
+ if (*p + arg->length > end)
+ return -1;
+ memcpy(*p, arg->elements, len);
+ *p += len;
+ return 0;
+}
+
+static inline int
+get_bytes(char **ptr, const char *end, void *res, int len)
+{
+ char *p, *q;
+ p = *ptr;
+ q = p + len;
+ if (q > end || q < p)
+ return -1;
+ memcpy(res, p, len);
+ *ptr = q;
+ return 0;
+}
+
+static inline int
+get_buffer(char **ptr, const char *end, gss_buffer_desc *res)
+{
+ char *p, *q;
+ p = *ptr;
+ int len;
+ if (get_bytes(&p, end, &len, sizeof(len)))
+ return -1;
+ res->length = len; /* promote to size_t if necessary */
+ q = p + res->length;
+ if (q > end || q < p)
+ return -1;
+ if (!(res->value = malloc(res->length)))
+ return -1;
+ memcpy(res->value, p, res->length);
+ *ptr = q;
+ return 0;
+}
+
+static inline int
+xdr_get_u32(u_int32_t **ptr, const u_int32_t *end, u_int32_t *res)
+{
+ if (get_bytes((char **)ptr, (char *)end, res, sizeof(res)))
+ return -1;
+ *res = ntohl(*res);
+ return 0;
+}
+
+static inline int
+xdr_get_buffer(u_int32_t **ptr, const u_int32_t *end, gss_buffer_desc *res)
+{
+ u_int32_t *p, *q;
+ u_int32_t len;
+ p = *ptr;
+ if (xdr_get_u32(&p, end, &len))
+ return -1;
+ res->length = len;
+ q = p + ((res->length + 3) >> 2);
+ if (q > end || q < p)
+ return -1;
+ if (!(res->value = malloc(res->length)))
+ return -1;
+ memcpy(res->value, p, res->length);
+ *ptr = q;
+ return 0;
+}
+
+static inline int
+xdr_write_u32(u_int32_t **ptr, const u_int32_t *end, u_int32_t arg)
+{
+ u_int32_t tmp;
+
+ tmp = htonl(arg);
+ return WRITE_BYTES((char **)ptr, (char *)end, tmp);
+}
+
+static inline int
+xdr_write_buffer(u_int32_t **ptr, const u_int32_t *end, gss_buffer_desc *arg)
+{
+ int len = arg->length;
+ if (xdr_write_u32(ptr, end, len))
+ return -1;
+ return write_bytes((char **)ptr, (char *)end, arg->value,
+ (arg->length + 3) & ~3);
+}
+
+#endif /* _WRITE_BYTES_H_ */
diff --git a/utils/idmapd/Makefile.am b/utils/idmapd/Makefile.am
new file mode 100644
index 0000000..e09e8c5
--- /dev/null
+++ b/utils/idmapd/Makefile.am
@@ -0,0 +1,65 @@
+## Process this file with automake to produce Makefile.in
+
+man8_MANS = idmapd.man
+
+AM_CPPFLAGS += -I ../../support/nfsidmap
+
+RPCPREFIX = rpc.
+KPREFIX = @kprefix@
+sbin_PROGRAMS = idmapd
+
+EXTRA_DIST = \
+ $(man8_MANS)
+
+idmapd_SOURCES = \
+ idmapd.c \
+ \
+ nfs_idmap.h \
+ queue.h
+
+idmapd_LDADD = ../../support/nfs/libnfs.la \
+ ../../support/nfsidmap/libnfsidmap.la \
+ $(LIBEVENT)
+
+MAINTAINERCLEANFILES = Makefile.in
+
+#######################################################################
+# The following allows the current practice of having
+# daemons renamed during the install to include RPCPREFIX
+# and the KPREFIX
+# This could all be done much easier with program_transform_name
+# ( program_transform_name = s/^/$(RPCPREFIX)$(KPREFIX)/ )
+# but that also renames the man pages, which the current
+# practice does not do.
+install-exec-hook:
+ (cd $(DESTDIR)$(sbindir) && \
+ for p in $(sbin_PROGRAMS); do \
+ mv -f $$p$(EXEEXT) $(RPCPREFIX)$(KPREFIX)$$p$(EXEEXT) ;\
+ done)
+uninstall-hook:
+ (cd $(DESTDIR)$(sbindir) && \
+ for p in $(sbin_PROGRAMS); do \
+ rm -f $(RPCPREFIX)$(KPREFIX)$$p$(EXEEXT) ;\
+ done)
+
+
+# XXX This makes some assumptions about what automake does.
+# XXX But there is no install-man-hook or install-man-local.
+install-man: install-man8 install-man-links
+uninstall-man: uninstall-man8 uninstall-man-links
+
+install-man-links:
+ (cd $(DESTDIR)$(man8dir) && \
+ for m in $(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS); do \
+ inst=`echo $$m | sed -e 's/man$$/8/'`; \
+ rm -f $(RPCPREFIX)$$inst ; \
+ $(LN_S) $$inst $(RPCPREFIX)$$inst ; \
+ done)
+
+uninstall-man-links:
+ (cd $(DESTDIR)$(man8dir) && \
+ for m in $(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS); do \
+ inst=`echo $$m | sed -e 's/man$$/8/'`; \
+ rm -f $(RPCPREFIX)$$inst ; \
+ done)
+
diff --git a/utils/idmapd/Makefile.in b/utils/idmapd/Makefile.in
new file mode 100644
index 0000000..e81f630
--- /dev/null
+++ b/utils/idmapd/Makefile.in
@@ -0,0 +1,859 @@
+# 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@
+sbin_PROGRAMS = idmapd$(EXEEXT)
+subdir = utils/idmapd
+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 =
+am__installdirs = "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man8dir)"
+PROGRAMS = $(sbin_PROGRAMS)
+am_idmapd_OBJECTS = idmapd.$(OBJEXT)
+idmapd_OBJECTS = $(am_idmapd_OBJECTS)
+am__DEPENDENCIES_1 =
+idmapd_DEPENDENCIES = ../../support/nfs/libnfs.la \
+ ../../support/nfsidmap/libnfsidmap.la $(am__DEPENDENCIES_1)
+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 =
+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)/idmapd.Po
+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 = $(idmapd_SOURCES)
+DIST_SOURCES = $(idmapd_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+man8dir = $(mandir)/man8
+NROFF = nroff
+MANS = $(man8_MANS)
+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@ -I ../../support/nfsidmap
+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@
+man8_MANS = idmapd.man
+RPCPREFIX = rpc.
+KPREFIX = @kprefix@
+EXTRA_DIST = \
+ $(man8_MANS)
+
+idmapd_SOURCES = \
+ idmapd.c \
+ \
+ nfs_idmap.h \
+ queue.h
+
+idmapd_LDADD = ../../support/nfs/libnfs.la \
+ ../../support/nfsidmap/libnfsidmap.la \
+ $(LIBEVENT)
+
+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 utils/idmapd/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu utils/idmapd/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):
+install-sbinPROGRAMS: $(sbin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(sbindir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-sbinPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(sbindir)" && rm -f $$files
+
+clean-sbinPROGRAMS:
+ @list='$(sbin_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+idmapd$(EXEEXT): $(idmapd_OBJECTS) $(idmapd_DEPENDENCIES) $(EXTRA_idmapd_DEPENDENCIES)
+ @rm -f idmapd$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(idmapd_OBJECTS) $(idmapd_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/idmapd.Po@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 $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-man8: $(man8_MANS)
+ @$(NORMAL_INSTALL)
+ @list1='$(man8_MANS)'; \
+ list2=''; \
+ test -n "$(man8dir)" \
+ && test -n "`echo $$list1$$list2`" \
+ || exit 0; \
+ echo " $(MKDIR_P) '$(DESTDIR)$(man8dir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(man8dir)" || exit 1; \
+ { for i in $$list1; do echo "$$i"; done; \
+ if test -n "$$list2"; then \
+ for i in $$list2; do echo "$$i"; done \
+ | sed -n '/\.8[a-z]*$$/p'; \
+ fi; \
+ } | while read p; do \
+ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; echo "$$p"; \
+ done | \
+ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \
+ sed 'N;N;s,\n, ,g' | { \
+ list=; while read file base inst; do \
+ if test "$$base" = "$$inst"; then list="$$list $$file"; else \
+ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man8dir)/$$inst'"; \
+ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man8dir)/$$inst" || exit $$?; \
+ fi; \
+ done; \
+ for i in $$list; do echo "$$i"; done | $(am__base_list) | \
+ while read files; do \
+ test -z "$$files" || { \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man8dir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(man8dir)" || exit $$?; }; \
+ done; }
+
+uninstall-man8:
+ @$(NORMAL_UNINSTALL)
+ @list='$(man8_MANS)'; test -n "$(man8dir)" || exit 0; \
+ files=`{ for i in $$list; do echo "$$i"; done; \
+ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
+ dir='$(DESTDIR)$(man8dir)'; $(am__uninstall_files_from_dir)
+
+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 $(PROGRAMS) $(MANS)
+installdirs:
+ for dir in "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man8dir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+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-sbinPROGRAMS \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/idmapd.Po
+ -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-man
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-sbinPROGRAMS
+ @$(NORMAL_INSTALL)
+ $(MAKE) $(AM_MAKEFLAGS) install-exec-hook
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+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)/idmapd.Po
+ -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: uninstall-man uninstall-sbinPROGRAMS
+ @$(NORMAL_INSTALL)
+ $(MAKE) $(AM_MAKEFLAGS) uninstall-hook
+.MAKE: install-am install-exec-am install-strip uninstall-am
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-sbinPROGRAMS 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-exec-hook install-html install-html-am \
+ install-info install-info-am install-man install-man8 \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-sbinPROGRAMS 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 uninstall-hook \
+ uninstall-man uninstall-man8 uninstall-sbinPROGRAMS
+
+.PRECIOUS: Makefile
+
+
+#######################################################################
+# The following allows the current practice of having
+# daemons renamed during the install to include RPCPREFIX
+# and the KPREFIX
+# This could all be done much easier with program_transform_name
+# ( program_transform_name = s/^/$(RPCPREFIX)$(KPREFIX)/ )
+# but that also renames the man pages, which the current
+# practice does not do.
+install-exec-hook:
+ (cd $(DESTDIR)$(sbindir) && \
+ for p in $(sbin_PROGRAMS); do \
+ mv -f $$p$(EXEEXT) $(RPCPREFIX)$(KPREFIX)$$p$(EXEEXT) ;\
+ done)
+uninstall-hook:
+ (cd $(DESTDIR)$(sbindir) && \
+ for p in $(sbin_PROGRAMS); do \
+ rm -f $(RPCPREFIX)$(KPREFIX)$$p$(EXEEXT) ;\
+ done)
+
+# XXX This makes some assumptions about what automake does.
+# XXX But there is no install-man-hook or install-man-local.
+install-man: install-man8 install-man-links
+uninstall-man: uninstall-man8 uninstall-man-links
+
+install-man-links:
+ (cd $(DESTDIR)$(man8dir) && \
+ for m in $(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS); do \
+ inst=`echo $$m | sed -e 's/man$$/8/'`; \
+ rm -f $(RPCPREFIX)$$inst ; \
+ $(LN_S) $$inst $(RPCPREFIX)$$inst ; \
+ done)
+
+uninstall-man-links:
+ (cd $(DESTDIR)$(man8dir) && \
+ for m in $(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS); do \
+ inst=`echo $$m | sed -e 's/man$$/8/'`; \
+ rm -f $(RPCPREFIX)$$inst ; \
+ done)
+
+# 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/utils/idmapd/idmapd.c b/utils/idmapd/idmapd.c
new file mode 100644
index 0000000..cd9a965
--- /dev/null
+++ b/utils/idmapd/idmapd.c
@@ -0,0 +1,1091 @@
+/*
+ * idmapd.c
+ *
+ * Userland daemon for idmap.
+ *
+ * Copyright (c) 2002 The Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Marius Aamodt Eriksen <marius@umich.edu>
+ *
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/inotify.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <time.h>
+
+#include "nfs_idmap.h"
+
+#include <err.h>
+#include <errno.h>
+#include <event2/event.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <pwd.h>
+#include <grp.h>
+#include <limits.h>
+#include <ctype.h>
+#include <libgen.h>
+#include <nfsidmap.h>
+
+#include "xlog.h"
+#include "conffile.h"
+#include "queue.h"
+#include "nfslib.h"
+
+#ifndef PIPEFS_DIR
+#define PIPEFS_DIR NFS_STATEDIR "/rpc_pipefs/"
+#endif
+
+#ifndef NFSD_DIR
+#define NFSD_DIR "/proc/net/rpc"
+#endif
+
+#ifndef CLIENT_CACHE_TIMEOUT_FILE
+#define CLIENT_CACHE_TIMEOUT_FILE "/proc/sys/fs/nfs/idmap_cache_timeout"
+#endif
+
+#ifndef NFS4NOBODY_USER
+#define NFS4NOBODY_USER "nobody"
+#endif
+
+#ifndef NFS4NOBODY_GROUP
+#define NFS4NOBODY_GROUP "nobody"
+#endif
+
+/* From Niels */
+#define CONF_SAVE(w, f) do { \
+ char *p = f; \
+ if (p != NULL) \
+ (w) = p; \
+} while (0)
+
+#define IC_IDNAME 0
+#define IC_IDNAME_CHAN NFSD_DIR "/nfs4.idtoname/channel"
+#define IC_IDNAME_FLUSH NFSD_DIR "/nfs4.idtoname/flush"
+
+#define IC_NAMEID 1
+#define IC_NAMEID_CHAN NFSD_DIR "/nfs4.nametoid/channel"
+#define IC_NAMEID_FLUSH NFSD_DIR "/nfs4.nametoid/flush"
+
+struct idmap_client {
+ short ic_which;
+ char ic_clid[30];
+ char *ic_id;
+ char ic_path[PATH_MAX];
+ int ic_fd;
+ int ic_dirfd;
+ int ic_scanned;
+ struct event *ic_event;
+ TAILQ_ENTRY(idmap_client) ic_next;
+};
+static struct idmap_client nfsd_ic[2] = {
+{
+ .ic_which = IC_IDNAME,
+ .ic_clid = "",
+ .ic_id = "Server",
+ .ic_path = IC_IDNAME_CHAN,
+ .ic_fd = -1,
+ .ic_dirfd = -1,
+ .ic_scanned = 0
+},
+{
+ .ic_which = IC_NAMEID,
+ .ic_clid = "",
+ .ic_id = "Server",
+ .ic_path = IC_NAMEID_CHAN,
+ .ic_fd = -1,
+ .ic_dirfd = -1,
+ .ic_scanned = 0
+},
+};
+
+TAILQ_HEAD(idmap_clientq, idmap_client);
+
+static void dirscancb(int, short, void *);
+static void clntscancb(int, short, void *);
+static void svrreopen(int, short, void *);
+static int nfsopen(struct idmap_client *);
+static void nfscb(int, short, void *);
+static void nfsdcb(int, short, void *);
+static int addfield(char **, ssize_t *, char *);
+static int getfield(char **, char *, size_t);
+
+static void imconv(struct idmap_client *, struct idmap_msg *);
+static void idtonameres(struct idmap_msg *);
+static void nametoidres(struct idmap_msg *);
+
+static int nfsdopen(void);
+static void nfsdclose(void);
+static int nfsdopenone(struct idmap_client *);
+static void nfsdreopen_one(struct idmap_client *);
+static void nfsdreopen(void);
+
+static int verbose = 0;
+#define DEFAULT_IDMAP_CACHE_EXPIRY 600 /* seconds */
+static int cache_entry_expiration = 0;
+static char pipefsdir[PATH_MAX];
+static char *nobodyuser, *nobodygroup;
+static uid_t nobodyuid;
+static gid_t nobodygid;
+static struct event_base *evbase = NULL;
+static bool signal_received = false;
+static int inotify_fd = -1;
+
+static void
+sig_die(int signal)
+{
+ if (signal_received) {
+ xlog_warn("forced exiting on signal %d\n", signal);
+ exit(0);
+ }
+
+ signal_received = true;
+ xlog_warn("exiting on signal %d\n", signal);
+ event_base_loopexit(evbase, NULL);
+}
+
+static int
+flush_nfsd_cache(char *path, time_t now)
+{
+ int fd;
+ char stime[32];
+
+ sprintf(stime, "%" PRId64 "\n", (int64_t)now);
+ fd = open(path, O_RDWR);
+ if (fd == -1)
+ return -1;
+ if (write(fd, stime, strlen(stime)) != (ssize_t)strlen(stime)) {
+ errx(1, "Flushing nfsd cache failed: errno %d (%s)",
+ errno, strerror(errno));
+ }
+ close(fd);
+ return 0;
+}
+
+static int
+flush_nfsd_idmap_cache(void)
+{
+ time_t now = time(NULL);
+ int ret;
+
+ ret = flush_nfsd_cache(IC_IDNAME_FLUSH, now);
+ if (ret)
+ return ret;
+ ret = flush_nfsd_cache(IC_NAMEID_FLUSH, now);
+ return ret;
+}
+
+static void usage(char *progname)
+{
+ fprintf(stderr, "Usage: %s [-hfvCS] [-p path] [-c path]\n",
+ basename(progname));
+}
+
+int
+main(int argc, char **argv)
+{
+ int wd = -1, opt, fg = 0, nfsdret = -1;
+ struct idmap_clientq icq;
+ struct event *rootdirev = NULL, *clntdirev = NULL,
+ *svrdirev = NULL, *inotifyev = NULL;
+ struct event *initialize = NULL;
+ struct passwd *pw;
+ struct group *gr;
+ struct stat sb;
+ char *xpipefsdir = NULL;
+ int serverstart = 1, clientstart = 1;
+ int ret;
+ char *progname;
+ char *conf_path = NULL;
+
+ nobodyuser = NFS4NOBODY_USER;
+ nobodygroup = NFS4NOBODY_GROUP;
+ strlcpy(pipefsdir, PIPEFS_DIR, sizeof(pipefsdir));
+
+ if ((progname = strrchr(argv[0], '/')))
+ progname++;
+ else
+ progname = argv[0];
+ xlog_open(progname);
+
+#define GETOPTSTR "hvfd:p:U:G:c:CS"
+ opterr=0; /* Turn off error messages */
+ while ((opt = getopt(argc, argv, GETOPTSTR)) != -1) {
+ if (opt == 'c') {
+ warnx("-c is deprecated and may be removed in the "
+ "future. See idmapd(8).");
+ conf_path = optarg;
+ }
+ if (opt == '?') {
+ if (strchr(GETOPTSTR, optopt))
+ warnx("'-%c' option requires an argument.", optopt);
+ else
+ warnx("'-%c' is an invalid argument.", optopt);
+ usage(progname);
+ exit(1);
+ }
+ }
+ optind = 1;
+
+ if (conf_path) { /* deprecated -c option was specified */
+ if (stat(conf_path, &sb) == -1 && (errno == ENOENT || errno == EACCES)) {
+ warn("Skipping configuration file \"%s\"", conf_path);
+ conf_path = NULL;
+ } else {
+ conf_init_file(conf_path);
+ verbose = conf_get_num("General", "Verbosity", 0);
+ cache_entry_expiration = conf_get_num("General",
+ "Cache-Expiration", DEFAULT_IDMAP_CACHE_EXPIRY);
+ CONF_SAVE(xpipefsdir, conf_get_str("General", "Pipefs-Directory"));
+ if (xpipefsdir != NULL)
+ strlcpy(pipefsdir, xpipefsdir, sizeof(pipefsdir));
+ CONF_SAVE(nobodyuser, conf_get_str("Mapping", "Nobody-User"));
+ CONF_SAVE(nobodygroup, conf_get_str("Mapping", "Nobody-Group"));
+ if (conf_get_bool("General", "server-only", false))
+ clientstart = 0;
+ if (conf_get_bool("General", "client-only", false))
+ serverstart = 0;
+ }
+ } else {
+ conf_path = NFS_CONFFILE;
+ conf_init_file(conf_path);
+ CONF_SAVE(xpipefsdir, conf_get_str("General", "Pipefs-Directory"));
+ if (xpipefsdir != NULL)
+ strlcpy(pipefsdir, xpipefsdir, sizeof(pipefsdir));
+
+ conf_path = _PATH_IDMAPDCONF;
+ conf_init_file(conf_path);
+ verbose = conf_get_num("General", "Verbosity", 0);
+ cache_entry_expiration = conf_get_num("General",
+ "cache-expiration", DEFAULT_IDMAP_CACHE_EXPIRY);
+ CONF_SAVE(nobodyuser, conf_get_str("Mapping", "Nobody-User"));
+ CONF_SAVE(nobodygroup, conf_get_str("Mapping", "Nobody-Group"));
+ if (conf_get_bool("General", "server-only", false))
+ clientstart = 0;
+ if (conf_get_bool("General", "client-only", false))
+ serverstart = 0;
+ }
+
+ while ((opt = getopt(argc, argv, GETOPTSTR)) != -1)
+ switch (opt) {
+ case 'v':
+ verbose++;
+ break;
+ case 'f':
+ fg = 1;
+ break;
+ case 'p':
+ strlcpy(pipefsdir, optarg, sizeof(pipefsdir));
+ break;
+ case 'd':
+ case 'U':
+ case 'G':
+ errx(1, "the -d, -U, and -G options have been removed;"
+ " please use the configuration file instead.");
+ case 'C':
+ serverstart = 0;
+ break;
+ case 'S':
+ clientstart = 0;
+ break;
+ case 'h':
+ usage(progname);
+ exit(0);
+ default:
+ break;
+ }
+
+ if (!serverstart && !clientstart)
+ errx(1, "it is illegal to specify both -C and -S");
+
+ strncat(pipefsdir, "/nfs", sizeof(pipefsdir)-1);
+
+ daemon_init(fg);
+
+ if ((pw = getpwnam(nobodyuser)) == NULL)
+ errx(1, "Could not find user \"%s\"", nobodyuser);
+ nobodyuid = pw->pw_uid;
+
+ if ((gr = getgrnam(nobodygroup)) == NULL)
+ errx(1, "Could not find group \"%s\"", nobodygroup);
+ nobodygid = gr->gr_gid;
+
+#ifdef HAVE_NFS4_SET_DEBUG
+ nfs4_set_debug(verbose, xlog_warn);
+#endif
+ if (conf_path == NULL)
+ conf_path = _PATH_IDMAPDCONF;
+ if (nfs4_init_name_mapping(conf_path))
+ errx(1, "Unable to create name to user id mappings.");
+
+ evbase = event_base_new();
+ if (evbase == NULL)
+ errx(1, "Failed to create event base.");
+
+ if (verbose > 1)
+ xlog_warn("Expiration time is %d seconds.",
+ cache_entry_expiration);
+ if (serverstart) {
+ nfsdret = nfsdopen();
+ if (nfsdret == 0) {
+ ret = flush_nfsd_idmap_cache();
+ if (ret)
+ xlog_err("main: Failed to flush nfsd idmap cache\n: %s", strerror(errno));
+ }
+ }
+
+ if (clientstart) {
+ struct timeval now = {
+ .tv_sec = 0,
+ .tv_usec = 0,
+ };
+
+ if (cache_entry_expiration != DEFAULT_IDMAP_CACHE_EXPIRY) {
+ int timeout_fd, len;
+ char timeout_buf[12];
+ if ((timeout_fd = open(CLIENT_CACHE_TIMEOUT_FILE,
+ O_RDWR)) == -1) {
+ xlog_warn("Unable to open '%s' to set "
+ "client cache expiration time "
+ "to %d seconds\n",
+ CLIENT_CACHE_TIMEOUT_FILE,
+ cache_entry_expiration);
+ } else {
+ len = snprintf(timeout_buf, sizeof(timeout_buf),
+ "%d", cache_entry_expiration);
+ if ((write(timeout_fd, timeout_buf, len)) != len)
+ xlog_warn("Error writing '%s' to "
+ "'%s' to set client "
+ "cache expiration time\n",
+ timeout_buf,
+ CLIENT_CACHE_TIMEOUT_FILE);
+ close(timeout_fd);
+ }
+ }
+
+ inotify_fd = inotify_init1(IN_NONBLOCK);
+ if (inotify_fd == -1) {
+ xlog_err("Unable to initialise inotify_init1: %s\n", strerror(errno));
+ } else {
+ wd = inotify_add_watch(inotify_fd, pipefsdir, IN_CREATE | IN_DELETE);
+ if (wd < 0)
+ xlog_err("Unable to inotify_add_watch(%s): %s\n", pipefsdir, strerror(errno));
+ }
+
+ TAILQ_INIT(&icq);
+
+ signal(SIGINT, sig_die);
+ signal(SIGTERM, sig_die);
+
+ /* These events are persistent */
+ rootdirev = evsignal_new(evbase, SIGUSR1, dirscancb, &icq);
+ if (rootdirev == NULL)
+ errx(1, "Failed to create SIGUSR1 event.");
+ evsignal_add(rootdirev, NULL);
+ clntdirev = evsignal_new(evbase, SIGUSR2, clntscancb, &icq);
+ if (clntdirev == NULL)
+ errx(1, "Failed to create SIGUSR2 event.");
+ evsignal_add(clntdirev, NULL);
+ svrdirev = evsignal_new(evbase, SIGHUP, svrreopen, NULL);
+ if (svrdirev == NULL)
+ errx(1, "Failed to create SIGHUP event.");
+ evsignal_add(svrdirev, NULL);
+ if ( wd >= 0) {
+ inotifyev = event_new(evbase, inotify_fd,
+ EV_READ | EV_PERSIST, dirscancb, &icq);
+ if (inotifyev == NULL)
+ errx(1, "Failed to create inotify read event.");
+ event_add(inotifyev, NULL);
+ }
+
+ /* Fetch current state */
+ /* (Delay till start of event_dispatch to avoid possibly losing
+ * a SIGUSR1 between here and the call to event_dispatch().) */
+ initialize = evtimer_new(evbase, dirscancb, &icq);
+ if (initialize == NULL)
+ errx(1, "Failed to create initialize event.");
+ evtimer_add(initialize, &now);
+ }
+
+ if (nfsdret != 0 && wd < 0)
+ xlog_err("main: Neither NFS client nor NFSd found");
+
+ daemon_ready();
+
+ if (event_base_dispatch(evbase) < 0)
+ xlog_err("main: event_dispatch returns errno %d (%s)",
+ errno, strerror(errno));
+
+ nfs4_term_name_mapping();
+ nfsdclose();
+
+ if (inotifyev)
+ event_free(inotifyev);
+ if (inotify_fd != -1)
+ close(inotify_fd);
+
+ if (initialize)
+ event_free(initialize);
+ if (rootdirev)
+ event_free(rootdirev);
+ if (clntdirev)
+ event_free(clntdirev);
+ if (svrdirev)
+ event_free(svrdirev);
+ event_base_free(evbase);
+
+ return 0;
+}
+
+static void
+flush_inotify(int fd)
+{
+ while (true) {
+ char buf[4096] __attribute__ ((aligned(__alignof__(struct inotify_event))));
+ const struct inotify_event *ev;
+ ssize_t len;
+ char *ptr;
+
+ len = read(fd, buf, sizeof(buf));
+ if (len == -1 && errno == EINTR)
+ continue;
+
+ if (len <= 0)
+ break;
+
+ for (ptr = buf; ptr < buf + len;
+ ptr += sizeof(struct inotify_event) + ev->len) {
+
+ ev = (const struct inotify_event *)ptr;
+ if (verbose > 2)
+ xlog_warn("pipefs inotify: wd=%i, mask=0x%08x, len=%i, name=%s",
+ ev->wd, ev->mask, ev->len, ev->len ? ev->name : "");
+ }
+ }
+}
+
+static void
+dirscancb(int fd, short UNUSED(which), void *data)
+{
+ int nent, i;
+ struct dirent **ents;
+ struct idmap_client *ic, *nextic;
+ char path[PATH_MAX+256]; /* + sizeof(d_name) */
+ struct idmap_clientq *icq = data;
+
+ if (fd != -1)
+ flush_inotify(fd);
+
+ TAILQ_FOREACH(ic, icq, ic_next) {
+ ic->ic_scanned = 0;
+ }
+
+ nent = scandir(pipefsdir, &ents, NULL, alphasort);
+ if (nent == -1) {
+ xlog_warn("dirscancb: scandir(%s): %s", pipefsdir, strerror(errno));
+ return;
+ }
+
+ for (i = 0; i < nent; i++) {
+ if (ents[i]->d_reclen > 4 &&
+ strncmp(ents[i]->d_name, "clnt", 4) == 0) {
+ TAILQ_FOREACH(ic, icq, ic_next)
+ if (strcmp(ents[i]->d_name + 4, ic->ic_clid) == 0)
+ break;
+ if (ic != NULL)
+ goto next;
+
+ if ((ic = calloc(1, sizeof(*ic))) == NULL)
+ goto out;
+ strlcpy(ic->ic_clid, ents[i]->d_name + 4,
+ sizeof(ic->ic_clid));
+ path[0] = '\0';
+ snprintf(path, sizeof(path), "%s/%s",
+ pipefsdir, ents[i]->d_name);
+
+ if ((ic->ic_dirfd = open(path, O_RDONLY, 0)) == -1) {
+ if (verbose > 0)
+ xlog_warn("dirscancb: open(%s): %s", path, strerror(errno));
+ free(ic);
+ goto out;
+ }
+
+ strlcat(path, "/idmap", sizeof(path));
+ strlcpy(ic->ic_path, path, sizeof(ic->ic_path));
+
+ if (nfsopen(ic) == -1) {
+ close(ic->ic_dirfd);
+ free(ic);
+ goto out;
+ }
+
+ if (verbose > 2)
+ xlog_warn("New client: %s", ic->ic_clid);
+
+ ic->ic_id = "Client";
+
+ TAILQ_INSERT_TAIL(icq, ic, ic_next);
+
+ next:
+ ic->ic_scanned = 1;
+ }
+ }
+
+ ic = TAILQ_FIRST(icq);
+ while(ic != NULL) {
+ nextic=TAILQ_NEXT(ic, ic_next);
+ if (!ic->ic_scanned) {
+ if (ic->ic_event)
+ event_free(ic->ic_event);
+ if (ic->ic_fd != -1)
+ close(ic->ic_fd);
+ if (ic->ic_dirfd != -1)
+ close(ic->ic_dirfd);
+ TAILQ_REMOVE(icq, ic, ic_next);
+ if (verbose > 2) {
+ xlog_warn("Stale client: %s", ic->ic_clid);
+ xlog_warn("\t-> closed %s", ic->ic_path);
+ }
+ free(ic);
+ }
+ ic = nextic;
+ }
+
+out:
+ for (i = 0; i < nent; i++)
+ free(ents[i]);
+ free(ents);
+ return;
+}
+
+static void
+svrreopen(int UNUSED(fd), short UNUSED(which), void *UNUSED(data))
+{
+ nfsdreopen();
+}
+
+static void
+clntscancb(int UNUSED(fd), short UNUSED(which), void *data)
+{
+ struct idmap_clientq *icq = data;
+ struct idmap_client *ic, *ic_next;
+
+ for (ic = TAILQ_FIRST(icq); ic != NULL; ic = ic_next) {
+ ic_next = TAILQ_NEXT(ic, ic_next);
+ if (ic->ic_fd == -1 && nfsopen(ic) == -1) {
+ close(ic->ic_dirfd);
+ TAILQ_REMOVE(icq, ic, ic_next);
+ free(ic);
+ }
+ }
+}
+
+static void
+nfsdcb(int UNUSED(fd), short which, void *data)
+{
+ struct idmap_client *ic = data;
+ struct idmap_msg im;
+ u_char buf[IDMAP_MAXMSGSZ + 1];
+ ssize_t len;
+ ssize_t bsiz;
+ char *bp, typebuf[IDMAP_MAXMSGSZ],
+ buf1[IDMAP_MAXMSGSZ], authbuf[IDMAP_MAXMSGSZ], *p;
+ unsigned long tmp;
+
+ if (which != EV_READ)
+ return;
+
+ len = read(ic->ic_fd, buf, sizeof(buf));
+ if (len == 0)
+ /* No upcall to read; not necessarily a problem: */
+ return;
+ if (len < 0) {
+ xlog_warn("nfsdcb: read(%s) failed: errno %d (%s)",
+ ic->ic_path, errno,
+ strerror(errno));
+ nfsdreopen_one(ic);
+ return;
+ }
+
+ /* Get rid of newline and terminate buffer*/
+ buf[len - 1] = '\0';
+ bp = (char *)buf;
+
+ memset(&im, 0, sizeof(im));
+
+ /* Authentication name -- ignored for now*/
+ if (getfield(&bp, authbuf, sizeof(authbuf)) == -1) {
+ xlog_warn("nfsdcb: bad authentication name in upcall\n");
+ return;
+ }
+ if (getfield(&bp, typebuf, sizeof(typebuf)) == -1) {
+ xlog_warn("nfsdcb: bad type in upcall\n");
+ return;
+ }
+ if (verbose > 2)
+ xlog_warn("nfsdcb: authbuf=%s authtype=%s",
+ authbuf, typebuf);
+
+ im.im_type = strcmp(typebuf, "user") == 0 ?
+ IDMAP_TYPE_USER : IDMAP_TYPE_GROUP;
+
+ switch (ic->ic_which) {
+ case IC_NAMEID:
+ im.im_conv = IDMAP_CONV_NAMETOID;
+ if (getfield(&bp, im.im_name, sizeof(im.im_name)) == -1) {
+ xlog_warn("nfsdcb: bad name in upcall\n");
+ return;
+ }
+ break;
+ case IC_IDNAME:
+ im.im_conv = IDMAP_CONV_IDTONAME;
+ if (getfield(&bp, buf1, sizeof(buf1)) == -1) {
+ xlog_warn("nfsdcb: bad id in upcall\n");
+ return;
+ }
+ tmp = strtoul(buf1, (char **)NULL, 10);
+ im.im_id = (u_int32_t)tmp;
+ if ((tmp == ULONG_MAX && errno == ERANGE)
+ || (unsigned long)im.im_id != tmp) {
+ xlog_warn("nfsdcb: id '%s' too big!\n", buf1);
+ return;
+ }
+ break;
+ default:
+ xlog_warn("nfsdcb: Unknown which type %d", ic->ic_which);
+ return;
+ }
+
+ imconv(ic, &im);
+
+ buf[0] = '\0';
+ bp = (char *)buf;
+ bsiz = sizeof(buf);
+
+ /* Authentication name */
+ addfield(&bp, &bsiz, authbuf);
+
+ switch (ic->ic_which) {
+ case IC_NAMEID:
+ /* Type */
+ p = im.im_type == IDMAP_TYPE_USER ? "user" : "group";
+ addfield(&bp, &bsiz, p);
+ /* Name */
+ addfield(&bp, &bsiz, im.im_name);
+ /* expiry */
+ snprintf(buf1, sizeof(buf1), "%" PRId64,
+ (int64_t)time(NULL) + cache_entry_expiration);
+ addfield(&bp, &bsiz, buf1);
+ /* Note that we don't want to write the id if the mapping
+ * failed; instead, by leaving it off, we write a negative
+ * cache entry which will result in an error returned to
+ * the client. We don't want a chown or setacl referring
+ * to an unknown user to result in giving permissions to
+ * "nobody"! */
+ if (im.im_status == IDMAP_STATUS_SUCCESS) {
+ /* ID */
+ snprintf(buf1, sizeof(buf1), "%u", im.im_id);
+ addfield(&bp, &bsiz, buf1);
+
+ }
+ //if (bsiz == sizeof(buf)) /* XXX */
+
+ bp[-1] = '\n';
+
+ break;
+ case IC_IDNAME:
+ /* Type */
+ p = im.im_type == IDMAP_TYPE_USER ? "user" : "group";
+ addfield(&bp, &bsiz, p);
+ /* ID */
+ snprintf(buf1, sizeof(buf1), "%u", im.im_id);
+ addfield(&bp, &bsiz, buf1);
+ /* expiry */
+ snprintf(buf1, sizeof(buf1), "%" PRId64,
+ (int64_t)time(NULL) + cache_entry_expiration);
+ addfield(&bp, &bsiz, buf1);
+ /* Note we're ignoring the status field in this case; we'll
+ * just map to nobody instead. */
+ /* Name */
+ addfield(&bp, &bsiz, im.im_name);
+
+ bp[-1] = '\n';
+
+ break;
+ default:
+ xlog_warn("nfsdcb: Unknown which type %d", ic->ic_which);
+ return;
+ }
+
+ bsiz = sizeof(buf) - bsiz;
+
+ if (atomicio((void*)write, ic->ic_fd, buf, bsiz) != bsiz)
+ xlog_warn("nfsdcb: write(%s) failed: errno %d (%s)",
+ ic->ic_path, errno, strerror(errno));
+}
+
+static void
+imconv(struct idmap_client *ic, struct idmap_msg *im)
+{
+ u_int32_t len;
+
+ switch (im->im_conv) {
+ case IDMAP_CONV_IDTONAME:
+ idtonameres(im);
+ if (verbose > 1)
+ xlog_warn("%s %s: (%s) id \"%d\" -> name \"%s\"",
+ ic->ic_id, ic->ic_clid,
+ im->im_type == IDMAP_TYPE_USER ? "user" : "group",
+ im->im_id, im->im_name);
+ break;
+ case IDMAP_CONV_NAMETOID:
+ len = strnlen(im->im_name, IDMAP_NAMESZ - 1);
+ /* Check for NULL termination just to be careful */
+ if (im->im_name[len+1] != '\0')
+ return;
+ nametoidres(im);
+ if (verbose > 1)
+ xlog_warn("%s %s: (%s) name \"%s\" -> id \"%d\"",
+ ic->ic_id, ic->ic_clid,
+ im->im_type == IDMAP_TYPE_USER ? "user" : "group",
+ im->im_name, im->im_id);
+ break;
+ default:
+ xlog_warn("imconv: Invalid conversion type (%d) in message",
+ im->im_conv);
+ im->im_status |= IDMAP_STATUS_INVALIDMSG;
+ break;
+ }
+}
+
+static void
+nfscb(int UNUSED(fd), short which, void *data)
+{
+ struct idmap_client *ic = data;
+ struct idmap_msg im;
+
+ if (which != EV_READ)
+ return;
+
+ if (atomicio(read, ic->ic_fd, &im, sizeof(im)) != sizeof(im)) {
+ if (verbose > 0)
+ xlog_warn("nfscb: read(%s): %s", ic->ic_path, strerror(errno));
+ return;
+ }
+
+ imconv(ic, &im);
+
+ /* XXX: I don't like ignoring this error in the id->name case,
+ * but we've never returned it, and I need to check that the client
+ * can handle it gracefully before starting to return it now. */
+
+ if (im.im_status == IDMAP_STATUS_LOOKUPFAIL)
+ im.im_status = IDMAP_STATUS_SUCCESS;
+
+ if (atomicio((void*)write, ic->ic_fd, &im, sizeof(im)) != sizeof(im))
+ xlog_warn("nfscb: write(%s): %s", ic->ic_path, strerror(errno));
+}
+
+static void
+nfsdclose_one(struct idmap_client *ic)
+{
+ if (ic->ic_event) {
+ event_free(ic->ic_event);
+ ic->ic_event = NULL;
+ }
+ if (ic->ic_fd != -1) {
+ close(ic->ic_fd);
+ ic->ic_fd = -1;
+ }
+}
+
+static void
+nfsdreopen_one(struct idmap_client *ic)
+{
+ int fd;
+
+ if (verbose > 2)
+ xlog_warn("ReOpening %s", ic->ic_path);
+
+ if ((fd = open(ic->ic_path, O_RDWR, 0)) != -1) {
+ nfsdclose_one(ic);
+
+ ic->ic_fd = fd;
+ ic->ic_event = event_new(evbase, ic->ic_fd, EV_READ | EV_PERSIST, nfsdcb, ic);
+ if (ic->ic_event == NULL) {
+ xlog_warn("nfsdreopen: Failed to create event for '%s'",
+ ic->ic_path);
+ close(ic->ic_fd);
+ ic->ic_fd = -1;
+ return;
+ }
+ event_add(ic->ic_event, NULL);
+ } else {
+ xlog_warn("nfsdreopen: Opening '%s' failed: errno %d (%s)",
+ ic->ic_path, errno, strerror(errno));
+ }
+}
+
+static void
+nfsdreopen(void)
+{
+ nfsdreopen_one(&nfsd_ic[IC_NAMEID]);
+ nfsdreopen_one(&nfsd_ic[IC_IDNAME]);
+ return;
+}
+
+static int
+nfsdopen(void)
+{
+ return ((nfsdopenone(&nfsd_ic[IC_NAMEID]) == 0 &&
+ nfsdopenone(&nfsd_ic[IC_IDNAME]) == 0) ? 0 : -1);
+}
+
+static void
+nfsdclose(void)
+{
+ nfsdclose_one(&nfsd_ic[IC_NAMEID]);
+ nfsdclose_one(&nfsd_ic[IC_IDNAME]);
+}
+
+static int
+nfsdopenone(struct idmap_client *ic)
+{
+ if ((ic->ic_fd = open(ic->ic_path, O_RDWR, 0)) == -1) {
+ if (verbose > 0)
+ xlog_warn("nfsdopenone: Opening %s failed: "
+ "errno %d (%s)",
+ ic->ic_path, errno, strerror(errno));
+ return (-1);
+ }
+
+ ic->ic_event = event_new(evbase, ic->ic_fd, EV_READ | EV_PERSIST, nfsdcb, ic);
+ if (ic->ic_event == NULL) {
+ if (verbose > 0)
+ xlog_warn("nfsdopenone: Create event for %s failed",
+ ic->ic_path);
+ close(ic->ic_fd);
+ ic->ic_fd = -1;
+ return (-1);
+ }
+ event_add(ic->ic_event, NULL);
+
+ if (verbose > 2)
+ xlog_warn("Opened %s", ic->ic_path);
+
+ return (0);
+}
+
+static int
+nfsopen(struct idmap_client *ic)
+{
+ if ((ic->ic_fd = open(ic->ic_path, O_RDWR, 0)) == -1) {
+ if (errno == ENOENT) {
+ char *slash;
+
+ slash = strrchr(ic->ic_path, '/');
+ if (!slash)
+ return -1;
+ *slash = 0;
+ inotify_add_watch(inotify_fd, ic->ic_path, IN_CREATE | IN_ONLYDIR | IN_ONESHOT);
+ *slash = '/';
+ if (verbose > 2)
+ xlog_warn("Path %s not available. waiting...", ic->ic_path);
+ return -1;
+ }
+
+ xlog_warn("nfsopen: open(%s): %s", ic->ic_path, strerror(errno));
+ return (-1);
+ }
+
+ ic->ic_event = event_new(evbase, ic->ic_fd, EV_READ | EV_PERSIST, nfscb, ic);
+ if (ic->ic_event == NULL) {
+ xlog_warn("nfsopen: Create event for %s failed", ic->ic_path);
+ close(ic->ic_fd);
+ ic->ic_fd = -1;
+ return -1;
+ }
+ event_add(ic->ic_event, NULL);
+ if (verbose > 2)
+ xlog_warn("Opened %s", ic->ic_path);
+
+ return (0);
+}
+
+static void
+idtonameres(struct idmap_msg *im)
+{
+ char domain[NFS4_MAX_DOMAIN_LEN];
+ int ret = 0;
+
+ ret = nfs4_get_default_domain(NULL, domain, sizeof(domain));
+ switch (im->im_type) {
+ case IDMAP_TYPE_USER:
+ ret = nfs4_uid_to_name(im->im_id, domain, im->im_name,
+ sizeof(im->im_name));
+ if (ret) {
+ if (strlen(nobodyuser) < sizeof(im->im_name))
+ strcpy(im->im_name, nobodyuser);
+ else
+ strcpy(im->im_name, NFS4NOBODY_USER);
+ }
+ break;
+ case IDMAP_TYPE_GROUP:
+ ret = nfs4_gid_to_name(im->im_id, domain, im->im_name,
+ sizeof(im->im_name));
+ if (ret) {
+ if (strlen(nobodygroup) < sizeof(im->im_name))
+ strcpy(im->im_name, nobodygroup);
+ else
+ strcpy(im->im_name, NFS4NOBODY_GROUP);
+ }
+ break;
+ }
+ if (ret)
+ im->im_status = IDMAP_STATUS_LOOKUPFAIL;
+ else
+ im->im_status = IDMAP_STATUS_SUCCESS;
+}
+
+static void
+nametoidres(struct idmap_msg *im)
+{
+ uid_t uid;
+ gid_t gid;
+ int ret = 0;
+
+ /* XXX: move nobody stuff to library calls
+ * (nfs4_get_nobody_user(domain), nfs4_get_nobody_group(domain)) */
+
+ im->im_status = IDMAP_STATUS_SUCCESS;
+
+ switch (im->im_type) {
+ case IDMAP_TYPE_USER:
+ ret = nfs4_name_to_uid(im->im_name, &uid);
+ im->im_id = (u_int32_t) uid;
+ if (ret) {
+ im->im_status = IDMAP_STATUS_LOOKUPFAIL;
+ im->im_id = nobodyuid;
+ }
+ return;
+ case IDMAP_TYPE_GROUP:
+ ret = nfs4_name_to_gid(im->im_name, &gid);
+ im->im_id = (u_int32_t) gid;
+ if (ret) {
+ im->im_status = IDMAP_STATUS_LOOKUPFAIL;
+ im->im_id = nobodygid;
+ }
+ return;
+ }
+}
+
+static int
+addfield(char **bpp, ssize_t *bsizp, char *fld)
+{
+ char ch, *bp = *bpp;
+ ssize_t bsiz = *bsizp;
+
+ while ((ch = *fld++) != '\0' && bsiz > 0) {
+ switch(ch) {
+ case ' ':
+ case '\t':
+ case '\n':
+ case '\\':
+ if (bsiz >= 4) {
+ bp += snprintf(bp, bsiz, "\\%03o", ch);
+ bsiz -= 4;
+ }
+ break;
+ default:
+ *bp++ = ch;
+ bsiz--;
+ break;
+ }
+ }
+
+ if (bsiz < 1 || ch != '\0')
+ return (-1);
+
+ *bp++ = ' ';
+ bsiz--;
+
+ *bpp = bp;
+ *bsizp = bsiz;
+
+ return (0);
+}
+
+static int
+getfield(char **bpp, char *fld, size_t fldsz)
+{
+ char *bp;
+ unsigned int val;
+ int n;
+
+ while ((bp = strsep(bpp, " ")) != NULL && bp[0] == '\0')
+ ;
+
+ if (bp == NULL || bp[0] == '\0' || bp[0] == '\n')
+ return (-1);
+
+ while (*bp != '\0' && fldsz > 1) {
+ if (*bp == '\\') {
+ if ((n = sscanf(bp, "\\%03o", &val)) != 1)
+ return (-1);
+ if (val > UCHAR_MAX)
+ return (-1);
+ *fld++ = val;
+ bp += 4;
+ } else {
+ *fld++ = *bp;
+ bp++;
+ }
+ fldsz--;
+ }
+
+ if (*bp != '\0')
+ return (-1);
+ *fld = '\0';
+
+ return (0);
+}
diff --git a/utils/idmapd/idmapd.man b/utils/idmapd/idmapd.man
new file mode 100644
index 0000000..8215d25
--- /dev/null
+++ b/utils/idmapd/idmapd.man
@@ -0,0 +1,134 @@
+.\" $OpenBSD: mdoc.template,v 1.6 2001/02/03 08:22:44 niklas Exp $
+.\"
+.\" The following requests are required for all man pages.
+.Dd February 3, 2003
+.Dt RPC.IDMAPD 8
+.Os
+.Sh NAME
+.Nm rpc.idmapd
+.Nd NFSv4 ID <-> Name Mapper
+.Sh SYNOPSIS
+.\" For a program: program [-abc] file ...
+.Nm rpc.idmapd
+.Op Fl h
+.Op Fl f
+.Op Fl v
+.Op Fl C
+.Op Fl S
+.Op Fl p Ar path
+.Op Fl c Ar path
+.Sh DESCRIPTION
+.Nm
+is the NFSv4 ID <-> name mapping daemon. It provides functionality to
+the NFSv4 kernel client and server, to which it communicates via
+upcalls, by translating user and group IDs to names, and vice versa.
+.Pp
+The system derives the
+.Em user
+part of the string by performing a password or group lookup.
+The lookup mechanism is configured in
+.Pa /etc/idmapd.conf
+.Pp
+By default, the
+.Em domain
+part of the string is the system's DNS domain name.
+It can also be specified in
+.Pa /etc/idmapd.conf
+if the system is multi-homed,
+or if the system's DNS domain name does
+not match the name of the system's Kerberos realm.
+.Pp
+When the domain is not specified in /etc/idmapd.conf
+the local DNS server will be queried for the
+.Sy _nfsv4idmapdomain
+text record. If the record exists
+that will be used as the domain. When the record
+does not exist, the domain part of the DNS domain
+will used.
+.Pp
+Note that on more recent kernels only the NFSv4 server uses
+.Nm .
+The NFSv4 client instead uses
+.Xr nfsidmap 8 ,
+and only falls back to
+.Nm
+if there was a problem running the
+.Xr nfsidmap 8
+program.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds_imagedir
+.It Fl h
+Display usage message.
+.It Fl v
+Increases the verbosity level (can be specified multiple times).
+.It Fl f
+Runs
+.Nm
+in the foreground and prints all output to the terminal.
+.It Fl p Ar path
+Specifies the location of the RPC pipefs to be
+.Ar path .
+The default value is \&"/var/lib/nfs/rpc_pipefs\&".
+.It Fl c Ar path
+Use configuration file
+.Ar path .
+This option is deprecated.
+.It Fl C
+Client-only: perform no idmapping for any NFS server, even if one is detected.
+.It Fl S
+Server-only: perform no idmapping for any NFS client, even if one is detected.
+.El
+.Sh CONFIGURATION FILES
+.Nm
+recognizes the following value from the
+.Sy [general]
+section of the
+.Pa /etc/nfs.conf
+configuration file:
+.Bl -tag -width Ds_imagedir
+.It Sy pipefs-directory
+Equivalent to
+.Sy -p .
+.El
+.Pp
+All other settings related to id mapping are found in the
+.Pa /etc/idmapd.conf
+configuration file.
+.Sh EXAMPLES
+.Cm rpc.idmapd -f -vvv
+.Pp
+Runs
+.Nm
+printing all
+messages to console, and with a verbosity level of 3.
+.\" This next request is for sections 2 and 3 function return values only.
+.\" .Sh RETURN VALUES
+.\" The next request is for sections 2 and 3 error and signal handling only.
+.\" .Sh ERRORS
+.\" This next request is for section 4 only.
+.\" .Sh DIAGNOSTICS
+.\" This next request is for sections 1, 6, 7 & 8 only.
+.\" .Sh ENVIRONMENT
+.Sh FILES
+.Pa /etc/idmapd.conf ,
+.Pa /etc/nfs.conf
+.Sh SEE ALSO
+.Xr idmapd.conf 5 ,
+.Xr nfs.conf 5 ,
+.Xr nfsidmap 8
+.\".Sh SEE ALSO
+.\".Xr nylon.conf 4
+.\" .Sh COMPATIBILITY
+.\".Sh STANDARDS
+.\".Sh ACKNOWLEDGEMENTS
+.Sh AUTHORS
+The
+.Nm
+software has been developed by Marius Aamodt Eriksen
+.Aq marius@citi.umich.edu .
+.\" .Sh HISTORY
+.\".Sh BUGS
+.\"Please report any bugs to Marius Aamodt Eriksen
+.\".Aq marius@monkey.org .
+.\" .Sh CAVEATS
diff --git a/utils/idmapd/nfs_idmap.h b/utils/idmapd/nfs_idmap.h
new file mode 100644
index 0000000..59bced3
--- /dev/null
+++ b/utils/idmapd/nfs_idmap.h
@@ -0,0 +1,64 @@
+/*
+ * include/linux/nfs_idmap.h
+ *
+ * UID and GID to name mapping for clients.
+ *
+ * Copyright (c) 2002 The Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Marius Aamodt Eriksen <marius@umich.edu>
+ *
+ * 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.
+ */
+
+#ifndef NFS_IDMAP_H
+#define NFS_IDMAP_H
+
+/* XXX from bits/utmp.h */
+#define IDMAP_NAMESZ 128
+
+#define IDMAP_TYPE_USER 0
+#define IDMAP_TYPE_GROUP 1
+
+#define IDMAP_CONV_IDTONAME 0
+#define IDMAP_CONV_NAMETOID 1
+
+#define IDMAP_STATUS_INVALIDMSG 0x01
+#define IDMAP_STATUS_AGAIN 0x02
+#define IDMAP_STATUS_LOOKUPFAIL 0x04
+#define IDMAP_STATUS_SUCCESS 0x08
+
+#define IDMAP_MAXMSGSZ 256
+
+struct idmap_msg {
+ u_int8_t im_type;
+ u_int8_t im_conv;
+ char im_name[IDMAP_NAMESZ];
+ u_int32_t im_id;
+ u_int8_t im_status;
+};
+
+#endif /* NFS_IDMAP_H */
diff --git a/utils/idmapd/queue.h b/utils/idmapd/queue.h
new file mode 100644
index 0000000..2823fe7
--- /dev/null
+++ b/utils/idmapd/queue.h
@@ -0,0 +1,499 @@
+/* $OpenBSD: queue.h,v 1.22 2001/06/23 04:39:35 angelos Exp $ */
+/* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */
+
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)queue.h 8.5 (Berkeley) 8/20/94
+ */
+
+#ifndef _SYS_QUEUE_H_
+#define _SYS_QUEUE_H_
+
+/*
+ * This file defines five types of data structures: singly-linked lists,
+ * lists, simple queues, tail queues, and circular queues.
+ *
+ *
+ * A singly-linked list is headed by a single forward pointer. The elements
+ * are singly linked for minimum space and pointer manipulation overhead at
+ * the expense of O(n) removal for arbitrary elements. New elements can be
+ * added to the list after an existing element or at the head of the list.
+ * Elements being removed from the head of the list should use the explicit
+ * macro for this purpose for optimum efficiency. A singly-linked list may
+ * only be traversed in the forward direction. Singly-linked lists are ideal
+ * for applications with large datasets and few or no removals or for
+ * implementing a LIFO queue.
+ *
+ * A list is headed by a single forward pointer (or an array of forward
+ * pointers for a hash table header). The elements are doubly linked
+ * so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before
+ * or after an existing element or at the head of the list. A list
+ * may only be traversed in the forward direction.
+ *
+ * A simple queue is headed by a pair of pointers, one the head of the
+ * list and the other to the tail of the list. The elements are singly
+ * linked to save space, so elements can only be removed from the
+ * head of the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the
+ * list. A simple queue may only be traversed in the forward direction.
+ *
+ * A tail queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or
+ * after an existing element, at the head of the list, or at the end of
+ * the list. A tail queue may be traversed in either direction.
+ *
+ * A circle queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the list.
+ * A circle queue may be traversed in either direction, but has a more
+ * complex end of list detection.
+ *
+ * For details on the use of these macros, see the queue(3) manual page.
+ */
+
+/*
+ * Singly-linked List definitions.
+ */
+#define SLIST_HEAD(name, type) \
+struct name { \
+ struct type *slh_first; /* first element */ \
+}
+
+#define SLIST_HEAD_INITIALIZER(head) \
+ { NULL }
+
+#define SLIST_ENTRY(type) \
+struct { \
+ struct type *sle_next; /* next element */ \
+}
+
+/*
+ * Singly-linked List access methods.
+ */
+#define SLIST_FIRST(head) ((head)->slh_first)
+#define SLIST_END(head) NULL
+#define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head))
+#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
+
+#define SLIST_FOREACH(var, head, field) \
+ for((var) = SLIST_FIRST(head); \
+ (var) != SLIST_END(head); \
+ (var) = SLIST_NEXT(var, field))
+
+/*
+ * Singly-linked List functions.
+ */
+#define SLIST_INIT(head) { \
+ SLIST_FIRST(head) = SLIST_END(head); \
+}
+
+#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
+ (elm)->field.sle_next = (slistelm)->field.sle_next; \
+ (slistelm)->field.sle_next = (elm); \
+} while (0)
+
+#define SLIST_INSERT_HEAD(head, elm, field) do { \
+ (elm)->field.sle_next = (head)->slh_first; \
+ (head)->slh_first = (elm); \
+} while (0)
+
+#define SLIST_REMOVE_HEAD(head, field) do { \
+ (head)->slh_first = (head)->slh_first->field.sle_next; \
+} while (0)
+
+#define SLIST_REMOVE(head, elm, type, field) do { \
+ if ((head)->slh_first == (elm)) { \
+ SLIST_REMOVE_HEAD((head), field); \
+ } \
+ else { \
+ struct type *curelm = (head)->slh_first; \
+ while( curelm->field.sle_next != (elm) ) \
+ curelm = curelm->field.sle_next; \
+ curelm->field.sle_next = \
+ curelm->field.sle_next->field.sle_next; \
+ } \
+} while (0)
+
+/*
+ * List definitions.
+ */
+#define LIST_HEAD(name, type) \
+struct name { \
+ struct type *lh_first; /* first element */ \
+}
+
+#define LIST_HEAD_INITIALIZER(head) \
+ { NULL }
+
+#define LIST_ENTRY(type) \
+struct { \
+ struct type *le_next; /* next element */ \
+ struct type **le_prev; /* address of previous next element */ \
+}
+
+/*
+ * List access methods
+ */
+#define LIST_FIRST(head) ((head)->lh_first)
+#define LIST_END(head) NULL
+#define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head))
+#define LIST_NEXT(elm, field) ((elm)->field.le_next)
+
+#define LIST_FOREACH(var, head, field) \
+ for((var) = LIST_FIRST(head); \
+ (var)!= LIST_END(head); \
+ (var) = LIST_NEXT(var, field))
+
+/*
+ * List functions.
+ */
+#define LIST_INIT(head) do { \
+ LIST_FIRST(head) = LIST_END(head); \
+} while (0)
+
+#define LIST_INSERT_AFTER(listelm, elm, field) do { \
+ if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \
+ (listelm)->field.le_next->field.le_prev = \
+ &(elm)->field.le_next; \
+ (listelm)->field.le_next = (elm); \
+ (elm)->field.le_prev = &(listelm)->field.le_next; \
+} while (0)
+
+#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
+ (elm)->field.le_prev = (listelm)->field.le_prev; \
+ (elm)->field.le_next = (listelm); \
+ *(listelm)->field.le_prev = (elm); \
+ (listelm)->field.le_prev = &(elm)->field.le_next; \
+} while (0)
+
+#define LIST_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.le_next = (head)->lh_first) != NULL) \
+ (head)->lh_first->field.le_prev = &(elm)->field.le_next;\
+ (head)->lh_first = (elm); \
+ (elm)->field.le_prev = &(head)->lh_first; \
+} while (0)
+
+#define LIST_REMOVE(elm, field) do { \
+ if ((elm)->field.le_next != NULL) \
+ (elm)->field.le_next->field.le_prev = \
+ (elm)->field.le_prev; \
+ *(elm)->field.le_prev = (elm)->field.le_next; \
+} while (0)
+
+#define LIST_REPLACE(elm, elm2, field) do { \
+ if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \
+ (elm2)->field.le_next->field.le_prev = \
+ &(elm2)->field.le_next; \
+ (elm2)->field.le_prev = (elm)->field.le_prev; \
+ *(elm2)->field.le_prev = (elm2); \
+} while (0)
+
+/*
+ * Simple queue definitions.
+ */
+#define SIMPLEQ_HEAD(name, type) \
+struct name { \
+ struct type *sqh_first; /* first element */ \
+ struct type **sqh_last; /* addr of last next element */ \
+}
+
+#define SIMPLEQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).sqh_first }
+
+#define SIMPLEQ_ENTRY(type) \
+struct { \
+ struct type *sqe_next; /* next element */ \
+}
+
+/*
+ * Simple queue access methods.
+ */
+#define SIMPLEQ_FIRST(head) ((head)->sqh_first)
+#define SIMPLEQ_END(head) NULL
+#define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head))
+#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next)
+
+#define SIMPLEQ_FOREACH(var, head, field) \
+ for((var) = SIMPLEQ_FIRST(head); \
+ (var) != SIMPLEQ_END(head); \
+ (var) = SIMPLEQ_NEXT(var, field))
+
+/*
+ * Simple queue functions.
+ */
+#define SIMPLEQ_INIT(head) do { \
+ (head)->sqh_first = NULL; \
+ (head)->sqh_last = &(head)->sqh_first; \
+} while (0)
+
+#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+ (head)->sqh_first = (elm); \
+} while (0)
+
+#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.sqe_next = NULL; \
+ *(head)->sqh_last = (elm); \
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+} while (0)
+
+#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+ (listelm)->field.sqe_next = (elm); \
+} while (0)
+
+#define SIMPLEQ_REMOVE_HEAD(head, elm, field) do { \
+ if (((head)->sqh_first = (elm)->field.sqe_next) == NULL) \
+ (head)->sqh_last = &(head)->sqh_first; \
+} while (0)
+
+/*
+ * Tail queue definitions.
+ */
+#define TAILQ_HEAD(name, type) \
+struct name { \
+ struct type *tqh_first; /* first element */ \
+ struct type **tqh_last; /* addr of last next element */ \
+}
+
+#define TAILQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).tqh_first }
+
+#define TAILQ_ENTRY(type) \
+struct { \
+ struct type *tqe_next; /* next element */ \
+ struct type **tqe_prev; /* address of previous next element */ \
+}
+
+/*
+ * tail queue access methods
+ */
+#define TAILQ_FIRST(head) ((head)->tqh_first)
+#define TAILQ_END(head) NULL
+#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
+#define TAILQ_LAST(head, headname) \
+ (*(((struct headname *)((head)->tqh_last))->tqh_last))
+/* XXX */
+#define TAILQ_PREV(elm, headname, field) \
+ (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
+#define TAILQ_EMPTY(head) \
+ (TAILQ_FIRST(head) == TAILQ_END(head))
+
+#define TAILQ_FOREACH(var, head, field) \
+ for((var) = TAILQ_FIRST(head); \
+ (var) != TAILQ_END(head); \
+ (var) = TAILQ_NEXT(var, field))
+
+#define TAILQ_FOREACH_REVERSE(var, head, field, headname) \
+ for((var) = TAILQ_LAST(head, headname); \
+ (var) != TAILQ_END(head); \
+ (var) = TAILQ_PREV(var, headname, field))
+
+/*
+ * Tail queue functions.
+ */
+#define TAILQ_INIT(head) do { \
+ (head)->tqh_first = NULL; \
+ (head)->tqh_last = &(head)->tqh_first; \
+} while (0)
+
+#define TAILQ_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
+ (head)->tqh_first->field.tqe_prev = \
+ &(elm)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+ (head)->tqh_first = (elm); \
+ (elm)->field.tqe_prev = &(head)->tqh_first; \
+} while (0)
+
+#define TAILQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.tqe_next = NULL; \
+ (elm)->field.tqe_prev = (head)->tqh_last; \
+ *(head)->tqh_last = (elm); \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+} while (0)
+
+#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
+ (elm)->field.tqe_next->field.tqe_prev = \
+ &(elm)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+ (listelm)->field.tqe_next = (elm); \
+ (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
+} while (0)
+
+#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
+ (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
+ (elm)->field.tqe_next = (listelm); \
+ *(listelm)->field.tqe_prev = (elm); \
+ (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
+} while (0)
+
+#define TAILQ_REMOVE(head, elm, field) do { \
+ if (((elm)->field.tqe_next) != NULL) \
+ (elm)->field.tqe_next->field.tqe_prev = \
+ (elm)->field.tqe_prev; \
+ else \
+ (head)->tqh_last = (elm)->field.tqe_prev; \
+ *(elm)->field.tqe_prev = (elm)->field.tqe_next; \
+} while (0)
+
+#define TAILQ_REPLACE(head, elm, elm2, field) do { \
+ if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \
+ (elm2)->field.tqe_next->field.tqe_prev = \
+ &(elm2)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm2)->field.tqe_next; \
+ (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \
+ *(elm2)->field.tqe_prev = (elm2); \
+} while (0)
+
+/*
+ * Circular queue definitions.
+ */
+#define CIRCLEQ_HEAD(name, type) \
+struct name { \
+ struct type *cqh_first; /* first element */ \
+ struct type *cqh_last; /* last element */ \
+}
+
+#define CIRCLEQ_HEAD_INITIALIZER(head) \
+ { CIRCLEQ_END(&head), CIRCLEQ_END(&head) }
+
+#define CIRCLEQ_ENTRY(type) \
+struct { \
+ struct type *cqe_next; /* next element */ \
+ struct type *cqe_prev; /* previous element */ \
+}
+
+/*
+ * Circular queue access methods
+ */
+#define CIRCLEQ_FIRST(head) ((head)->cqh_first)
+#define CIRCLEQ_LAST(head) ((head)->cqh_last)
+#define CIRCLEQ_END(head) ((void *)(head))
+#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next)
+#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev)
+#define CIRCLEQ_EMPTY(head) \
+ (CIRCLEQ_FIRST(head) == CIRCLEQ_END(head))
+
+#define CIRCLEQ_FOREACH(var, head, field) \
+ for((var) = CIRCLEQ_FIRST(head); \
+ (var) != CIRCLEQ_END(head); \
+ (var) = CIRCLEQ_NEXT(var, field))
+
+#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \
+ for((var) = CIRCLEQ_LAST(head); \
+ (var) != CIRCLEQ_END(head); \
+ (var) = CIRCLEQ_PREV(var, field))
+
+/*
+ * Circular queue functions.
+ */
+#define CIRCLEQ_INIT(head) do { \
+ (head)->cqh_first = CIRCLEQ_END(head); \
+ (head)->cqh_last = CIRCLEQ_END(head); \
+} while (0)
+
+#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ (elm)->field.cqe_next = (listelm)->field.cqe_next; \
+ (elm)->field.cqe_prev = (listelm); \
+ if ((listelm)->field.cqe_next == CIRCLEQ_END(head)) \
+ (head)->cqh_last = (elm); \
+ else \
+ (listelm)->field.cqe_next->field.cqe_prev = (elm); \
+ (listelm)->field.cqe_next = (elm); \
+} while (0)
+
+#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \
+ (elm)->field.cqe_next = (listelm); \
+ (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \
+ if ((listelm)->field.cqe_prev == CIRCLEQ_END(head)) \
+ (head)->cqh_first = (elm); \
+ else \
+ (listelm)->field.cqe_prev->field.cqe_next = (elm); \
+ (listelm)->field.cqe_prev = (elm); \
+} while (0)
+
+#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \
+ (elm)->field.cqe_next = (head)->cqh_first; \
+ (elm)->field.cqe_prev = CIRCLEQ_END(head); \
+ if ((head)->cqh_last == CIRCLEQ_END(head)) \
+ (head)->cqh_last = (elm); \
+ else \
+ (head)->cqh_first->field.cqe_prev = (elm); \
+ (head)->cqh_first = (elm); \
+} while (0)
+
+#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.cqe_next = CIRCLEQ_END(head); \
+ (elm)->field.cqe_prev = (head)->cqh_last; \
+ if ((head)->cqh_first == CIRCLEQ_END(head)) \
+ (head)->cqh_first = (elm); \
+ else \
+ (head)->cqh_last->field.cqe_next = (elm); \
+ (head)->cqh_last = (elm); \
+} while (0)
+
+#define CIRCLEQ_REMOVE(head, elm, field) do { \
+ if ((elm)->field.cqe_next == CIRCLEQ_END(head)) \
+ (head)->cqh_last = (elm)->field.cqe_prev; \
+ else \
+ (elm)->field.cqe_next->field.cqe_prev = \
+ (elm)->field.cqe_prev; \
+ if ((elm)->field.cqe_prev == CIRCLEQ_END(head)) \
+ (head)->cqh_first = (elm)->field.cqe_next; \
+ else \
+ (elm)->field.cqe_prev->field.cqe_next = \
+ (elm)->field.cqe_next; \
+} while (0)
+
+#define CIRCLEQ_REPLACE(head, elm, elm2, field) do { \
+ if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \
+ CIRCLEQ_END(head)) \
+ (head).cqh_last = (elm2); \
+ else \
+ (elm2)->field.cqe_next->field.cqe_prev = (elm2); \
+ if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == \
+ CIRCLEQ_END(head)) \
+ (head).cqh_first = (elm2); \
+ else \
+ (elm2)->field.cqe_prev->field.cqe_next = (elm2); \
+} while (0)
+
+#endif /* !_SYS_QUEUE_H_ */
diff --git a/utils/mount/Makefile.am b/utils/mount/Makefile.am
new file mode 100644
index 0000000..5ff1148
--- /dev/null
+++ b/utils/mount/Makefile.am
@@ -0,0 +1,82 @@
+## Process this file with automake to produce Makefile.in
+
+# These binaries go in /sbin (not /usr/sbin), unless CONFIG_SBIN_OVERRIDE
+# is disabled as may be appropriate when /sbin is a symlink.
+# Note that we don't use "if CONFIG_SBIN_OVERRIDE" as that
+# causes autotools to notice the override and disable it.
+@CONFIG_SBIN_OVERRIDE_TRUE@sbindir = /sbin
+
+man8_MANS = mount.nfs.man umount.nfs.man
+man5_MANS = nfs.man
+
+sbin_PROGRAMS = mount.nfs
+EXTRA_DIST = nfsmount.conf $(man8_MANS) $(man5_MANS)
+mount_common = error.c network.c token.c \
+ parse_opt.c parse_dev.c \
+ nfsmount.c nfs4mount.c stropts.c\
+ mount_constants.h error.h network.h token.h \
+ parse_opt.h parse_dev.h \
+ nfs4_mount.h stropts.h version.h \
+ mount_config.h utils.c utils.h \
+ nfs_mount.h
+
+if MOUNT_CONFIG
+mount_common += configfile.c
+man5_MANS += nfsmount.conf.man
+else
+EXTRA_DIST += nfsmount.conf.man
+endif
+
+mount_nfs_LDADD = ../../support/nfs/libnfs.la \
+ ../../support/export/libexport.a \
+ ../../support/reexport/libreexport.a \
+ ../../support/misc/libmisc.a \
+ $(LIBTIRPC) $(LIBPTHREAD)
+
+mount_nfs_SOURCES = $(mount_common)
+
+if CONFIG_LIBMOUNT
+mount_nfs_SOURCES += mount_libmount.c
+mount_nfs_LDADD += $(LIBMOUNT)
+else
+mount_nfs_SOURCES += mount.c fstab.c nfsumount.c fstab.h
+
+endif
+
+MAINTAINERCLEANFILES = Makefile.in
+
+install-exec-hook:
+ (cd $(DESTDIR)$(sbindir) && \
+ ln -sf mount.nfs mount.nfs4 && \
+ ln -sf mount.nfs umount.nfs && \
+ ln -sf mount.nfs umount.nfs4 && \
+ chmod 4511 mount.nfs )
+uninstall-hook:
+ (cd $(DESTDIR)$(sbindir) && \
+ rm -f mount.nfs4 umount.nfs umount.nfs4)
+
+
+install-man-links:
+ (cd $(DESTDIR)$(man8dir) && \
+ for m in $(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS); do \
+ inst=`echo $$m | sed -e 's/man$$/8/'`; \
+ rm -f $$inst ; \
+ done)
+ (cd $(DESTDIR)$(man5dir) && \
+ for m in $(man5_MANS) $(dist_man5_MANS) $(nodist_man5_MANS); do \
+ inst=`echo $$m | sed -e 's/man$$/5/'`; \
+ rm -f $$inst ; \
+ done)
+
+uninstall-man-links:
+ (cd $(DESTDIR)$(man8dir) && \
+ for m in $(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS); do \
+ inst=`echo $$m | sed -e 's/man$$/8/'`; \
+ rm -f $$inst ; \
+ done)
+ (cd $(DESTDIR)$(man5dir) && \
+ for m in $(man5_MANS) $(dist_man5_MANS) $(nodist_man5_MANS); do \
+ inst=`echo $$m | sed -e 's/man$$/5/'`; \
+ rm -f $$inst ; \
+ done)
+
diff --git a/utils/mount/Makefile.in b/utils/mount/Makefile.in
new file mode 100644
index 0000000..5bbbb5d
--- /dev/null
+++ b/utils/mount/Makefile.in
@@ -0,0 +1,976 @@
+# 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@
+sbin_PROGRAMS = mount.nfs$(EXEEXT)
+@MOUNT_CONFIG_TRUE@am__append_1 = configfile.c
+@MOUNT_CONFIG_TRUE@am__append_2 = nfsmount.conf.man
+@MOUNT_CONFIG_FALSE@am__append_3 = nfsmount.conf.man
+@CONFIG_LIBMOUNT_TRUE@am__append_4 = mount_libmount.c
+@CONFIG_LIBMOUNT_TRUE@am__append_5 = $(LIBMOUNT)
+@CONFIG_LIBMOUNT_FALSE@am__append_6 = mount.c fstab.c nfsumount.c fstab.h
+subdir = utils/mount
+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 =
+am__installdirs = "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man5dir)" \
+ "$(DESTDIR)$(man8dir)"
+PROGRAMS = $(sbin_PROGRAMS)
+am__mount_nfs_SOURCES_DIST = error.c network.c token.c parse_opt.c \
+ parse_dev.c nfsmount.c nfs4mount.c stropts.c mount_constants.h \
+ error.h network.h token.h parse_opt.h parse_dev.h nfs4_mount.h \
+ stropts.h version.h mount_config.h utils.c utils.h nfs_mount.h \
+ configfile.c mount_libmount.c mount.c fstab.c nfsumount.c \
+ fstab.h
+@MOUNT_CONFIG_TRUE@am__objects_1 = configfile.$(OBJEXT)
+am__objects_2 = error.$(OBJEXT) network.$(OBJEXT) token.$(OBJEXT) \
+ parse_opt.$(OBJEXT) parse_dev.$(OBJEXT) nfsmount.$(OBJEXT) \
+ nfs4mount.$(OBJEXT) stropts.$(OBJEXT) utils.$(OBJEXT) \
+ $(am__objects_1)
+@CONFIG_LIBMOUNT_TRUE@am__objects_3 = mount_libmount.$(OBJEXT)
+@CONFIG_LIBMOUNT_FALSE@am__objects_4 = mount.$(OBJEXT) fstab.$(OBJEXT) \
+@CONFIG_LIBMOUNT_FALSE@ nfsumount.$(OBJEXT)
+am_mount_nfs_OBJECTS = $(am__objects_2) $(am__objects_3) \
+ $(am__objects_4)
+mount_nfs_OBJECTS = $(am_mount_nfs_OBJECTS)
+am__DEPENDENCIES_1 =
+@CONFIG_LIBMOUNT_TRUE@am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1)
+mount_nfs_DEPENDENCIES = ../../support/nfs/libnfs.la \
+ ../../support/export/libexport.a \
+ ../../support/reexport/libreexport.a \
+ ../../support/misc/libmisc.a $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_2)
+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 =
+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)/configfile.Po ./$(DEPDIR)/error.Po \
+ ./$(DEPDIR)/fstab.Po ./$(DEPDIR)/mount.Po \
+ ./$(DEPDIR)/mount_libmount.Po ./$(DEPDIR)/network.Po \
+ ./$(DEPDIR)/nfs4mount.Po ./$(DEPDIR)/nfsmount.Po \
+ ./$(DEPDIR)/nfsumount.Po ./$(DEPDIR)/parse_dev.Po \
+ ./$(DEPDIR)/parse_opt.Po ./$(DEPDIR)/stropts.Po \
+ ./$(DEPDIR)/token.Po ./$(DEPDIR)/utils.Po
+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 = $(mount_nfs_SOURCES)
+DIST_SOURCES = $(am__mount_nfs_SOURCES_DIST)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+man5dir = $(mandir)/man5
+man8dir = $(mandir)/man8
+NROFF = nroff
+MANS = $(man5_MANS) $(man8_MANS)
+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@
+
+# These binaries go in /sbin (not /usr/sbin), unless CONFIG_SBIN_OVERRIDE
+# is disabled as may be appropriate when /sbin is a symlink.
+# Note that we don't use "if CONFIG_SBIN_OVERRIDE" as that
+# causes autotools to notice the override and disable it.
+@CONFIG_SBIN_OVERRIDE_TRUE@sbindir = /sbin
+man8_MANS = mount.nfs.man umount.nfs.man
+man5_MANS = nfs.man $(am__append_2)
+EXTRA_DIST = nfsmount.conf $(man8_MANS) $(man5_MANS) $(am__append_3)
+mount_common = error.c network.c token.c parse_opt.c parse_dev.c \
+ nfsmount.c nfs4mount.c stropts.c mount_constants.h error.h \
+ network.h token.h parse_opt.h parse_dev.h nfs4_mount.h \
+ stropts.h version.h mount_config.h utils.c utils.h nfs_mount.h \
+ $(am__append_1)
+mount_nfs_LDADD = ../../support/nfs/libnfs.la \
+ ../../support/export/libexport.a \
+ ../../support/reexport/libreexport.a \
+ ../../support/misc/libmisc.a $(LIBTIRPC) $(LIBPTHREAD) \
+ $(am__append_5)
+mount_nfs_SOURCES = $(mount_common) $(am__append_4) $(am__append_6)
+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 utils/mount/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu utils/mount/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):
+install-sbinPROGRAMS: $(sbin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(sbindir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-sbinPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(sbindir)" && rm -f $$files
+
+clean-sbinPROGRAMS:
+ @list='$(sbin_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+mount.nfs$(EXEEXT): $(mount_nfs_OBJECTS) $(mount_nfs_DEPENDENCIES) $(EXTRA_mount_nfs_DEPENDENCIES)
+ @rm -f mount.nfs$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(mount_nfs_OBJECTS) $(mount_nfs_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/configfile.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/error.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fstab.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mount.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mount_libmount.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/network.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nfs4mount.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nfsmount.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nfsumount.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/parse_dev.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/parse_opt.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stropts.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/token.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/utils.Po@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 $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-man5: $(man5_MANS)
+ @$(NORMAL_INSTALL)
+ @list1='$(man5_MANS)'; \
+ list2=''; \
+ test -n "$(man5dir)" \
+ && test -n "`echo $$list1$$list2`" \
+ || exit 0; \
+ echo " $(MKDIR_P) '$(DESTDIR)$(man5dir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(man5dir)" || exit 1; \
+ { for i in $$list1; do echo "$$i"; done; \
+ if test -n "$$list2"; then \
+ for i in $$list2; do echo "$$i"; done \
+ | sed -n '/\.5[a-z]*$$/p'; \
+ fi; \
+ } | while read p; do \
+ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; echo "$$p"; \
+ done | \
+ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^5][0-9a-z]*$$,5,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \
+ sed 'N;N;s,\n, ,g' | { \
+ list=; while read file base inst; do \
+ if test "$$base" = "$$inst"; then list="$$list $$file"; else \
+ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man5dir)/$$inst'"; \
+ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man5dir)/$$inst" || exit $$?; \
+ fi; \
+ done; \
+ for i in $$list; do echo "$$i"; done | $(am__base_list) | \
+ while read files; do \
+ test -z "$$files" || { \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man5dir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(man5dir)" || exit $$?; }; \
+ done; }
+
+uninstall-man5:
+ @$(NORMAL_UNINSTALL)
+ @list='$(man5_MANS)'; test -n "$(man5dir)" || exit 0; \
+ files=`{ for i in $$list; do echo "$$i"; done; \
+ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^5][0-9a-z]*$$,5,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
+ dir='$(DESTDIR)$(man5dir)'; $(am__uninstall_files_from_dir)
+install-man8: $(man8_MANS)
+ @$(NORMAL_INSTALL)
+ @list1='$(man8_MANS)'; \
+ list2=''; \
+ test -n "$(man8dir)" \
+ && test -n "`echo $$list1$$list2`" \
+ || exit 0; \
+ echo " $(MKDIR_P) '$(DESTDIR)$(man8dir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(man8dir)" || exit 1; \
+ { for i in $$list1; do echo "$$i"; done; \
+ if test -n "$$list2"; then \
+ for i in $$list2; do echo "$$i"; done \
+ | sed -n '/\.8[a-z]*$$/p'; \
+ fi; \
+ } | while read p; do \
+ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; echo "$$p"; \
+ done | \
+ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \
+ sed 'N;N;s,\n, ,g' | { \
+ list=; while read file base inst; do \
+ if test "$$base" = "$$inst"; then list="$$list $$file"; else \
+ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man8dir)/$$inst'"; \
+ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man8dir)/$$inst" || exit $$?; \
+ fi; \
+ done; \
+ for i in $$list; do echo "$$i"; done | $(am__base_list) | \
+ while read files; do \
+ test -z "$$files" || { \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man8dir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(man8dir)" || exit $$?; }; \
+ done; }
+
+uninstall-man8:
+ @$(NORMAL_UNINSTALL)
+ @list='$(man8_MANS)'; test -n "$(man8dir)" || exit 0; \
+ files=`{ for i in $$list; do echo "$$i"; done; \
+ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
+ dir='$(DESTDIR)$(man8dir)'; $(am__uninstall_files_from_dir)
+
+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 $(PROGRAMS) $(MANS)
+installdirs:
+ for dir in "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man5dir)" "$(DESTDIR)$(man8dir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+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-sbinPROGRAMS \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/configfile.Po
+ -rm -f ./$(DEPDIR)/error.Po
+ -rm -f ./$(DEPDIR)/fstab.Po
+ -rm -f ./$(DEPDIR)/mount.Po
+ -rm -f ./$(DEPDIR)/mount_libmount.Po
+ -rm -f ./$(DEPDIR)/network.Po
+ -rm -f ./$(DEPDIR)/nfs4mount.Po
+ -rm -f ./$(DEPDIR)/nfsmount.Po
+ -rm -f ./$(DEPDIR)/nfsumount.Po
+ -rm -f ./$(DEPDIR)/parse_dev.Po
+ -rm -f ./$(DEPDIR)/parse_opt.Po
+ -rm -f ./$(DEPDIR)/stropts.Po
+ -rm -f ./$(DEPDIR)/token.Po
+ -rm -f ./$(DEPDIR)/utils.Po
+ -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-man
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-sbinPROGRAMS
+ @$(NORMAL_INSTALL)
+ $(MAKE) $(AM_MAKEFLAGS) install-exec-hook
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man: install-man5 install-man8
+
+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)/configfile.Po
+ -rm -f ./$(DEPDIR)/error.Po
+ -rm -f ./$(DEPDIR)/fstab.Po
+ -rm -f ./$(DEPDIR)/mount.Po
+ -rm -f ./$(DEPDIR)/mount_libmount.Po
+ -rm -f ./$(DEPDIR)/network.Po
+ -rm -f ./$(DEPDIR)/nfs4mount.Po
+ -rm -f ./$(DEPDIR)/nfsmount.Po
+ -rm -f ./$(DEPDIR)/nfsumount.Po
+ -rm -f ./$(DEPDIR)/parse_dev.Po
+ -rm -f ./$(DEPDIR)/parse_opt.Po
+ -rm -f ./$(DEPDIR)/stropts.Po
+ -rm -f ./$(DEPDIR)/token.Po
+ -rm -f ./$(DEPDIR)/utils.Po
+ -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: uninstall-man uninstall-sbinPROGRAMS
+ @$(NORMAL_INSTALL)
+ $(MAKE) $(AM_MAKEFLAGS) uninstall-hook
+uninstall-man: uninstall-man5 uninstall-man8
+
+.MAKE: install-am install-exec-am install-strip uninstall-am
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-sbinPROGRAMS 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-exec-hook install-html install-html-am \
+ install-info install-info-am install-man install-man5 \
+ install-man8 install-pdf install-pdf-am install-ps \
+ install-ps-am install-sbinPROGRAMS 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 uninstall-hook \
+ uninstall-man uninstall-man5 uninstall-man8 \
+ uninstall-sbinPROGRAMS
+
+.PRECIOUS: Makefile
+
+
+install-exec-hook:
+ (cd $(DESTDIR)$(sbindir) && \
+ ln -sf mount.nfs mount.nfs4 && \
+ ln -sf mount.nfs umount.nfs && \
+ ln -sf mount.nfs umount.nfs4 && \
+ chmod 4511 mount.nfs )
+uninstall-hook:
+ (cd $(DESTDIR)$(sbindir) && \
+ rm -f mount.nfs4 umount.nfs umount.nfs4)
+
+install-man-links:
+ (cd $(DESTDIR)$(man8dir) && \
+ for m in $(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS); do \
+ inst=`echo $$m | sed -e 's/man$$/8/'`; \
+ rm -f $$inst ; \
+ done)
+ (cd $(DESTDIR)$(man5dir) && \
+ for m in $(man5_MANS) $(dist_man5_MANS) $(nodist_man5_MANS); do \
+ inst=`echo $$m | sed -e 's/man$$/5/'`; \
+ rm -f $$inst ; \
+ done)
+
+uninstall-man-links:
+ (cd $(DESTDIR)$(man8dir) && \
+ for m in $(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS); do \
+ inst=`echo $$m | sed -e 's/man$$/8/'`; \
+ rm -f $$inst ; \
+ done)
+ (cd $(DESTDIR)$(man5dir) && \
+ for m in $(man5_MANS) $(dist_man5_MANS) $(nodist_man5_MANS); do \
+ inst=`echo $$m | sed -e 's/man$$/5/'`; \
+ rm -f $$inst ; \
+ done)
+
+# 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/utils/mount/configfile.c b/utils/mount/configfile.c
new file mode 100644
index 0000000..1d88cbf
--- /dev/null
+++ b/utils/mount/configfile.c
@@ -0,0 +1,347 @@
+/*
+ * configfile.c -- mount configuration file manipulation
+ * Copyright (C) 2008 Red Hat, Inc <nfs@redhat.com>
+ *
+ * - Routines use to create mount options from the mount
+ * configuration file.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include "xlog.h"
+#include "mount.h"
+#include "parse_opt.h"
+#include "network.h"
+#include "conffile.h"
+#include "mount_config.h"
+
+#define KBYTES(x) ((x) * (1024))
+#define MEGABYTES(x) ((x) * (1048576))
+#define GIGABYTES(x) ((x) * (1073741824))
+
+#ifndef NFSMOUNT_GLOBAL_OPTS
+#define NFSMOUNT_GLOBAL_OPTS "NFSMount_Global_Options"
+#endif
+
+#ifndef NFSMOUNT_MOUNTPOINT
+#define NFSMOUNT_MOUNTPOINT "MountPoint"
+#endif
+
+#ifndef NFSMOUNT_SERVER
+#define NFSMOUNT_SERVER "Server"
+#endif
+
+enum {
+ MNT_NOARG=0,
+ MNT_INTARG,
+ MNT_STRARG,
+ MNT_SPEC,
+ MNT_UNSET
+};
+struct mnt_alias {
+ char *alias;
+ char *opt;
+ int argtype;
+} mnt_alias_tab[] = {
+ {"background", "bg", MNT_NOARG},
+ {"foreground", "fg", MNT_NOARG},
+ {"sloppy", "sloppy", MNT_NOARG},
+};
+int mnt_alias_sz = (sizeof(mnt_alias_tab)/sizeof(mnt_alias_tab[0]));
+
+static const char *version_keys[] = {
+ "v3", "v4", "vers", "nfsvers", "minorversion", NULL
+};
+
+static int strict;
+
+static int is_version(const char *field)
+{
+ int i;
+ for (i = 0; version_keys[i] ; i++)
+ if (strcmp(version_keys[i], field) == 0)
+ return 1;
+ if (strncmp(field, "v4.", 3) == 0)
+ return 1;
+ return 0;
+}
+
+/*
+ * See if the option is an alias, if so return the
+ * real mount option along with the argument type.
+ */
+inline static
+char *mountopts_alias(char *opt, int *argtype)
+{
+ int i;
+
+ *argtype = MNT_UNSET;
+ for (i=0; i < mnt_alias_sz; i++) {
+ if (strcasecmp(opt, mnt_alias_tab[i].alias) != 0)
+ continue;
+ *argtype = mnt_alias_tab[i].argtype;
+ return mnt_alias_tab[i].opt;
+ }
+ /* Make option names case-insensitive */
+ upper2lower(opt);
+
+ return opt;
+}
+/*
+ * Convert numeric strings that end with 'k', 'm' or 'g'
+ * into numeric strings with the real value.
+ * Meaning '8k' becomes '8094'.
+ */
+static char *mountopts_convert(char *value)
+{
+ unsigned long long factor, num;
+ static char buf[64];
+ char *ch;
+
+ ch = &value[strlen(value)-1];
+ switch (tolower(*ch)) {
+ case 'k':
+ factor = KBYTES(1);
+ break;
+ case 'm':
+ factor = MEGABYTES(1);
+ break;
+ case 'g':
+ factor = GIGABYTES(1);
+ break;
+ default:
+ return value;
+ }
+ *ch = '\0';
+ if (strncmp(value, "0x", 2) == 0) {
+ num = strtol(value, (char **)NULL, 16);
+ } else if (strncmp(value, "0", 1) == 0) {
+ num = strtol(value, (char **)NULL, 8);
+ } else {
+ num = strtol(value, (char **)NULL, 10);
+ }
+ num *= factor;
+ snprintf(buf, 64, "%lld", num);
+
+ return buf;
+}
+
+struct nfs_version config_default_vers;
+unsigned long config_default_proto;
+extern sa_family_t config_default_family;
+
+/*
+ * Check to see if a default value is being set.
+ * If so, set the appropriate global value which will
+ * be used as the initial value in the server negation.
+ */
+static int
+default_value(char *mopt)
+{
+ struct mount_options *options = NULL;
+ int dftlen = strlen("default");
+ char *field;
+
+ if (strncasecmp(mopt, "default", dftlen) != 0)
+ return 0;
+
+ field = mopt + dftlen;
+ if (strncasecmp(field, "proto", strlen("proto")) == 0) {
+ if ((options = po_split(field)) != NULL) {
+ if (!nfs_nfs_protocol(options, &config_default_proto)) {
+ xlog_warn("Unable to set default protocol : %s",
+ strerror(errno));
+ }
+ if (!nfs_nfs_proto_family(options, &config_default_family)) {
+ xlog_warn("Unable to set default family : %s",
+ strerror(errno));
+ }
+ } else {
+ xlog_warn("Unable to alloc memory for default protocol");
+ }
+ } else if (strncasecmp(field, "vers", strlen("vers")) == 0) {
+ if ((options = po_split(field)) != NULL) {
+ if (!nfs_nfs_version("nfs", options, &config_default_vers)) {
+ xlog_warn("Unable to set default version: %s",
+ strerror(errno));
+ }
+ } else {
+ xlog_warn("Unable to alloc memory for default version");
+ }
+ } else
+ xlog_warn("Invalid default setting: '%s'", mopt);
+
+ if (options)
+ po_destroy(options);
+
+ return 1;
+}
+/*
+ * Parse the given section of the configuration
+ * file to if there are any mount options set.
+ * If so, added them to link list.
+ */
+static void
+conf_parse_mntopts(char *section, char *arg, struct mount_options *options)
+{
+ struct conf_list *list;
+ struct conf_list_node *node;
+ char buf[BUFSIZ], *value, *field;
+ char *nvalue, *ptr;
+ int argtype;
+ int have_version = 0;
+
+ if (po_rightmost(options, version_keys) >= 0 ||
+ po_contains_prefix(options, "v4.", NULL, 0) == PO_FOUND)
+ have_version = 1;
+
+ list = conf_get_tag_list(section, arg);
+ TAILQ_FOREACH(node, &list->fields, link) {
+ /*
+ * Do not overwrite options if already exists
+ */
+ field = mountopts_alias(node->field, &argtype);
+ if (po_contains(options, field) == PO_FOUND)
+ continue;
+ /* Some options can be inverted by a "no" prefix.
+ * Check for these.
+ * "no" prefixes are unlikely in the config file as
+ * "option=false" is preferred, but still possible.
+ */
+ if (strncmp(field, "no", 2) == 0 &&
+ po_contains(options, field+2) == PO_FOUND)
+ continue;
+ if (strlen(field) < BUFSIZ-3) {
+ strcat(strcpy(buf, "no"), field);
+ if (po_contains(options, buf) == PO_FOUND)
+ continue;
+ }
+
+ /* If fg or bg already present, ignore bg or fg */
+ if (strcmp(field, "fg") == 0 &&
+ po_contains(options, "bg") == PO_FOUND)
+ continue;
+ if (strcmp(field, "bg") == 0 &&
+ po_contains(options, "fg") == PO_FOUND)
+ continue;
+
+ if (is_version(field)) {
+ if (have_version)
+ continue;
+ have_version = 1;
+ }
+
+ buf[0] = '\0';
+ value = conf_get_section(section, arg, node->field);
+ if (value == NULL)
+ continue;
+ if (strcasecmp(value, "false") == 0) {
+ if (argtype != MNT_NOARG)
+ snprintf(buf, BUFSIZ, "no%s", field);
+ else if (strcasecmp(field, "bg") == 0)
+ snprintf(buf, BUFSIZ, "fg");
+ else if (strcasecmp(field, "fg") == 0)
+ snprintf(buf, BUFSIZ, "bg");
+ else if (strcasecmp(field, "sloppy") == 0)
+ strict = 1;
+ } else if (strcasecmp(value, "true") == 0) {
+ if ((strcasecmp(field, "sloppy") == 0) && strict)
+ continue;
+ snprintf(buf, BUFSIZ, "%s", field);
+ } else {
+ nvalue = strdup(value);
+ ptr = mountopts_convert(nvalue);
+ snprintf(buf, BUFSIZ, "%s=%s", field, ptr);
+ free(nvalue);
+ }
+ if (buf[0] == '\0')
+ continue;
+
+ po_append(options, buf);
+ default_value(buf);
+ }
+ conf_free_list(list);
+}
+
+/*
+ * Concatenate options from the configuration file with the
+ * given options by building a link list of options from the
+ * different sections in the conf file. Options that exists
+ * in the either the given options or link list are not
+ * overwritten so it matter which when each section is
+ * parsed.
+ */
+char *conf_get_mntopts(char *spec, char *mount_point,
+ char *mount_opts)
+{
+ struct mount_options *options;
+ char *ptr, *server;
+
+ strict = 0;
+ options = po_split(mount_opts);
+ if (!options) {
+ xlog_warn("conf_get_mountops: Unable calloc memory for options");
+ return mount_opts;
+ }
+ /*
+ * First see if there are any mount options relative
+ * to the mount point.
+ */
+ conf_parse_mntopts(NFSMOUNT_MOUNTPOINT, mount_point, options);
+
+ /*
+ * Next, see if there are any mount options relative
+ * to the server
+ */
+ server = strdup(spec);
+ if (server == NULL) {
+ xlog_warn("conf_get_mountops: Unable calloc memory for server");
+ po_destroy(options);
+ return mount_opts;
+ }
+ if ((ptr = strchr(server, ':')) != NULL)
+ *ptr='\0';
+ conf_parse_mntopts(NFSMOUNT_SERVER, server, options);
+ free(server);
+
+ /*
+ * Finally process all the global mount options.
+ */
+ conf_parse_mntopts(NFSMOUNT_GLOBAL_OPTS, NULL, options);
+
+ /*
+ * Strip out defaults, which have already been handled,
+ * then join the rest and return.
+ */
+ while (po_contains_prefix(options, "default", &ptr, 0) == PO_FOUND) {
+ ptr = strdup(ptr);
+ po_remove_all(options, ptr);
+ free(ptr);
+ }
+
+ po_join(options, &mount_opts);
+ po_destroy(options);
+
+ return mount_opts;
+}
diff --git a/utils/mount/error.c b/utils/mount/error.c
new file mode 100644
index 0000000..9ddbcc0
--- /dev/null
+++ b/utils/mount/error.c
@@ -0,0 +1,360 @@
+/*
+ * error.c -- Common error handling functions
+ *
+ * Copyright (C) 2007 Oracle. All rights reserved.
+ * Copyright (C) 2007 Chuck Lever <chuck.lever@oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ *
+ * To Do:
+ * + Proper support for internationalization
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <rpc/rpc.h>
+
+#include "xcommon.h"
+#include "nls.h"
+#include "mount.h"
+#include "error.h"
+
+#ifdef HAVE_RPCSVC_NFS_PROT_H
+#include <rpcsvc/nfs_prot.h>
+#else
+#include <linux/nfs.h>
+#define nfsstat nfs_stat
+#endif
+
+extern char *progname;
+
+static char errbuf[PATH_MAX];
+static char *erreob = &errbuf[PATH_MAX];
+
+/* Convert RPC errors into strings */
+static int rpc_strerror(int spos)
+{
+ int cf_stat = rpc_createerr.cf_stat;
+ int pos = 0, cf_errno = rpc_createerr.cf_error.re_errno;
+ char *ptr, *estr = clnt_sperrno(cf_stat);
+ char *tmp;
+
+ if (estr) {
+ if ((ptr = strchr(estr, ':')))
+ estr = ++ptr;
+
+ tmp = &errbuf[spos];
+ if (cf_stat == RPC_SYSTEMERROR)
+ pos = snprintf(tmp, (erreob - tmp),
+ _("System Error: %s"),
+ strerror(cf_errno));
+ else {
+ if (cf_errno)
+ pos = snprintf(tmp, (erreob - tmp),
+ _("RPC Error:%s; errno = %s"),
+ estr, strerror(cf_errno));
+ else
+ pos = snprintf(tmp, (erreob - tmp),
+ _("RPC Error:%s"), estr);
+ }
+ }
+ return pos;
+}
+
+/**
+ * rpc_mount_errors - log an RPC error that occurred during a user-space mount
+ * @server: C string containing name of server we are attempting to mount
+ * @will_retry: one indicates mount will retry at some later point
+ * @bg: one indicates this is a background mount
+ *
+ * Extracts the error code from the user-space RPC library, and reports it
+ * on stderr (fg mount) or in the system log (bg mount).
+ */
+void rpc_mount_errors(char *server, int will_retry, int bg)
+{
+ int pos = 0;
+ char *tmp;
+ static int onlyonce = 0;
+
+ tmp = &errbuf[pos];
+ if (bg)
+ pos = snprintf(tmp, (erreob - tmp),
+ _("mount to NFS server '%s' failed: "),
+ server);
+ else
+ pos = snprintf(tmp, (erreob - tmp),
+ _("%s: mount to NFS server '%s' failed: "),
+ progname, server);
+
+ tmp = &errbuf[pos];
+ if (rpc_createerr.cf_stat == RPC_TIMEDOUT) {
+ if (will_retry)
+ pos = snprintf(tmp, (erreob - tmp),
+ _("timed out, retrying"));
+ else
+ pos = snprintf(tmp, (erreob - tmp),
+ _("timed out, giving up"));
+ } else {
+ pos += rpc_strerror(pos);
+ tmp = &errbuf[pos];
+ if (bg) {
+ if (will_retry)
+ pos = snprintf(tmp, (erreob - tmp),
+ _(", retrying"));
+ else
+ pos = snprintf(tmp, (erreob - tmp),
+ _(", giving up"));
+ }
+ }
+
+ if (bg) {
+ if (onlyonce++ < 1)
+ openlog("mount", LOG_CONS|LOG_PID, LOG_AUTH);
+ syslog(LOG_ERR, "%s", errbuf);
+ } else
+ fprintf(stderr, "%s\n", errbuf);
+}
+
+/**
+ * sys_mount_errors - log an error that occurred during a mount system call
+ * @server: C string containing name of server we are attempting to mount
+ * @error: errno value to report
+ * @will_retry: one indicates mount will retry at some later point
+ * @bg: one indicates this is a background mount
+ *
+ * Passed an errno value generated by a mount system call, and reports it
+ * on stderr (fg mount) or in the system log (bg mount).
+ */
+void sys_mount_errors(char *server, int error, int will_retry, int bg)
+{
+ int pos = 0;
+ char *tmp;
+ static int onlyonce = 0;
+
+ tmp = &errbuf[pos];
+ if (bg)
+ pos = snprintf(tmp, (erreob - tmp),
+ _("mount to NFS server '%s' failed: "),
+ server);
+ else
+ pos = snprintf(tmp, (erreob - tmp),
+ _("%s: mount to NFS server '%s' failed: "),
+ progname, server);
+
+ tmp = &errbuf[pos];
+ if (error == ETIMEDOUT) {
+ if (will_retry)
+ pos = snprintf(tmp, (erreob - tmp),
+ _("timed out, retrying"));
+ else
+ pos = snprintf(tmp, (erreob - tmp),
+ _("timed out, giving up"));
+ } else {
+ if (bg) {
+ if (will_retry)
+ pos = snprintf(tmp, (erreob - tmp),
+ _("%s, retrying"),
+ strerror(error));
+ else
+ pos = snprintf(tmp, (erreob - tmp),
+ _("%s, giving up"),
+ strerror(error));
+ }
+ }
+
+ if (bg) {
+ if (onlyonce++ < 1)
+ openlog("mount", LOG_CONS|LOG_PID, LOG_AUTH);
+ syslog(LOG_ERR, "%s", errbuf);
+ } else
+ fprintf(stderr, "%s\n", errbuf);
+}
+
+/**
+ * mount_error - report a foreground mount error
+ * @spec: C string containing the device name being mounted
+ * @mount_point: C string containing the pathname of the local mounted on dir
+ * @error: errno value to report
+ *
+ */
+void mount_error(const char *spec, const char *mount_point, int error)
+{
+ switch(error) {
+ case EACCES:
+ nfs_error(_("%s: access denied by server while mounting %s"),
+ progname, spec);
+ break;
+ case EINVAL:
+ nfs_error(_("%s: an incorrect mount option was specified for %s"),
+ progname, mount_point);
+ break;
+ case EOPNOTSUPP:
+ nfs_error(_("%s: requested NFS version or transport protocol is not supported for %s"),
+ progname, mount_point);
+ break;
+ case ENOTDIR:
+ if (spec)
+ nfs_error(_("%s: mount spec %s or point %s is not a directory"),
+ progname, spec, mount_point);
+ else
+ nfs_error(_("%s: mount point %s is not a directory"),
+ progname, mount_point);
+ break;
+ case EBUSY:
+ nfs_error(_("%s: %s is busy or already mounted or sharecache fail"),
+ progname, mount_point);
+ break;
+ case ENOENT:
+ if (spec)
+ nfs_error(_("%s: mounting %s failed, reason given by server: %s"),
+ progname, spec, strerror(error));
+ else
+ nfs_error(_("%s: mount point %s does not exist"),
+ progname, mount_point);
+ break;
+ case ESPIPE:
+ rpc_mount_errors((char *)spec, 0, 0);
+ break;
+ case EIO:
+ nfs_error(_("%s: mount system call failed for %s"),
+ progname, mount_point);
+ break;
+ case EFAULT:
+ nfs_error(_("%s: encountered unexpected error condition for %s."),
+ progname, mount_point);
+ nfs_error(_("%s: please report the error to" PACKAGE_BUGREPORT),
+ progname);
+ break;
+ case EALREADY:
+ /* Error message has already been provided */
+ break;
+ default:
+ nfs_error(_("%s: %s for %s on %s"),
+ progname, strerror(error), spec, mount_point);
+ }
+}
+
+/*
+ * umount_error - report a failed umount request
+ * @err: errno value to report
+ * @dev: C string containing the pathname of the local mounted on dir
+ *
+ */
+void umount_error(int err, const char *dev)
+{
+ switch (err) {
+ case ENXIO:
+ nfs_error(_("%s: %s: invalid block device"),
+ progname, dev);
+ break;
+ case EINVAL:
+ nfs_error(_("%s: %s: not mounted"),
+ progname, dev);
+ break;
+ case EIO:
+ nfs_error(_("%s: %s: can't write superblock"),
+ progname, dev);
+ break;
+ case EBUSY:
+ nfs_error(_("%s: %s: device is busy"),
+ progname, dev);
+ break;
+ case ENOENT:
+ nfs_error(_("%s: %s: not found"),
+ progname, dev);
+ break;
+ case EPERM:
+ nfs_error(_("%s: %s: must be superuser to umount"),
+ progname, dev);
+ break;
+ case EACCES:
+ nfs_error(_("%s: %s: block devices not permitted on fs"),
+ progname, dev);
+ break;
+ default:
+ nfs_error(_("%s: %s: %s"),
+ progname, dev, strerror(err));
+ break;
+ }
+}
+
+/*
+ * We need to translate between nfs status return values and
+ * the local errno values which may not be the same.
+ *
+ * Andreas Schwab <schwab@LS5.informatik.uni-dortmund.de>: change errno:
+ * "after #include <errno.h> the symbol errno is reserved for any use,
+ * it cannot even be used as a struct tag or field name".
+ */
+
+#ifndef EDQUOT
+#define EDQUOT ENOSPC
+#endif
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
+static struct {
+ enum nfsstat stat;
+ int errnum;
+} nfs_errtbl[] = {
+ { NFS_OK, 0 },
+ { NFSERR_PERM, EPERM },
+ { NFSERR_NOENT, ENOENT },
+ { NFSERR_IO, EIO },
+ { NFSERR_NXIO, ENXIO },
+ { NFSERR_ACCES, EACCES },
+ { NFSERR_EXIST, EEXIST },
+ { NFSERR_NODEV, ENODEV },
+ { NFSERR_NOTDIR, ENOTDIR },
+ { NFSERR_ISDIR, EISDIR },
+#ifdef NFSERR_INVAL
+ { NFSERR_INVAL, EINVAL }, /* that Sun forgot */
+#endif
+ { NFSERR_FBIG, EFBIG },
+ { NFSERR_NOSPC, ENOSPC },
+ { NFSERR_ROFS, EROFS },
+ { NFSERR_NAMETOOLONG, ENAMETOOLONG },
+ { NFSERR_NOTEMPTY, ENOTEMPTY },
+ { NFSERR_DQUOT, EDQUOT },
+ { NFSERR_STALE, ESTALE },
+#ifdef EWFLUSH
+ { NFSERR_WFLUSH, EWFLUSH },
+#endif
+ /* Throw in some NFSv3 values for even more fun (HP returns these) */
+ { 71, EREMOTE },
+};
+
+char *nfs_strerror(unsigned int stat)
+{
+ unsigned int i;
+ static char buf[256];
+
+ for (i = 0; i < ARRAY_SIZE(nfs_errtbl); i++) {
+ if (nfs_errtbl[i].stat == stat)
+ return strerror(nfs_errtbl[i].errnum);
+ }
+ sprintf(buf, _("unknown nfs status return value: %u"), stat);
+ return buf;
+}
diff --git a/utils/mount/error.h b/utils/mount/error.h
new file mode 100644
index 0000000..ef80fd0
--- /dev/null
+++ b/utils/mount/error.h
@@ -0,0 +1,35 @@
+/*
+ * error.h: Common error handling functions
+ *
+ * Copyright (C) 2007 Oracle. All rights reserved.
+ * Copyright (C) 2007 Chuck Lever <chuck.lever@oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ *
+ */
+
+#ifndef _NFS_UTILS_MOUNT_ERROR_H
+#define _NFS_UTILS_MOUNT_ERROR_H
+
+char *nfs_strerror(unsigned int);
+
+void mount_error(const char *, const char *, int);
+void rpc_mount_errors(char *, int, int);
+void sys_mount_errors(char *, int, int, int);
+
+void umount_error(int, const char *);
+
+#endif /* _NFS_UTILS_MOUNT_ERROR_H */
diff --git a/utils/mount/fstab.c b/utils/mount/fstab.c
new file mode 100644
index 0000000..146d8f4
--- /dev/null
+++ b/utils/mount/fstab.c
@@ -0,0 +1,653 @@
+/* 1999-02-22 Arkadiusz Miskiewicz <misiek@pld.ORG.PL>
+ * - added Native Language Support
+ * Sun Mar 21 1999 - Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+ * - fixed strerr(errno) in gettext calls
+ *
+ * 2006-06-08 Amit Gud <agud@redhat.com>
+ * - Moved code to nfs-utils/support/nfs from util-linux/mount.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <mntent.h>
+
+#include "fstab.h"
+#include "xcommon.h"
+#include "nfs_mntent.h"
+#include "nfs_paths.h"
+#include "nls.h"
+
+#define LOCK_TIMEOUT 10
+#define streq(s, t) (strcmp ((s), (t)) == 0)
+#define PROC_MOUNTS "/proc/mounts"
+
+extern char *progname;
+extern int verbose;
+
+/* Information about mtab. ------------------------------------*/
+static int have_mtab_info = 0;
+static int var_mtab_does_not_exist = 0;
+static int var_mtab_is_a_symlink = 0;
+
+static void
+get_mtab_info(void) {
+ struct stat mtab_stat;
+
+ if (!have_mtab_info) {
+ if (lstat(MOUNTED, &mtab_stat))
+ var_mtab_does_not_exist = 1;
+ else if (S_ISLNK(mtab_stat.st_mode))
+ var_mtab_is_a_symlink = 1;
+ have_mtab_info = 1;
+ }
+}
+
+void
+reset_mtab_info(void) {
+ have_mtab_info = 0;
+}
+
+int
+mtab_does_not_exist(void) {
+ get_mtab_info();
+ return var_mtab_does_not_exist;
+}
+
+int
+mtab_is_a_symlink(void) {
+ get_mtab_info();
+ return var_mtab_is_a_symlink;
+}
+
+int
+mtab_is_writable() {
+ int fd;
+
+ /* Should we write to /etc/mtab upon an update?
+ Probably not if it is a symlink to /proc/mounts, since that
+ would create a file /proc/mounts in case the proc filesystem
+ is not mounted. */
+ if (mtab_is_a_symlink())
+ return 0;
+
+ fd = open(MOUNTED, O_RDWR | O_CREAT, 0644);
+ if (fd >= 0) {
+ close(fd);
+ return 1;
+ } else
+ return 0;
+}
+
+/* Contents of mtab and fstab ---------------------------------*/
+
+struct mntentchn mounttable;
+static int got_mtab = 0;
+struct mntentchn procmounts;
+static int got_procmounts = 0;
+struct mntentchn fstab;
+static int got_fstab = 0;
+
+static void read_mounttable(void);
+static void read_procmounts(void);
+static void read_fstab(void);
+
+static struct mntentchn *
+mtab_head(void)
+{
+ if (!got_mtab)
+ read_mounttable();
+ return &mounttable;
+}
+
+static struct mntentchn *
+procmounts_head(void)
+{
+ if (!got_procmounts)
+ read_procmounts();
+ return &procmounts;
+}
+
+static struct mntentchn *
+fstab_head(void)
+{
+ if (!got_fstab)
+ read_fstab();
+ return &fstab;
+}
+
+#if 0
+static void
+my_free(const void *s) {
+ if (s)
+ free((void *) s);
+}
+
+static void
+discard_mntentchn(struct mntentchn *mc0) {
+ struct mntentchn *mc, *mc1;
+
+ for (mc = mc0->nxt; mc && mc != mc0; mc = mc1) {
+ mc1 = mc->nxt;
+ my_free(mc->m.mnt_fsname);
+ my_free(mc->m.mnt_dir);
+ my_free(mc->m.mnt_type);
+ my_free(mc->m.mnt_opts);
+ free(mc);
+ }
+}
+#endif
+
+static void
+read_mntentchn(mntFILE *mfp, const char *fnam, struct mntentchn *mc0) {
+ struct mntentchn *mc = mc0;
+ struct mntent *mnt;
+
+ while ((mnt = nfs_getmntent(mfp)) != NULL) {
+ if (!streq(mnt->mnt_type, MNTTYPE_IGNORE)) {
+ mc->nxt = (struct mntentchn *) xmalloc(sizeof(*mc));
+ mc->nxt->prev = mc;
+ mc = mc->nxt;
+ mc->m = *mnt;
+ mc->nxt = mc0;
+ }
+ }
+ mc0->prev = mc;
+ if (ferror(mfp->mntent_fp)) {
+ int errsv = errno;
+ nfs_error(_("warning: error reading %s: %s"),
+ fnam, strerror (errsv));
+ mc0->nxt = mc0->prev = NULL;
+ }
+ nfs_endmntent(mfp);
+}
+
+/*
+ * Read /etc/mtab. If that fails, try /proc/mounts.
+ * This produces a linked list. The list head mounttable is a dummy.
+ * Return 0 on success.
+ */
+static void
+read_mounttable() {
+ mntFILE *mfp;
+ const char *fnam;
+ struct mntentchn *mc = &mounttable;
+
+ got_mtab = 1;
+ mc->nxt = mc->prev = NULL;
+
+ fnam = MOUNTED;
+ mfp = nfs_setmntent (fnam, "r");
+ if (mfp == NULL || mfp->mntent_fp == NULL) {
+ int errsv = errno;
+ fnam = PROC_MOUNTS;
+ mfp = nfs_setmntent (fnam, "r");
+ if (mfp == NULL || mfp->mntent_fp == NULL) {
+ nfs_error(_("warning: can't open %s: %s"),
+ MOUNTED, strerror (errsv));
+ return;
+ }
+ if (verbose)
+ printf(_("%s: could not open %s; using %s instead\n"),
+ progname, MOUNTED, PROC_MOUNTS);
+ }
+ read_mntentchn(mfp, fnam, mc);
+}
+
+/*
+ * Read /proc/mounts.
+ * This produces a linked list. The list head procmounts is a dummy.
+ * Return 0 on success.
+ */
+static void
+read_procmounts() {
+ mntFILE *mfp;
+ const char *fnam;
+ struct mntentchn *mc = &procmounts;
+
+ got_procmounts = 1;
+ mc->nxt = mc->prev = NULL;
+
+ fnam = PROC_MOUNTS;
+ mfp = nfs_setmntent(fnam, "r");
+ if (mfp == NULL || mfp->mntent_fp == NULL) {
+ nfs_error(_("warning: can't open %s: %s"),
+ PROC_MOUNTS, strerror (errno));
+ return;
+ }
+ read_mntentchn(mfp, fnam, mc);
+}
+
+static void
+read_fstab()
+{
+ mntFILE *mfp = NULL;
+ const char *fnam;
+ struct mntentchn *mc = &fstab;
+
+ got_fstab = 1;
+ mc->nxt = mc->prev = NULL;
+
+ fnam = _PATH_FSTAB;
+ mfp = nfs_setmntent (fnam, "r");
+ if (mfp == NULL || mfp->mntent_fp == NULL) {
+ int errsv = errno;
+ nfs_error(_("warning: can't open %s: %s"),
+ _PATH_FSTAB, strerror (errsv));
+ return;
+ }
+ read_mntentchn(mfp, fnam, mc);
+}
+
+/*
+ * Given the directory name NAME, and the place MCPREV we found it last time,
+ * try to find more occurrences.
+ */
+struct mntentchn *
+getmntdirbackward (const char *name, struct mntentchn *mcprev) {
+ struct mntentchn *mc, *mc0;
+
+ mc0 = mtab_head();
+ if (!mcprev)
+ mcprev = mc0;
+ for (mc = mcprev->prev; mc && mc != mc0; mc = mc->prev)
+ if (streq(mc->m.mnt_dir, name))
+ return mc;
+ return NULL;
+}
+
+/*
+ * Given the directory name NAME, and the place MCPREV we found it last time,
+ * try to find more occurrences.
+ */
+struct mntentchn *
+getprocmntdirbackward (const char *name, struct mntentchn *mcprev) {
+ struct mntentchn *mc, *mc0;
+
+ mc0 = procmounts_head();
+ if (!mcprev)
+ mcprev = mc0;
+ for (mc = mcprev->prev; mc && mc != mc0; mc = mc->prev)
+ if (streq(mc->m.mnt_dir, name))
+ return mc;
+ return NULL;
+}
+
+/*
+ * Given the device name NAME, and the place MCPREV we found it last time,
+ * try to find more occurrences.
+ */
+struct mntentchn *
+getmntdevbackward (const char *name, struct mntentchn *mcprev) {
+ struct mntentchn *mc, *mc0;
+
+ mc0 = mtab_head();
+ if (!mcprev)
+ mcprev = mc0;
+ for (mc = mcprev->prev; mc && mc != mc0; mc = mc->prev)
+ if (streq(mc->m.mnt_fsname, name))
+ return mc;
+ return NULL;
+}
+
+/* Find the dir FILE in fstab. */
+struct mntentchn *
+getfsfile (const char *file)
+{
+ struct mntentchn *mc, *mc0;
+
+ mc0 = fstab_head();
+ for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt)
+ if (streq(mc->m.mnt_dir, file))
+ return mc;
+ return NULL;
+}
+
+/* Find the device SPEC in fstab. */
+struct mntentchn *
+getfsspec (const char *spec)
+{
+ struct mntentchn *mc, *mc0;
+
+ mc0 = fstab_head();
+ for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt)
+ if (streq(mc->m.mnt_fsname, spec))
+ return mc;
+ return NULL;
+}
+
+/* Updating mtab ----------------------------------------------*/
+
+/* Flag for already existing lock file. */
+static int we_created_lockfile = 0;
+static int lockfile_fd = -1;
+
+/* Flag to indicate that signals have been set up. */
+static int signals_have_been_setup = 0;
+
+/* Ensure that the lock is released if we are interrupted. */
+extern char *strsignal(int sig); /* not always in <string.h> */
+
+static void
+handler (int sig) {
+ die(EX_USER, "%s", strsignal(sig));
+}
+
+static void
+setlkw_timeout (__attribute__((unused)) int sig) {
+ /* nothing, fcntl will fail anyway */
+}
+
+/* Remove lock file. */
+void
+unlock_mtab (void) {
+ if (we_created_lockfile) {
+ close(lockfile_fd);
+ lockfile_fd = -1;
+ unlink (MOUNTED_LOCK);
+ we_created_lockfile = 0;
+ }
+}
+
+/* Create the lock file.
+ The lock file will be removed if we catch a signal or when we exit. */
+/* The old code here used flock on a lock file /etc/mtab~ and deleted
+ this lock file afterwards. However, as rgooch remarks, that has a
+ race: a second mount may be waiting on the lock and proceed as
+ soon as the lock file is deleted by the first mount, and immediately
+ afterwards a third mount comes, creates a new /etc/mtab~, applies
+ flock to that, and also proceeds, so that the second and third mount
+ now both are scribbling in /etc/mtab.
+ The new code uses a link() instead of a creat(), where we proceed
+ only if it was us that created the lock, and hence we always have
+ to delete the lock afterwards. Now the use of flock() is in principle
+ superfluous, but avoids an arbitrary sleep(). */
+
+/* Where does the link point to? Obvious choices are mtab and mtab~~.
+ HJLu points out that the latter leads to races. Right now we use
+ mtab~.<pid> instead. Use 20 as upper bound for the length of %d. */
+#define MOUNTLOCK_LINKTARGET MOUNTED_LOCK "%d"
+#define MOUNTLOCK_LINKTARGET_LTH (sizeof(MOUNTED_LOCK)+20)
+
+void
+lock_mtab (void) {
+ int tries = 100000, i;
+ char linktargetfile[MOUNTLOCK_LINKTARGET_LTH];
+
+ at_die = unlock_mtab;
+
+ if (!signals_have_been_setup) {
+ int sig = 0;
+ struct sigaction sa;
+
+ sa.sa_flags = 0;
+ sigfillset (&sa.sa_mask);
+
+ while (sigismember (&sa.sa_mask, ++sig) != -1) {
+ switch(sig) {
+ case SIGCHLD:
+ case SIGKILL:
+ case SIGCONT:
+ case SIGSTOP:
+ /* The cannot be caught, or should not,
+ * so don't even try.
+ */
+ continue;
+ case SIGALRM:
+ sa.sa_handler = setlkw_timeout;
+ break;
+ case SIGHUP:
+ case SIGINT:
+ case SIGQUIT:
+ case SIGWINCH:
+ case SIGTSTP:
+ case SIGTTIN:
+ case SIGTTOU:
+ case SIGPIPE:
+ case SIGXFSZ:
+ case SIGXCPU:
+ /* non-priv user can cause these to be
+ * generated, so ignore them.
+ */
+ sa.sa_handler = SIG_IGN;
+ break;
+ default:
+ /* The rest should not be possible, so just
+ * print a message and unlock mtab.
+ */
+ sa.sa_handler = handler;
+ }
+ sigaction (sig, &sa, (struct sigaction *) 0);
+ }
+ signals_have_been_setup = 1;
+ }
+
+ sprintf(linktargetfile, MOUNTLOCK_LINKTARGET, getpid ());
+
+ i = open (linktargetfile, O_WRONLY|O_CREAT, 0);
+ if (i < 0) {
+ int errsv = errno;
+ /* linktargetfile does not exist (as a file)
+ and we cannot create it. Read-only filesystem?
+ Too many files open in the system?
+ Filesystem full? */
+ die (EX_FILEIO, _("can't create lock file %s: %s "
+ "(use -n flag to override)"),
+ linktargetfile, strerror (errsv));
+ }
+ close(i);
+
+ /* Repeat until it was us who made the link */
+ while (!we_created_lockfile) {
+ struct flock flock;
+ int j;
+
+ j = link(linktargetfile, MOUNTED_LOCK);
+
+ {
+ int errsv = errno;
+
+ if (j == 0)
+ we_created_lockfile = 1;
+
+ if (j < 0 && errsv != EEXIST) {
+ (void) unlink(linktargetfile);
+ die (EX_FILEIO, _("can't link lock file %s: %s "
+ "(use -n flag to override)"),
+ MOUNTED_LOCK, strerror (errsv));
+ }
+ }
+
+ lockfile_fd = open (MOUNTED_LOCK, O_WRONLY);
+
+ if (lockfile_fd < 0) {
+ int errsv = errno;
+ /* Strange... Maybe the file was just deleted? */
+ if (errno == ENOENT && tries-- > 0) {
+ if (tries % 200 == 0)
+ usleep(30);
+ continue;
+ }
+ (void) unlink(linktargetfile);
+ die (EX_FILEIO, _("can't open lock file %s: %s "
+ "(use -n flag to override)"),
+ MOUNTED_LOCK, strerror (errsv));
+ }
+
+ flock.l_type = F_WRLCK;
+ flock.l_whence = SEEK_SET;
+ flock.l_start = 0;
+ flock.l_len = 0;
+
+ if (j == 0) {
+ /* We made the link. Now claim the lock. */
+ if (fcntl (lockfile_fd, F_SETLK, &flock) == -1) {
+ if (verbose) {
+ int errsv = errno;
+ nfs_error(_("%s: Can't lock lock file "
+ "%s: %s"), progname,
+ MOUNTED_LOCK,
+ strerror (errsv));
+ }
+ /* proceed anyway */
+ }
+ (void) unlink(linktargetfile);
+ } else {
+ static int retries = 0;
+
+ /* Someone else made the link. Wait. */
+ alarm(LOCK_TIMEOUT);
+ if (fcntl (lockfile_fd, F_SETLKW, &flock) == -1) {
+ int errsv = errno;
+ (void) unlink(linktargetfile);
+ die (EX_FILEIO, _("can't lock lock file %s: %s"),
+ MOUNTED_LOCK, (errno == EINTR) ?
+ _("timed out") : strerror (errsv));
+ }
+ alarm(0);
+ /* Limit the number of iterations - maybe there
+ still is some old /etc/mtab~ */
+ ++retries;
+ if (retries % 200 == 0)
+ usleep(30);
+ if (retries > 100000) {
+ (void) unlink(linktargetfile);
+ close(lockfile_fd);
+ die (EX_FILEIO, _("Cannot create link %s\n"
+ "Perhaps there is a stale lock file?\n"),
+ MOUNTED_LOCK);
+ }
+ close(lockfile_fd);
+ }
+ }
+}
+
+/*
+ * Update the mtab.
+ * Used by umount with null INSTEAD: remove the last DIR entry.
+ * Used by mount upon a remount: update option part,
+ * and complain if a wrong device or type was given.
+ * [Note that often a remount will be a rw remount of /
+ * where there was no entry before, and we'll have to believe
+ * the values given in INSTEAD.]
+ */
+
+void
+update_mtab (const char *dir, struct mntent *instead)
+{
+ mntFILE *mfp, *mftmp;
+ const char *fnam = MOUNTED;
+ struct mntentchn mtabhead; /* dummy */
+ struct mntentchn *mc, *mc0, *absent = NULL;
+
+ if (mtab_does_not_exist() || !mtab_is_writable())
+ return;
+
+ lock_mtab();
+
+ /* having locked mtab, read it again */
+ mc0 = mc = &mtabhead;
+ mc->nxt = mc->prev = NULL;
+
+ mfp = nfs_setmntent(fnam, "r");
+ if (mfp == NULL || mfp->mntent_fp == NULL) {
+ int errsv = errno;
+ nfs_error (_("cannot open %s (%s) - mtab not updated"),
+ fnam, strerror (errsv));
+ goto leave;
+ }
+
+ read_mntentchn(mfp, fnam, mc);
+
+ /* find last occurrence of dir */
+ for (mc = mc0->prev; mc && mc != mc0; mc = mc->prev)
+ if (streq(mc->m.mnt_dir, dir))
+ break;
+ if (mc && mc != mc0) {
+ if (instead == NULL) {
+ /* An umount - remove entry */
+ if (mc && mc != mc0) {
+ mc->prev->nxt = mc->nxt;
+ mc->nxt->prev = mc->prev;
+ free(mc);
+ }
+ } else {
+ /* A remount */
+ mc->m.mnt_opts = instead->mnt_opts;
+ }
+ } else if (instead) {
+ /* not found, add a new entry */
+ absent = xmalloc(sizeof(*absent));
+ absent->m = *instead;
+ absent->nxt = mc0;
+ absent->prev = mc0->prev;
+ mc0->prev = absent;
+ if (mc0->nxt == NULL)
+ mc0->nxt = absent;
+ }
+
+ /* write chain to mtemp */
+ mftmp = nfs_setmntent (MOUNTED_TEMP, "w");
+ if (mftmp == NULL || mftmp->mntent_fp == NULL) {
+ int errsv = errno;
+ nfs_error (_("cannot open %s (%s) - mtab not updated"),
+ MOUNTED_TEMP, strerror (errsv));
+ goto leave;
+ }
+
+ for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt) {
+ if (nfs_addmntent(mftmp, &(mc->m)) == 1) {
+ int errsv = errno;
+ die (EX_FILEIO, _("error writing %s: %s"),
+ MOUNTED_TEMP, strerror (errsv));
+ }
+ }
+
+#if 0
+ /* the chain might have strings copied from 'instead',
+ * so we cannot safely free it.
+ * And there is no need anyway because we are going to exit
+ * shortly. So just don't call discard_mntentchn....
+ */
+ discard_mntentchn(mc0);
+#endif
+ if (fchmod (fileno (mftmp->mntent_fp),
+ S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) {
+ int errsv = errno;
+ nfs_error(_("%s: error changing mode of %s: %s"),
+ progname, MOUNTED_TEMP, strerror (errsv));
+ }
+ nfs_endmntent (mftmp);
+
+ { /*
+ * If mount is setuid and some non-root user mounts sth,
+ * then mtab.tmp might get the group of this user. Copy uid/gid
+ * from the present mtab before renaming.
+ */
+ struct stat sbuf;
+ if (stat (MOUNTED, &sbuf) == 0) {
+ if (chown (MOUNTED_TEMP, sbuf.st_uid, sbuf.st_gid) < 0) {
+ nfs_error(_("%s: error changing owner of %s: %s"),
+ progname, MOUNTED_TEMP, strerror (errno));
+ }
+ }
+ }
+
+ /* rename mtemp to mtab */
+ if (rename (MOUNTED_TEMP, MOUNTED) < 0) {
+ int errsv = errno;
+ nfs_error(_("%s: can't rename %s to %s: %s\n"),
+ progname, MOUNTED_TEMP, MOUNTED,
+ strerror(errsv));
+ }
+
+ leave:
+ unlock_mtab();
+}
diff --git a/utils/mount/fstab.h b/utils/mount/fstab.h
new file mode 100644
index 0000000..8676c8c
--- /dev/null
+++ b/utils/mount/fstab.h
@@ -0,0 +1,32 @@
+#ifndef _NFS_UTILS_MOUNT_FSTAB_H
+#define _NFS_UTILS_MOUNT_FSTAB_H
+
+#include "nfs_mntent.h"
+
+#ifndef _PATH_FSTAB
+#define _PATH_FSTAB "/etc/fstab"
+#endif
+
+int mtab_is_a_symlink(void);
+int mtab_is_writable(void);
+int mtab_does_not_exist(void);
+void reset_mtab_info(void);
+
+struct mntentchn {
+ struct mntentchn *nxt, *prev;
+ struct mntent m;
+};
+
+struct mntentchn *getmntoptfile (const char *file);
+struct mntentchn *getmntdirbackward (const char *dir, struct mntentchn *mc);
+struct mntentchn *getprocmntdirbackward (const char *name, struct mntentchn *mc);
+struct mntentchn *getmntdevbackward (const char *dev, struct mntentchn *mc);
+
+struct mntentchn *getfsfile (const char *file);
+struct mntentchn *getfsspec (const char *spec);
+
+void lock_mtab (void);
+void unlock_mtab (void);
+void update_mtab (const char *special, struct mntent *with);
+
+#endif /* _NFS_UTILS_MOUNT_FSTAB_H */
diff --git a/utils/mount/mount.c b/utils/mount/mount.c
new file mode 100644
index 0000000..b98f9e0
--- /dev/null
+++ b/utils/mount/mount.c
@@ -0,0 +1,558 @@
+/*
+ * mount.c -- Linux NFS mount
+ *
+ * Copyright (C) 2006 Amit Gud <agud@redhat.com>
+ *
+ * - Basic code and wrapper around mount and umount code of NFS.
+ * Based on util-linux/mount/mount.c.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/mount.h>
+#include <getopt.h>
+#include <mntent.h>
+#include <pwd.h>
+#include <libgen.h>
+
+#include "fstab.h"
+#include "xcommon.h"
+#include "nls.h"
+#include "mount_constants.h"
+#include "mount_config.h"
+#include "nfs_paths.h"
+#include "nfs_mntent.h"
+
+#include "nfs_mount.h"
+#include "nfs4_mount.h"
+#include "mount.h"
+#include "error.h"
+#include "stropts.h"
+#include "utils.h"
+
+char *progname;
+int nfs_mount_data_version;
+int nomtab;
+int verbose;
+int sloppy;
+int string;
+
+#define FOREGROUND (0)
+#define BACKGROUND (1)
+
+static struct option longopts[] = {
+ { "fake", 0, 0, 'f' },
+ { "help", 0, 0, 'h' },
+ { "no-mtab", 0, 0, 'n' },
+ { "read-only", 0, 0, 'r' },
+ { "ro", 0, 0, 'r' },
+ { "verbose", 0, 0, 'v' },
+ { "version", 0, 0, 'V' },
+ { "read-write", 0, 0, 'w' },
+ { "rw", 0, 0, 'w' },
+ { "options", 1, 0, 'o' },
+ { NULL, 0, 0, 0 }
+};
+
+/*
+ * Map from -o and fstab option strings to the flag argument to mount(2).
+ */
+struct opt_map {
+ const char *opt; /* option name */
+ int skip; /* skip in mtab option string */
+ int inv; /* true if flag value should be inverted */
+ int mask; /* flag mask value */
+};
+
+static const struct opt_map opt_map[] = {
+ { "defaults", 0, 0, 0 }, /* default options */
+ { "ro", 1, 0, MS_RDONLY }, /* read-only */
+ { "rw", 1, 1, MS_RDONLY }, /* read-write */
+ { "exec", 0, 1, MS_NOEXEC }, /* permit execution of binaries */
+ { "noexec", 0, 0, MS_NOEXEC }, /* don't execute binaries */
+ { "suid", 0, 1, MS_NOSUID }, /* honor suid executables */
+ { "nosuid", 0, 0, MS_NOSUID }, /* don't honor suid executables */
+ { "dev", 0, 1, MS_NODEV }, /* interpret device files */
+ { "nodev", 0, 0, MS_NODEV }, /* don't interpret devices */
+ { "sync", 0, 0, MS_SYNCHRONOUS}, /* synchronous I/O */
+ { "async", 0, 1, MS_SYNCHRONOUS}, /* asynchronous I/O */
+ { "dirsync", 0, 0, MS_DIRSYNC}, /* synchronous directory modifications */
+ { "remount", 0, 0, MS_REMOUNT}, /* Alter flags of mounted FS */
+ { "bind", 0, 0, MS_BIND }, /* Remount part of tree elsewhere */
+ { "rbind", 0, 0, MS_BIND|MS_REC }, /* Idem, plus mounted subtrees */
+ { "auto", 0, 0, MS_DUMMY }, /* Can be mounted using -a */
+ { "noauto", 0, 0, MS_DUMMY }, /* Can only be mounted explicitly */
+ { "users", 1, 0, MS_USERS }, /* Allow ordinary user to mount */
+ { "nousers", 0, 1, MS_DUMMY }, /* Forbid ordinary user to mount */
+ { "user", 1, 0, MS_USER }, /* Allow ordinary user to mount */
+ { "nouser", 0, 1, MS_DUMMY }, /* Forbid ordinary user to mount */
+ { "owner", 0, 0, MS_DUMMY }, /* Let the owner of the device mount */
+ { "noowner", 0, 0, MS_DUMMY }, /* Device owner has no special privs */
+ { "group", 0, 0, MS_DUMMY }, /* Let the group of the device mount */
+ { "nogroup", 0, 0, MS_DUMMY }, /* Device group has no special privs */
+ { "_netdev", 0, 0, MS_DUMMY}, /* Device requires network */
+ { "comment", 0, 0, MS_DUMMY}, /* fstab comment only (kudzu,_netdev)*/
+
+ /* add new options here */
+#ifdef MS_NOSUB
+ { "sub", 0, 1, MS_NOSUB }, /* allow submounts */
+ { "nosub", 0, 0, MS_NOSUB }, /* don't allow submounts */
+#endif
+#ifdef MS_SILENT
+ { "quiet", 0, 0, MS_SILENT }, /* be quiet */
+ { "loud", 0, 1, MS_SILENT }, /* print out messages. */
+#endif
+#ifdef MS_MANDLOCK
+ { "mand", 0, 0, MS_MANDLOCK }, /* Allow mandatory locks on this FS */
+ { "nomand", 0, 1, MS_MANDLOCK }, /* Forbid mandatory locks on this FS */
+#endif
+ { "loop", 1, 0, MS_DUMMY }, /* use a loop device */
+#ifdef MS_NOATIME
+ { "atime", 0, 1, MS_NOATIME }, /* Update access time */
+ { "noatime", 0, 0, MS_NOATIME }, /* Do not update access time */
+#endif
+#ifdef MS_NODIRATIME
+ { "diratime", 0, 1, MS_NODIRATIME }, /* Update dir access times */
+ { "nodiratime", 0, 0, MS_NODIRATIME },/* Do not update dir access times */
+#endif
+#ifdef MS_RELATIME
+ { "relatime", 0, 0, MS_RELATIME }, /* Update access times relative to
+ mtime/ctime */
+ { "norelatime", 0, 1, MS_RELATIME }, /* Update access time without regard
+ to mtime/ctime */
+#endif
+ { "noquota", 0, 0, MS_DUMMY }, /* Don't enforce quota */
+ { "quota", 0, 0, MS_DUMMY }, /* Enforce user quota */
+ { "usrquota", 0, 0, MS_DUMMY }, /* Enforce user quota */
+ { "grpquota", 0, 0, MS_DUMMY }, /* Enforce group quota */
+ { NULL, 0, 0, 0 }
+};
+
+static void parse_opts(const char *options, int *flags, char **extra_opts);
+
+/*
+ * Build a canonical mount option string for /etc/mtab.
+ */
+static char *fix_opts_string(int flags, const char *extra_opts)
+{
+ const struct opt_map *om;
+ char *new_opts;
+
+ new_opts = xstrdup((flags & MS_RDONLY) ? "ro" : "rw");
+ if (flags & MS_USER) {
+ /* record who mounted this so they can unmount */
+ struct passwd *pw = getpwuid(getuid());
+ if(pw)
+ new_opts = xstrconcat3(new_opts, ",user=", pw->pw_name);
+ }
+ if (flags & MS_USERS)
+ new_opts = xstrconcat3(new_opts, ",users", "");
+
+ for (om = opt_map; om->opt != NULL; om++) {
+ if (om->skip)
+ continue;
+ if (om->inv || !om->mask || (flags & om->mask) != om->mask)
+ continue;
+ new_opts = xstrconcat3(new_opts, ",", om->opt);
+ flags &= ~om->mask;
+ }
+ if (extra_opts && *extra_opts) {
+ new_opts = xstrconcat3(new_opts, ",", extra_opts);
+ }
+ return new_opts;
+}
+
+static void
+init_mntent(struct mntent *mnt, char *fsname, char *dir, char *type,
+ int flags, char *opts)
+{
+ mnt->mnt_fsname = fsname;
+ mnt->mnt_dir = dir;
+ mnt->mnt_type = type;
+ mnt->mnt_opts = fix_opts_string(flags & ~MS_NOMTAB, opts);
+
+ /* these are always zero for NFS */
+ mnt->mnt_freq = 0;
+ mnt->mnt_passno = 0;
+}
+
+/* Create mtab with a root entry. */
+static void
+create_mtab (void) {
+ struct mntentchn *fstab;
+ struct mntent mnt;
+ int flags;
+ mntFILE *mfp;
+
+ /* Avoid writing if the mtab is a symlink to /proc/mounts, since
+ that would create a file /proc/mounts in case the proc filesystem
+ is not mounted, and the fchmod below would also fail. */
+ if (mtab_is_a_symlink()) {
+ return;
+ }
+
+ lock_mtab();
+
+ mfp = nfs_setmntent (MOUNTED, "a+");
+ if (mfp == NULL || mfp->mntent_fp == NULL) {
+ int errsv = errno;
+ die (EX_FILEIO, _("mount: can't open %s for writing: %s"),
+ MOUNTED, strerror (errsv));
+ }
+
+ /* Find the root entry by looking it up in fstab */
+ if ((fstab = getfsfile ("/")) || (fstab = getfsfile ("root"))) {
+ char *extra_opts;
+ parse_opts (fstab->m.mnt_opts, &flags, &extra_opts);
+ init_mntent(&mnt, xstrdup(fstab->m.mnt_fsname), "/",
+ fstab->m.mnt_type, flags, extra_opts);
+ free(extra_opts);
+
+ if (nfs_addmntent (mfp, &mnt) == 1) {
+ int errsv = errno;
+ die (EX_FILEIO, _("mount: error writing %s: %s"),
+ _PATH_MOUNTED, strerror (errsv));
+ }
+ }
+ if (fchmod (fileno (mfp->mntent_fp), 0644) < 0)
+ if (errno != EROFS) {
+ int errsv = errno;
+ die (EX_FILEIO,
+ _("mount: error changing mode of %s: %s"),
+ _PATH_MOUNTED, strerror (errsv));
+ }
+ nfs_endmntent (mfp);
+
+ unlock_mtab();
+
+ reset_mtab_info();
+}
+
+static int add_mtab(char *spec, char *mount_point, char *fstype,
+ int flags, char *opts)
+{
+ struct mntent ment;
+ int result = EX_SUCCESS;
+
+ init_mntent(&ment, spec, mount_point, fstype, flags, opts);
+
+ if (!nomtab && mtab_does_not_exist()) {
+ if (verbose > 1)
+ printf(_("mount: no %s found - creating it..\n"),
+ MOUNTED);
+ create_mtab ();
+ }
+
+ if (!nomtab && mtab_is_writable()) {
+ if (flags & MS_REMOUNT)
+ update_mtab(ment.mnt_dir, &ment);
+ else {
+ mntFILE *mtab;
+
+ lock_mtab();
+ mtab = nfs_setmntent(MOUNTED, "a+");
+ if (mtab == NULL || mtab->mntent_fp == NULL) {
+ nfs_error(_("Can't open mtab: %s"),
+ strerror(errno));
+ result = EX_FILEIO;
+ } else {
+ if (nfs_addmntent(mtab, &ment) == 1) {
+ nfs_error(_("Can't write mount entry to mtab: %s"),
+ strerror(errno));
+ result = EX_FILEIO;
+ }
+ }
+ nfs_endmntent(mtab);
+ unlock_mtab();
+ }
+ }
+
+ free(ment.mnt_opts);
+
+ return result;
+}
+
+static void parse_opt(const char *opt, int *mask, char *extra_opts, size_t len)
+{
+ const struct opt_map *om;
+
+ for (om = opt_map; om->opt != NULL; om++) {
+ if (!strcmp (opt, om->opt)) {
+ if (om->inv)
+ *mask &= ~om->mask;
+ else
+ *mask |= om->mask;
+ return;
+ }
+ }
+
+ len -= strlen(extra_opts);
+
+ if (*extra_opts && --len > 0)
+ strcat(extra_opts, ",");
+
+ if ((len -= strlen(opt)) > 0)
+ strcat(extra_opts, opt);
+}
+
+/*
+ * Convert the provided mount command-line options into the 4th &
+ * 5th arguments to mount(2). Output parameter "@flags" gets the
+ * standard options (indicated by MS_ bits), and output parameter
+ * "@extra_opts" gets all the filesystem-specific options.
+ */
+static void parse_opts(const char *options, int *flags, char **extra_opts)
+{
+ if (options != NULL) {
+ char *opts = xstrdup(options);
+ char *opt, *p;
+ size_t len = strlen(opts) + 1; /* include room for a null */
+ int open_quote = 0;
+
+ *extra_opts = xmalloc(len);
+ **extra_opts = '\0';
+
+ for (p = opts, opt = NULL; p && *p; p++) {
+ if (!opt)
+ opt = p; /* begin of the option item */
+ if (*p == '"')
+ open_quote ^= 1; /* reverse the status */
+ if (open_quote)
+ continue; /* still in a quoted block */
+ if (*p == ',')
+ *p = '\0'; /* terminate the option item */
+
+ /* end of option item or last item */
+ if (*p == '\0' || *(p + 1) == '\0') {
+ parse_opt(opt, flags, *extra_opts, len);
+ opt = NULL;
+ }
+ }
+ free(opts);
+ }
+}
+
+static int try_mount(char *spec, char *mount_point, int flags,
+ char *fs_type, char **extra_opts, char *mount_opts,
+ int fake, int bg)
+{
+ int ret;
+
+ if (string)
+ ret = nfsmount_string(spec, mount_point, fs_type, flags,
+ extra_opts, fake, bg);
+ else {
+ if (strcmp(fs_type, "nfs4") == 0)
+ ret = nfs4mount(spec, mount_point, flags,
+ extra_opts, fake, bg);
+ else
+ ret = nfsmount(spec, mount_point, flags,
+ extra_opts, fake, bg);
+ }
+
+ if (ret)
+ return ret;
+
+ if (!fake)
+ print_one(spec, mount_point, fs_type, mount_opts);
+
+ return add_mtab(spec, mount_point, fs_type, flags, *extra_opts);
+}
+
+int main(int argc, char *argv[])
+{
+ int c, flags = 0, mnt_err = 1, fake = 0;
+ char *spec = NULL, *mount_point = NULL, *fs_type = "nfs";
+ char *extra_opts = NULL, *mount_opts = NULL;
+ uid_t uid = getuid();
+
+ progname = basename(argv[0]);
+
+ nfs_mount_data_version = discover_nfs_mount_data_version(&string);
+
+ if(!strncmp(progname, "umount", strlen("umount")))
+ exit(nfsumount(argc, argv));
+
+ mount_config_init(progname);
+
+ while ((c = getopt_long(argc, argv, "rvVwfno:hs",
+ longopts, NULL)) != -1) {
+ switch (c) {
+ case 'r':
+ flags |= MS_RDONLY;
+ break;
+ case 'v':
+ ++verbose;
+ break;
+ case 'V':
+ printf("%s: ("PACKAGE_STRING")\n", progname);
+ exit(EX_SUCCESS);
+ case 'w':
+ flags &= ~MS_RDONLY;
+ break;
+ case 'f':
+ ++fake;
+ break;
+ case 'n':
+ ++nomtab;
+ break;
+ case 'o': /* specify mount options */
+ if (mount_opts)
+ mount_opts = xstrconcat3(mount_opts, ",", optarg);
+ else
+ mount_opts = xstrdup(optarg);
+ break;
+ case 's':
+ ++sloppy;
+ break;
+ case 'h':
+ default:
+ mount_usage();
+ goto out_usage;
+ }
+ }
+
+ if ((argc < 3)) {
+ mount_usage();
+ exit(EX_USAGE);
+ }
+
+ /*
+ * Extra non-option words at the end are bogus...
+ */
+ if (optind != argc - 2) {
+ mount_usage();
+ goto out_usage;
+ } else {
+ while (optind < argc) {
+ if (!spec)
+ spec = argv[optind];
+ else
+ mount_point = argv[optind];
+ optind++;
+ }
+ }
+
+ if (strcmp(progname, "mount.nfs4") == 0)
+ fs_type = "nfs4";
+
+ /*
+ * If a non-root user is attempting to mount, make sure the
+ * user's requested options match the options specified in
+ * /etc/fstab; otherwise, don't allow the mount.
+ */
+ if (uid != 0) {
+ struct mntentchn *mc;
+
+ if ((mc = getfsfile(mount_point)) == NULL ||
+ strcmp(mc->m.mnt_fsname, spec) != 0 ||
+ strcmp(mc->m.mnt_type, fs_type) != 0) {
+ nfs_error(_("%s: permission denied: no match for %s "
+ "found in /etc/fstab"), progname, mount_point);
+ goto out_usage;
+ }
+
+ /*
+ * 'mount' munges the options from fstab before passing them
+ * to us, so it is non-trivial to test that we have the correct
+ * set of options and we don't want to trust what the user
+ * gave us, so just take whatever is in /etc/fstab.
+ */
+ mount_opts = strdup(mc->m.mnt_opts);
+ }
+
+ mount_point = canonicalize(mount_point);
+ if (!mount_point) {
+ nfs_error(_("%s: no mount point provided"), progname);
+ goto out_usage;
+ }
+ if (mount_point[0] != '/') {
+ nfs_error(_("%s: unrecognized mount point %s"),
+ progname, mount_point);
+ mnt_err = EX_USAGE;
+ goto out;
+ }
+ /*
+ * Concatenate mount options from the configuration file
+ */
+ mount_opts = mount_config_opts(spec, mount_point, mount_opts);
+
+ parse_opts(mount_opts, &flags, &extra_opts);
+
+ if (uid != 0) {
+ if (!(flags & (MS_USERS|MS_USER))) {
+ nfs_error(_("%s: permission denied"), progname);
+ mnt_err = EX_USAGE;
+ goto out;
+ }
+
+ if (geteuid() != 0) {
+ nfs_error(_("%s: not installed setuid - "
+ "\"user\" NFS mounts not supported."), progname);
+ exit(EX_FAIL);
+ }
+ }
+
+ if (chk_mountpoint(mount_point)) {
+ mnt_err = EX_USAGE;
+ goto out;
+ }
+
+ mnt_err = try_mount(spec, mount_point, flags, fs_type, &extra_opts,
+ mount_opts, fake, FOREGROUND);
+ if (mnt_err == EX_BG) {
+ printf(_("%s: backgrounding \"%s\"\n"),
+ progname, spec);
+ printf(_("%s: mount options: \"%s\"\n"),
+ progname, extra_opts);
+
+ fflush(stdout);
+
+ /*
+ * Parent exits immediately with success.
+ */
+ if (daemon(0, 0)) {
+ nfs_error(_("%s: failed to start "
+ "background process: %s\n"),
+ progname, strerror(errno));
+ exit(EX_FAIL);
+ }
+
+ mnt_err = try_mount(spec, mount_point, flags, fs_type,
+ &extra_opts, mount_opts, fake,
+ BACKGROUND);
+ if (verbose && mnt_err)
+ printf(_("%s: giving up \"%s\"\n"),
+ progname, spec);
+ }
+
+out:
+ free(mount_opts);
+ free(extra_opts);
+ free(mount_point);
+ exit(mnt_err);
+
+out_usage:
+ free(mount_opts);
+ exit(EX_USAGE);
+}
diff --git a/utils/mount/mount.nfs.man b/utils/mount/mount.nfs.man
new file mode 100644
index 0000000..a78a3b0
--- /dev/null
+++ b/utils/mount/mount.nfs.man
@@ -0,0 +1,93 @@
+.\"@(#)mount.nfs.8"
+.TH MOUNT.NFS 8 "5 Jun 2006"
+.SH NAME
+mount.nfs, mount.nfs4 \- mount a Network File System
+.SH SYNOPSIS
+.BI "mount.nfs" " remotetarget dir" " [\-rvVwfnsh ] [\-o " options "]
+.SH DESCRIPTION
+.BR mount.nfs
+is a part of
+.BR nfs (5)
+utilities package, which provides NFS client functionality.
+
+.BR mount.nfs
+is meant to be used by the
+.BR mount (8)
+command for mounting NFS shares. This subcommand, however, can also be used as a standalone command with limited functionality.
+
+.I remotetarget
+is a server share usually in the form of
+.BR servername:/path/to/share.
+.I dir
+is the directory on which the file system is to be mounted.
+
+Under Linux 2.6.32 and later kernel versions,
+.BR mount.nfs
+can mount all NFS file system versions. Under earlier Linux kernel versions,
+.BR mount.nfs4
+must be used for mounting NFSv4 file systems while
+.BR mount.nfs
+must be used for NFSv3.
+
+.SH OPTIONS
+.TP
+.BI "\-r"
+Mount file system readonly.
+.TP
+.BI "\-v"
+Be verbose.
+.TP
+.BI "\-V"
+Print version.
+.TP
+.BI "\-w"
+Mount file system read-write.
+.TP
+.BI "\-f"
+Fake mount. Don't actually call the mount system call.
+.TP
+.BI "\-n"
+Do not update
+.I /etc/mtab.
+By default, an entry is created in
+.I /etc/mtab
+for every mounted file system. Use this option to skip making an entry.
+.TP
+.BI "\-s"
+Tolerate sloppy mount options rather than fail.
+.TP
+.BI "\-h"
+Print help message.
+.TP
+.BI "nfsoptions"
+Refer to
+.BR nfs (5)
+or
+.BR mount (8)
+manual pages.
+
+.SH NOTE
+For further information please refer
+.BR nfs (5)
+and
+.BR mount (8)
+manual pages.
+
+.SH FILES
+.TP 18n
+.I /etc/fstab
+file system table
+.TP
+.I /etc/mtab
+table of mounted file systems
+.TP
+.I /etc/nfsmount.conf
+Configuration file for NFS mounts
+.PD
+.SH "SEE ALSO"
+.BR nfs (5),
+.BR nfsmount.conf (5),
+.BR mount (8),
+
+.SH "AUTHOR"
+Amit Gud <agud@redhat.com>
diff --git a/utils/mount/mount_config.h b/utils/mount/mount_config.h
new file mode 100644
index 0000000..7cc72fc
--- /dev/null
+++ b/utils/mount/mount_config.h
@@ -0,0 +1,55 @@
+#ifndef _LINUX_MOUNT_CONFIG_H
+#define _LINUX_MOUNT_CONFIG_H
+/*
+ * mount_config.h -- mount configuration file routines
+ * Copyright (C) 2008 Red Hat, Inc <nfs@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifdef MOUNT_CONFIG
+#include "conffile.h"
+#include "xlog.h"
+
+#ifndef MOUNTOPTS_CONFFILE
+#define MOUNTOPTS_CONFFILE "/etc/nfsmount.conf"
+#endif
+
+extern char *conf_get_mntopts(char *, char *, char *);
+
+static inline void mount_config_init(char *program)
+{
+ xlog_open(program);
+ /*
+ * Read the the default mount options
+ */
+ conf_init_file(MOUNTOPTS_CONFFILE);
+}
+
+static inline char *mount_config_opts(char *spec,
+ char *mount_point, char *mount_opts)
+{
+ return conf_get_mntopts(spec, mount_point, mount_opts);
+}
+
+#else /* MOUNT_CONFIG */
+
+static inline void mount_config_init(__attribute__ ((unused)) char *program) { }
+
+static inline char *mount_config_opts(__attribute__ ((unused)) char *spec,
+ __attribute__ ((unused)) char *mount_point, char *mount_opts)
+{
+ return mount_opts;
+}
+#endif /* MOUNT_CONFIG */
+
+#endif /* _LINUX_MOUNT_CONFIG_H */
diff --git a/utils/mount/mount_constants.h b/utils/mount/mount_constants.h
new file mode 100644
index 0000000..4d050d8
--- /dev/null
+++ b/utils/mount/mount_constants.h
@@ -0,0 +1,71 @@
+#ifndef _NFS_UTILS_MOUNT_CONSTANTS_H
+#define _NFS_UTILS_MOUNT_CONSTANTS_H
+
+#ifndef MS_DIRSYNC
+#define MS_DIRSYNC 128 /* Directory modifications are synchronous */
+#endif
+
+#ifndef MS_ACTION_MASK
+#define MS_ACTION_MASK 0x380
+/* Remount, but new filesystem may be different from old. Atomic
+ (i.e. there is no interval when nothing is mounted at the mountpoint).
+ If new fs differs from the old one and old is busy - -EBUSY. */
+#define MS_REPLACE 0x080 /* 128 */
+/* After, Before: as soon as we get unions these will add a new member
+ in the end or beginning of the chain. Fail if there is a stack
+ on the mountpoint. */
+#define MS_AFTER 0x100 /* 256 */
+#define MS_BEFORE 0x180
+/* Over: if nothing mounted on a mountpoint - same as if none of these
+flags had been set; if we have a union with more than one element - fail;
+if we have a stack or plain mount - mount atop of it, forming a stack. */
+#define MS_OVER 0x200 /* 512 */
+#endif
+#ifndef MS_NOATIME
+#define MS_NOATIME 0x400 /* 1024: Do not update access times. */
+#endif
+#ifndef MS_NODIRATIME
+#define MS_NODIRATIME 0x800 /* 2048: Don't update directory access times */
+#endif
+#ifndef MS_BIND
+#define MS_BIND 0x1000 /* 4096: Mount existing tree also elsewhere */
+#endif
+#ifndef MS_MOVE
+#define MS_MOVE 0x2000 /* 8192: Atomically move tree */
+#endif
+#ifndef MS_REC
+#define MS_REC 0x4000 /* 16384: Recursive loopback */
+#endif
+#ifndef MS_VERBOSE
+#define MS_VERBOSE 0x8000 /* 32768 */
+#endif
+#ifndef MS_RELATIME
+#define MS_RELATIME 0x200000 /* 200000: Update access times relative
+ to mtime/ctime */
+#endif
+/*
+ * NFS fs-specific mount option flags
+ *
+ * MS_DUMMY is assigned to mount options that are not to be
+ * passed to the kernel via the "flags" argument. These are
+ * generally ignored or handled entirely in user space.
+ */
+#define MS_DUMMY 0x00000000
+#define MS_USERS 0x40000000
+#define MS_USER 0x80000000
+
+/*
+ * Magic mount flag number. Had to be or-ed to the flag values.
+ */
+#ifndef MS_MGC_VAL
+#define MS_MGC_VAL 0xC0ED0000 /* magic flag number to indicate "new" flags */
+#endif
+#ifndef MS_MGC_MSK
+#define MS_MGC_MSK 0xffff0000 /* magic flag number mask */
+#endif
+
+/* Generic options that are prevented from appearing
+ * in the options field in /etc/mtab. */
+#define MS_NOMTAB (MS_REMOUNT)
+
+#endif /* _NFS_UTILS_MOUNT_CONSTANTS_H */
diff --git a/utils/mount/mount_libmount.c b/utils/mount/mount_libmount.c
new file mode 100644
index 0000000..aa4ac5c
--- /dev/null
+++ b/utils/mount/mount_libmount.c
@@ -0,0 +1,461 @@
+/*
+ * mount_libmount.c -- Linux NFS [u]mount based on libmount
+ *
+ * Copyright (C) 2011 Karel Zak <kzak@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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 <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <getopt.h>
+#include <libgen.h>
+
+#include <libmount/libmount.h>
+
+#include "nls.h"
+#include "mount_config.h"
+
+#include "nfs_mount.h"
+#include "nfs4_mount.h"
+#include "stropts.h"
+#include "version.h"
+#include "xcommon.h"
+
+#include "error.h"
+#include "utils.h"
+
+char *retrieve_mount_options(struct libmnt_fs *fs);
+
+char *progname;
+int nfs_mount_data_version;
+int verbose;
+int sloppy;
+int string;
+int nomtab;
+
+#define FOREGROUND (0)
+#define BACKGROUND (1)
+
+/*
+ * Store mount options to mtab (or /dev/.mount/utab), called from mount.nfs.
+ *
+ * Note that on systems without /etc/mtab the fs-specific options are not
+ * managed by libmount at all. We have to use "mount attributes" that are
+ * private for mount.<type> helpers.
+ */
+static void store_mount_options(struct libmnt_fs *fs, const char *nfs_opts)
+{
+ char *o = NULL;
+
+ mnt_fs_set_attributes(fs, nfs_opts); /* for non-mtab systems */
+
+ /* for mtab create a new options list */
+ mnt_optstr_append_option(&o, mnt_fs_get_vfs_options(fs), NULL);
+ mnt_optstr_append_option(&o, nfs_opts, NULL);
+ mnt_optstr_append_option(&o, mnt_fs_get_user_options(fs), NULL);
+
+ mnt_fs_set_options(fs, o);
+ free(o);
+}
+
+/*
+ * Retrieve mount options from mtab (or /dev/.mount/utab) called from umount.nfs.
+ *
+ * The result can passed to free().
+ */
+char *retrieve_mount_options(struct libmnt_fs *fs)
+{
+ const char *opts;
+
+ if (!fs)
+ return NULL;
+
+ opts = mnt_fs_get_attributes(fs); /* /dev/.mount/utab */
+ if (opts)
+ return strdup(opts);
+
+ return mnt_fs_strdup_options(fs); /* /etc/mtab */
+}
+
+static int try_mount(struct libmnt_context *cxt, int bg)
+{
+ struct libmnt_fs *fs;
+ const char *p;
+ char *src = NULL, *tgt = NULL, *type = NULL, *opts = NULL;
+ unsigned long flags = 0;
+ int fake, ret = 0;
+
+ fs = mnt_context_get_fs(cxt);
+
+ /* libmount returns read-only pointers (const char)
+ * so, reallocate for nfsmount() functions.
+ */
+ if ((p = mnt_fs_get_source(fs))) /* spec */
+ src = strdup(p);
+ if ((p = mnt_fs_get_target(fs))) /* mountpoint */
+ tgt = strdup(p);
+ if ((p = mnt_fs_get_fstype(fs))) /* FS type */
+ type = strdup(p);
+ if ((p = mnt_fs_get_fs_options(fs))) /* mount options */
+ opts = strdup(p);
+
+ mnt_context_get_mflags(cxt, &flags); /* mount(2) flags */
+ fake = mnt_context_is_fake(cxt);
+
+ if (string)
+ ret = nfsmount_string(src, tgt, type, flags, &opts, fake, bg);
+
+ else if (strcmp(type, "nfs4") == 0)
+ ret = nfs4mount(src, tgt, flags, &opts, fake, bg);
+ else
+ ret = nfsmount(src, tgt, flags, &opts, fake, bg);
+
+ /* Store mount options if not called with mount --no-mtab */
+ if (!ret && !mnt_context_is_nomtab(cxt))
+ store_mount_options(fs, opts);
+
+ free(src);
+ free(tgt);
+ free(type);
+ free(opts);
+
+ return ret;
+}
+
+/* returns: error = -1, success = 1 , not vers4 == 0 */
+static int is_vers4(struct libmnt_context *cxt)
+{
+ struct libmnt_fs *fs = mnt_context_get_fs(cxt);
+ struct libmnt_table *tb = NULL;
+ const char *src = mnt_context_get_source(cxt),
+ *tgt = mnt_context_get_target(cxt);
+ int rc = 0;
+
+ if (!src || !tgt)
+ return -1;
+
+ if (!mnt_fs_is_kernel(fs)) {
+ struct libmnt_table *tb = mnt_new_table_from_file("/proc/mounts");
+
+ if (!tb)
+ return -1;
+ fs = mnt_table_find_pair(tb, src, tgt, MNT_ITER_BACKWARD);
+ }
+
+ if (fs) {
+ const char *type = mnt_fs_get_fstype(fs);
+ if (type && strcmp(type, "nfs4") == 0)
+ rc = 1;
+ }
+ mnt_free_table(tb);
+ return rc;
+}
+
+static int umount_main(struct libmnt_context *cxt, int argc, char **argv)
+{
+ int rc, c;
+ char *spec = NULL, *opts = NULL;
+ int ret = EX_FAIL, verbose = 0;
+
+ static const struct option longopts[] = {
+ { "force", 0, 0, 'f' },
+ { "help", 0, 0, 'h' },
+ { "no-mtab", 0, 0, 'n' },
+ { "verbose", 0, 0, 'v' },
+ { "read-only", 0, 0, 'r' },
+ { "lazy", 0, 0, 'l' },
+ { "types", 1, 0, 't' },
+ { NULL, 0, 0, 0 }
+ };
+
+ mnt_context_init_helper(cxt, MNT_ACT_UMOUNT, 0);
+ mnt_context_disable_canonicalize(cxt, 1);
+
+ while ((c = getopt_long (argc, argv, "fvnrlh", longopts, NULL)) != -1) {
+
+ rc = mnt_context_helper_setopt(cxt, c, optarg);
+ if (rc == 0) /* valid option */
+ continue;
+ if (rc < 0) /* error (probably ENOMEM) */
+ goto err;
+ /* rc==1 means unknow option */
+ umount_usage();
+ return EX_USAGE;
+ }
+
+ verbose = mnt_context_is_verbose(cxt);
+
+ if (optind < argc)
+ spec = argv[optind++];
+
+ if (!spec || (*spec != '/' && strchr(spec,':') == NULL)) {
+ nfs_error(_("%s: no mount point provided"), progname);
+ umount_usage();
+ return EX_USAGE;
+ }
+
+ if (mnt_context_set_target(cxt, spec))
+ goto err;
+
+ /* read mtab/fstab, evaluate permissions, etc. */
+ rc = mnt_context_prepare_umount(cxt);
+ if (rc) {
+ nfs_error(_("%s: failed to prepare umount: %s\n"),
+ progname, strerror(-rc));
+ goto err;
+ }
+
+ if (mnt_context_get_fstype(cxt) &&
+ !mnt_match_fstype(mnt_context_get_fstype(cxt), "nfs,nfs4")) {
+
+ nfs_error(_("%s: %s: is not an NFS filesystem"), progname, spec);
+ ret = EX_USAGE;
+ goto err;
+ }
+
+ if (verbose)
+ printf(_("%s: %s mount point detected\n"), spec,
+ mnt_context_get_fstype(cxt));
+
+ opts = retrieve_mount_options(mnt_context_get_fs(cxt));
+
+ if (!mnt_context_is_lazy(cxt)) {
+ if (opts) {
+ /* we have full FS description (e.g. from mtab or /proc) */
+ switch (is_vers4(cxt)) {
+ case 0:
+ /* We ignore the error from nfs_umount23.
+ * If the actual umount succeeds (in del_mtab),
+ * we don't want to signal an error, as that
+ * could cause /sbin/mount to retry!
+ */
+ nfs_umount23(mnt_context_get_source(cxt), opts);
+ break;
+ case 1: /* unknown */
+ break;
+ default: /* error */
+ goto err;
+ }
+ } else
+ /* strange, no entry in mtab or /proc not mounted */
+ nfs_umount23(spec, "tcp,v3");
+ }
+
+ ret = EX_FILEIO;
+ rc = mnt_context_do_umount(cxt); /* call umount(2) syscall */
+ mnt_context_finalize_mount(cxt); /* mtab update */
+
+ if (rc && !mnt_context_get_status(cxt)) {
+ /* mnt_context_do_umount() returns errno if umount(2) failed */
+ umount_error(rc, spec);
+ goto err;
+ }
+ ret = EX_SUCCESS;
+err:
+ if (verbose) {
+ if (ret == EX_SUCCESS)
+ printf(_("%s: umounted\n"), spec);
+ else
+ printf(_("%s: umount failed\n"), spec);
+ }
+ free(opts);
+ return ret;
+}
+
+static int mount_main(struct libmnt_context *cxt, int argc, char **argv)
+{
+ int rc, c;
+ struct libmnt_fs *fs;
+ char *spec = NULL, *mount_point = NULL, *opts = NULL;
+
+ static const struct option longopts[] = {
+ { "fake", 0, 0, 'f' },
+ { "help", 0, 0, 'h' },
+ { "no-mtab", 0, 0, 'n' },
+ { "read-only", 0, 0, 'r' },
+ { "ro", 0, 0, 'r' },
+ { "verbose", 0, 0, 'v' },
+ { "version", 0, 0, 'V' },
+ { "read-write", 0, 0, 'w' },
+ { "rw", 0, 0, 'w' },
+ { "options", 1, 0, 'o' },
+ { "sloppy", 0, 0, 's' },
+ { NULL, 0, 0, 0 }
+ };
+
+ mount_config_init(progname);
+ mnt_context_init_helper(cxt, MNT_ACT_MOUNT, 0);
+
+ while ((c = getopt_long(argc, argv, "fhnrVvwo:s", longopts, NULL)) != -1) {
+
+ rc = mnt_context_helper_setopt(cxt, c, optarg);
+ if (rc == 0) /* valid option */
+ continue;
+ if (rc < 0) /* error (probably ENOMEM) */
+ goto err;
+ /* rc==1 means unknow option */
+ switch (c) {
+ case 'V':
+ printf("%s: ("PACKAGE_STRING")\n", progname);
+ return EX_SUCCESS;
+ case 'h':
+ default:
+ mount_usage();
+ return EX_USAGE;
+ }
+ }
+
+ if (optind < argc)
+ spec = argv[optind++];
+ if (optind < argc)
+ mount_point = argv[optind++];
+
+ if (!mount_point) {
+ nfs_error(_("%s: no mount point provided"), progname);
+ mount_usage();
+ goto err;
+ }
+ if (!spec) {
+ nfs_error(_("%s: no mount spec provided"), progname);
+ goto err;
+ }
+
+ if (geteuid() != 0) {
+ nfs_error(_("%s: not installed setuid - "
+ "\"user\" NFS mounts not supported."), progname);
+ goto err;
+ }
+
+ verbose = mnt_context_is_verbose(cxt);
+ sloppy = mnt_context_is_sloppy(cxt);
+ nomtab = mnt_context_is_nomtab(cxt);
+
+ if (strcmp(progname, "mount.nfs4") == 0)
+ mnt_context_set_fstype(cxt, "nfs4");
+ else
+ mnt_context_set_fstype(cxt, "nfs"); /* default */
+
+ rc = mnt_context_set_source(cxt, spec);
+ if (!rc)
+ mnt_context_set_target(cxt, mount_point);
+ if (rc) {
+ nfs_error(_("%s: failed to set spec or mountpoint: %s"),
+ progname, strerror(errno));
+ goto err;
+ }
+
+ mount_point = mnt_resolve_path(mount_point,
+ mnt_context_get_cache(cxt));
+
+ if (chk_mountpoint(mount_point))
+ goto err;
+
+ /*
+ * The libmount strictly uses only options from fstab if running in
+ * restricted mode (suid, non-root user). This is done in
+ * mnt_context_prepare_mount() by default.
+ *
+ * We have to read fstab before nfsmount.conf, otherwise the options
+ * from nfsmount.conf will be ignored (overwrited).
+ */
+ rc = mnt_context_apply_fstab(cxt);
+ if (rc) {
+ nfs_error(_("%s: failed to apply fstab options\n"), progname);
+ goto err;
+ }
+
+ /*
+ * Concatenate mount options from the configuration file
+ */
+ fs = mnt_context_get_fs(cxt);
+ if (fs) {
+ opts = mnt_fs_strdup_options(fs);
+
+ opts = mount_config_opts(spec, mount_point, opts);
+ mnt_fs_set_options(fs, opts);
+ }
+
+ rc = mnt_context_prepare_mount(cxt);
+ if (rc) {
+ nfs_error(_("%s: failed to prepare mount: %s\n"),
+ progname, strerror(-rc));
+ goto err;
+ }
+
+ rc = try_mount(cxt, FOREGROUND);
+
+ if (rc == EX_BG) {
+ printf(_("%s: backgrounding \"%s\"\n"),
+ progname, mnt_context_get_source(cxt));
+ printf(_("%s: mount options: \"%s\"\n"),
+ progname, opts);
+
+ fflush(stdout);
+
+ if (daemon(0, 0)) {
+ nfs_error(_("%s: failed to start "
+ "background process: %s\n"),
+ progname, strerror(errno));
+ exit(EX_FAIL);
+ }
+
+ rc = try_mount(cxt, BACKGROUND);
+
+ if (verbose && rc)
+ printf(_("%s: giving up \"%s\"\n"),
+ progname, mnt_context_get_source(cxt));
+ }
+
+ mnt_context_set_syscall_status(cxt, rc == EX_SUCCESS ? 0 : -1);
+ mnt_context_finalize_mount(cxt); /* mtab update */
+ return rc;
+err:
+ return EX_FAIL;
+}
+
+int main(int argc, char *argv[])
+{
+ struct libmnt_context *cxt;
+ int rc;
+
+ mnt_init_debug(0);
+ cxt = mnt_new_context();
+ if (!cxt) {
+ nfs_error(_("Can't initilize libmount: %s"),
+ strerror(errno));
+ rc = EX_FAIL;
+ goto done;
+ }
+
+ progname = basename(argv[0]);
+ nfs_mount_data_version = discover_nfs_mount_data_version(&string);
+
+ if(strncmp(progname, "umount", 6) == 0)
+ rc = umount_main(cxt, argc, argv);
+ else
+ rc = mount_main(cxt, argc, argv);
+done:
+ mnt_free_context(cxt);
+ return rc;
+}
diff --git a/utils/mount/network.c b/utils/mount/network.c
new file mode 100644
index 0000000..01ead49
--- /dev/null
+++ b/utils/mount/network.c
@@ -0,0 +1,1801 @@
+/*
+ * network.c -- Provide common network functions for NFS mount/umount
+ *
+ * Copyright (C) 2007 Oracle. All rights reserved.
+ * Copyright (C) 2007 Chuck Lever <chuck.lever@oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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 <ctype.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <netdb.h>
+#include <time.h>
+#include <grp.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#if defined(__GLIBC__) && ((__GLIBC__ < 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 24))
+/* Cannot safely include linux/in6.h in old glibc, so hardcode the needed values */
+# define IPV6_PREFER_SRC_PUBLIC 2
+# define IPV6_ADDR_PREFERENCES 72
+#else
+# include <linux/in6.h>
+#endif
+#include <netinet/in.h>
+#include <rpc/rpc.h>
+#include <rpc/pmap_prot.h>
+#include <rpc/pmap_clnt.h>
+#include <net/if.h>
+#include <ifaddrs.h>
+
+#include "sockaddr.h"
+#include "xcommon.h"
+#include "mount.h"
+#include "nls.h"
+#include "nfs_mount.h"
+#include "mount_constants.h"
+#include "nfsrpc.h"
+#include "parse_opt.h"
+#include "network.h"
+#include "conffile.h"
+#include "nfslib.h"
+
+#define PMAP_TIMEOUT (10)
+#define CONNECT_TIMEOUT (20)
+#define MOUNT_TIMEOUT (30)
+#define STATD_TIMEOUT (10)
+
+#define SAFE_SOCKADDR(x) (struct sockaddr *)(char *)(x)
+
+extern int nfs_mount_data_version;
+extern char *progname;
+extern int verbose;
+
+static const char *nfs_mnt_pgmtbl[] = {
+ "mount",
+ "mountd",
+ NULL,
+};
+
+static const char *nfs_nfs_pgmtbl[] = {
+ "nfs",
+ "nfsprog",
+ NULL,
+};
+
+static const char *nfs_transport_opttbl[] = {
+ "udp",
+ "tcp",
+ "rdma",
+ "proto",
+ NULL,
+};
+
+static const char *nfs_version_opttbl[] = {
+ "v2", /* no longer supported */
+ "v3",
+ "v4",
+ "vers",
+ "nfsvers",
+ NULL,
+};
+
+static const unsigned long nfs_to_mnt[] = {
+ 0,
+ 0,
+ 1,
+ 3,
+};
+
+static const unsigned long mnt_to_nfs[] = {
+ 0,
+ 2,
+ 2,
+ 3,
+};
+
+/*
+ * Map an NFS version into the corresponding Mountd version
+ */
+unsigned long nfsvers_to_mnt(const unsigned long vers)
+{
+ if (vers <= 3)
+ return nfs_to_mnt[vers];
+ return 0;
+}
+
+/*
+ * Map a Mountd version into the corresponding NFS version
+ */
+static unsigned long mntvers_to_nfs(const unsigned long vers)
+{
+ if (vers <= 3)
+ return mnt_to_nfs[vers];
+ return 0;
+}
+
+static const unsigned int probe_udp_only[] = {
+ IPPROTO_UDP,
+ 0,
+};
+
+static const unsigned int probe_udp_first[] = {
+ IPPROTO_UDP,
+ IPPROTO_TCP,
+ 0,
+};
+
+static const unsigned int probe_tcp_first[] = {
+ IPPROTO_TCP,
+ IPPROTO_UDP,
+ 0,
+};
+
+static const unsigned long probe_nfs2_only[] = {
+ 2,
+ 0,
+};
+
+static const unsigned long probe_nfs3_only[] = {
+ 3,
+ 0,
+};
+
+static const unsigned long probe_mnt1_first[] = {
+ 1,
+ 2,
+ 0,
+};
+
+static const unsigned long probe_mnt3_only[] = {
+ 3,
+ 0,
+};
+
+static const unsigned int *nfs_default_proto(void);
+#ifdef MOUNT_CONFIG
+static const unsigned int *nfs_default_proto(void)
+{
+ extern unsigned long config_default_proto;
+ /*
+ * If the default proto has been set and
+ * its not TCP, start with UDP
+ */
+ if (config_default_proto && config_default_proto != IPPROTO_TCP)
+ return probe_udp_first;
+
+ return probe_tcp_first;
+}
+#else
+static const unsigned int *nfs_default_proto()
+{
+ return probe_tcp_first;
+}
+#endif /* MOUNT_CONFIG */
+
+/**
+ * nfs_lookup - resolve hostname to an IPv4 or IPv6 socket address
+ * @hostname: pointer to C string containing DNS hostname to resolve
+ * @family: address family hint
+ * @sap: pointer to buffer to fill with socket address
+ * @len: IN: size of buffer to fill; OUT: size of socket address
+ *
+ * Returns 1 and places a socket address at @sap if successful;
+ * otherwise zero.
+ */
+int nfs_lookup(const char *hostname, const sa_family_t family,
+ struct sockaddr *sap, socklen_t *salen)
+{
+ struct addrinfo *gai_results;
+ struct addrinfo gai_hint = {
+ .ai_family = family,
+ };
+ socklen_t len = *salen;
+ int error, ret = 0;
+
+ *salen = 0;
+
+ error = getaddrinfo(hostname, NULL, &gai_hint, &gai_results);
+ switch (error) {
+ case 0:
+ break;
+ case EAI_SYSTEM:
+ nfs_error(_("%s: DNS resolution failed for %s: %s"),
+ progname, hostname, strerror(errno));
+ return ret;
+ default:
+ nfs_error(_("%s: DNS resolution failed for %s: %s"),
+ progname, hostname, gai_strerror(error));
+ return ret;
+ }
+
+ switch (gai_results->ai_addr->sa_family) {
+ case AF_INET:
+ case AF_INET6:
+ if (len >= gai_results->ai_addrlen) {
+ *salen = gai_results->ai_addrlen;
+ memcpy(sap, gai_results->ai_addr, *salen);
+ ret = 1;
+ }
+ break;
+ default:
+ /* things are really broken if we get here, so warn */
+ nfs_error(_("%s: unrecognized DNS resolution results for %s"),
+ progname, hostname);
+ break;
+ }
+
+ nfs_freeaddrinfo(gai_results);
+ return ret;
+}
+
+/**
+ * nfs_gethostbyname - resolve a hostname to an IPv4 address
+ * @hostname: pointer to a C string containing a DNS hostname
+ * @sin: returns an IPv4 address
+ *
+ * Returns 1 if successful, otherwise zero.
+ */
+int nfs_gethostbyname(const char *hostname, struct sockaddr_in *sin)
+{
+ socklen_t len = sizeof(*sin);
+
+ return nfs_lookup(hostname, AF_INET, (struct sockaddr *)sin, &len);
+}
+
+/**
+ * nfs_string_to_sockaddr - convert string address to sockaddr
+ * @address: pointer to presentation format address to convert
+ * @sap: pointer to socket address buffer to fill in
+ * @salen: IN: length of address buffer
+ * OUT: length of converted socket address
+ *
+ * Convert a presentation format address string to a socket address.
+ * Similar to nfs_lookup(), but the DNS query is squelched, and it
+ * won't make any noise if the getaddrinfo() call fails.
+ *
+ * Returns 1 and fills in @sap and @salen if successful; otherwise zero.
+ *
+ * See RFC 4038 section 5.1 or RFC 3513 section 2.2 for more details
+ * on presenting IPv6 addresses as text strings.
+ */
+int nfs_string_to_sockaddr(const char *address, struct sockaddr *sap,
+ socklen_t *salen)
+{
+ struct addrinfo *gai_results;
+ struct addrinfo gai_hint = {
+ .ai_flags = AI_NUMERICHOST,
+ };
+ socklen_t len = *salen;
+ int ret = 0;
+
+ *salen = 0;
+
+ if (getaddrinfo(address, NULL, &gai_hint, &gai_results) == 0) {
+ switch (gai_results->ai_addr->sa_family) {
+ case AF_INET:
+ case AF_INET6:
+ if (len >= gai_results->ai_addrlen) {
+ *salen = gai_results->ai_addrlen;
+ memcpy(sap, gai_results->ai_addr, *salen);
+ ret = 1;
+ }
+ break;
+ }
+ nfs_freeaddrinfo(gai_results);
+ }
+
+ return ret;
+}
+
+/**
+ * nfs_present_sockaddr - convert sockaddr to string
+ * @sap: pointer to socket address to convert
+ * @salen: length of socket address
+ * @buf: pointer to buffer to fill in
+ * @buflen: length of buffer
+ *
+ * Convert the passed-in sockaddr-style address to presentation format.
+ * The presentation format address is placed in @buf and is
+ * '\0'-terminated.
+ *
+ * Returns 1 if successful; otherwise zero.
+ *
+ * See RFC 4038 section 5.1 or RFC 3513 section 2.2 for more details
+ * on presenting IPv6 addresses as text strings.
+ */
+int nfs_present_sockaddr(const struct sockaddr *sap, const socklen_t salen,
+ char *buf, const size_t buflen)
+{
+#ifdef HAVE_GETNAMEINFO
+ int result;
+
+ result = getnameinfo(sap, salen, buf, buflen,
+ NULL, 0, NI_NUMERICHOST);
+ if (!result)
+ return 1;
+
+ nfs_error(_("%s: invalid server address: %s"), progname,
+ gai_strerror(result));
+ return 0;
+#else /* HAVE_GETNAMEINFO */
+ char *addr;
+
+ if (sap->sa_family == AF_INET) {
+ addr = inet_ntoa(((struct sockaddr_in *)sap)->sin_addr);
+ if (addr && strlen(addr) < buflen) {
+ strcpy(buf, addr);
+ return 1;
+ }
+ }
+
+ nfs_error(_("%s: invalid server address"), progname);
+ return 0;
+#endif /* HAVE_GETNAMEINFO */
+}
+
+/*
+ * Attempt to connect a socket, but time out after "timeout" seconds.
+ *
+ * On error return, caller closes the socket.
+ */
+static int connect_to(int fd, struct sockaddr *addr,
+ socklen_t addrlen, int timeout)
+{
+ int ret, saved;
+ fd_set rset, wset;
+ struct timeval tv = {
+ .tv_sec = timeout,
+ };
+
+ saved = fcntl(fd, F_GETFL, 0);
+ fcntl(fd, F_SETFL, saved | O_NONBLOCK);
+
+ ret = connect(fd, addr, addrlen);
+ if (ret < 0 && errno != EINPROGRESS)
+ return -1;
+ if (ret == 0)
+ goto out;
+
+ FD_ZERO(&rset);
+ FD_SET(fd, &rset);
+ wset = rset;
+ ret = select(fd + 1, &rset, &wset, NULL, &tv);
+ if (ret == 0) {
+ errno = ETIMEDOUT;
+ return -1;
+ }
+ if (FD_ISSET(fd, &rset) || FD_ISSET(fd, &wset)) {
+ int error;
+ socklen_t len = sizeof(error);
+ if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0)
+ return -1;
+ if (error) {
+ errno = error;
+ return -1;
+ }
+ } else
+ return -1;
+
+out:
+ fcntl(fd, F_SETFL, saved);
+ return 0;
+}
+
+/*
+ * Create a socket that is locally bound to a reserved or non-reserved port.
+ *
+ * The caller should check rpc_createerr to determine the cause of any error.
+ */
+static int get_socket(struct sockaddr_in *saddr, unsigned int p_prot,
+ unsigned int timeout, int resvp, int conn)
+{
+ int so, cc, type;
+ struct sockaddr_in laddr;
+ socklen_t namelen = sizeof(laddr);
+
+ type = (p_prot == IPPROTO_UDP ? SOCK_DGRAM : SOCK_STREAM);
+ if ((so = socket (AF_INET, type, p_prot)) < 0)
+ goto err_socket;
+
+ laddr.sin_family = AF_INET;
+ laddr.sin_port = 0;
+ laddr.sin_addr.s_addr = htonl(INADDR_ANY);
+ if (resvp) {
+ if (bindresvport(so, &laddr) < 0)
+ goto err_bindresvport;
+ }
+ if (type == SOCK_STREAM || (conn && type == SOCK_DGRAM)) {
+ cc = connect_to(so, SAFE_SOCKADDR(saddr), namelen,
+ timeout);
+ if (cc < 0)
+ goto err_connect;
+ }
+ return so;
+
+err_socket:
+ rpc_createerr.cf_stat = RPC_SYSTEMERROR;
+ rpc_createerr.cf_error.re_errno = errno;
+ if (verbose) {
+ nfs_error(_("%s: Unable to create %s socket: errno %d (%s)\n"),
+ progname, p_prot == IPPROTO_UDP ? _("UDP") : _("TCP"),
+ errno, strerror(errno));
+ }
+ return RPC_ANYSOCK;
+
+err_bindresvport:
+ rpc_createerr.cf_stat = RPC_SYSTEMERROR;
+ rpc_createerr.cf_error.re_errno = errno;
+ if (verbose) {
+ nfs_error(_("%s: Unable to bindresvport %s socket: errno %d"
+ " (%s)\n"),
+ progname, p_prot == IPPROTO_UDP ? _("UDP") : _("TCP"),
+ errno, strerror(errno));
+ }
+ close(so);
+ return RPC_ANYSOCK;
+
+err_connect:
+ rpc_createerr.cf_stat = RPC_SYSTEMERROR;
+ rpc_createerr.cf_error.re_errno = errno;
+ if (verbose) {
+ nfs_error(_("%s: Unable to connect to %s:%d, errno %d (%s)\n"),
+ progname, inet_ntoa(saddr->sin_addr),
+ ntohs(saddr->sin_port), errno, strerror(errno));
+ }
+ close(so);
+ return RPC_ANYSOCK;
+}
+
+static void nfs_pp_debug(const struct sockaddr *sap, const socklen_t salen,
+ const rpcprog_t program, const rpcvers_t version,
+ const unsigned short protocol,
+ const unsigned short port)
+{
+ char buf[NI_MAXHOST];
+
+ if (!verbose)
+ return;
+
+ if (nfs_present_sockaddr(sap, salen, buf, sizeof(buf)) == 0) {
+ buf[0] = '\0';
+ strcat(buf, "unknown host");
+ }
+
+ fprintf(stderr, _("%s: trying %s prog %lu vers %lu prot %s port %d\n"),
+ progname, buf, (unsigned long)program,
+ (unsigned long)version,
+ (protocol == IPPROTO_UDP ? _("UDP") : _("TCP")),
+ port);
+}
+
+static void nfs_pp_debug2(const char *str)
+{
+ if (!verbose)
+ return;
+
+ if (rpc_createerr.cf_error.re_status == RPC_CANTRECV ||
+ rpc_createerr.cf_error.re_status == RPC_CANTSEND)
+ nfs_error(_("%s: portmap query %s%s - %s"),
+ progname, str, clnt_spcreateerror(""),
+ strerror(rpc_createerr.cf_error.re_errno));
+ else
+ nfs_error(_("%s: portmap query %s%s"),
+ progname, str, clnt_spcreateerror(""));
+}
+
+/*
+ * Use the portmapper to discover whether or not the service we want is
+ * available. The lists 'versions' and 'protos' define ordered sequences
+ * of service versions and udp/tcp protocols to probe for.
+ *
+ * Returns 1 if the requested service port is unambiguous and pingable;
+ * @pmap is filled in with the version, port, and transport protocol used
+ * during the successful ping. Note that if a port is already specified
+ * in @pmap and it matches the rpcbind query result, nfs_probe_port() does
+ * not perform an RPC ping.
+ *
+ * If an error occurs or the requested service isn't available, zero is
+ * returned; rpccreateerr.cf_stat is set to reflect the nature of the error.
+ */
+static int nfs_probe_port(const struct sockaddr *sap, const socklen_t salen,
+ struct pmap *pmap, const unsigned long *versions,
+ const unsigned int *protos)
+{
+ union nfs_sockaddr address;
+ struct sockaddr *saddr = &address.sa;
+ const unsigned long prog = pmap->pm_prog, *p_vers;
+ const unsigned int prot = (u_int)pmap->pm_prot, *p_prot;
+ const u_short port = (u_short) pmap->pm_port;
+ unsigned long vers = pmap->pm_vers;
+ unsigned short p_port;
+
+ memcpy(saddr, sap, salen);
+ p_prot = prot ? &prot : protos;
+ p_vers = vers ? &vers : versions;
+
+ for (;;) {
+ if (verbose)
+ printf(_("%s: prog %lu, trying vers=%lu, prot=%u\n"),
+ progname, prog, *p_vers, *p_prot);
+ p_port = nfs_getport(saddr, salen, prog, *p_vers, *p_prot);
+ if (p_port) {
+ if (!port || port == p_port) {
+ nfs_set_port(saddr, p_port);
+ nfs_pp_debug(saddr, salen, prog, *p_vers,
+ *p_prot, p_port);
+ if (nfs_rpc_ping(saddr, salen, prog,
+ *p_vers, *p_prot, NULL))
+ goto out_ok;
+ } else
+ rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
+ }
+ if (rpc_createerr.cf_stat != RPC_PROGNOTREGISTERED &&
+ rpc_createerr.cf_stat != RPC_TIMEDOUT &&
+ rpc_createerr.cf_stat != RPC_CANTRECV &&
+ rpc_createerr.cf_stat != RPC_PROGVERSMISMATCH)
+ break;
+
+ if (!prot) {
+ if (*++p_prot) {
+ nfs_pp_debug2("retrying");
+ continue;
+ }
+ p_prot = protos;
+ }
+ if (rpc_createerr.cf_stat == RPC_TIMEDOUT ||
+ rpc_createerr.cf_stat == RPC_CANTRECV)
+ break;
+
+ if (vers || !*++p_vers)
+ break;
+ }
+
+ nfs_pp_debug2("failed");
+ return 0;
+
+out_ok:
+ if (!vers)
+ pmap->pm_vers = *p_vers;
+ if (!prot)
+ pmap->pm_prot = *p_prot;
+ if (!port)
+ pmap->pm_port = p_port;
+ nfs_clear_rpc_createerr();
+ return 1;
+}
+/*
+ * Probe a server's NFS service to determine which versions and
+ * transport protocols are supported.
+ *
+ * Returns 1 if the requested service port is unambiguous and pingable;
+ * @pmap is filled in with the version, port, and transport protocol used
+ * during the successful ping. If all three are already specified, simply
+ * return success without an rpcbind query or RPC ping (we may be trying
+ * to mount an NFS service that is not advertised via rpcbind).
+ *
+ * If an error occurs or the requested service isn't available, zero is
+ * returned; rpccreateerr.cf_stat is set to reflect the nature of the error.
+ */
+static int nfs_probe_nfsport(const struct sockaddr *sap, const socklen_t salen,
+ struct pmap *pmap, int checkv4)
+{
+ if (pmap->pm_vers && pmap->pm_prot && pmap->pm_port)
+ return 1;
+
+ if (nfs_mount_data_version >= 4) {
+ const unsigned int *probe_proto;
+ int ret;
+ struct sockaddr_storage save_sa;
+
+ probe_proto = nfs_default_proto();
+ memcpy(&save_sa, sap, salen);
+
+ ret = nfs_probe_port(sap, salen, pmap,
+ probe_nfs3_only, probe_proto);
+ if (!ret || !checkv4 || probe_proto != probe_tcp_first)
+ return ret;
+
+ nfs_set_port((struct sockaddr *)&save_sa, NFS_PORT);
+ ret = nfs_rpc_ping((struct sockaddr *)&save_sa, salen,
+ NFS_PROGRAM, 4, IPPROTO_TCP, NULL);
+ if (ret) {
+ rpc_createerr.cf_stat = RPC_FAILED;
+ rpc_createerr.cf_error.re_errno = EAGAIN;
+ return 0;
+ }
+ return 1;
+ } else
+ return nfs_probe_port(sap, salen, pmap,
+ probe_nfs2_only, probe_udp_only);
+}
+
+/*
+ * Probe a server's mountd service to determine which versions and
+ * transport protocols are supported.
+ *
+ * Returns 1 if the requested service port is unambiguous and pingable;
+ * @pmap is filled in with the version, port, and transport protocol used
+ * during the successful ping. If all three are already specified, simply
+ * return success without an rpcbind query or RPC ping (we may be trying
+ * to mount an NFS service that is not advertised via rpcbind).
+ *
+ * If an error occurs or the requested service isn't available, zero is
+ * returned; rpccreateerr.cf_stat is set to reflect the nature of the error.
+ */
+static int nfs_probe_mntport(const struct sockaddr *sap, const socklen_t salen,
+ struct pmap *pmap)
+{
+ if (pmap->pm_vers && pmap->pm_prot && pmap->pm_port)
+ return 1;
+
+ if (nfs_mount_data_version >= 4)
+ return nfs_probe_port(sap, salen, pmap,
+ probe_mnt3_only, probe_udp_first);
+ else
+ return nfs_probe_port(sap, salen, pmap,
+ probe_mnt1_first, probe_udp_only);
+}
+
+/*
+ * Probe a server's mountd service to determine which versions and
+ * transport protocols are supported. Invoked when the protocol
+ * version is already known for both the NFS and mountd service.
+ *
+ * Returns 1 and fills in both @pmap structs if the requested service
+ * ports are unambiguous and pingable. Otherwise zero is returned;
+ * rpccreateerr.cf_stat is set to reflect the nature of the error.
+ */
+static int nfs_probe_version_fixed(const struct sockaddr *mnt_saddr,
+ const socklen_t mnt_salen,
+ struct pmap *mnt_pmap,
+ const struct sockaddr *nfs_saddr,
+ const socklen_t nfs_salen,
+ struct pmap *nfs_pmap)
+{
+ if (!nfs_probe_nfsport(nfs_saddr, nfs_salen, nfs_pmap, 0))
+ return 0;
+ return nfs_probe_mntport(mnt_saddr, mnt_salen, mnt_pmap);
+}
+
+/**
+ * nfs_probe_bothports - discover the RPC endpoints of mountd and NFS server
+ * @mnt_saddr: pointer to socket address of mountd server
+ * @mnt_salen: length of mountd server's address
+ * @mnt_pmap: IN: partially filled-in mountd RPC service tuple;
+ * OUT: fully filled-in mountd RPC service tuple
+ * @nfs_saddr: pointer to socket address of NFS server
+ * @nfs_salen: length of NFS server's address
+ * @nfs_pmap: IN: partially filled-in NFS RPC service tuple;
+ * OUT: fully filled-in NFS RPC service tuple
+ * @checkv4: Flag indicating that if v3 is available we must also
+ * check v4, and if that is available, set re_errno to EAGAIN.
+ *
+ * Returns 1 and fills in both @pmap structs if the requested service
+ * ports are unambiguous and pingable. Otherwise zero is returned;
+ * rpccreateerr.cf_stat is set to reflect the nature of the error.
+ */
+int nfs_probe_bothports(const struct sockaddr *mnt_saddr,
+ const socklen_t mnt_salen,
+ struct pmap *mnt_pmap,
+ const struct sockaddr *nfs_saddr,
+ const socklen_t nfs_salen,
+ struct pmap *nfs_pmap,
+ int checkv4)
+{
+ struct pmap save_nfs, save_mnt;
+ const unsigned long *probe_vers;
+
+ if (mnt_pmap->pm_vers && !nfs_pmap->pm_vers)
+ nfs_pmap->pm_vers = mntvers_to_nfs(mnt_pmap->pm_vers);
+ else if (nfs_pmap->pm_vers && !mnt_pmap->pm_vers)
+ mnt_pmap->pm_vers = nfsvers_to_mnt(nfs_pmap->pm_vers);
+
+ if (nfs_pmap->pm_vers)
+ return nfs_probe_version_fixed(mnt_saddr, mnt_salen, mnt_pmap,
+ nfs_saddr, nfs_salen, nfs_pmap);
+
+ memcpy(&save_nfs, nfs_pmap, sizeof(save_nfs));
+ memcpy(&save_mnt, mnt_pmap, sizeof(save_mnt));
+ probe_vers = (nfs_mount_data_version >= 4) ?
+ probe_mnt3_only : probe_mnt1_first;
+
+ for (; *probe_vers; probe_vers++) {
+ nfs_pmap->pm_vers = mntvers_to_nfs(*probe_vers);
+ if (nfs_probe_nfsport(nfs_saddr, nfs_salen, nfs_pmap, checkv4) != 0) {
+ mnt_pmap->pm_vers = *probe_vers;
+ if (nfs_probe_mntport(mnt_saddr, mnt_salen, mnt_pmap) != 0)
+ return 1;
+ memcpy(mnt_pmap, &save_mnt, sizeof(*mnt_pmap));
+ }
+ switch (rpc_createerr.cf_stat) {
+ case RPC_PROGVERSMISMATCH:
+ case RPC_PROGNOTREGISTERED:
+ break;
+ default:
+ return 0;
+ }
+ memcpy(nfs_pmap, &save_nfs, sizeof(*nfs_pmap));
+ }
+
+ return 0;
+}
+
+/**
+ * probe_bothports - discover the RPC endpoints of mountd and NFS server
+ * @mnt_server: pointer to address and pmap argument for mountd results
+ * @nfs_server: pointer to address and pmap argument for NFS server
+ *
+ * This is the legacy API that takes "clnt_addr_t" for both servers,
+ * but supports only AF_INET addresses.
+ *
+ * Returns 1 and fills in the pmap field in both clnt_addr_t structs
+ * if the requested service ports are unambiguous and pingable.
+ * Otherwise zero is returned; rpccreateerr.cf_stat is set to reflect
+ * the nature of the error.
+ */
+int probe_bothports(clnt_addr_t *mnt_server, clnt_addr_t *nfs_server)
+{
+ struct sockaddr *mnt_addr = SAFE_SOCKADDR(&mnt_server->saddr);
+ struct sockaddr *nfs_addr = SAFE_SOCKADDR(&nfs_server->saddr);
+
+ return nfs_probe_bothports(mnt_addr, sizeof(mnt_server->saddr),
+ &mnt_server->pmap,
+ nfs_addr, sizeof(nfs_server->saddr),
+ &nfs_server->pmap, 0);
+}
+
+/**
+ * start_statd - attempt to start rpc.statd
+ *
+ * Returns 1 if statd is running; otherwise zero.
+ */
+int start_statd(void)
+{
+#ifdef START_STATD
+ struct stat stb;
+#endif
+
+ if (nfs_probe_statd())
+ return 1;
+
+#ifdef START_STATD
+ if (stat(START_STATD, &stb) == 0) {
+ if (S_ISREG(stb.st_mode) && (stb.st_mode & S_IXUSR)) {
+ int cnt = STATD_TIMEOUT * 10;
+ int status = 0;
+ char * const envp[1] = { NULL };
+ const struct timespec ts = {
+ .tv_sec = 0,
+ .tv_nsec = 100000000,
+ };
+ pid_t pid = fork();
+ switch (pid) {
+ case 0: /* child */
+ setgroups(0, NULL);
+ if (setgid(0) < 0)
+ nfs_error(_("%s: setgid(0) failed: %s"),
+ progname, strerror(errno));
+ if (setuid(0) < 0)
+ nfs_error(_("%s: setuid(0) failed: %s"),
+ progname, strerror(errno));
+ execle(START_STATD, START_STATD, NULL, envp);
+ exit(1);
+ case -1: /* error */
+ nfs_error(_("%s: fork failed: %s"),
+ progname, strerror(errno));
+ break;
+ default: /* parent */
+ if (waitpid(pid, &status,0) == pid &&
+ status == 0)
+ /* assume it worked */
+ return 1;
+ break;
+ }
+ while (1) {
+ if (nfs_probe_statd())
+ return 1;
+ if (! cnt--)
+ return 0;
+ nanosleep(&ts, NULL);
+ }
+ }
+ }
+#endif
+
+ return 0;
+}
+
+/**
+ * nfs_advise_umount - ask the server to remove a share from it's rmtab
+ * @sap: pointer to IP address of server to call
+ * @salen: length of server address
+ * @pmap: partially filled-in mountd RPC service tuple
+ * @argp: directory path of share to "unmount"
+ *
+ * Returns one if the unmount call succeeded; zero if the unmount
+ * failed for any reason; rpccreateerr.cf_stat is set to reflect
+ * the nature of the error.
+ *
+ * We use a fast timeout since this call is advisory only.
+ */
+int nfs_advise_umount(const struct sockaddr *sap, const socklen_t salen,
+ const struct pmap *pmap, const dirpath *argp)
+{
+ union nfs_sockaddr address;
+ struct sockaddr *saddr = &address.sa;
+ struct pmap mnt_pmap = *pmap;
+ struct timeval timeout = {
+ .tv_sec = MOUNT_TIMEOUT >> 3,
+ };
+ CLIENT *client;
+ enum clnt_stat res = 0;
+
+ memcpy(saddr, sap, salen);
+ if (nfs_probe_mntport(saddr, salen, &mnt_pmap) == 0) {
+ if (verbose)
+ nfs_error(_("%s: Failed to discover mountd port%s"),
+ progname, clnt_spcreateerror(""));
+ return 0;
+ }
+ nfs_set_port(saddr, mnt_pmap.pm_port);
+
+ client = nfs_get_priv_rpcclient(saddr, salen, mnt_pmap.pm_prot,
+ mnt_pmap.pm_prog, mnt_pmap.pm_vers,
+ &timeout);
+ if (client == NULL) {
+ if (verbose)
+ nfs_error(_("%s: Failed to create RPC client%s"),
+ progname, clnt_spcreateerror(""));
+ return 0;
+ }
+
+ client->cl_auth = nfs_authsys_create();
+ if (client->cl_auth == NULL) {
+ if (verbose)
+ nfs_error(_("%s: Failed to create RPC auth handle"),
+ progname);
+ CLNT_DESTROY(client);
+ return 0;
+ }
+
+ res = CLNT_CALL(client, MOUNTPROC_UMNT,
+ (xdrproc_t)xdr_dirpath, (caddr_t)argp,
+ (xdrproc_t)xdr_void, NULL,
+ timeout);
+ if (res != RPC_SUCCESS) {
+ rpc_createerr.cf_stat = res;
+ CLNT_GETERR(client, &rpc_createerr.cf_error);
+ if (verbose)
+ nfs_error(_("%s: UMNT call failed: %s"),
+ progname, clnt_sperrno(res));
+
+ }
+ auth_destroy(client->cl_auth);
+ CLNT_DESTROY(client);
+
+ if (res != RPC_SUCCESS)
+ return 0;
+ return 1;
+}
+
+/**
+ * nfs_call_umount - ask the server to remove a share from it's rmtab
+ * @mnt_server: address of RPC MNT program server
+ * @argp: directory path of share to "unmount"
+ *
+ * Returns one if the unmount call succeeded; zero if the unmount
+ * failed for any reason.
+ *
+ * Note that a side effect of calling this function is that rpccreateerr
+ * is set.
+ */
+int nfs_call_umount(clnt_addr_t *mnt_server, dirpath *argp)
+{
+ struct sockaddr *sap = SAFE_SOCKADDR(&mnt_server->saddr);
+ socklen_t salen = sizeof(mnt_server->saddr);
+ struct pmap *pmap = &mnt_server->pmap;
+ CLIENT *clnt;
+ enum clnt_stat res = 0;
+ int msock;
+
+ if (!nfs_probe_mntport(sap, salen, pmap))
+ return 0;
+ clnt = mnt_openclnt(mnt_server, &msock);
+ if (!clnt)
+ return 0;
+ res = clnt_call(clnt, MOUNTPROC_UMNT,
+ (xdrproc_t)xdr_dirpath, (caddr_t)argp,
+ (xdrproc_t)xdr_void, NULL,
+ TIMEOUT);
+ mnt_closeclnt(clnt, msock);
+
+ if (res == RPC_SUCCESS)
+ return 1;
+ return 0;
+}
+
+/**
+ * mnt_openclnt - get a handle for a remote mountd service
+ * @mnt_server: address and pmap arguments of mountd service
+ * @msock: returns a file descriptor of the underlying transport socket
+ *
+ * Returns an active handle for the remote's mountd service
+ */
+CLIENT *mnt_openclnt(clnt_addr_t *mnt_server, int *msock)
+{
+ struct sockaddr_in *mnt_saddr = &mnt_server->saddr;
+ struct pmap *mnt_pmap = &mnt_server->pmap;
+ CLIENT *clnt = NULL;
+
+ mnt_saddr->sin_port = htons((u_short)mnt_pmap->pm_port);
+ *msock = get_socket(mnt_saddr, mnt_pmap->pm_prot, MOUNT_TIMEOUT,
+ TRUE, FALSE);
+ if (*msock == RPC_ANYSOCK) {
+ if (rpc_createerr.cf_error.re_errno == EADDRINUSE)
+ /*
+ * Probably in-use by a TIME_WAIT connection,
+ * It is worth waiting a while and trying again.
+ */
+ rpc_createerr.cf_stat = RPC_TIMEDOUT;
+ return NULL;
+ }
+
+ switch (mnt_pmap->pm_prot) {
+ case IPPROTO_UDP:
+ clnt = clntudp_bufcreate(mnt_saddr,
+ mnt_pmap->pm_prog, mnt_pmap->pm_vers,
+ RETRY_TIMEOUT, msock,
+ MNT_SENDBUFSIZE, MNT_RECVBUFSIZE);
+ break;
+ case IPPROTO_TCP:
+ clnt = clnttcp_create(mnt_saddr,
+ mnt_pmap->pm_prog, mnt_pmap->pm_vers,
+ msock,
+ MNT_SENDBUFSIZE, MNT_RECVBUFSIZE);
+ break;
+ }
+ if (clnt) {
+ /* try to mount hostname:dirname */
+ clnt->cl_auth = nfs_authsys_create();
+ if (clnt->cl_auth)
+ return clnt;
+ CLNT_DESTROY(clnt);
+ }
+ return NULL;
+}
+
+/**
+ * mnt_closeclnt - terminate a handle for a remote mountd service
+ * @clnt: pointer to an active handle for a remote mountd service
+ * @msock: file descriptor of the underlying transport socket
+ *
+ */
+void mnt_closeclnt(CLIENT *clnt, int msock)
+{
+ auth_destroy(clnt->cl_auth);
+ clnt_destroy(clnt);
+ close(msock);
+}
+
+/**
+ * clnt_ping - send an RPC ping to the remote RPC service endpoint
+ * @saddr: server's address
+ * @prog: target RPC program number
+ * @vers: target RPC version number
+ * @prot: target RPC protocol
+ * @caddr: filled in with our network address
+ *
+ * Sigh... GETPORT queries don't actually check the version number.
+ * In order to make sure that the server actually supports the service
+ * we're requesting, we open an RPC client, and fire off a NULL
+ * RPC call.
+ *
+ * caddr is the network address that the server will use to call us back.
+ * On multi-homed clients, this address depends on which NIC we use to
+ * route requests to the server.
+ *
+ * Returns one if successful, otherwise zero.
+ */
+int clnt_ping(struct sockaddr_in *saddr, const unsigned long prog,
+ const unsigned long vers, const unsigned int prot,
+ struct sockaddr_in *caddr)
+{
+ CLIENT *clnt = NULL;
+ int sock, status;
+ static char clnt_res;
+ struct sockaddr dissolve;
+
+ rpc_createerr.cf_stat = status = 0;
+ sock = get_socket(saddr, prot, CONNECT_TIMEOUT, FALSE, TRUE);
+ if (sock == RPC_ANYSOCK) {
+ if (rpc_createerr.cf_error.re_errno == ETIMEDOUT) {
+ /*
+ * TCP timeout. Bubble up the error to see
+ * how it should be handled.
+ */
+ rpc_createerr.cf_stat = RPC_TIMEDOUT;
+ }
+ return 0;
+ }
+
+ if (caddr) {
+ /* Get the address of our end of this connection */
+ socklen_t len = sizeof(*caddr);
+ if (getsockname(sock, (struct sockaddr *) caddr, &len) != 0)
+ caddr->sin_family = 0;
+ }
+
+ switch(prot) {
+ case IPPROTO_UDP:
+ /* The socket is connected (so we could getsockname successfully),
+ * but some servers on multi-homed hosts reply from
+ * the wrong address, so if we stay connected, we lose the reply.
+ */
+ dissolve.sa_family = AF_UNSPEC;
+ connect(sock, &dissolve, sizeof(dissolve));
+
+ clnt = clntudp_bufcreate(saddr, prog, vers,
+ RETRY_TIMEOUT, &sock,
+ RPCSMALLMSGSIZE, RPCSMALLMSGSIZE);
+ break;
+ case IPPROTO_TCP:
+ clnt = clnttcp_create(saddr, prog, vers, &sock,
+ RPCSMALLMSGSIZE, RPCSMALLMSGSIZE);
+ break;
+ }
+ if (!clnt) {
+ close(sock);
+ return 0;
+ }
+ memset(&clnt_res, 0, sizeof(clnt_res));
+ status = clnt_call(clnt, NULLPROC,
+ (xdrproc_t)xdr_void, (caddr_t)NULL,
+ (xdrproc_t)xdr_void, (caddr_t)&clnt_res,
+ TIMEOUT);
+ if (status) {
+ clnt_geterr(clnt, &rpc_createerr.cf_error);
+ rpc_createerr.cf_stat = status;
+ }
+ clnt_destroy(clnt);
+ close(sock);
+
+ if (status == RPC_SUCCESS)
+ return 1;
+ else
+ return 0;
+}
+
+/*
+ * Try a getsockname() on a connected datagram socket.
+ *
+ * Returns 1 and fills in @buf if successful; otherwise, zero.
+ *
+ * A connected datagram socket prevents leaving a socket in TIME_WAIT.
+ * This conserves the ephemeral port number space, helping reduce failed
+ * socket binds during mount storms.
+ */
+static int nfs_ca_sockname(const struct sockaddr *sap, const socklen_t salen,
+ struct sockaddr *buf, socklen_t *buflen)
+{
+ 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,
+ };
+ int sock, result = 0;
+ int val;
+
+ sock = socket(sap->sa_family, SOCK_DGRAM, IPPROTO_UDP);
+ if (sock < 0)
+ return 0;
+
+ switch (sap->sa_family) {
+ case AF_INET:
+ if (bind(sock, SAFE_SOCKADDR(&sin), sizeof(sin)) < 0)
+ goto out;
+ break;
+ case AF_INET6:
+ /* Make sure the call-back address is public/permanent */
+ val = IPV6_PREFER_SRC_PUBLIC;
+ setsockopt(sock, SOL_IPV6, IPV6_ADDR_PREFERENCES, &val, sizeof(val));
+ if (bind(sock, SAFE_SOCKADDR(&sin6), sizeof(sin6)) < 0)
+ goto out;
+ break;
+ default:
+ errno = EAFNOSUPPORT;
+ goto out;
+ }
+
+ if (connect(sock, sap, salen) < 0)
+ goto out;
+
+ result = !getsockname(sock, buf, buflen);
+
+out:
+ close(sock);
+ return result;
+}
+
+/*
+ * Try to generate an address that prevents the server from calling us.
+ *
+ * Returns 1 and fills in @buf if successful; otherwise, zero.
+ */
+static int nfs_ca_gai(const struct sockaddr *sap,
+ struct sockaddr *buf, socklen_t *buflen)
+{
+ struct addrinfo *gai_results;
+ struct addrinfo gai_hint = {
+ .ai_family = sap->sa_family,
+ .ai_flags = AI_PASSIVE, /* ANYADDR */
+ };
+
+ if (getaddrinfo(NULL, "", &gai_hint, &gai_results))
+ return 0;
+
+ *buflen = gai_results->ai_addrlen;
+ memcpy(buf, gai_results->ai_addr, *buflen);
+
+ nfs_freeaddrinfo(gai_results);
+
+ return 1;
+}
+
+/**
+ * nfs_callback_address - acquire our local network address
+ * @sap: pointer to address of remote
+ * @sap_len: length of address
+ * @buf: pointer to buffer to be filled in with local network address
+ * @buflen: IN: length of buffer to fill in; OUT: length of filled-in address
+ *
+ * Discover a network address that an NFSv4 server can use to call us back.
+ * On multi-homed clients, this address depends on which NIC we use to
+ * route requests to the server.
+ *
+ * Returns 1 and fills in @buf if an unambiguous local address is
+ * available; returns 1 and fills in an appropriate ANYADDR address
+ * if a local address isn't available; otherwise, returns zero.
+ */
+int nfs_callback_address(const struct sockaddr *sap, const socklen_t salen,
+ struct sockaddr *buf, socklen_t *buflen)
+{
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)buf;
+
+ if (nfs_ca_sockname(sap, salen, buf, buflen) == 0)
+ if (nfs_ca_gai(sap, buf, buflen) == 0)
+ goto out_failed;
+
+ /*
+ * The server can't use an interface ID that was generated
+ * here on the client, so always clear sin6_scope_id.
+ */
+ if (sin6->sin6_family == AF_INET6)
+ sin6->sin6_scope_id = 0;
+
+ return 1;
+
+out_failed:
+ *buflen = 0;
+ if (verbose)
+ nfs_error(_("%s: failed to construct callback address"),
+ progname);
+ return 0;
+}
+
+/*
+ * "nfsprog" is supported only by the legacy mount command. The
+ * kernel mount client does not support this option.
+ *
+ * Returns TRUE if @program contains a valid value for this option,
+ * or FALSE if the option was specified with an invalid value.
+ */
+static int
+nfs_nfs_program(struct mount_options *options, unsigned long *program)
+{
+ long tmp;
+
+ switch (po_get_numeric(options, "nfsprog", &tmp)) {
+ case PO_NOT_FOUND:
+ break;
+ case PO_FOUND:
+ if (tmp > 0) {
+ *program = tmp;
+ return 1;
+ }
+ /* FALLTHRU */
+ case PO_BAD_VALUE:
+ nfs_error(_("%s: invalid value for 'nfsprog=' option"),
+ progname);
+ return 0;
+ }
+
+ /*
+ * NFS RPC program wasn't specified. The RPC program
+ * cannot be determined via an rpcbind query.
+ */
+ *program = nfs_getrpcbyname(NFSPROG, nfs_nfs_pgmtbl);
+ return 1;
+}
+
+/*
+ * Returns TRUE if @version contains a valid value for this option,
+ * or FALSE if the option was specified with an invalid value.
+ */
+int
+nfs_nfs_version(char *type, struct mount_options *options, struct nfs_version *version)
+{
+ char *version_key, *version_val = NULL, *cptr;
+ int i, found = -1;
+
+ version->v_mode = V_DEFAULT;
+
+ for (i = 0; nfs_version_opttbl[i]; i++) {
+ if (po_contains_prefix(options, nfs_version_opttbl[i],
+ &version_key, 0) == PO_FOUND) {
+ if (found >= 0)
+ goto ret_error_multiple;
+ if (po_contains_prefix(options, nfs_version_opttbl[i],
+ NULL, 1) == PO_FOUND)
+ goto ret_error_multiple;
+ found = i;
+ }
+ }
+
+ if (found < 0 && strcmp(type, "nfs4") == 0)
+ version_val = type + 3;
+ else if (found < 0)
+ return 1;
+ else if (found <= 2 ) {
+ /* v3, v4 */
+ version_val = version_key + 1;
+ version->v_mode = V_SPECIFIC;
+ } else if (found > 2 ) {
+ /* vers=, nfsvers= */
+ version_val = po_get(options, version_key);
+ }
+
+ if (!version_val)
+ goto ret_error;
+
+ version->major = strtol(version_val, &cptr, 10);
+ if (cptr == version_val || (*cptr && *cptr != '.'))
+ goto ret_error;
+ if (version->major == 4 && *cptr != '.' &&
+ (version_val = po_get(options, "minorversion")) != NULL) {
+ version->minor = strtol(version_val, &cptr, 10);
+ found = -1;
+ if (*cptr)
+ goto ret_error;
+ version->v_mode = V_SPECIFIC;
+ } else if (version->major < 4)
+ version->v_mode = V_SPECIFIC;
+ else if (*cptr == '.') {
+ version_val = ++cptr;
+ if (!(version->minor = strtol(version_val, &cptr, 10)) && cptr == version_val)
+ goto ret_error;
+ version->v_mode = V_SPECIFIC;
+ } else if (version->major > 3 && *cptr == '\0') {
+ version_val = po_get(options, "minorversion");
+ if (version_val != NULL) {
+ version->minor = strtol(version_val, &cptr, 10);
+ version->v_mode = V_SPECIFIC;
+ } else
+ version->v_mode = V_GENERAL;
+ }
+ if (*cptr != '\0')
+ goto ret_error;
+
+ return 1;
+
+ret_error_multiple:
+ nfs_error(_("%s: multiple version options not permitted"),
+ progname);
+ found = 10; /* avoid other errors */
+ret_error:
+ if (found < 0) {
+ nfs_error(_("%s: parsing error on 'minorversion=' option"),
+ progname);
+ } else if (found <= 2 ) {
+ nfs_error(_("%s: parsing error on 'v' option"),
+ progname);
+ } else if (found == 3 ) {
+ nfs_error(_("%s: parsing error on 'vers=' option"),
+ progname);
+ } else if (found == 4) {
+ nfs_error(_("%s: parsing error on 'nfsvers=' option"),
+ progname);
+ }
+ version->v_mode = V_PARSE_ERR;
+ errno = EINVAL;
+ return 0;
+}
+
+/*
+ * Returns TRUE if @protocol contains a valid value for this option,
+ * or FALSE if the option was specified with an invalid value. On
+ * error, errno is set.
+ */
+int
+nfs_nfs_protocol(struct mount_options *options, unsigned long *protocol)
+{
+ sa_family_t family;
+ char *option;
+
+ switch (po_rightmost(options, nfs_transport_opttbl)) {
+ case 0: /* udp */
+ *protocol = IPPROTO_UDP;
+ return 1;
+ case 1: /* tcp */
+ *protocol = IPPROTO_TCP;
+ return 1;
+ case 2: /* rdma */
+ *protocol = NFSPROTO_RDMA;
+ return 1;
+ case 3: /* proto */
+ option = po_get(options, "proto");
+ if (option != NULL) {
+ if (!nfs_get_proto(option, &family, protocol)) {
+ errno = EPROTONOSUPPORT;
+ nfs_error(_("%s: Failed to find '%s' protocol"),
+ progname, option);
+ return 0;
+ }
+ return 1;
+ }
+ }
+
+ /*
+ * NFS transport protocol wasn't specified. The pmap
+ * protocol value will be filled in later by an rpcbind
+ * query in this case.
+ */
+ *protocol = 0;
+ return 1;
+}
+
+/*
+ * Returns TRUE if @port contains a valid value for this option,
+ * or FALSE if the option was specified with an invalid value.
+ */
+static int
+nfs_nfs_port(struct mount_options *options, unsigned long *port)
+{
+ long tmp;
+
+ switch (po_get_numeric(options, "port", &tmp)) {
+ case PO_NOT_FOUND:
+ break;
+ case PO_FOUND:
+ if (tmp >= 0 && tmp <= 65535) {
+ *port = tmp;
+ return 1;
+ }
+ /* FALLTHRU */
+ case PO_BAD_VALUE:
+ nfs_error(_("%s: invalid value for 'port=' option"),
+ progname);
+ return 0;
+ }
+
+ /*
+ * NFS service port wasn't specified. The pmap port value
+ * will be filled in later by an rpcbind query in this case.
+ */
+ *port = 0;
+ return 1;
+}
+
+#ifdef IPV6_SUPPORTED
+sa_family_t config_default_family = AF_UNSPEC;
+
+static int
+nfs_verify_family(sa_family_t UNUSED(family))
+{
+ return 1;
+}
+#else /* IPV6_SUPPORTED */
+sa_family_t config_default_family = AF_INET;
+
+static int
+nfs_verify_family(sa_family_t family)
+{
+ if (family != AF_INET)
+ return 0;
+
+ return 1;
+}
+#endif /* IPV6_SUPPORTED */
+
+/*
+ * Returns TRUE and fills in @family if a valid NFS protocol option
+ * is found, or FALSE if the option was specified with an invalid value
+ * or if the protocol family isn't supported. On error, errno is set.
+ */
+int nfs_nfs_proto_family(struct mount_options *options,
+ sa_family_t *family)
+{
+ unsigned long protocol;
+ char *option;
+ sa_family_t tmp_family = config_default_family;
+
+ switch (po_rightmost(options, nfs_transport_opttbl)) {
+ case 0: /* udp */
+ case 1: /* tcp */
+ case 2: /* rdma */
+ /* for compatibility; these are always AF_INET */
+ *family = AF_INET;
+ return 1;
+ case 3: /* proto */
+ option = po_get(options, "proto");
+ if (option != NULL &&
+ !nfs_get_proto(option, &tmp_family, &protocol)) {
+
+ nfs_error(_("%s: Failed to find '%s' protocol"),
+ progname, option);
+ errno = EPROTONOSUPPORT;
+ return 0;
+ }
+ }
+
+ if (!nfs_verify_family(tmp_family))
+ goto out_err;
+ *family = tmp_family;
+ return 1;
+out_err:
+ errno = EAFNOSUPPORT;
+ return 0;
+}
+
+/*
+ * "mountprog" is supported only by the legacy mount command. The
+ * kernel mount client does not support this option.
+ *
+ * Returns TRUE if @program contains a valid value for this option,
+ * or FALSE if the option was specified with an invalid value.
+ */
+static int
+nfs_mount_program(struct mount_options *options, unsigned long *program)
+{
+ long tmp;
+
+ switch (po_get_numeric(options, "mountprog", &tmp)) {
+ case PO_NOT_FOUND:
+ break;
+ case PO_FOUND:
+ if (tmp > 0) {
+ *program = tmp;
+ return 1;
+ }
+ /* FALLTHRU */
+ case PO_BAD_VALUE:
+ nfs_error(_("%s: invalid value for 'mountprog=' option"),
+ progname);
+ return 0;
+ }
+
+ /*
+ * MNT RPC program wasn't specified. The RPC program
+ * cannot be determined via an rpcbind query.
+ */
+ *program = nfs_getrpcbyname(MOUNTPROG, nfs_mnt_pgmtbl);
+ return 1;
+}
+
+/*
+ * Returns TRUE if @version contains a valid value for this option,
+ * or FALSE if the option was specified with an invalid value.
+ */
+static int
+nfs_mount_version(struct mount_options *options, unsigned long *version)
+{
+ long tmp;
+
+ switch (po_get_numeric(options, "mountvers", &tmp)) {
+ case PO_NOT_FOUND:
+ break;
+ case PO_FOUND:
+ if (tmp >= 1 && tmp <= 4) {
+ *version = tmp;
+ return 1;
+ }
+ /* FALLTHRU */
+ case PO_BAD_VALUE:
+ nfs_error(_("%s: invalid value for 'mountvers=' option"),
+ progname);
+ return 0;
+ }
+
+ /*
+ * MNT version wasn't specified. The pmap version value
+ * will be filled in later by an rpcbind query in this case.
+ */
+ *version = 0;
+ return 1;
+}
+
+/*
+ * Returns TRUE if @protocol contains a valid value for this option,
+ * or FALSE if the option was specified with an invalid value. On
+ * error, errno is set.
+ */
+static int
+nfs_mount_protocol(struct mount_options *options, unsigned long *protocol)
+{
+ sa_family_t family;
+ char *option;
+
+ option = po_get(options, "mountproto");
+ if (option != NULL) {
+ if (!nfs_get_proto(option, &family, protocol)) {
+ errno = EPROTONOSUPPORT;
+ nfs_error(_("%s: Failed to find '%s' protocol"),
+ progname, option);
+ return 0;
+ }
+ return 1;
+ }
+
+ /*
+ * MNT transport protocol wasn't specified. If the NFS
+ * transport protocol was specified, use that; otherwise
+ * set @protocol to zero. The pmap protocol value will
+ * be filled in later by an rpcbind query in this case.
+ */
+ if (!nfs_nfs_protocol(options, protocol))
+ return 0;
+ if (*protocol == NFSPROTO_RDMA)
+ *protocol = IPPROTO_TCP;
+ return 1;
+}
+
+/*
+ * Returns TRUE if @port contains a valid value for this option,
+ * or FALSE if the option was specified with an invalid value.
+ */
+static int
+nfs_mount_port(struct mount_options *options, unsigned long *port)
+{
+ long tmp;
+
+ switch (po_get_numeric(options, "mountport", &tmp)) {
+ case PO_NOT_FOUND:
+ break;
+ case PO_FOUND:
+ if (tmp >= 0 && tmp <= 65535) {
+ *port = tmp;
+ return 1;
+ }
+ /* FALLTHRU */
+ case PO_BAD_VALUE:
+ nfs_error(_("%s: invalid value for 'mountport=' option"),
+ progname);
+ return 0;
+ }
+
+ /*
+ * MNT service port wasn't specified. The pmap port value
+ * will be filled in later by an rpcbind query in this case.
+ */
+ *port = 0;
+ return 1;
+}
+
+/*
+ * Returns TRUE and fills in @family if a valid MNT protocol option
+ * is found, or FALSE if the option was specified with an invalid value
+ * or if the protocol family isn't supported. On error, errno is set.
+ */
+int nfs_mount_proto_family(struct mount_options *options,
+ sa_family_t *family)
+{
+ unsigned long protocol;
+ char *option;
+ sa_family_t tmp_family = config_default_family;
+
+ option = po_get(options, "mountproto");
+ if (option != NULL) {
+ if (!nfs_get_proto(option, &tmp_family, &protocol)) {
+ nfs_error(_("%s: Failed to find '%s' protocol"),
+ progname, option);
+ errno = EPROTONOSUPPORT;
+ goto out_err;
+ }
+ if (!nfs_verify_family(tmp_family))
+ goto out_err;
+ *family = tmp_family;
+ return 1;
+ }
+
+ /*
+ * MNT transport protocol wasn't specified. If the NFS
+ * transport protocol was specified, derive the family
+ * from that; otherwise, return the default family for
+ * NFS.
+ */
+ return nfs_nfs_proto_family(options, family);
+out_err:
+ errno = EAFNOSUPPORT;
+ return 0;
+}
+
+/**
+ * nfs_options2pmap - set up pmap structs based on mount options
+ * @options: pointer to mount options
+ * @nfs_pmap: OUT: pointer to pmap arguments for NFS server
+ * @mnt_pmap: OUT: pointer to pmap arguments for mountd server
+ *
+ * Returns TRUE if the pmap options specified in @options have valid
+ * values; otherwise FALSE is returned.
+ */
+int nfs_options2pmap(struct mount_options *options,
+ struct pmap *nfs_pmap, struct pmap *mnt_pmap)
+{
+ struct nfs_version version;
+ memset(&version, 0, sizeof(version));
+
+ if (!nfs_nfs_program(options, &nfs_pmap->pm_prog))
+ return 0;
+ if (!nfs_nfs_version("nfs", options, &version))
+ return 0;
+ if (version.v_mode == V_DEFAULT)
+ nfs_pmap->pm_vers = 0;
+ else
+ nfs_pmap->pm_vers = version.major;
+ if (!nfs_nfs_protocol(options, &nfs_pmap->pm_prot))
+ return 0;
+ if (!nfs_nfs_port(options, &nfs_pmap->pm_port))
+ return 0;
+
+ if (!nfs_mount_program(options, &mnt_pmap->pm_prog))
+ return 0;
+ if (!nfs_mount_version(options, &mnt_pmap->pm_vers))
+ return 0;
+ if (!nfs_mount_protocol(options, &mnt_pmap->pm_prot))
+ return 0;
+ if (!nfs_mount_port(options, &mnt_pmap->pm_port))
+ return 0;
+
+ return 1;
+}
+
+/*
+ * Discover mount server's hostname/address by examining mount options
+ *
+ * Returns a pointer to a string that the caller must free, on
+ * success; otherwise NULL is returned.
+ */
+static char *nfs_umount_hostname(struct mount_options *options,
+ char *hostname)
+{
+ char *option;
+
+ option = po_get(options, "mountaddr");
+ if (option)
+ goto out;
+ option = po_get(options, "mounthost");
+ if (option)
+ goto out;
+ option = po_get(options, "addr");
+ if (option)
+ goto out;
+
+ return hostname;
+
+out:
+ free(hostname);
+ return strdup(option);
+}
+
+
+/*
+ * Returns EX_SUCCESS if mount options and device name have been
+ * parsed successfully; otherwise EX_FAIL.
+ */
+int nfs_umount_do_umnt(struct mount_options *options,
+ char **hostname, char **dirname)
+{
+ union nfs_sockaddr address;
+ struct sockaddr *sap = &address.sa;
+ socklen_t salen = sizeof(address);
+ struct pmap nfs_pmap, mnt_pmap;
+ sa_family_t family;
+
+ if (!nfs_options2pmap(options, &nfs_pmap, &mnt_pmap))
+ return EX_FAIL;
+
+ /* Skip UMNT call for vers=4 mounts */
+ if (nfs_pmap.pm_vers == 4)
+ return EX_SUCCESS;
+
+ *hostname = nfs_umount_hostname(options, *hostname);
+ if (!*hostname) {
+ nfs_error(_("%s: out of memory"), progname);
+ return EX_FAIL;
+ }
+
+ if (!nfs_mount_proto_family(options, &family))
+ return 0;
+ if (!nfs_lookup(*hostname, family, sap, &salen))
+ /* nfs_lookup reports any errors */
+ return EX_FAIL;
+
+ if (nfs_advise_umount(sap, salen, &mnt_pmap, dirname) == 0)
+ /* nfs_advise_umount reports any errors */
+ return EX_FAIL;
+
+ return EX_SUCCESS;
+}
+
+int nfs_is_inaddr_any(struct sockaddr *nfs_saddr)
+{
+ switch (nfs_saddr->sa_family) {
+ case AF_INET: {
+ if (((struct sockaddr_in *)nfs_saddr)->sin_addr.s_addr ==
+ INADDR_ANY)
+ return 1;
+ break;
+ }
+ case AF_INET6:
+ if (!memcmp(&((struct sockaddr_in6 *)nfs_saddr)->sin6_addr,
+ &in6addr_any, sizeof(in6addr_any)))
+ return 1;
+ break;
+ }
+ return 0;
+}
+
+int nfs_addr_matches_localips(struct sockaddr *nfs_saddr)
+{
+ struct ifaddrs *myaddrs, *ifa;
+ int found = 0;
+
+ /* acquire exiting network interfaces */
+ if (getifaddrs(&myaddrs) != 0)
+ return 0;
+
+ /* interate over the available interfaces and check if we
+ * we find a match to the supplied clientaddr value
+ */
+ for (ifa = myaddrs; ifa != NULL; ifa = ifa->ifa_next) {
+ if (ifa->ifa_addr == NULL)
+ continue;
+ if (!(ifa->ifa_flags & IFF_UP))
+ continue;
+ if (!memcmp(ifa->ifa_addr, nfs_saddr,
+ sizeof(struct sockaddr))) {
+ found = 1;
+ break;
+ }
+ }
+ freeifaddrs(myaddrs);
+ return found;
+}
diff --git a/utils/mount/network.h b/utils/mount/network.h
new file mode 100644
index 0000000..0fc98ac
--- /dev/null
+++ b/utils/mount/network.h
@@ -0,0 +1,96 @@
+/*
+ * network.h -- Provide common network functions for NFS mount/umount
+ *
+ * Copyright (C) 2007 Oracle. All rights reserved.
+ * Copyright (C) 2007 Chuck Lever <chuck.lever@oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ *
+ */
+
+#ifndef _NFS_UTILS_MOUNT_NETWORK_H
+#define _NFS_UTILS_MOUNT_NETWORK_H
+
+#include <rpc/pmap_prot.h>
+
+#define MNT_SENDBUFSIZE (2048U)
+#define MNT_RECVBUFSIZE (1024U)
+
+typedef struct {
+ char **hostname;
+ struct sockaddr_in saddr;
+ struct pmap pmap;
+} clnt_addr_t;
+
+/* RPC call timeout values */
+static const struct timeval TIMEOUT = { 20, 0 };
+static const struct timeval RETRY_TIMEOUT = { 3, 0 };
+
+int probe_bothports(clnt_addr_t *, clnt_addr_t *);
+int nfs_probe_bothports(const struct sockaddr *, const socklen_t,
+ struct pmap *, const struct sockaddr *,
+ const socklen_t, struct pmap *, int);
+int nfs_gethostbyname(const char *, struct sockaddr_in *);
+int nfs_lookup(const char *hostname, const sa_family_t family,
+ struct sockaddr *sap, socklen_t *salen);
+int nfs_string_to_sockaddr(const char *, struct sockaddr *, socklen_t *);
+int nfs_present_sockaddr(const struct sockaddr *,
+ const socklen_t, char *, const size_t);
+int nfs_callback_address(const struct sockaddr *, const socklen_t,
+ struct sockaddr *, socklen_t *);
+int clnt_ping(struct sockaddr_in *, const unsigned long,
+ const unsigned long, const unsigned int,
+ struct sockaddr_in *);
+int nfs_is_inaddr_any(struct sockaddr *);
+int nfs_addr_matches_localips(struct sockaddr *);
+
+struct mount_options;
+
+enum {
+ V_DEFAULT = 0,
+ V_GENERAL,
+ V_SPECIFIC,
+ V_PARSE_ERR,
+};
+
+struct nfs_version {
+ unsigned long major;
+ unsigned long minor;
+ int v_mode;
+};
+
+int nfs_nfs_proto_family(struct mount_options *options, sa_family_t *family);
+int nfs_mount_proto_family(struct mount_options *options, sa_family_t *family);
+int nfs_nfs_version(char *type, struct mount_options *options, struct nfs_version *version);
+int nfs_nfs_protocol(struct mount_options *options, unsigned long *protocol);
+
+int nfs_options2pmap(struct mount_options *,
+ struct pmap *, struct pmap *);
+
+int start_statd(void);
+
+unsigned long nfsvers_to_mnt(const unsigned long);
+
+int nfs_call_umount(clnt_addr_t *, dirpath *);
+int nfs_advise_umount(const struct sockaddr *, const socklen_t,
+ const struct pmap *, const dirpath *);
+CLIENT *mnt_openclnt(clnt_addr_t *, int *);
+void mnt_closeclnt(CLIENT *, int);
+
+int nfs_umount_do_umnt(struct mount_options *options,
+ char **hostname, char **dirname);
+
+#endif /* _NFS_UTILS_MOUNT_NETWORK_H */
diff --git a/utils/mount/nfs.man b/utils/mount/nfs.man
new file mode 100644
index 0000000..c0ba4d0
--- /dev/null
+++ b/utils/mount/nfs.man
@@ -0,0 +1,1968 @@
+.\"@(#)nfs.5"
+.TH NFS 5 "9 October 2012"
+.SH NAME
+nfs \- fstab format and options for the
+.B nfs
+file systems
+.SH SYNOPSIS
+.I /etc/fstab
+.SH DESCRIPTION
+NFS is an Internet Standard protocol
+created by Sun Microsystems in 1984. NFS was developed
+to allow file sharing between systems residing
+on a local area network.
+Depending on kernel configuration, the Linux NFS client may
+support NFS versions 3, 4.0, 4.1, or 4.2.
+.P
+The
+.BR mount (8)
+command attaches a file system to the system's
+name space hierarchy at a given mount point.
+The
+.I /etc/fstab
+file describes how
+.BR mount (8)
+should assemble a system's file name hierarchy
+from various independent file systems
+(including file systems exported by NFS servers).
+Each line in the
+.I /etc/fstab
+file describes a single file system, its mount point,
+and a set of default mount options for that mount point.
+.P
+For NFS file system mounts, a line in the
+.I /etc/fstab
+file specifies the server name,
+the path name of the exported server directory to mount,
+the local directory that is the mount point,
+the type of file system that is being mounted,
+and a list of mount options that control
+the way the filesystem is mounted and
+how the NFS client behaves when accessing
+files on this mount point.
+The fifth and sixth fields on each line are not used
+by NFS, thus conventionally each contain the digit zero. For example:
+.P
+.nf
+.ta 8n +14n +14n +9n +20n
+ server:path /mountpoint fstype option,option,... 0 0
+.fi
+.P
+The server's hostname and export pathname
+are separated by a colon, while
+the mount options are separated by commas. The remaining fields
+are separated by blanks or tabs.
+.P
+The server's hostname can be an unqualified hostname,
+a fully qualified domain name,
+a dotted quad IPv4 address, or
+an IPv6 address enclosed in square brackets.
+Link-local and site-local IPv6 addresses must be accompanied by an
+interface identifier.
+See
+.BR ipv6 (7)
+for details on specifying raw IPv6 addresses.
+.P
+The
+.I fstype
+field contains "nfs". Use of the "nfs4" fstype in
+.I /etc/fstab
+is deprecated.
+.SH "MOUNT OPTIONS"
+Refer to
+.BR mount (8)
+for a description of generic mount options
+available for all file systems. If you do not need to
+specify any mount options, use the generic option
+.B defaults
+in
+.IR /etc/fstab .
+.DT
+.SS "Options supported by all versions"
+These options are valid to use with any NFS version.
+.TP 1.5i
+.BI nfsvers= n
+The NFS protocol version number used to contact the server's NFS service.
+If the server does not support the requested version, the mount request
+fails.
+If this option is not specified, the client tries version 4.2 first,
+then negotiates down until it finds a version supported by the server.
+.TP 1.5i
+.BI vers= n
+This option is an alternative to the
+.B nfsvers
+option.
+It is included for compatibility with other operating systems
+.TP 1.5i
+.BR soft " / " softerr " / " hard
+Determines the recovery behavior of the NFS client
+after an NFS request times out.
+If no option is specified (or if the
+.B hard
+option is specified), NFS requests are retried indefinitely.
+If either the
+.BR soft " or " softerr
+option is specified, then the NFS client fails an NFS request
+after
+.B retrans
+retransmissions have been sent,
+causing the NFS client to return either the error
+.B EIO
+(for the
+.B soft
+option) or
+.B ETIMEDOUT
+(for the
+.B softerr
+option) to the calling application.
+.IP
+.I NB:
+A so-called "soft" timeout can cause
+silent data corruption in certain cases. As such, use the
+.BR soft " or " softerr
+option only when client responsiveness
+is more important than data integrity.
+Using NFS over TCP or increasing the value of the
+.B retrans
+option may mitigate some of the risks of using the
+.BR soft " or " softerr
+option.
+.TP 1.5i
+.BR softreval " / " nosoftreval
+In cases where the NFS server is down, it may be useful to
+allow the NFS client to continue to serve up paths and
+attributes from cache after
+.B retrans
+attempts to revalidate that cache have timed out.
+This may, for instance, be helpful when trying to unmount a
+filesystem tree from a server that is permanently down.
+.IP
+It is possible to combine
+.BR softreval
+with the
+.B soft
+mount option, in which case operations that cannot be served up
+from cache will time out and return an error after
+.B retrans
+attempts. The combination with the default
+.B hard
+mount option implies those uncached operations will continue to
+retry until a response is received from the server.
+.IP
+Note: the default mount option is
+.BR nosoftreval
+which disallows fallback to cache when revalidation fails, and
+instead follows the behavior dictated by the
+.B hard
+or
+.B soft
+mount option.
+.TP 1.5i
+.BR intr " / " nointr
+This option is provided for backward compatibility.
+It is ignored after kernel 2.6.25.
+.TP 1.5i
+.BI timeo= n
+The time in deciseconds (tenths of a second) the NFS client waits for a
+response before it retries an NFS request.
+.IP
+For NFS over TCP the default
+.B timeo
+value is 600 (60 seconds).
+The NFS client performs linear backoff: After each retransmission the
+timeout is increased by
+.BR timeo
+up to the maximum of 600 seconds.
+.IP
+However, for NFS over UDP, the client uses an adaptive
+algorithm to estimate an appropriate timeout value for frequently used
+request types (such as READ and WRITE requests), but uses the
+.B timeo
+setting for infrequently used request types (such as FSINFO requests).
+If the
+.B timeo
+option is not specified,
+infrequently used request types are retried after 1.1 seconds.
+After each retransmission, the NFS client doubles the timeout for
+that request,
+up to a maximum timeout length of 60 seconds.
+.TP 1.5i
+.BI retrans= n
+The number of times the NFS client retries a request before
+it attempts further recovery action. If the
+.B retrans
+option is not specified, the NFS client tries each UDP request
+three times and each TCP request twice.
+.IP
+The NFS client generates a "server not responding" message
+after
+.B retrans
+retries, then attempts further recovery (depending on whether the
+.B hard
+mount option is in effect).
+.TP 1.5i
+.BI rsize= n
+The maximum number of bytes in each network READ request
+that the NFS client can receive when reading data from a file
+on an NFS server.
+The actual data payload size of each NFS READ request is equal to
+or smaller than the
+.B rsize
+setting. The largest read payload supported by the Linux NFS client
+is 1,048,576 bytes (one megabyte).
+.IP
+The
+.B rsize
+value is a positive integral multiple of 1024.
+Specified
+.B rsize
+values lower than 1024 are replaced with 4096; values larger than
+1048576 are replaced with 1048576. If a specified value is within the supported
+range but not a multiple of 1024, it is rounded down to the nearest
+multiple of 1024.
+.IP
+If an
+.B rsize
+value is not specified, or if the specified
+.B rsize
+value is larger than the maximum that either client or server can support,
+the client and server negotiate the largest
+.B rsize
+value that they can both support.
+.IP
+The
+.B rsize
+mount option as specified on the
+.BR mount (8)
+command line appears in the
+.I /etc/mtab
+file. However, the effective
+.B rsize
+value negotiated by the client and server is reported in the
+.I /proc/mounts
+file.
+.TP 1.5i
+.BI wsize= n
+The maximum number of bytes per network WRITE request
+that the NFS client can send when writing data to a file
+on an NFS server. The actual data payload size of each
+NFS WRITE request is equal to
+or smaller than the
+.B wsize
+setting. The largest write payload supported by the Linux NFS client
+is 1,048,576 bytes (one megabyte).
+.IP
+Similar to
+.B rsize
+, the
+.B wsize
+value is a positive integral multiple of 1024.
+Specified
+.B wsize
+values lower than 1024 are replaced with 4096; values larger than
+1048576 are replaced with 1048576. If a specified value is within the supported
+range but not a multiple of 1024, it is rounded down to the nearest
+multiple of 1024.
+.IP
+If a
+.B wsize
+value is not specified, or if the specified
+.B wsize
+value is larger than the maximum that either client or server can support,
+the client and server negotiate the largest
+.B wsize
+value that they can both support.
+.IP
+The
+.B wsize
+mount option as specified on the
+.BR mount (8)
+command line appears in the
+.I /etc/mtab
+file. However, the effective
+.B wsize
+value negotiated by the client and server is reported in the
+.I /proc/mounts
+file.
+.TP 1.5i
+.BR ac " / " noac
+Selects whether the client may cache file attributes. If neither
+option is specified (or if
+.B ac
+is specified), the client caches file
+attributes.
+.IP
+To improve performance, NFS clients cache file
+attributes. Every few seconds, an NFS client checks the server's version of each
+file's attributes for updates. Changes that occur on the server in
+those small intervals remain undetected until the client checks the
+server again. The
+.B noac
+option prevents clients from caching file
+attributes so that applications can more quickly detect file changes
+on the server.
+.IP
+In addition to preventing the client from caching file attributes,
+the
+.B noac
+option forces application writes to become synchronous so
+that local changes to a file become visible on the server
+immediately. That way, other clients can quickly detect recent
+writes when they check the file's attributes.
+.IP
+Using the
+.B noac
+option provides greater cache coherence among NFS clients
+accessing the same files,
+but it extracts a significant performance penalty.
+As such, judicious use of file locking is encouraged instead.
+The DATA AND METADATA COHERENCE section contains a detailed discussion
+of these trade-offs.
+.TP 1.5i
+.BI acregmin= n
+The minimum time (in seconds) that the NFS client caches
+attributes of a regular file before it requests
+fresh attribute information from a server.
+If this option is not specified, the NFS client uses
+a 3-second minimum.
+See the DATA AND METADATA COHERENCE section
+for a full discussion of attribute caching.
+.TP 1.5i
+.BI acregmax= n
+The maximum time (in seconds) that the NFS client caches
+attributes of a regular file before it requests
+fresh attribute information from a server.
+If this option is not specified, the NFS client uses
+a 60-second maximum.
+See the DATA AND METADATA COHERENCE section
+for a full discussion of attribute caching.
+.TP 1.5i
+.BI acdirmin= n
+The minimum time (in seconds) that the NFS client caches
+attributes of a directory before it requests
+fresh attribute information from a server.
+If this option is not specified, the NFS client uses
+a 30-second minimum.
+See the DATA AND METADATA COHERENCE section
+for a full discussion of attribute caching.
+.TP 1.5i
+.BI acdirmax= n
+The maximum time (in seconds) that the NFS client caches
+attributes of a directory before it requests
+fresh attribute information from a server.
+If this option is not specified, the NFS client uses
+a 60-second maximum.
+See the DATA AND METADATA COHERENCE section
+for a full discussion of attribute caching.
+.TP 1.5i
+.BI actimeo= n
+Using
+.B actimeo
+sets all of
+.BR acregmin ,
+.BR acregmax ,
+.BR acdirmin ,
+and
+.B acdirmax
+to the same value.
+If this option is not specified, the NFS client uses
+the defaults for each of these options listed above.
+.TP 1.5i
+.BR bg " / " fg
+Determines how the
+.BR mount (8)
+command behaves if an attempt to mount an export fails.
+The
+.B fg
+option causes
+.BR mount (8)
+to exit with an error status if any part of the mount request
+times out or fails outright.
+This is called a "foreground" mount,
+and is the default behavior if neither the
+.B fg
+nor
+.B bg
+mount option is specified.
+.IP
+If the
+.B bg
+option is specified, a timeout or failure causes the
+.BR mount (8)
+command to fork a child which continues to attempt
+to mount the export.
+The parent immediately returns with a zero exit code.
+This is known as a "background" mount.
+.IP
+If the local mount point directory is missing, the
+.BR mount (8)
+command acts as if the mount request timed out.
+This permits nested NFS mounts specified in
+.I /etc/fstab
+to proceed in any order during system initialization,
+even if some NFS servers are not yet available.
+Alternatively these issues can be addressed
+using an automounter (refer to
+.BR automount (8)
+for details).
+.TP 1.5i
+.BR nconnect= n
+When using a connection oriented protocol such as TCP, it may
+sometimes be advantageous to set up multiple connections between
+the client and server. For instance, if your clients and/or servers
+are equipped with multiple network interface cards (NICs), using multiple
+connections to spread the load may improve overall performance.
+In such cases, the
+.BR nconnect
+option allows the user to specify the number of connections
+that should be established between the client and server up to
+a limit of 16.
+.IP
+Note that the
+.BR nconnect
+option may also be used by some pNFS drivers to decide how many
+connections to set up to the data servers.
+.TP 1.5i
+.BR rdirplus " / " nordirplus
+Selects whether to use NFS v3 or v4 READDIRPLUS requests.
+If this option is not specified, the NFS client uses READDIRPLUS requests
+on NFS v3 or v4 mounts to read small directories.
+Some applications perform better if the client uses only READDIR requests
+for all directories.
+.TP 1.5i
+.BI retry= n
+The number of minutes that the
+.BR mount (8)
+command retries an NFS mount operation
+in the foreground or background before giving up.
+If this option is not specified, the default value for foreground mounts
+is 2 minutes, and the default value for background mounts is 10000 minutes
+(80 minutes shy of one week).
+If a value of zero is specified, the
+.BR mount (8)
+command exits immediately after the first failure.
+.IP
+Note that this only affects how many retries are made and doesn't
+affect the delay caused by each retry. For UDP each retry takes the
+time determined by the
+.BR timeo
+and
+.BR retrans
+options, which by default will be about 7 seconds. For TCP the
+default is 3 minutes, but system TCP connection timeouts will
+sometimes limit the timeout of each retransmission to around 2 minutes.
+.TP 1.5i
+.BI sec= flavors
+A colon-separated list of one or more security flavors to use for accessing
+files on the mounted export. If the server does not support any of these
+flavors, the mount operation fails.
+If
+.B sec=
+is not specified, the client attempts to find
+a security flavor that both the client and the server supports.
+Valid
+.I flavors
+are
+.BR none ,
+.BR sys ,
+.BR krb5 ,
+.BR krb5i ,
+and
+.BR krb5p .
+Refer to the SECURITY CONSIDERATIONS section for details.
+.TP 1.5i
+.BR sharecache " / " nosharecache
+Determines how the client's data cache and attribute cache are shared
+when mounting the same export more than once concurrently. Using the
+same cache reduces memory requirements on the client and presents
+identical file contents to applications when the same remote file is
+accessed via different mount points.
+.IP
+If neither option is specified, or if the
+.B sharecache
+option is
+specified, then a single cache is used for all mount points that
+access the same export. If the
+.B nosharecache
+option is specified,
+then that mount point gets a unique cache. Note that when data and
+attribute caches are shared, the mount options from the first mount
+point take effect for subsequent concurrent mounts of the same export.
+.IP
+As of kernel 2.6.18, the behavior specified by
+.B nosharecache
+is legacy caching behavior. This
+is considered a data risk since multiple cached copies
+of the same file on the same client can become out of sync
+following a local update of one of the copies.
+.TP 1.5i
+.BR resvport " / " noresvport
+Specifies whether the NFS client should use a privileged source port
+when communicating with an NFS server for this mount point.
+If this option is not specified, or the
+.B resvport
+option is specified, the NFS client uses a privileged source port.
+If the
+.B noresvport
+option is specified, the NFS client uses a non-privileged source port.
+This option is supported in kernels 2.6.28 and later.
+.IP
+Using non-privileged source ports helps increase the maximum number of
+NFS mount points allowed on a client, but NFS servers must be configured
+to allow clients to connect via non-privileged source ports.
+.IP
+Refer to the SECURITY CONSIDERATIONS section for important details.
+.TP 1.5i
+.BI lookupcache= mode
+Specifies how the kernel manages its cache of directory entries
+for a given mount point.
+.I mode
+can be one of
+.BR all ,
+.BR none ,
+.BR pos ,
+or
+.BR positive .
+This option is supported in kernels 2.6.28 and later.
+.IP
+The Linux NFS client caches the result of all NFS LOOKUP requests.
+If the requested directory entry exists on the server,
+the result is referred to as
+.IR positive .
+If the requested directory entry does not exist on the server,
+the result is referred to as
+.IR negative .
+.IP
+If this option is not specified, or if
+.B all
+is specified, the client assumes both types of directory cache entries
+are valid until their parent directory's cached attributes expire.
+.IP
+If
+.BR pos " or " positive
+is specified, the client assumes positive entries are valid
+until their parent directory's cached attributes expire, but
+always revalidates negative entires before an application
+can use them.
+.IP
+If
+.B none
+is specified,
+the client revalidates both types of directory cache entries
+before an application can use them.
+This permits quick detection of files that were created or removed
+by other clients, but can impact application and server performance.
+.IP
+The DATA AND METADATA COHERENCE section contains a
+detailed discussion of these trade-offs.
+.TP 1.5i
+.BR fsc " / " nofsc
+Enable/Disables the cache of (read-only) data pages to the local disk
+using the FS-Cache facility. See cachefilesd(8)
+and <kernel_source>/Documentation/filesystems/caching
+for detail on how to configure the FS-Cache facility.
+Default value is nofsc.
+.TP 1.5i
+.B sloppy
+The
+.B sloppy
+option is an alternative to specifying
+.BR mount.nfs " -s " option.
+.TP 1.5i
+.BI xprtsec= policy
+Specifies the use of transport layer security to protect NFS network
+traffic on behalf of this mount point.
+.I policy
+can be one of
+.BR none ,
+.BR tls ,
+or
+.BR mtls .
+.IP
+If
+.B none
+is specified,
+transport layer security is forced off, even if the NFS server supports
+transport layer security.
+.IP
+If
+.B tls
+is specified, the client uses RPC-with-TLS to provide in-transit
+confidentiality.
+.IP
+If
+.B mtls
+is specified, the client uses RPC-with-TLS to authenticate itself and
+to provide in-transit confidentiality.
+.IP
+If either
+.B tls
+or
+.B mtls
+is specified and the server does not support RPC-with-TLS or peer
+authentication fails, the mount attempt fails.
+.IP
+If the
+.B xprtsec=
+option is not specified,
+the default behavior depends on the kernel version,
+but is usually equivalent to
+.BR "xprtsec=none" .
+.SS "Options for NFS versions 2 and 3 only"
+Use these options, along with the options in the above subsection,
+for NFS versions 2 and 3 only.
+.TP 1.5i
+.BI proto= netid
+The
+.I netid
+determines the transport that is used to communicate with the NFS
+server. Available options are
+.BR udp ", " udp6 ", "tcp ", " tcp6 ", " rdma ", and " rdma6 .
+Those which end in
+.B 6
+use IPv6 addresses and are only available if support for TI-RPC is
+built in. Others use IPv4 addresses.
+.IP
+Each transport protocol uses different default
+.B retrans
+and
+.B timeo
+settings.
+Refer to the description of these two mount options for details.
+.IP
+In addition to controlling how the NFS client transmits requests to
+the server, this mount option also controls how the
+.BR mount (8)
+command communicates with the server's rpcbind and mountd services.
+Specifying a netid that uses TCP forces all traffic from the
+.BR mount (8)
+command and the NFS client to use TCP.
+Specifying a netid that uses UDP forces all traffic types to use UDP.
+.IP
+.B Before using NFS over UDP, refer to the TRANSPORT METHODS section.
+.IP
+If the
+.B proto
+mount option is not specified, the
+.BR mount (8)
+command discovers which protocols the server supports
+and chooses an appropriate transport for each service.
+Refer to the TRANSPORT METHODS section for more details.
+.TP 1.5i
+.B udp
+The
+.B udp
+option is an alternative to specifying
+.BR proto=udp.
+It is included for compatibility with other operating systems.
+.IP
+.B Before using NFS over UDP, refer to the TRANSPORT METHODS section.
+.TP 1.5i
+.B tcp
+The
+.B tcp
+option is an alternative to specifying
+.BR proto=tcp.
+It is included for compatibility with other operating systems.
+.TP 1.5i
+.B rdma
+The
+.B rdma
+option is an alternative to specifying
+.BR proto=rdma.
+.TP 1.5i
+.BI port= n
+The numeric value of the server's NFS service port.
+If the server's NFS service is not available on the specified port,
+the mount request fails.
+.IP
+If this option is not specified, or if the specified port value is 0,
+then the NFS client uses the NFS service port number
+advertised by the server's rpcbind service.
+The mount request fails if the server's rpcbind service is not available,
+the server's NFS service is not registered with its rpcbind service,
+or the server's NFS service is not available on the advertised port.
+.TP 1.5i
+.BI mountport= n
+The numeric value of the server's mountd port.
+If the server's mountd service is not available on the specified port,
+the mount request fails.
+.IP
+If this option is not specified,
+or if the specified port value is 0, then the
+.BR mount (8)
+command uses the mountd service port number
+advertised by the server's rpcbind service.
+The mount request fails if the server's rpcbind service is not available,
+the server's mountd service is not registered with its rpcbind service,
+or the server's mountd service is not available on the advertised port.
+.IP
+This option can be used when mounting an NFS server
+through a firewall that blocks the rpcbind protocol.
+.TP 1.5i
+.BI mountproto= netid
+The transport the NFS client uses
+to transmit requests to the NFS server's mountd service when performing
+this mount request, and when later unmounting this mount point.
+.IP
+.I netid
+may be one of
+.BR udp ", and " tcp
+which use IPv4 address or, if TI-RPC is built into the
+.B mount.nfs
+command,
+.BR udp6 ", and " tcp6
+which use IPv6 addresses.
+.IP
+This option can be used when mounting an NFS server
+through a firewall that blocks a particular transport.
+When used in combination with the
+.B proto
+option, different transports for mountd requests and NFS requests
+can be specified.
+If the server's mountd service is not available via the specified
+transport, the mount request fails.
+.IP
+Refer to the TRANSPORT METHODS section for more on how the
+.B mountproto
+mount option interacts with the
+.B proto
+mount option.
+.TP 1.5i
+.BI mounthost= name
+The hostname of the host running mountd.
+If this option is not specified, the
+.BR mount (8)
+command assumes that the mountd service runs
+on the same host as the NFS service.
+.TP 1.5i
+.BI mountvers= n
+The RPC version number used to contact the server's mountd.
+If this option is not specified, the client uses a version number
+appropriate to the requested NFS version.
+This option is useful when multiple NFS services
+are running on the same remote server host.
+.TP 1.5i
+.BI namlen= n
+The maximum length of a pathname component on this mount.
+If this option is not specified, the maximum length is negotiated
+with the server. In most cases, this maximum length is 255 characters.
+.IP
+Some early versions of NFS did not support this negotiation.
+Using this option ensures that
+.BR pathconf (3)
+reports the proper maximum component length to applications
+in such cases.
+.TP 1.5i
+.BR lock " / " nolock
+Selects whether to use the NLM sideband protocol to lock files on the server.
+If neither option is specified (or if
+.B lock
+is specified), NLM locking is used for this mount point.
+When using the
+.B nolock
+option, applications can lock files,
+but such locks provide exclusion only against other applications
+running on the same client.
+Remote applications are not affected by these locks.
+.IP
+NLM locking must be disabled with the
+.B nolock
+option when using NFS to mount
+.I /var
+because
+.I /var
+contains files used by the NLM implementation on Linux.
+Using the
+.B nolock
+option is also required when mounting exports on NFS servers
+that do not support the NLM protocol.
+.TP 1.5i
+.BR cto " / " nocto
+Selects whether to use close-to-open cache coherence semantics.
+If neither option is specified (or if
+.B cto
+is specified), the client uses close-to-open
+cache coherence semantics. If the
+.B nocto
+option is specified, the client uses a non-standard heuristic to determine when
+files on the server have changed.
+.IP
+Using the
+.B nocto
+option may improve performance for read-only mounts,
+but should be used only if the data on the server changes only occasionally.
+The DATA AND METADATA COHERENCE section discusses the behavior
+of this option in more detail.
+.TP 1.5i
+.BR acl " / " noacl
+Selects whether to use the NFSACL sideband protocol on this mount point.
+The NFSACL sideband protocol is a proprietary protocol
+implemented in Solaris that manages Access Control Lists. NFSACL was never
+made a standard part of the NFS protocol specification.
+.IP
+If neither
+.B acl
+nor
+.B noacl
+option is specified,
+the NFS client negotiates with the server
+to see if the NFSACL protocol is supported,
+and uses it if the server supports it.
+Disabling the NFSACL sideband protocol may be necessary
+if the negotiation causes problems on the client or server.
+Refer to the SECURITY CONSIDERATIONS section for more details.
+.TP 1.5i
+.BR local_lock= mechanism
+Specifies whether to use local locking for any or both of the flock and the
+POSIX locking mechanisms.
+.I mechanism
+can be one of
+.BR all ,
+.BR flock ,
+.BR posix ,
+or
+.BR none .
+This option is supported in kernels 2.6.37 and later.
+.IP
+The Linux NFS client provides a way to make locks local. This means, the
+applications can lock files, but such locks provide exclusion only against
+other applications running on the same client. Remote applications are not
+affected by these locks.
+.IP
+If this option is not specified, or if
+.B none
+is specified, the client assumes that the locks are not local.
+.IP
+If
+.BR all
+is specified, the client assumes that both flock and POSIX locks are local.
+.IP
+If
+.BR flock
+is specified, the client assumes that only flock locks are local and uses
+NLM sideband protocol to lock files when POSIX locks are used.
+.IP
+If
+.BR posix
+is specified, the client assumes that POSIX locks are local and uses NLM
+sideband protocol to lock files when flock locks are used.
+.IP
+To support legacy flock behavior similar to that of NFS clients < 2.6.12,
+use 'local_lock=flock'. This option is required when exporting NFS mounts via
+Samba as Samba maps Windows share mode locks as flock. Since NFS clients >
+2.6.12 implement flock by emulating POSIX locks, this will result in
+conflicting locks.
+.IP
+NOTE: When used together, the 'local_lock' mount option will be overridden
+by 'nolock'/'lock' mount option.
+.SS "Options for NFS version 4 only"
+Use these options, along with the options in the first subsection above,
+for NFS version 4.0 and newer.
+.TP 1.5i
+.BI proto= netid
+The
+.I netid
+determines the transport that is used to communicate with the NFS
+server. Supported options are
+.BR tcp ", " tcp6 ", " rdma ", and " rdma6 .
+.B tcp6
+use IPv6 addresses and is only available if support for TI-RPC is
+built in. Both others use IPv4 addresses.
+.IP
+All NFS version 4 servers are required to support TCP,
+so if this mount option is not specified, the NFS version 4 client
+uses the TCP protocol.
+Refer to the TRANSPORT METHODS section for more details.
+.TP 1.5i
+.BI minorversion= n
+Specifies the protocol minor version number.
+NFSv4 introduces "minor versioning," where NFS protocol enhancements can
+be introduced without bumping the NFS protocol version number.
+Before kernel 2.6.38, the minor version is always zero, and this
+option is not recognized.
+After this kernel, specifying "minorversion=1" enables a number of
+advanced features, such as NFSv4 sessions.
+.IP
+Recent kernels allow the minor version to be specified using the
+.B vers=
+option.
+For example, specifying
+.B vers=4.1
+is the same as specifying
+.BR vers=4,minorversion=1 .
+.TP 1.5i
+.BI port= n
+The numeric value of the server's NFS service port.
+If the server's NFS service is not available on the specified port,
+the mount request fails.
+.IP
+If this mount option is not specified,
+the NFS client uses the standard NFS port number of 2049
+without first checking the server's rpcbind service.
+This allows an NFS version 4 client to contact an NFS version 4
+server through a firewall that may block rpcbind requests.
+.IP
+If the specified port value is 0,
+then the NFS client uses the NFS service port number
+advertised by the server's rpcbind service.
+The mount request fails if the server's rpcbind service is not available,
+the server's NFS service is not registered with its rpcbind service,
+or the server's NFS service is not available on the advertised port.
+.TP 1.5i
+.BR cto " / " nocto
+Selects whether to use close-to-open cache coherence semantics
+for NFS directories on this mount point.
+If neither
+.B cto
+nor
+.B nocto
+is specified,
+the default is to use close-to-open cache coherence
+semantics for directories.
+.IP
+File data caching behavior is not affected by this option.
+The DATA AND METADATA COHERENCE section discusses
+the behavior of this option in more detail.
+.TP 1.5i
+.BI clientaddr= n.n.n.n
+.TP 1.5i
+.BI clientaddr= n:n: ... :n
+Specifies a single IPv4 address (in dotted-quad form),
+or a non-link-local IPv6 address,
+that the NFS client advertises to allow servers
+to perform NFS version 4.0 callback requests against
+files on this mount point. If the server is unable to
+establish callback connections to clients, performance
+may degrade, or accesses to files may temporarily hang.
+Can specify a value of IPv4_ANY (0.0.0.0) or equivalent
+IPv6 any address which will signal to the NFS server that
+this NFS client does not want delegations.
+.IP
+If this option is not specified, the
+.BR mount (8)
+command attempts to discover an appropriate callback address automatically.
+The automatic discovery process is not perfect, however.
+In the presence of multiple client network interfaces,
+special routing policies,
+or atypical network topologies,
+the exact address to use for callbacks may be nontrivial to determine.
+.IP
+NFS protocol versions 4.1 and 4.2 use the client-established
+TCP connection for callback requests, so do not require the server to
+connect to the client. This option is therefore only affect NFS version
+4.0 mounts.
+.TP 1.5i
+.BR migration " / " nomigration
+Selects whether the client uses an identification string that is compatible
+with NFSv4 Transparent State Migration (TSM).
+If the mounted server supports NFSv4 migration with TSM, specify the
+.B migration
+option.
+.IP
+Some server features misbehave in the face of a migration-compatible
+identification string.
+The
+.B nomigration
+option retains the use of a traditional client indentification string
+which is compatible with legacy NFS servers.
+This is also the behavior if neither option is specified.
+A client's open and lock state cannot be migrated transparently
+when it identifies itself via a traditional identification string.
+.IP
+This mount option has no effect with NFSv4 minor versions newer than zero,
+which always use TSM-compatible client identification strings.
+.TP 1.5i
+.BR max_connect= n
+While
+.BR nconnect
+option sets a limit on the number of connections that can be established
+to a given server IP,
+.BR max_connect
+option allows the user to specify maximum number of connections to different
+server IPs that belong to the same NFSv4.1+ server (session trunkable
+connections) up to a limit of 16. When client discovers that it established
+a client ID to an already existing server, instead of dropping the newly
+created network transport, the client will add this new connection to the
+list of available transports for that RPC client.
+.TP 1.5i
+.BR trunkdiscovery " / " notrunkdiscovery
+When the client discovers a new filesystem on a NFSv4.1+ server, the
+.BR trunkdiscovery
+mount option will cause it to send a GETATTR for the fs_locations attribute.
+If is receives a non-zero length reply, it will iterate through the response,
+and for each server location it will establish a connection, send an
+EXCHANGE_ID, and test for session trunking. If the trunking test succeeds,
+the connection will be added to the existing set of transports for the server,
+subject to the limit specified by the
+.BR max_connect
+option. The default is
+.BR notrunkdiscovery .
+.SH nfs4 FILE SYSTEM TYPE
+The
+.BR nfs4
+file system type is an old syntax for specifying NFSv4 usage. It can still
+be used with all NFSv4-specific and common options, excepted the
+.B nfsvers
+mount option.
+.SH MOUNT CONFIGURATION FILE
+If the mount command is configured to do so, all of the mount options
+described in the previous section can also be configured in the
+.I /etc/nfsmount.conf
+file. See
+.BR nfsmount.conf(5)
+for details.
+.SH EXAMPLES
+To mount using NFS version 3,
+use the
+.B nfs
+file system type and specify the
+.B nfsvers=3
+mount option.
+To mount using NFS version 4,
+use either the
+.B nfs
+file system type, with the
+.B nfsvers=4
+mount option, or the
+.B nfs4
+file system type.
+.P
+The following example from an
+.I /etc/fstab
+file causes the mount command to negotiate
+reasonable defaults for NFS behavior.
+.P
+.nf
+.ta 8n +16n +6n +6n +30n
+ server:/export /mnt nfs defaults 0 0
+.fi
+.P
+This example shows how to mount using NFS version 4 over TCP
+with Kerberos 5 mutual authentication.
+.P
+.nf
+.ta 8n +16n +6n +6n +30n
+ server:/export /mnt nfs4 sec=krb5 0 0
+.fi
+.P
+This example shows how to mount using NFS version 4 over TCP
+with Kerberos 5 privacy or data integrity mode.
+.P
+.nf
+.ta 8n +16n +6n +6n +30n
+ server:/export /mnt nfs4 sec=krb5p:krb5i 0 0
+.fi
+.P
+This example can be used to mount /usr over NFS.
+.P
+.nf
+.ta 8n +16n +6n +6n +30n
+ server:/export /usr nfs ro,nolock,nocto,actimeo=3600 0 0
+.fi
+.P
+This example shows how to mount an NFS server
+using a raw IPv6 link-local address.
+.P
+.nf
+.ta 8n +40n +5n +4n +9n
+ [fe80::215:c5ff:fb3e:e2b1%eth0]:/export /mnt nfs defaults 0 0
+.fi
+.SH "TRANSPORT METHODS"
+NFS clients send requests to NFS servers via
+Remote Procedure Calls, or
+.IR RPCs .
+The RPC client discovers remote service endpoints automatically,
+handles per-request authentication,
+adjusts request parameters for different byte endianness on client and server,
+and retransmits requests that may have been lost by the network or server.
+RPC requests and replies flow over a network transport.
+.P
+In most cases, the
+.BR mount (8)
+command, NFS client, and NFS server
+can automatically negotiate proper transport
+and data transfer size settings for a mount point.
+In some cases, however, it pays to specify
+these settings explicitly using mount options.
+.P
+Traditionally, NFS clients used the UDP transport exclusively for
+transmitting requests to servers. Though its implementation is
+simple, NFS over UDP has many limitations that prevent smooth
+operation and good performance in some common deployment
+environments. Even an insignificant packet loss rate results in the
+loss of whole NFS requests; as such, retransmit timeouts are usually
+in the subsecond range to allow clients to recover quickly from
+dropped requests, but this can result in extraneous network traffic
+and server load.
+.P
+However, UDP can be quite effective in specialized settings where
+the networks MTU is large relative to NFSs data transfer size (such
+as network environments that enable jumbo Ethernet frames). In such
+environments, trimming the
+.B rsize
+and
+.B wsize
+settings so that each
+NFS read or write request fits in just a few network frames (or even
+in a single frame) is advised. This reduces the probability that
+the loss of a single MTU-sized network frame results in the loss of
+an entire large read or write request.
+.P
+TCP is the default transport protocol used for all modern NFS
+implementations. It performs well in almost every conceivable
+network environment and provides excellent guarantees against data
+corruption caused by network unreliability. TCP is often a
+requirement for mounting a server through a network firewall.
+.P
+Under normal circumstances, networks drop packets much more
+frequently than NFS servers drop requests. As such, an aggressive
+retransmit timeout setting for NFS over TCP is unnecessary. Typical
+timeout settings for NFS over TCP are between one and ten minutes.
+After the client exhausts its retransmits (the value of the
+.B retrans
+mount option), it assumes a network partition has occurred,
+and attempts to reconnect to the server on a fresh socket. Since
+TCP itself makes network data transfer reliable,
+.B rsize
+and
+.B wsize
+can safely be allowed to default to the largest values supported by
+both client and server, independent of the network's MTU size.
+.SS "Using the mountproto mount option"
+This section applies only to NFS version 3 mounts
+since NFS version 4 does not use a separate protocol for mount
+requests.
+.P
+The Linux NFS client can use a different transport for
+contacting an NFS server's rpcbind service, its mountd service,
+its Network Lock Manager (NLM) service, and its NFS service.
+The exact transports employed by the Linux NFS client for
+each mount point depends on the settings of the transport
+mount options, which include
+.BR proto ,
+.BR mountproto ,
+.BR udp ", and " tcp .
+.P
+The client sends Network Status Manager (NSM) notifications
+via UDP no matter what transport options are specified, but
+listens for server NSM notifications on both UDP and TCP.
+The NFS Access Control List (NFSACL) protocol shares the same
+transport as the main NFS service.
+.P
+If no transport options are specified, the Linux NFS client
+uses UDP to contact the server's mountd service, and TCP to
+contact its NLM and NFS services by default.
+.P
+If the server does not support these transports for these services, the
+.BR mount (8)
+command attempts to discover what the server supports, and then retries
+the mount request once using the discovered transports.
+If the server does not advertise any transport supported by the client
+or is misconfigured, the mount request fails.
+If the
+.B bg
+option is in effect, the mount command backgrounds itself and continues
+to attempt the specified mount request.
+.P
+When the
+.B proto
+option, the
+.B udp
+option, or the
+.B tcp
+option is specified but the
+.B mountproto
+option is not, the specified transport is used to contact
+both the server's mountd service and for the NLM and NFS services.
+.P
+If the
+.B mountproto
+option is specified but none of the
+.BR proto ", " udp " or " tcp
+options are specified, then the specified transport is used for the
+initial mountd request, but the mount command attempts to discover
+what the server supports for the NFS protocol, preferring TCP if
+both transports are supported.
+.P
+If both the
+.BR mountproto " and " proto
+(or
+.BR udp " or " tcp )
+options are specified, then the transport specified by the
+.B mountproto
+option is used for the initial mountd request, and the transport
+specified by the
+.B proto
+option (or the
+.BR udp " or " tcp " options)"
+is used for NFS, no matter what order these options appear.
+No automatic service discovery is performed if these options are
+specified.
+.P
+If any of the
+.BR proto ", " udp ", " tcp ", "
+or
+.B mountproto
+options are specified more than once on the same mount command line,
+then the value of the rightmost instance of each of these options
+takes effect.
+.SS "Using NFS over UDP on high-speed links"
+Using NFS over UDP on high-speed links such as Gigabit
+.BR "can cause silent data corruption" .
+.P
+The problem can be triggered at high loads, and is caused by problems in
+IP fragment reassembly. NFS read and writes typically transmit UDP packets
+of 4 Kilobytes or more, which have to be broken up into several fragments
+in order to be sent over the Ethernet link, which limits packets to 1500
+bytes by default. This process happens at the IP network layer and is
+called fragmentation.
+.P
+In order to identify fragments that belong together, IP assigns a 16bit
+.I IP ID
+value to each packet; fragments generated from the same UDP packet
+will have the same IP ID. The receiving system will collect these
+fragments and combine them to form the original UDP packet. This process
+is called reassembly. The default timeout for packet reassembly is
+30 seconds; if the network stack does not receive all fragments of
+a given packet within this interval, it assumes the missing fragment(s)
+got lost and discards those it already received.
+.P
+The problem this creates over high-speed links is that it is possible
+to send more than 65536 packets within 30 seconds. In fact, with
+heavy NFS traffic one can observe that the IP IDs repeat after about
+5 seconds.
+.P
+This has serious effects on reassembly: if one fragment gets lost,
+another fragment
+.I from a different packet
+but with the
+.I same IP ID
+will arrive within the 30 second timeout, and the network stack will
+combine these fragments to form a new packet. Most of the time, network
+layers above IP will detect this mismatched reassembly - in the case
+of UDP, the UDP checksum, which is a 16 bit checksum over the entire
+packet payload, will usually not match, and UDP will discard the
+bad packet.
+.P
+However, the UDP checksum is 16 bit only, so there is a chance of 1 in
+65536 that it will match even if the packet payload is completely
+random (which very often isn't the case). If that is the case,
+silent data corruption will occur.
+.P
+This potential should be taken seriously, at least on Gigabit
+Ethernet.
+Network speeds of 100Mbit/s should be considered less
+problematic, because with most traffic patterns IP ID wrap around
+will take much longer than 30 seconds.
+.P
+It is therefore strongly recommended to use
+.BR "NFS over TCP where possible" ,
+since TCP does not perform fragmentation.
+.P
+If you absolutely have to use NFS over UDP over Gigabit Ethernet,
+some steps can be taken to mitigate the problem and reduce the
+probability of corruption:
+.TP +1.5i
+.I Jumbo frames:
+Many Gigabit network cards are capable of transmitting
+frames bigger than the 1500 byte limit of traditional Ethernet, typically
+9000 bytes. Using jumbo frames of 9000 bytes will allow you to run NFS over
+UDP at a page size of 8K without fragmentation. Of course, this is
+only feasible if all involved stations support jumbo frames.
+.IP
+To enable a machine to send jumbo frames on cards that support it,
+it is sufficient to configure the interface for a MTU value of 9000.
+.TP +1.5i
+.I Lower reassembly timeout:
+By lowering this timeout below the time it takes the IP ID counter
+to wrap around, incorrect reassembly of fragments can be prevented
+as well. To do so, simply write the new timeout value (in seconds)
+to the file
+.BR /proc/sys/net/ipv4/ipfrag_time .
+.IP
+A value of 2 seconds will greatly reduce the probability of IPID clashes on
+a single Gigabit link, while still allowing for a reasonable timeout
+when receiving fragmented traffic from distant peers.
+.SH "DATA AND METADATA COHERENCE"
+Some modern cluster file systems provide
+perfect cache coherence among their clients.
+Perfect cache coherence among disparate NFS clients
+is expensive to achieve, especially on wide area networks.
+As such, NFS settles for weaker cache coherence that
+satisfies the requirements of most file sharing types.
+.SS "Close-to-open cache consistency"
+Typically file sharing is completely sequential.
+First client A opens a file, writes something to it, then closes it.
+Then client B opens the same file, and reads the changes.
+.P
+When an application opens a file stored on an NFS version 3 server,
+the NFS client checks that the file exists on the server
+and is permitted to the opener by sending a GETATTR or ACCESS request.
+The NFS client sends these requests
+regardless of the freshness of the file's cached attributes.
+.P
+When the application closes the file,
+the NFS client writes back any pending changes
+to the file so that the next opener can view the changes.
+This also gives the NFS client an opportunity to report
+write errors to the application via the return code from
+.BR close (2).
+.P
+The behavior of checking at open time and flushing at close time
+is referred to as
+.IR "close-to-open cache consistency" ,
+or
+.IR CTO .
+It can be disabled for an entire mount point using the
+.B nocto
+mount option.
+.SS "Weak cache consistency"
+There are still opportunities for a client's data cache
+to contain stale data.
+The NFS version 3 protocol introduced "weak cache consistency"
+(also known as WCC) which provides a way of efficiently checking
+a file's attributes before and after a single request.
+This allows a client to help identify changes
+that could have been made by other clients.
+.P
+When a client is using many concurrent operations
+that update the same file at the same time
+(for example, during asynchronous write behind),
+it is still difficult to tell whether it was
+that client's updates or some other client's updates
+that altered the file.
+.SS "Attribute caching"
+Use the
+.B noac
+mount option to achieve attribute cache coherence
+among multiple clients.
+Almost every file system operation checks
+file attribute information.
+The client keeps this information cached
+for a period of time to reduce network and server load.
+When
+.B noac
+is in effect, a client's file attribute cache is disabled,
+so each operation that needs to check a file's attributes
+is forced to go back to the server.
+This permits a client to see changes to a file very quickly,
+at the cost of many extra network operations.
+.P
+Be careful not to confuse the
+.B noac
+option with "no data caching."
+The
+.B noac
+mount option prevents the client from caching file metadata,
+but there are still races that may result in data cache incoherence
+between client and server.
+.P
+The NFS protocol is not designed to support
+true cluster file system cache coherence
+without some type of application serialization.
+If absolute cache coherence among clients is required,
+applications should use file locking. Alternatively, applications
+can also open their files with the O_DIRECT flag
+to disable data caching entirely.
+.SS "File timestamp maintenance"
+NFS servers are responsible for managing file and directory timestamps
+.RB ( atime ,
+.BR ctime ", and"
+.BR mtime ).
+When a file is accessed or updated on an NFS server,
+the file's timestamps are updated just like they would be on a filesystem
+local to an application.
+.P
+NFS clients cache file attributes, including timestamps.
+A file's timestamps are updated on NFS clients when its attributes
+are retrieved from the NFS server.
+Thus there may be some delay before timestamp updates
+on an NFS server appear to applications on NFS clients.
+.P
+To comply with the POSIX filesystem standard, the Linux NFS client
+relies on NFS servers to keep a file's
+.B mtime
+and
+.B ctime
+timestamps properly up to date.
+It does this by flushing local data changes to the server
+before reporting
+.B mtime
+to applications via system calls such as
+.BR stat (2).
+.P
+The Linux client handles
+.B atime
+updates more loosely, however.
+NFS clients maintain good performance by caching data,
+but that means that application reads, which normally update
+.BR atime ,
+are not reflected to the server where a file's
+.B atime
+is actually maintained.
+.P
+Because of this caching behavior,
+the Linux NFS client does not support generic atime-related mount options.
+See
+.BR mount (8)
+for details on these options.
+.P
+In particular, the
+.BR atime / noatime ,
+.BR diratime / nodiratime ,
+.BR relatime / norelatime ,
+and
+.BR strictatime / nostrictatime
+mount options have no effect on NFS mounts.
+.P
+.I /proc/mounts
+may report that the
+.B relatime
+mount option is set on NFS mounts, but in fact the
+.B atime
+semantics are always as described here, and are not like
+.B relatime
+semantics.
+.SS "Directory entry caching"
+The Linux NFS client caches the result of all NFS LOOKUP requests.
+If the requested directory entry exists on the server,
+the result is referred to as a
+.IR positive " lookup result.
+If the requested directory entry does not exist on the server
+(that is, the server returned ENOENT),
+the result is referred to as
+.IR negative " lookup result.
+.P
+To detect when directory entries have been added or removed
+on the server,
+the Linux NFS client watches a directory's mtime.
+If the client detects a change in a directory's mtime,
+the client drops all cached LOOKUP results for that directory.
+Since the directory's mtime is a cached attribute, it may
+take some time before a client notices it has changed.
+See the descriptions of the
+.BR acdirmin ", " acdirmax ", and " noac
+mount options for more information about
+how long a directory's mtime is cached.
+.P
+Caching directory entries improves the performance of applications that
+do not share files with applications on other clients.
+Using cached information about directories can interfere
+with applications that run concurrently on multiple clients and
+need to detect the creation or removal of files quickly, however.
+The
+.B lookupcache
+mount option allows some tuning of directory entry caching behavior.
+.P
+Before kernel release 2.6.28,
+the Linux NFS client tracked only positive lookup results.
+This permitted applications to detect new directory entries
+created by other clients quickly while still providing some of the
+performance benefits of caching.
+If an application depends on the previous lookup caching behavior
+of the Linux NFS client, you can use
+.BR lookupcache=positive .
+.P
+If the client ignores its cache and validates every application
+lookup request with the server,
+that client can immediately detect when a new directory
+entry has been either created or removed by another client.
+You can specify this behavior using
+.BR lookupcache=none .
+The extra NFS requests needed if the client does not
+cache directory entries can exact a performance penalty.
+Disabling lookup caching
+should result in less of a performance penalty than using
+.BR noac ,
+and has no effect on how the NFS client caches the attributes of files.
+.P
+.SS "The sync mount option"
+The NFS client treats the
+.B sync
+mount option differently than some other file systems
+(refer to
+.BR mount (8)
+for a description of the generic
+.B sync
+and
+.B async
+mount options).
+If neither
+.B sync
+nor
+.B async
+is specified (or if the
+.B async
+option is specified),
+the NFS client delays sending application
+writes to the server
+until any of these events occur:
+.IP
+Memory pressure forces reclamation of system memory resources.
+.IP
+An application flushes file data explicitly with
+.BR sync (2),
+.BR msync (2),
+or
+.BR fsync (3).
+.IP
+An application closes a file with
+.BR close (2).
+.IP
+The file is locked/unlocked via
+.BR fcntl (2).
+.P
+In other words, under normal circumstances,
+data written by an application may not immediately appear
+on the server that hosts the file.
+.P
+If the
+.B sync
+option is specified on a mount point,
+any system call that writes data to files on that mount point
+causes that data to be flushed to the server
+before the system call returns control to user space.
+This provides greater data cache coherence among clients,
+but at a significant performance cost.
+.P
+Applications can use the O_SYNC open flag to force application
+writes to individual files to go to the server immediately without
+the use of the
+.B sync
+mount option.
+.SS "Using file locks with NFS"
+The Network Lock Manager protocol is a separate sideband protocol
+used to manage file locks in NFS version 3.
+To support lock recovery after a client or server reboot,
+a second sideband protocol --
+known as the Network Status Manager protocol --
+is also required.
+In NFS version 4,
+file locking is supported directly in the main NFS protocol,
+and the NLM and NSM sideband protocols are not used.
+.P
+In most cases, NLM and NSM services are started automatically,
+and no extra configuration is required.
+Configure all NFS clients with fully-qualified domain names
+to ensure that NFS servers can find clients to notify them of server reboots.
+.P
+NLM supports advisory file locks only.
+To lock NFS files, use
+.BR fcntl (2)
+with the F_GETLK and F_SETLK commands.
+The NFS client converts file locks obtained via
+.BR flock (2)
+to advisory locks.
+.P
+When mounting servers that do not support the NLM protocol,
+or when mounting an NFS server through a firewall
+that blocks the NLM service port,
+specify the
+.B nolock
+mount option. NLM locking must be disabled with the
+.B nolock
+option when using NFS to mount
+.I /var
+because
+.I /var
+contains files used by the NLM implementation on Linux.
+.P
+Specifying the
+.B nolock
+option may also be advised to improve the performance
+of a proprietary application which runs on a single client
+and uses file locks extensively.
+.SS "NFS version 4 caching features"
+The data and metadata caching behavior of NFS version 4
+clients is similar to that of earlier versions.
+However, NFS version 4 adds two features that improve
+cache behavior:
+.I change attributes
+and
+.IR "file delegation" .
+.P
+The
+.I change attribute
+is a new part of NFS file and directory metadata
+which tracks data changes.
+It replaces the use of a file's modification
+and change time stamps
+as a way for clients to validate the content
+of their caches.
+Change attributes are independent of the time stamp
+resolution on either the server or client, however.
+.P
+A
+.I file delegation
+is a contract between an NFS version 4 client
+and server that allows the client to treat a file temporarily
+as if no other client is accessing it.
+The server promises to notify the client (via a callback request) if another client
+attempts to access that file.
+Once a file has been delegated to a client, the client can
+cache that file's data and metadata aggressively without
+contacting the server.
+.P
+File delegations come in two flavors:
+.I read
+and
+.IR write .
+A
+.I read
+delegation means that the server notifies the client
+about any other clients that want to write to the file.
+A
+.I write
+delegation means that the client gets notified about
+either read or write accessors.
+.P
+Servers grant file delegations when a file is opened,
+and can recall delegations at any time when another
+client wants access to the file that conflicts with
+any delegations already granted.
+Delegations on directories are not supported.
+.P
+In order to support delegation callback, the server
+checks the network return path to the client during
+the client's initial contact with the server.
+If contact with the client cannot be established,
+the server simply does not grant any delegations to
+that client.
+.SH "SECURITY CONSIDERATIONS"
+NFS servers control access to file data,
+but they depend on their RPC implementation
+to provide authentication of NFS requests.
+Traditional NFS access control mimics
+the standard mode bit access control provided in local file systems.
+Traditional RPC authentication uses a number
+to represent each user
+(usually the user's own uid),
+a number to represent the user's group (the user's gid),
+and a set of up to 16 auxiliary group numbers
+to represent other groups of which the user may be a member.
+.P
+Typically, file data and user ID values appear unencrypted
+(i.e. "in the clear") on the network.
+Moreover, NFS versions 2 and 3 use
+separate sideband protocols for mounting,
+locking and unlocking files,
+and reporting system status of clients and servers.
+These auxiliary protocols use no authentication.
+.P
+In addition to combining these sideband protocols with the main NFS protocol,
+NFS version 4 introduces more advanced forms of access control,
+authentication, and in-transit data protection.
+The NFS version 4 specification mandates support for
+strong authentication and security flavors
+that provide per-RPC integrity checking and encryption.
+Because NFS version 4 combines the
+function of the sideband protocols into the main NFS protocol,
+the new security features apply to all NFS version 4 operations
+including mounting, file locking, and so on.
+RPCGSS authentication can also be used with NFS versions 2 and 3,
+but it does not protect their sideband protocols.
+.P
+The
+.B sec
+mount option specifies the security flavor used for operations
+on behalf of users on that NFS mount point.
+Specifying
+.B sec=krb5
+provides cryptographic proof of a user's identity in each RPC request.
+This provides strong verification of the identity of users
+accessing data on the server.
+Note that additional configuration besides adding this mount option
+is required in order to enable Kerberos security.
+Refer to the
+.BR rpc.gssd (8)
+man page for details.
+.P
+Two additional flavors of Kerberos security are supported:
+.B krb5i
+and
+.BR krb5p .
+The
+.B krb5i
+security flavor provides a cryptographically strong guarantee
+that the data in each RPC request has not been tampered with.
+The
+.B krb5p
+security flavor encrypts every RPC request
+to prevent data exposure during network transit; however,
+expect some performance impact
+when using integrity checking or encryption.
+Similar support for other forms of cryptographic security
+is also available.
+.SS "NFS version 4 filesystem crossing"
+The NFS version 4 protocol allows
+a client to renegotiate the security flavor
+when the client crosses into a new filesystem on the server.
+The newly negotiated flavor effects only accesses of the new filesystem.
+.P
+Such negotiation typically occurs when a client crosses
+from a server's pseudo-fs
+into one of the server's exported physical filesystems,
+which often have more restrictive security settings than the pseudo-fs.
+.SS "NFS version 4 Leases"
+In NFS version 4, a lease is a period during which a server
+irrevocably grants a client file locks.
+Once the lease expires, the server may revoke those locks.
+Clients periodically renew their leases to prevent lock revocation.
+.P
+After an NFS version 4 server reboots, each client tells the
+server about existing file open and lock state under its lease
+before operation can continue.
+If a client reboots, the server frees all open and lock state
+associated with that client's lease.
+.P
+When establishing a lease, therefore,
+a client must identify itself to a server.
+Each client presents an arbitrary string
+to distinguish itself from other clients.
+The client administrator can
+supplement the default identity string using the
+.I nfs4.nfs4_unique_id
+module parameter to avoid collisions
+with other client identity strings.
+.P
+A client also uses a unique security flavor and principal
+when it establishes its lease.
+If two clients present the same identity string,
+a server can use client principals to distinguish between them,
+thus securely preventing one client from interfering with the other's lease.
+.P
+The Linux NFS client establishes one lease on each NFS version 4 server.
+Lease management operations, such as lease renewal, are not
+done on behalf of a particular file, lock, user, or mount
+point, but on behalf of the client that owns that lease.
+A client uses a consistent identity string, security flavor,
+and principal across client reboots to ensure that the server
+can promptly reap expired lease state.
+.P
+When Kerberos is configured on a Linux NFS client
+(i.e., there is a
+.I /etc/krb5.keytab
+on that client), the client attempts to use a Kerberos
+security flavor for its lease management operations.
+Kerberos provides secure authentication of each client.
+By default, the client uses the
+.I host/
+or
+.I nfs/
+service principal in its
+.I /etc/krb5.keytab
+for this purpose, as described in
+.BR rpc.gssd (8).
+.P
+If the client has Kerberos configured, but the server
+does not, or if the client does not have a keytab or
+the requisite service principals, the client uses
+.I AUTH_SYS
+and UID 0 for lease management.
+.SS "Using non-privileged source ports"
+NFS clients usually communicate with NFS servers via network sockets.
+Each end of a socket is assigned a port value, which is simply a number
+between 1 and 65535 that distinguishes socket endpoints at the same
+IP address.
+A socket is uniquely defined by a tuple that includes the transport
+protocol (TCP or UDP) and the port values and IP addresses of both
+endpoints.
+.P
+The NFS client can choose any source port value for its sockets,
+but usually chooses a
+.I privileged
+port.
+A privileged port is a port value less than 1024.
+Only a process with root privileges may create a socket
+with a privileged source port.
+.P
+The exact range of privileged source ports that can be chosen is
+set by a pair of sysctls to avoid choosing a well-known port, such as
+the port used by ssh.
+This means the number of source ports available for the NFS client,
+and therefore the number of socket connections that can be used
+at the same time,
+is practically limited to only a few hundred.
+.P
+As described above, the traditional default NFS authentication scheme,
+known as AUTH_SYS, relies on sending local UID and GID numbers to identify
+users making NFS requests.
+An NFS server assumes that if a connection comes from a privileged port,
+the UID and GID numbers in the NFS requests on this connection have been
+verified by the client's kernel or some other local authority.
+This is an easy system to spoof, but on a trusted physical network between
+trusted hosts, it is entirely adequate.
+.P
+Roughly speaking, one socket is used for each NFS mount point.
+If a client could use non-privileged source ports as well,
+the number of sockets allowed,
+and thus the maximum number of concurrent mount points,
+would be much larger.
+.P
+Using non-privileged source ports may compromise server security somewhat,
+since any user on AUTH_SYS mount points can now pretend to be any other
+when making NFS requests.
+Thus NFS servers do not support this by default.
+They explicitly allow it usually via an export option.
+.P
+To retain good security while allowing as many mount points as possible,
+it is best to allow non-privileged client connections only if the server
+and client both require strong authentication, such as Kerberos.
+.SS "Mounting through a firewall"
+A firewall may reside between an NFS client and server,
+or the client or server may block some of its own ports via IP
+filter rules.
+It is still possible to mount an NFS server through a firewall,
+though some of the
+.BR mount (8)
+command's automatic service endpoint discovery mechanisms may not work; this
+requires you to provide specific endpoint details via NFS mount options.
+.P
+NFS servers normally run a portmapper or rpcbind daemon to advertise
+their service endpoints to clients. Clients use the rpcbind daemon to determine:
+.IP
+What network port each RPC-based service is using
+.IP
+What transport protocols each RPC-based service supports
+.P
+The rpcbind daemon uses a well-known port number (111) to help clients find a service endpoint.
+Although NFS often uses a standard port number (2049),
+auxiliary services such as the NLM service can choose
+any unused port number at random.
+.P
+Common firewall configurations block the well-known rpcbind port.
+In the absense of an rpcbind service,
+the server administrator fixes the port number
+of NFS-related services so that the firewall
+can allow access to specific NFS service ports.
+Client administrators then specify the port number
+for the mountd service via the
+.BR mount (8)
+command's
+.B mountport
+option.
+It may also be necessary to enforce the use of TCP or UDP
+if the firewall blocks one of those transports.
+.SS "NFS Access Control Lists"
+Solaris allows NFS version 3 clients direct access
+to POSIX Access Control Lists stored in its local file systems.
+This proprietary sideband protocol, known as NFSACL,
+provides richer access control than mode bits.
+Linux implements this protocol
+for compatibility with the Solaris NFS implementation.
+The NFSACL protocol never became a standard part
+of the NFS version 3 specification, however.
+.P
+The NFS version 4 specification mandates a new version
+of Access Control Lists that are semantically richer than POSIX ACLs.
+NFS version 4 ACLs are not fully compatible with POSIX ACLs; as such,
+some translation between the two is required
+in an environment that mixes POSIX ACLs and NFS version 4.
+.SH "THE REMOUNT OPTION"
+Generic mount options such as
+.BR rw " and " sync
+can be modified on NFS mount points using the
+.BR remount
+option.
+See
+.BR mount (8)
+for more information on generic mount options.
+.P
+With few exceptions, NFS-specific options
+are not able to be modified during a remount.
+The underlying transport or NFS version
+cannot be changed by a remount, for example.
+.P
+Performing a remount on an NFS file system mounted with the
+.B noac
+option may have unintended consequences.
+The
+.B noac
+option is a combination of the generic option
+.BR sync ,
+and the NFS-specific option
+.BR actimeo=0 .
+.SS "Unmounting after a remount"
+For mount points that use NFS versions 2 or 3, the NFS umount subcommand
+depends on knowing the original set of mount options used to perform the
+MNT operation.
+These options are stored on disk by the NFS mount subcommand,
+and can be erased by a remount.
+.P
+To ensure that the saved mount options are not erased during a remount,
+specify either the local mount directory, or the server hostname and
+export pathname, but not both, during a remount. For example,
+.P
+.nf
+.ta 8n
+ mount -o remount,ro /mnt
+.fi
+.P
+merges the mount option
+.B ro
+with the mount options already saved on disk for the NFS server mounted at /mnt.
+.SH FILES
+.TP 1.5i
+.I /etc/fstab
+file system table
+.TP 1.5i
+.I /etc/nfsmount.conf
+Configuration file for NFS mounts
+.SH NOTES
+Before 2.4.7, the Linux NFS client did not support NFS over TCP.
+.P
+Before 2.4.20, the Linux NFS client used a heuristic
+to determine whether cached file data was still valid
+rather than using the standard close-to-open cache coherency method
+described above.
+.P
+Starting with 2.4.22, the Linux NFS client employs
+a Van Jacobsen-based RTT estimator to determine retransmit
+timeout values when using NFS over UDP.
+.P
+Before 2.6.0, the Linux NFS client did not support NFS version 4.
+.P
+Before 2.6.8, the Linux NFS client used only synchronous reads and writes
+when the
+.BR rsize " and " wsize
+settings were smaller than the system's page size.
+.P
+The Linux client's support for protocol versions depend on whether the
+kernel was built with options CONFIG_NFS_V2, CONFIG_NFS_V3,
+CONFIG_NFS_V4, CONFIG_NFS_V4_1, and CONFIG_NFS_V4_2.
+.SH "SEE ALSO"
+.BR fstab (5),
+.BR mount (8),
+.BR umount (8),
+.BR mount.nfs (5),
+.BR umount.nfs (5),
+.BR exports (5),
+.BR nfsmount.conf (5),
+.BR netconfig (5),
+.BR ipv6 (7),
+.BR nfsd (8),
+.BR sm-notify (8),
+.BR rpc.statd (8),
+.BR rpc.idmapd (8),
+.BR rpc.gssd (8),
+.BR rpc.svcgssd (8),
+.BR kerberos (1)
+.sp
+RFC 768 for the UDP specification.
+.br
+RFC 793 for the TCP specification.
+.br
+RFC 1813 for the NFS version 3 specification.
+.br
+RFC 1832 for the XDR specification.
+.br
+RFC 1833 for the RPC bind specification.
+.br
+RFC 2203 for the RPCSEC GSS API protocol specification.
+.br
+RFC 7530 for the NFS version 4.0 specification.
+.br
+RFC 5661 for the NFS version 4.1 specification.
+.br
+RFC 7862 for the NFS version 4.2 specification.
diff --git a/utils/mount/nfs4_mount.h b/utils/mount/nfs4_mount.h
new file mode 100644
index 0000000..b03792e
--- /dev/null
+++ b/utils/mount/nfs4_mount.h
@@ -0,0 +1,73 @@
+#ifndef _LINUX_NFS4_MOUNT_H
+#define _LINUX_NFS4_MOUNT_H
+
+/*
+ * linux/include/linux/nfs4_mount.h
+ *
+ * Copyright (C) 2002 Trond Myklebust
+ *
+ * structure passed from user-space to kernel-space during an nfsv4 mount
+ */
+
+/*
+ * WARNING! Do not delete or change the order of these fields. If
+ * a new field is required then add it to the end. The version field
+ * tracks which fields are present. This will ensure some measure of
+ * mount-to-kernel version compatibility. Some of these aren't used yet
+ * but here they are anyway.
+ */
+#define NFS4_MOUNT_VERSION 1
+
+struct nfs_string {
+ unsigned int len;
+ const char* data;
+};
+
+struct nfs4_mount_data {
+ int version; /* 1 */
+ int flags; /* 1 */
+ int rsize; /* 1 */
+ int wsize; /* 1 */
+ int timeo; /* 1 */
+ int retrans; /* 1 */
+ int acregmin; /* 1 */
+ int acregmax; /* 1 */
+ int acdirmin; /* 1 */
+ int acdirmax; /* 1 */
+
+ /* see the definition of 'struct clientaddr4' in RFC3010 */
+ struct nfs_string client_addr; /* 1 */
+
+ /* Mount path */
+ struct nfs_string mnt_path; /* 1 */
+
+ /* Server details */
+ struct nfs_string hostname; /* 1 */
+ /* Server IP address */
+ unsigned int host_addrlen; /* 1 */
+ struct sockaddr* host_addr; /* 1 */
+
+ /* Transport protocol to use */
+ int proto; /* 1 */
+
+ /* Pseudo-flavours to use for authentication. See RFC2623 */
+ int auth_flavourlen; /* 1 */
+ int *auth_flavours; /* 1 */
+};
+
+/* bits in the flags field */
+/* Note: the fields that correspond to existing NFSv2/v3 mount options
+ * should mirror the values from include/linux/nfs_mount.h
+ */
+
+#define NFS4_MOUNT_SOFT 0x0001 /* 1 */
+#define NFS4_MOUNT_INTR 0x0002 /* 1 */
+#define NFS4_MOUNT_NOCTO 0x0010 /* 1 */
+#define NFS4_MOUNT_NOAC 0x0020 /* 1 */
+#define NFS4_MOUNT_STRICTLOCK 0x1000 /* 1 */
+#define NFS4_MOUNT_UNSHARED 0x8000 /* 5 */
+#define NFS4_MOUNT_FLAGMASK 0xFFFF
+
+int nfs4mount(const char *, const char *, int, char **, int, int);
+
+#endif
diff --git a/utils/mount/nfs4mount.c b/utils/mount/nfs4mount.c
new file mode 100644
index 0000000..3e4f1e2
--- /dev/null
+++ b/utils/mount/nfs4mount.c
@@ -0,0 +1,481 @@
+/*
+ * nfs4mount.c -- Linux NFS mount
+ * Copyright (C) 2002 Trond Myklebust <trond.myklebust@fys.uio.no>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Note: this file based on the original nfsmount.c
+ *
+ * 2006-06-06 Amit Gud <agud@redhat.com>
+ * - Moved to nfs-utils/utils/mount from util-linux/mount.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <netdb.h>
+#include <time.h>
+#include <sys/stat.h>
+#include <sys/mount.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <rpc/auth.h>
+#include <rpc/rpc.h>
+
+#ifdef HAVE_RPCSVC_NFS_PROT_H
+#include <rpcsvc/nfs_prot.h>
+#else
+#include <linux/nfs.h>
+#define nfsstat nfs_stat
+#endif
+
+#include "pseudoflavors.h"
+#include "nls.h"
+#include "xcommon.h"
+
+#include "mount.h"
+#include "mount_constants.h"
+#include "nfs4_mount.h"
+#include "nfs_mount.h"
+#include "error.h"
+#include "network.h"
+
+#if defined(VAR_LOCK_DIR)
+#define DEFAULT_DIR VAR_LOCK_DIR
+#else
+#define DEFAULT_DIR "/var/lock/subsys"
+#endif
+
+extern char *progname;
+extern int verbose;
+extern int sloppy;
+
+char *IDMAPLCK = DEFAULT_DIR "/rpcidmapd";
+#define idmapd_check() do { \
+ if (access(IDMAPLCK, F_OK)) { \
+ printf(_("Warning: rpc.idmapd appears not to be running.\n" \
+ " All uids will be mapped to the nobody uid.\n")); \
+ } \
+} while(0);
+
+char *GSSDLCK = DEFAULT_DIR "/rpcgssd";
+#define gssd_check() do { \
+ if (access(GSSDLCK, F_OK)) { \
+ printf(_("Warning: rpc.gssd appears not to be running.\n")); \
+ } \
+} while(0);
+
+#ifndef NFS_PORT
+#define NFS_PORT 2049
+#endif
+
+#define MAX_USER_FLAVOUR 16
+
+static int parse_sec(char *sec, int *pseudoflavour)
+{
+ int i, num_flavour = 0;
+
+ for (sec = strtok(sec, ":"); sec; sec = strtok(NULL, ":")) {
+ if (num_flavour >= MAX_USER_FLAVOUR) {
+ nfs_error(_("%s: maximum number of security flavors "
+ "exceeded"), progname);
+ return 0;
+ }
+ for (i = 0; i < flav_map_size; i++) {
+ if (strcmp(sec, flav_map[i].flavour) == 0) {
+ pseudoflavour[num_flavour++] = flav_map[i].fnum;
+ break;
+ }
+ }
+ if (i == flav_map_size) {
+ nfs_error(_("%s: unknown security type %s\n"),
+ progname, sec);
+ return 0;
+ }
+ }
+ if (!num_flavour)
+ nfs_error(_("%s: no security flavors passed to sec= option"),
+ progname);
+ return num_flavour;
+}
+
+static int parse_devname(char *hostdir, char **hostname, char **dirname)
+{
+ char *s;
+
+ if (!(s = strchr(hostdir, ':'))) {
+ nfs_error(_("%s: directory to mount not in host:dir format"),
+ progname);
+ return -1;
+ }
+ *hostname = hostdir;
+ *dirname = s + 1;
+ *s = '\0';
+ /* Ignore all but first hostname in replicated mounts
+ until they can be fully supported. (mack@sgi.com) */
+ if ((s = strchr(hostdir, ','))) {
+ *s = '\0';
+ nfs_error(_("%s: warning: multiple hostnames not supported"),
+ progname);
+ }
+ return 0;
+}
+
+static int fill_ipv4_sockaddr(const char *hostname, struct sockaddr_in *addr)
+{
+ struct hostent *hp;
+ addr->sin_family = AF_INET;
+
+ if (inet_aton(hostname, &addr->sin_addr))
+ return 0;
+ if ((hp = gethostbyname(hostname)) == NULL) {
+ nfs_error(_("%s: can't get address for %s\n"),
+ progname, hostname);
+ return -1;
+ }
+ if (hp->h_length > (int)sizeof(struct in_addr)) {
+ nfs_error(_("%s: got bad hp->h_length"), progname);
+ hp->h_length = sizeof(struct in_addr);
+ }
+ memcpy(&addr->sin_addr, hp->h_addr, hp->h_length);
+ return 0;
+}
+
+static int get_my_ipv4addr(char *ip_addr, int len)
+{
+ char myname[1024];
+ struct sockaddr_in myaddr;
+
+ if (gethostname(myname, sizeof(myname))) {
+ nfs_error(_("%s: can't determine client address\n"),
+ progname);
+ return -1;
+ }
+ if (fill_ipv4_sockaddr(myname, &myaddr))
+ return -1;
+ snprintf(ip_addr, len, "%s", inet_ntoa(myaddr.sin_addr));
+ ip_addr[len-1] = '\0';
+ return 0;
+}
+
+int nfs4mount(const char *spec, const char *node, int flags,
+ char **extra_opts, int fake, int running_bg)
+{
+ static struct nfs4_mount_data data;
+ static char hostdir[1024];
+ static char ip_addr[16] = "127.0.0.1";
+ static struct sockaddr_in server_addr, client_addr;
+ static int pseudoflavour[MAX_USER_FLAVOUR];
+ int num_flavour = 0;
+ int ip_addr_in_opts = 0;
+
+ char *hostname, *dirname, *old_opts;
+ char new_opts[1024];
+ char *opt, *opteq;
+ char *s;
+ int val;
+ int bg, soft, intr;
+ int nocto, noac, unshared;
+ int retry;
+ int retval = EX_FAIL;
+ time_t timeout, t;
+
+ if (strlen(spec) >= sizeof(hostdir)) {
+ nfs_error(_("%s: excessively long host:dir argument\n"),
+ progname);
+ goto fail;
+ }
+ strcpy(hostdir, spec);
+ if (parse_devname(hostdir, &hostname, &dirname))
+ goto fail;
+
+ if (fill_ipv4_sockaddr(hostname, &server_addr))
+ goto fail;
+ if (get_my_ipv4addr(ip_addr, sizeof(ip_addr)))
+ goto fail;
+
+ /* add IP address to mtab options for use when unmounting */
+ s = inet_ntoa(server_addr.sin_addr);
+ old_opts = *extra_opts;
+ if (!old_opts)
+ old_opts = "";
+ if (strlen(old_opts) + strlen(s) + 10 >= sizeof(new_opts)) {
+ nfs_error(_("%s: excessively long option argument\n"),
+ progname);
+ goto fail;
+ }
+ if (running_bg)
+ strncpy(new_opts, old_opts, sizeof(new_opts)-1);
+ else
+ snprintf(new_opts, sizeof(new_opts), "%s%saddr=%s",
+ old_opts, *old_opts ? "," : "", s);
+ *extra_opts = xstrdup(new_opts);
+
+ /* Set default options.
+ * rsize/wsize and timeo are left 0 in order to
+ * let the kernel decide.
+ */
+ memset(&data, 0, sizeof(data));
+ data.retrans = 3;
+ data.acregmin = 3;
+ data.acregmax = 60;
+ data.acdirmin = 30;
+ data.acdirmax = 60;
+ data.proto = IPPROTO_TCP;
+
+ bg = 0;
+ soft = 0;
+ intr = NFS4_MOUNT_INTR;
+ nocto = 0;
+ noac = 0;
+ unshared = 0;
+ retry = -1;
+
+ /*
+ * NFSv4 specifies that the default port should be 2049
+ */
+ server_addr.sin_port = htons(NFS_PORT);
+
+ /* parse options */
+
+ for (opt = strtok(old_opts, ","); opt; opt = strtok(NULL, ",")) {
+ if ((opteq = strchr(opt, '='))) {
+ val = atoi(opteq + 1);
+ *opteq = '\0';
+ if (!strcmp(opt, "rsize"))
+ data.rsize = val;
+ else if (!strcmp(opt, "wsize"))
+ data.wsize = val;
+ else if (!strcmp(opt, "timeo"))
+ data.timeo = val;
+ else if (!strcmp(opt, "retrans"))
+ data.retrans = val;
+ else if (!strcmp(opt, "acregmin"))
+ data.acregmin = val;
+ else if (!strcmp(opt, "acregmax"))
+ data.acregmax = val;
+ else if (!strcmp(opt, "acdirmin"))
+ data.acdirmin = val;
+ else if (!strcmp(opt, "acdirmax"))
+ data.acdirmax = val;
+ else if (!strcmp(opt, "actimeo")) {
+ data.acregmin = val;
+ data.acregmax = val;
+ data.acdirmin = val;
+ data.acdirmax = val;
+ }
+ else if (!strcmp(opt, "retry"))
+ retry = val;
+ else if (!strcmp(opt, "port"))
+ server_addr.sin_port = htons(val);
+ else if (!strcmp(opt, "proto")) {
+ if (!strncmp(opteq+1, "tcp", 3))
+ data.proto = IPPROTO_TCP;
+ else if (!strncmp(opteq+1, "udp", 3))
+ data.proto = IPPROTO_UDP;
+ else
+ printf(_("Warning: Unrecognized proto= option.\n"));
+ } else if (!strcmp(opt, "clientaddr")) {
+ if (strlen(opteq+1) >= sizeof(ip_addr))
+ printf(_("Invalid client address %s"),
+ opteq+1);
+ strncpy(ip_addr,opteq+1, sizeof(ip_addr));
+ ip_addr[sizeof(ip_addr)-1] = '\0';
+ ip_addr_in_opts = 1;
+ } else if (!strcmp(opt, "sec")) {
+ num_flavour = parse_sec(opteq+1, pseudoflavour);
+ if (!num_flavour)
+ goto fail;
+ } else if (!strcmp(opt, "addr") || sloppy) {
+ /* ignore */;
+ } else {
+ printf(_("unknown nfs mount parameter: "
+ "%s=%d\n"), opt, val);
+ goto fail;
+ }
+ } else {
+ val = 1;
+ if (!strncmp(opt, "no", 2)) {
+ val = 0;
+ opt += 2;
+ }
+ if (!strcmp(opt, "bg"))
+ bg = val;
+ else if (!strcmp(opt, "fg"))
+ bg = !val;
+ else if (!strcmp(opt, "soft"))
+ soft = val;
+ else if (!strcmp(opt, "hard"))
+ soft = !val;
+ else if (!strcmp(opt, "intr"))
+ intr = val;
+ else if (!strcmp(opt, "cto"))
+ nocto = !val;
+ else if (!strcmp(opt, "ac"))
+ noac = !val;
+ else if (!strcmp(opt, "sharecache"))
+ unshared = !val;
+ else if (!sloppy) {
+ printf(_("unknown nfs mount option: %s%s\n"),
+ val ? "" : "no", opt);
+ goto fail;
+ }
+ }
+ }
+
+ /* if retry is still -1, then it wasn't set via an option */
+ if (retry == -1) {
+ if (bg)
+ retry = 10000; /* 10000 mins == ~1 week */
+ else
+ retry = 2; /* 2 min default on fg mounts */
+ }
+
+ data.flags = (soft ? NFS4_MOUNT_SOFT : 0)
+ | (intr ? NFS4_MOUNT_INTR : 0)
+ | (nocto ? NFS4_MOUNT_NOCTO : 0)
+ | (noac ? NFS4_MOUNT_NOAC : 0)
+ | (unshared ? NFS4_MOUNT_UNSHARED : 0);
+
+ /*
+ * Give a warning if the rpc.idmapd daemon is not running
+ */
+#if 0
+ /* We shouldn't have these checks as nothing in this package
+ * creates the files that are checked
+ */
+ idmapd_check();
+
+ if (num_flavour == 0)
+ pseudoflavour[num_flavour++] = AUTH_UNIX;
+ else {
+ /*
+ * ditto with rpc.gssd daemon
+ */
+ gssd_check();
+ }
+#endif
+ data.auth_flavourlen = num_flavour;
+ data.auth_flavours = pseudoflavour;
+
+ data.client_addr.data = ip_addr;
+ data.client_addr.len = strlen(ip_addr);
+
+ data.mnt_path.data = dirname;
+ data.mnt_path.len = strlen(dirname);
+
+ data.hostname.data = hostname;
+ data.hostname.len = strlen(hostname);
+ data.host_addr = (struct sockaddr *)&server_addr;
+ data.host_addrlen = sizeof(server_addr);
+
+#ifdef NFS_MOUNT_DEBUG
+ printf(_("rsize = %d, wsize = %d, timeo = %d, retrans = %d\n"),
+ data.rsize, data.wsize, data.timeo, data.retrans);
+ printf(_("acreg (min, max) = (%d, %d), acdir (min, max) = (%d, %d)\n"),
+ data.acregmin, data.acregmax, data.acdirmin, data.acdirmax);
+ printf(_("port = %d, bg = %d, retry = %d, flags = %.8x\n"),
+ ntohs(server_addr.sin_port), bg, retry, data.flags);
+ printf(_("soft = %d, intr = %d, nocto = %d, noac = %d, "
+ "nosharecache = %d\n"),
+ (data.flags & NFS4_MOUNT_SOFT) != 0,
+ (data.flags & NFS4_MOUNT_INTR) != 0,
+ (data.flags & NFS4_MOUNT_NOCTO) != 0,
+ (data.flags & NFS4_MOUNT_NOAC) != 0,
+ (data.flags & NFS4_MOUNT_UNSHARED) != 0);
+
+ if (num_flavour > 0) {
+ int pf_cnt, i;
+
+ printf(_("sec = "));
+ for (pf_cnt = 0; pf_cnt < num_flavour; pf_cnt++) {
+ for (i = 0; i < flav_map_size; i++) {
+ if (flav_map[i].fnum == pseudoflavour[pf_cnt]) {
+ printf("%s", flav_map[i].flavour);
+ break;
+ }
+ }
+ printf("%s", (pf_cnt < num_flavour-1) ? ":" : "\n");
+ }
+ }
+ printf(_("proto = %s\n"), (data.proto == IPPROTO_TCP) ? _("tcp") : _("udp"));
+#endif
+
+ timeout = time(NULL) + 60 * retry;
+ data.version = NFS4_MOUNT_VERSION;
+ for (;;) {
+ if (verbose) {
+ printf(_("%s: pinging: prog %d vers %d prot %s port %d\n"),
+ progname, NFS_PROGRAM, 4,
+ data.proto == IPPROTO_UDP ? "udp" : "tcp",
+ ntohs(server_addr.sin_port));
+ }
+ client_addr.sin_family = 0;
+ client_addr.sin_addr.s_addr = 0;
+ clnt_ping(&server_addr, NFS_PROGRAM, 4, data.proto, &client_addr);
+ if (rpc_createerr.cf_stat == RPC_SUCCESS) {
+ if (!ip_addr_in_opts &&
+ client_addr.sin_family != 0 &&
+ client_addr.sin_addr.s_addr != 0) {
+ snprintf(ip_addr, sizeof(ip_addr), "%s",
+ inet_ntoa(client_addr.sin_addr));
+ data.client_addr.len = strlen(ip_addr);
+ }
+ break;
+ }
+
+ if (!bg) {
+ switch(rpc_createerr.cf_stat) {
+ case RPC_TIMEDOUT:
+ break;
+ case RPC_SYSTEMERROR:
+ if (errno == ETIMEDOUT)
+ break;
+ /* FALLTHRU */
+ default:
+ rpc_mount_errors(hostname, 0, bg);
+ goto fail;
+ }
+ }
+
+ if (bg && !running_bg) {
+ if (retry > 0)
+ retval = EX_BG;
+ goto fail;
+ }
+
+ t = time(NULL);
+ if (t >= timeout) {
+ rpc_mount_errors(hostname, 0, bg);
+ goto fail;
+ }
+ rpc_mount_errors(hostname, 1, bg);
+ continue;
+ }
+
+ if (!fake) {
+ if (mount(spec, node, "nfs4",
+ flags & ~(MS_USER|MS_USERS), &data)) {
+ mount_error(spec, node, errno);
+ goto fail;
+ }
+ }
+
+ return EX_SUCCESS;
+
+fail:
+ return retval;
+}
diff --git a/utils/mount/nfs_mount.h b/utils/mount/nfs_mount.h
new file mode 100644
index 0000000..ec30c9b
--- /dev/null
+++ b/utils/mount/nfs_mount.h
@@ -0,0 +1,83 @@
+/*
+ * We want to be able to compile mount on old kernels in such a way
+ * that the binary will work well on more recent kernels.
+ * Thus, if necessary we teach nfsmount.c the structure of new fields
+ * that will come later.
+ *
+ * Moreover, the new kernel includes conflict with glibc includes
+ * so it is easiest to ignore the kernel altogether (at compile time).
+ */
+
+#ifndef _NFS_UTILS_MOUNT_NFS_MOUNT_H
+#define _NFS_UTILS_MOUNT_NFS_MOUNT_H
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#define NFS_MOUNT_VERSION 6
+#define NFS_MAX_CONTEXT_LEN 256
+
+struct nfs2_fh {
+ char data[32];
+};
+struct nfs3_fh {
+ unsigned short size;
+ unsigned char data[64];
+};
+
+struct nfs_mount_data {
+ int version; /* 1 */
+ int fd; /* 1 */
+ struct nfs2_fh old_root; /* 1 */
+ int flags; /* 1 */
+ int rsize; /* 1 */
+ int wsize; /* 1 */
+ int timeo; /* 1 */
+ int retrans; /* 1 */
+ int acregmin; /* 1 */
+ int acregmax; /* 1 */
+ int acdirmin; /* 1 */
+ int acdirmax; /* 1 */
+ struct sockaddr_in addr; /* 1 */
+ char hostname[256]; /* 1 */
+ int namlen; /* 2 */
+ unsigned int bsize; /* 3 */
+ struct nfs3_fh root; /* 4 */
+ int pseudoflavor; /* 5 */
+ char context[NFS_MAX_CONTEXT_LEN + 1]; /* 6 */
+
+};
+
+/* bits in the flags field */
+
+#define NFS_MOUNT_SOFT 0x0001 /* 1 */
+#define NFS_MOUNT_INTR 0x0002 /* 1 */
+#define NFS_MOUNT_SECURE 0x0004 /* 1 */
+#define NFS_MOUNT_POSIX 0x0008 /* 1 */
+#define NFS_MOUNT_NOCTO 0x0010 /* 1 */
+#define NFS_MOUNT_NOAC 0x0020 /* 1 */
+#define NFS_MOUNT_TCP 0x0040 /* 2 */
+#define NFS_MOUNT_VER3 0x0080 /* 3 */
+#define NFS_MOUNT_KERBEROS 0x0100 /* 3 */
+#define NFS_MOUNT_NONLM 0x0200 /* 3 */
+#define NFS_MOUNT_BROKEN_SUID 0x0400 /* 4 */
+#define NFS_MOUNT_NOACL 0x0800 /* 4 */
+#define NFS_MOUNT_SECFLAVOUR 0x2000 /* 5 */
+#define NFS_MOUNT_NORDIRPLUS 0x4000 /* 5 */
+#define NFS_MOUNT_UNSHARED 0x8000 /* 5 */
+
+/* security pseudoflavors */
+
+#ifndef AUTH_GSS_KRB5
+#define AUTH_GSS_KRB5 390003
+#define AUTH_GSS_KRB5I 390004
+#define AUTH_GSS_KRB5P 390005
+#define AUTH_GSS_LKEY 390006
+#define AUTH_GSS_LKEYI 390007
+#define AUTH_GSS_LKEYP 390008
+#endif
+
+int nfsmount(const char *, const char *, int , char **, int, int);
+int nfsumount(int, char **);
+
+#endif /* _NFS_UTILS_MOUNT_NFS_MOUNT_H */
diff --git a/utils/mount/nfsmount.c b/utils/mount/nfsmount.c
new file mode 100644
index 0000000..3d95da9
--- /dev/null
+++ b/utils/mount/nfsmount.c
@@ -0,0 +1,873 @@
+/*
+ * nfsmount.c -- Linux NFS mount
+ * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Wed Feb 8 12:51:48 1995, biro@yggdrasil.com (Ross Biro): allow all port
+ * numbers to be specified on the command line.
+ *
+ * Fri, 8 Mar 1996 18:01:39, Swen Thuemmler <swen@uni-paderborn.de>:
+ * Omit the call to connect() for Linux version 1.3.11 or later.
+ *
+ * Wed Oct 1 23:55:28 1997: Dick Streefland <dick_streefland@tasking.com>
+ * Implemented the "bg", "fg" and "retry" mount options for NFS.
+ *
+ * 1999-02-22 Arkadiusz Miskiewicz <misiek@pld.ORG.PL>
+ * - added Native Language Support
+ *
+ * Modified by Olaf Kirch and Trond Myklebust for new NFS code,
+ * plus NFSv3 stuff.
+ *
+ * 2006-06-06 Amit Gud <agud@redhat.com>
+ * - Moved with modifcations to nfs-utils/utils/mount from util-linux/mount.
+ */
+
+/*
+ * nfsmount.c,v 1.1.1.1 1993/11/18 08:40:51 jrs Exp
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <ctype.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <netdb.h>
+#include <time.h>
+#include <rpc/rpc.h>
+#include <rpc/pmap_prot.h>
+#include <rpc/pmap_clnt.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <mntent.h>
+#include <sys/mount.h>
+#include <paths.h>
+#include <syslog.h>
+
+#include "xcommon.h"
+#include "mount.h"
+#include "nfs_mount.h"
+#include "mount_constants.h"
+#include "nls.h"
+#include "error.h"
+#include "network.h"
+#include "version.h"
+
+#ifdef HAVE_RPCSVC_NFS_PROT_H
+#include <rpcsvc/nfs_prot.h>
+#else
+#include <linux/nfs.h>
+#define nfsstat nfs_stat
+#endif
+
+#ifndef NFS_PORT
+#define NFS_PORT 2049
+#endif
+#ifndef NFS_FHSIZE
+#define NFS_FHSIZE 32
+#endif
+
+#ifndef HAVE_INET_ATON
+#define inet_aton(a,b) (0)
+#endif
+
+typedef dirpath mnt2arg_t;
+typedef dirpath mnt3arg_t;
+typedef dirpath mntarg_t;
+
+typedef struct fhstatus mnt2res_t;
+typedef struct mountres3 mnt3res_t;
+typedef union {
+ mnt2res_t nfsv2;
+ mnt3res_t nfsv3;
+} mntres_t;
+
+extern int nfs_mount_data_version;
+extern char *progname;
+extern int verbose;
+extern int sloppy;
+
+static inline enum clnt_stat
+nfs3_mount(CLIENT *clnt, mnt3arg_t *mnt3arg, mnt3res_t *mnt3res)
+{
+ return clnt_call(clnt, MOUNTPROC3_MNT,
+ (xdrproc_t) xdr_dirpath, (caddr_t) mnt3arg,
+ (xdrproc_t) xdr_mountres3, (caddr_t) mnt3res,
+ TIMEOUT);
+}
+
+static inline enum clnt_stat
+nfs2_mount(CLIENT *clnt, mnt2arg_t *mnt2arg, mnt2res_t *mnt2res)
+{
+ return clnt_call(clnt, MOUNTPROC_MNT,
+ (xdrproc_t) xdr_dirpath, (caddr_t) mnt2arg,
+ (xdrproc_t) xdr_fhstatus, (caddr_t) mnt2res,
+ TIMEOUT);
+}
+
+static int
+nfs_call_mount(clnt_addr_t *mnt_server, clnt_addr_t *nfs_server,
+ mntarg_t *mntarg, mntres_t *mntres)
+{
+ CLIENT *clnt;
+ enum clnt_stat stat;
+ int msock;
+
+ if (!probe_bothports(mnt_server, nfs_server))
+ goto out_bad;
+
+ clnt = mnt_openclnt(mnt_server, &msock);
+ if (!clnt)
+ goto out_bad;
+ /* make pointers in xdr_mountres3 NULL so
+ * that xdr_array allocates memory for us
+ */
+ memset(mntres, 0, sizeof(*mntres));
+ switch (mnt_server->pmap.pm_vers) {
+ case 3:
+ stat = nfs3_mount(clnt, mntarg, &mntres->nfsv3);
+ break;
+ case 2:
+ case 1:
+ stat = nfs2_mount(clnt, mntarg, &mntres->nfsv2);
+ break;
+ default:
+ goto out_bad;
+ }
+ if (stat != RPC_SUCCESS) {
+ clnt_geterr(clnt, &rpc_createerr.cf_error);
+ rpc_createerr.cf_stat = stat;
+ }
+ mnt_closeclnt(clnt, msock);
+ if (stat == RPC_SUCCESS)
+ return 1;
+ out_bad:
+ return 0;
+}
+
+static int
+parse_options(char *old_opts, struct nfs_mount_data *data,
+ int *bg, int *retry, clnt_addr_t *mnt_server,
+ clnt_addr_t *nfs_server, char *new_opts, const int opt_size)
+{
+ struct sockaddr_in *mnt_saddr = &mnt_server->saddr;
+ struct pmap *mnt_pmap = &mnt_server->pmap;
+ struct pmap *nfs_pmap = &nfs_server->pmap;
+ int len;
+ char *opt, *opteq, *p, *opt_b, *tmp_opts;
+ char *mounthost = NULL;
+ char cbuf[128];
+ int open_quote = 0;
+
+ data->flags = 0;
+ *bg = 0;
+
+ len = strlen(new_opts);
+ tmp_opts = xstrdup(old_opts);
+ for (p=tmp_opts, opt_b=NULL; p && *p; p++) {
+ if (!opt_b)
+ opt_b = p; /* begin of the option item */
+ if (*p == '"')
+ open_quote ^= 1; /* reverse the status */
+ if (open_quote)
+ continue; /* still in a quoted block */
+ if (*p == ',')
+ *p = '\0'; /* terminate the option item */
+ if (*p == '\0' || *(p+1) == '\0') {
+ opt = opt_b; /* opt is useful now */
+ opt_b = NULL;
+ }
+ else
+ continue; /* still somewhere in the option item */
+
+ if (strlen(opt) >= sizeof(cbuf))
+ goto bad_parameter;
+ if ((opteq = strchr(opt, '=')) && isdigit(opteq[1])) {
+ int val = atoi(opteq + 1);
+ *opteq = '\0';
+ if (!strcmp(opt, "rsize"))
+ data->rsize = val;
+ else if (!strcmp(opt, "wsize"))
+ data->wsize = val;
+ else if (!strcmp(opt, "timeo"))
+ data->timeo = val;
+ else if (!strcmp(opt, "retrans"))
+ data->retrans = val;
+ else if (!strcmp(opt, "acregmin"))
+ data->acregmin = val;
+ else if (!strcmp(opt, "acregmax"))
+ data->acregmax = val;
+ else if (!strcmp(opt, "acdirmin"))
+ data->acdirmin = val;
+ else if (!strcmp(opt, "acdirmax"))
+ data->acdirmax = val;
+ else if (!strcmp(opt, "actimeo")) {
+ data->acregmin = val;
+ data->acregmax = val;
+ data->acdirmin = val;
+ data->acdirmax = val;
+ }
+ else if (!strcmp(opt, "retry"))
+ *retry = val;
+ else if (!strcmp(opt, "port"))
+ nfs_pmap->pm_port = val;
+ else if (!strcmp(opt, "mountport"))
+ mnt_pmap->pm_port = val;
+ else if (!strcmp(opt, "mountprog"))
+ mnt_pmap->pm_prog = val;
+ else if (!strcmp(opt, "mountvers"))
+ mnt_pmap->pm_vers = val;
+ else if (!strcmp(opt, "mounthost"))
+ mounthost=xstrndup(opteq+1, strcspn(opteq+1," \t\n\r,"));
+ else if (!strcmp(opt, "nfsprog"))
+ nfs_pmap->pm_prog = val;
+ else if (!strcmp(opt, "nfsvers") ||
+ !strcmp(opt, "vers")) {
+ nfs_pmap->pm_vers = val;
+ opt = "nfsvers";
+#if NFS_MOUNT_VERSION >= 2
+ } else if (!strcmp(opt, "namlen")) {
+ if (nfs_mount_data_version >= 2)
+ data->namlen = val;
+ else if (sloppy)
+ continue;
+ else
+ goto bad_parameter;
+#endif
+ } else if (!strcmp(opt, "addr")) {
+ /* ignore */;
+ continue;
+ } else if (sloppy)
+ continue;
+ else
+ goto bad_parameter;
+ sprintf(cbuf, "%s=%s,", opt, opteq+1);
+ } else if (opteq) {
+ *opteq = '\0';
+ if (!strcmp(opt, "proto")) {
+ if (!strcmp(opteq+1, "udp")) {
+ nfs_pmap->pm_prot = IPPROTO_UDP;
+ mnt_pmap->pm_prot = IPPROTO_UDP;
+#if NFS_MOUNT_VERSION >= 2
+ data->flags &= ~NFS_MOUNT_TCP;
+ } else if (!strcmp(opteq+1, "tcp") &&
+ nfs_mount_data_version > 2) {
+ nfs_pmap->pm_prot = IPPROTO_TCP;
+ mnt_pmap->pm_prot = IPPROTO_TCP;
+ data->flags |= NFS_MOUNT_TCP;
+#endif
+ } else if (sloppy)
+ continue;
+ else
+ goto bad_parameter;
+#if NFS_MOUNT_VERSION >= 5
+ } else if (!strcmp(opt, "sec")) {
+ char *secflavor = opteq+1;
+ /* see RFC 2623 */
+ if (nfs_mount_data_version < 5) {
+ printf(_("Warning: ignoring sec=%s option\n"),
+ secflavor);
+ continue;
+ } else if (!strcmp(secflavor, "none"))
+ data->pseudoflavor = AUTH_NONE;
+ else if (!strcmp(secflavor, "sys"))
+ data->pseudoflavor = AUTH_SYS;
+ else if (!strcmp(secflavor, "krb5"))
+ data->pseudoflavor = AUTH_GSS_KRB5;
+ else if (!strcmp(secflavor, "krb5i"))
+ data->pseudoflavor = AUTH_GSS_KRB5I;
+ else if (!strcmp(secflavor, "krb5p"))
+ data->pseudoflavor = AUTH_GSS_KRB5P;
+ else if (sloppy)
+ continue;
+ else {
+ printf(_("Warning: Unrecognized security flavor %s.\n"),
+ secflavor);
+ goto bad_parameter;
+ }
+ data->flags |= NFS_MOUNT_SECFLAVOUR;
+#endif
+ } else if (!strcmp(opt, "mounthost"))
+ mounthost=xstrndup(opteq+1,
+ strcspn(opteq+1," \t\n\r,"));
+ else if (!strcmp(opt, "context")) {
+ char *context = opteq + 1;
+ int ctxlen = strlen(context);
+
+ if (ctxlen > NFS_MAX_CONTEXT_LEN) {
+ nfs_error(_("context parameter exceeds"
+ " limit of %d"),
+ NFS_MAX_CONTEXT_LEN);
+ goto bad_parameter;
+ }
+ /* The context string is in the format of
+ * "system_u:object_r:...". We only want
+ * the context str between the quotes.
+ */
+ if (*context == '"')
+ strncpy(data->context, context+1,
+ ctxlen-2);
+ else
+ strncpy(data->context, context,
+ NFS_MAX_CONTEXT_LEN);
+ } else if (sloppy)
+ continue;
+ else
+ goto bad_parameter;
+ sprintf(cbuf, "%s=%s,", opt, opteq+1);
+ } else {
+ int val = 1;
+ if (!strncmp(opt, "no", 2)) {
+ val = 0;
+ opt += 2;
+ }
+ if (!strcmp(opt, "bg"))
+ *bg = val;
+ else if (!strcmp(opt, "fg"))
+ *bg = !val;
+ else if (!strcmp(opt, "soft")) {
+ data->flags &= ~NFS_MOUNT_SOFT;
+ if (val)
+ data->flags |= NFS_MOUNT_SOFT;
+ } else if (!strcmp(opt, "hard")) {
+ data->flags &= ~NFS_MOUNT_SOFT;
+ if (!val)
+ data->flags |= NFS_MOUNT_SOFT;
+ } else if (!strcmp(opt, "intr")) {
+ data->flags &= ~NFS_MOUNT_INTR;
+ if (val)
+ data->flags |= NFS_MOUNT_INTR;
+ } else if (!strcmp(opt, "posix")) {
+ data->flags &= ~NFS_MOUNT_POSIX;
+ if (val)
+ data->flags |= NFS_MOUNT_POSIX;
+ } else if (!strcmp(opt, "cto")) {
+ data->flags &= ~NFS_MOUNT_NOCTO;
+ if (!val)
+ data->flags |= NFS_MOUNT_NOCTO;
+ } else if (!strcmp(opt, "ac")) {
+ data->flags &= ~NFS_MOUNT_NOAC;
+ if (!val)
+ data->flags |= NFS_MOUNT_NOAC;
+#if NFS_MOUNT_VERSION >= 2
+ } else if (!strcmp(opt, "tcp")) {
+ data->flags &= ~NFS_MOUNT_TCP;
+ if (val) {
+ if (nfs_mount_data_version < 2)
+ goto bad_option;
+ nfs_pmap->pm_prot = IPPROTO_TCP;
+ mnt_pmap->pm_prot = IPPROTO_TCP;
+ data->flags |= NFS_MOUNT_TCP;
+ } else {
+ mnt_pmap->pm_prot = IPPROTO_UDP;
+ nfs_pmap->pm_prot = IPPROTO_UDP;
+ }
+ } else if (!strcmp(opt, "udp")) {
+ data->flags &= ~NFS_MOUNT_TCP;
+ if (!val) {
+ if (nfs_mount_data_version < 2)
+ goto bad_option;
+ nfs_pmap->pm_prot = IPPROTO_TCP;
+ mnt_pmap->pm_prot = IPPROTO_TCP;
+ data->flags |= NFS_MOUNT_TCP;
+ } else {
+ nfs_pmap->pm_prot = IPPROTO_UDP;
+ mnt_pmap->pm_prot = IPPROTO_UDP;
+ }
+#endif
+#if NFS_MOUNT_VERSION >= 3
+ } else if (!strcmp(opt, "lock")) {
+ data->flags &= ~NFS_MOUNT_NONLM;
+ if (!val) {
+ if (nfs_mount_data_version < 3)
+ goto bad_option;
+ data->flags |= NFS_MOUNT_NONLM;
+ }
+#endif
+#if NFS_MOUNT_VERSION >= 4
+ } else if (!strcmp(opt, "broken_suid")) {
+ data->flags &= ~NFS_MOUNT_BROKEN_SUID;
+ if (val) {
+ if (nfs_mount_data_version < 4)
+ goto bad_option;
+ data->flags |= NFS_MOUNT_BROKEN_SUID;
+ }
+ } else if (!strcmp(opt, "acl")) {
+ data->flags &= ~NFS_MOUNT_NOACL;
+ if (!val)
+ data->flags |= NFS_MOUNT_NOACL;
+ } else if (!strcmp(opt, "rdirplus")) {
+ data->flags &= ~NFS_MOUNT_NORDIRPLUS;
+ if (!val)
+ data->flags |= NFS_MOUNT_NORDIRPLUS;
+ } else if (!strcmp(opt, "sharecache")) {
+ data->flags &= ~NFS_MOUNT_UNSHARED;
+ if (!val)
+ data->flags |= NFS_MOUNT_UNSHARED;
+#endif
+ } else {
+ bad_option:
+ if (sloppy)
+ continue;
+ nfs_error(_("%s: Unsupported nfs mount option:"
+ " %s%s"), progname,
+ val ? "" : "no", opt);
+ goto out_bad;
+ }
+ sprintf(cbuf, val ? "%s," : "no%s,", opt);
+ }
+ len += strlen(cbuf);
+ if (len >= opt_size) {
+ nfs_error(_("%s: excessively long option argument"),
+ progname);
+ goto out_bad;
+ }
+ strcat(new_opts, cbuf);
+ }
+ /* See if the nfs host = mount host. */
+ if (mounthost) {
+ if (!nfs_gethostbyname(mounthost, mnt_saddr))
+ goto out_bad;
+ *mnt_server->hostname = mounthost;
+ }
+ free(tmp_opts);
+ return 1;
+ bad_parameter:
+ nfs_error(_("%s: Bad nfs mount parameter: %s\n"), progname, opt);
+ out_bad:
+ free(tmp_opts);
+ free(mounthost);
+ return 0;
+}
+
+static int nfsmnt_check_compat(const struct pmap *nfs_pmap,
+ const struct pmap *mnt_pmap)
+{
+ unsigned int max_nfs_vers = (nfs_mount_data_version >= 4) ? 3 : 2;
+ unsigned int max_mnt_vers = (nfs_mount_data_version >= 4) ? 3 : 2;
+
+ if (nfs_pmap->pm_vers == 4) {
+ nfs_error(_("%s: Please use '-t nfs4' "
+ "instead of '-o vers=4'"), progname);
+ goto out_bad;
+ }
+
+ if (nfs_pmap->pm_vers) {
+ if (nfs_pmap->pm_vers > max_nfs_vers || nfs_pmap->pm_vers < 2) {
+ nfs_error(_("%s: NFS version %ld is not supported"),
+ progname, nfs_pmap->pm_vers);
+ goto out_bad;
+ }
+ }
+
+ if (mnt_pmap->pm_vers > max_mnt_vers) {
+ nfs_error(_("%s: NFS mount version %ld is not supported"),
+ progname, mnt_pmap->pm_vers);
+ goto out_bad;
+ }
+
+ return 1;
+
+out_bad:
+ return 0;
+}
+
+int
+nfsmount(const char *spec, const char *node, int flags,
+ char **extra_opts, int fake, int running_bg)
+{
+ char hostdir[1024];
+ char *hostname, *dirname, *old_opts, *mounthost = NULL;
+ char new_opts[1024], cbuf[1024];
+ static struct nfs_mount_data data;
+ int val;
+ static int doonce = 0;
+
+ clnt_addr_t mnt_server = {
+ .hostname = &mounthost
+ };
+ clnt_addr_t nfs_server = {
+ .hostname = &hostname
+ };
+ struct sockaddr_in *nfs_saddr = &nfs_server.saddr;
+ struct pmap *mnt_pmap = &mnt_server.pmap,
+ *nfs_pmap = &nfs_server.pmap;
+ struct pmap save_mnt, save_nfs;
+
+ int fsock = -1;
+
+ mntres_t mntres;
+
+ struct stat statbuf;
+ char *s;
+ int bg, retry;
+ int retval = EX_FAIL;
+ time_t t;
+ time_t prevt;
+ time_t timeout;
+
+ if (strlen(spec) >= sizeof(hostdir)) {
+ nfs_error(_("%s: excessively long host:dir argument"),
+ progname);
+ goto fail;
+ }
+ strcpy(hostdir, spec);
+ if ((s = strchr(hostdir, ':'))) {
+ hostname = hostdir;
+ dirname = s + 1;
+ *s = '\0';
+ /* Ignore all but first hostname in replicated mounts
+ until they can be fully supported. (mack@sgi.com) */
+ if ((s = strchr(hostdir, ','))) {
+ *s = '\0';
+ nfs_error(_("%s: warning: "
+ "multiple hostnames not supported"),
+ progname);
+ }
+ } else {
+ nfs_error(_("%s: directory to mount not in host:dir format"),
+ progname);
+ goto fail;
+ }
+
+ if (!nfs_gethostbyname(hostname, nfs_saddr))
+ goto fail;
+ mounthost = hostname;
+ memcpy (&mnt_server.saddr, nfs_saddr, sizeof (mnt_server.saddr));
+
+ /* add IP address to mtab options for use when unmounting */
+
+ s = inet_ntoa(nfs_saddr->sin_addr);
+ old_opts = *extra_opts;
+ if (!old_opts)
+ old_opts = "";
+
+ /* Set default options.
+ * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to
+ * let the kernel decide.
+ * timeo is filled in after we know whether it'll be TCP or UDP. */
+ memset(&data, 0, sizeof(data));
+ data.acregmin = 3;
+ data.acregmax = 60;
+ data.acdirmin = 30;
+ data.acdirmax = 60;
+#if NFS_MOUNT_VERSION >= 2
+ data.namlen = NAME_MAX;
+#endif
+
+ bg = 0;
+ retry = -1;
+
+ memset(mnt_pmap, 0, sizeof(*mnt_pmap));
+ mnt_pmap->pm_prog = MOUNTPROG;
+ memset(nfs_pmap, 0, sizeof(*nfs_pmap));
+ nfs_pmap->pm_prog = NFS_PROGRAM;
+
+ /* parse options */
+ new_opts[0] = 0;
+ if (!parse_options(old_opts, &data, &bg, &retry, &mnt_server, &nfs_server,
+ new_opts, sizeof(new_opts)))
+ goto fail;
+ if (!nfsmnt_check_compat(nfs_pmap, mnt_pmap))
+ goto fail;
+
+ if (retry == -1) {
+ if (bg)
+ retry = 10000; /* 10000 mins == ~1 week*/
+ else
+ retry = 2; /* 2 min default on fg mounts */
+ }
+
+#ifdef NFS_MOUNT_DEBUG
+ printf(_("rsize = %d, wsize = %d, timeo = %d, retrans = %d\n"),
+ data.rsize, data.wsize, data.timeo, data.retrans);
+ printf(_("acreg (min, max) = (%d, %d), acdir (min, max) = (%d, %d)\n"),
+ data.acregmin, data.acregmax, data.acdirmin, data.acdirmax);
+ printf(_("port = %lu, bg = %d, retry = %d, flags = %.8x\n"),
+ nfs_pmap->pm_port, bg, retry, data.flags);
+ printf(_("mountprog = %lu, mountvers = %lu, nfsprog = %lu, nfsvers = %lu\n"),
+ mnt_pmap->pm_prog, mnt_pmap->pm_vers,
+ nfs_pmap->pm_prog, nfs_pmap->pm_vers);
+ printf(_("soft = %d, intr = %d, posix = %d, nocto = %d, noac = %d"),
+ (data.flags & NFS_MOUNT_SOFT) != 0,
+ (data.flags & NFS_MOUNT_INTR) != 0,
+ (data.flags & NFS_MOUNT_POSIX) != 0,
+ (data.flags & NFS_MOUNT_NOCTO) != 0,
+ (data.flags & NFS_MOUNT_NOAC) != 0);
+#if NFS_MOUNT_VERSION >= 2
+ printf(_(", tcp = %d"),
+ (data.flags & NFS_MOUNT_TCP) != 0);
+#endif
+#if NFS_MOUNT_VERSION >= 4
+ printf(_(", noacl = %d"), (data.flags & NFS_MOUNT_NOACL) != 0);
+#endif
+#if NFS_MOUNT_VERSION >= 5
+ printf(_(", sec = %u"), data.pseudoflavor);
+ printf(_(", readdirplus = %d"), (data.flags & NFS_MOUNT_NORDIRPLUS) != 0);
+#endif
+ printf("\n");
+#endif
+
+ data.version = nfs_mount_data_version;
+
+ if (flags & MS_REMOUNT)
+ goto out_ok;
+
+ /* create mount deamon client */
+
+ /*
+ * The following loop implements the mount retries. On the first
+ * call, "running_bg" is 0. When the mount times out, and the
+ * "bg" option is set, the exit status EX_BG will be returned.
+ * For a backgrounded mount, there will be a second call by the
+ * child process with "running_bg" set to 1.
+ *
+ * The case where the mount point is not present and the "bg"
+ * option is set, is treated as a timeout. This is done to
+ * support nested mounts.
+ *
+ * The "retry" count specified by the user is the number of
+ * minutes to retry before giving up.
+ *
+ * Only the first error message will be displayed.
+ */
+ timeout = time(NULL) + 60 * retry;
+ prevt = 0;
+ t = 30;
+ val = 1;
+
+ memcpy(&save_nfs, nfs_pmap, sizeof(save_nfs));
+ memcpy(&save_mnt, mnt_pmap, sizeof(save_mnt));
+ for (;;) {
+ if (bg && stat(node, &statbuf) == -1) {
+ /* no mount point yet - sleep */
+ if (running_bg) {
+ sleep(val); /* 1, 2, 4, 8, 16, 30, ... */
+ val *= 2;
+ if (val > 30)
+ val = 30;
+ }
+ } else {
+ int stat;
+ /* be careful not to use too many CPU cycles */
+ if (t - prevt < 30)
+ sleep(30);
+
+ stat = nfs_call_mount(&mnt_server, &nfs_server,
+ &dirname, &mntres);
+ if (stat)
+ break;
+ memcpy(nfs_pmap, &save_nfs, sizeof(*nfs_pmap));
+ memcpy(mnt_pmap, &save_mnt, sizeof(*mnt_pmap));
+ prevt = t;
+ }
+ if (!bg) {
+ switch(rpc_createerr.cf_stat){
+ case RPC_TIMEDOUT:
+ break;
+ case RPC_SYSTEMERROR:
+ if (errno == ETIMEDOUT)
+ break;
+ /* FALLTHRU */
+ default:
+ rpc_mount_errors(*nfs_server.hostname, 0, bg);
+ goto fail;
+ }
+ t = time(NULL);
+ if (t >= timeout) {
+ rpc_mount_errors(*nfs_server.hostname, 0, bg);
+ goto fail;
+ }
+ rpc_mount_errors(*nfs_server.hostname, 1, bg);
+ continue;
+ }
+ if (!running_bg) {
+ if (retry > 0)
+ retval = EX_BG;
+ goto fail;
+ }
+ t = time(NULL);
+ if (t >= timeout) {
+ rpc_mount_errors(*nfs_server.hostname, 0, bg);
+ goto fail;
+ }
+ if (doonce++ < 1)
+ rpc_mount_errors(*nfs_server.hostname, 1, bg);
+ }
+
+ if (mnt_pmap->pm_vers <= 2) {
+ if (mntres.nfsv2.fhs_status != 0) {
+ nfs_error(_("%s: %s:%s failed, reason given by server: %s"),
+ progname, hostname, dirname,
+ nfs_strerror(mntres.nfsv2.fhs_status));
+ goto fail;
+ }
+ memcpy(data.root.data,
+ (char *) mntres.nfsv2.fhstatus_u.fhs_fhandle,
+ NFS_FHSIZE);
+#if NFS_MOUNT_VERSION >= 4
+ data.root.size = NFS_FHSIZE;
+ memcpy(data.old_root.data,
+ (char *) mntres.nfsv2.fhstatus_u.fhs_fhandle,
+ NFS_FHSIZE);
+#endif
+ } else {
+#if NFS_MOUNT_VERSION >= 4
+ mountres3_ok *mountres;
+ fhandle3 *fhandle;
+ int i, n_flavors, *flavor, yum = 0;
+ if (mntres.nfsv3.fhs_status != 0) {
+ nfs_error(_("%s: %s:%s failed, reason given by server: %s"),
+ progname, hostname, dirname,
+ nfs_strerror(mntres.nfsv3.fhs_status));
+ goto fail;
+ }
+#if NFS_MOUNT_VERSION >= 5
+ mountres = &mntres.nfsv3.mountres3_u.mountinfo;
+ n_flavors = mountres->auth_flavors.auth_flavors_len;
+ if (n_flavors <= 0)
+ goto noauth_flavors;
+
+ flavor = mountres->auth_flavors.auth_flavors_val;
+ for (i = 0; i < n_flavors; ++i) {
+ /*
+ * Per RFC2623, section 2.7, we should prefer the
+ * flavour listed first.
+ * If no flavour requested, use the first simple
+ * flavour that is offered.
+ */
+ if (! (data.flags & NFS_MOUNT_SECFLAVOUR) &&
+ (flavor[i] == AUTH_SYS ||
+ flavor[i] == AUTH_NONE)) {
+ data.pseudoflavor = flavor[i];
+ data.flags |= NFS_MOUNT_SECFLAVOUR;
+ }
+ if (flavor[i] == data.pseudoflavor)
+ yum = 1;
+#ifdef NFS_MOUNT_DEBUG
+ printf(_("auth flavor %d: %d\n"), i, flavor[i]);
+#endif
+ }
+ if (!yum) {
+ nfs_error(_("%s: %s:%s failed, security flavor "
+ "not supported"),
+ progname, hostname, dirname);
+ /* server has registered us in rmtab, send umount */
+ nfs_call_umount(&mnt_server, &dirname);
+ goto fail;
+ }
+noauth_flavors:
+#endif
+ fhandle = &mntres.nfsv3.mountres3_u.mountinfo.fhandle;
+ memset(data.old_root.data, 0, NFS_FHSIZE);
+ memset(&data.root, 0, sizeof(data.root));
+ data.root.size = fhandle->fhandle3_len;
+ memcpy(data.root.data,
+ (char *) fhandle->fhandle3_val,
+ fhandle->fhandle3_len);
+
+ data.flags |= NFS_MOUNT_VER3;
+#endif
+ }
+
+ if (nfs_mount_data_version == 1) {
+ /* create nfs socket for kernel */
+ if (nfs_pmap->pm_prot == IPPROTO_TCP)
+ fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ else
+ fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (fsock < 0) {
+ perror(_("nfs socket"));
+ goto fail;
+ }
+ if (bindresvport(fsock, 0) < 0) {
+ perror(_("nfs bindresvport"));
+ goto fail;
+ }
+ }
+
+#ifdef NFS_MOUNT_DEBUG
+ printf(_("using port %lu for nfs deamon\n"), nfs_pmap->pm_port);
+#endif
+ nfs_saddr->sin_port = htons(nfs_pmap->pm_port);
+ /*
+ * connect() the socket for kernels 1.3.10 and below only,
+ * to avoid problems with multihomed hosts.
+ * --Swen
+ */
+ if (linux_version_code() <= MAKE_VERSION(1, 3, 10) && fsock != -1
+ && connect(fsock, (struct sockaddr *) nfs_saddr,
+ sizeof (*nfs_saddr)) < 0) {
+ perror(_("nfs connect"));
+ goto fail;
+ }
+
+#if NFS_MOUNT_VERSION >= 2
+ if (nfs_pmap->pm_prot == IPPROTO_TCP)
+ data.flags |= NFS_MOUNT_TCP;
+ else
+ data.flags &= ~NFS_MOUNT_TCP;
+#endif
+
+ /* prepare data structure for kernel */
+
+ data.fd = fsock;
+ memcpy((char *) &data.addr, (char *) nfs_saddr, sizeof(data.addr));
+ strncpy(data.hostname, hostname, sizeof(data.hostname)-1);
+
+ out_ok:
+ /* Ensure we have enough padding for the following strcat()s */
+ if (strlen(new_opts) + strlen(s) + 30 >= sizeof(new_opts)) {
+ nfs_error(_("%s: excessively long option argument"),
+ progname);
+ goto fail;
+ }
+
+ snprintf(cbuf, sizeof(cbuf)-1, "addr=%s", s);
+ strcat(new_opts, cbuf);
+
+ *extra_opts = xstrdup(new_opts);
+
+ if (!fake && !(data.flags & NFS_MOUNT_NONLM)) {
+ if (!start_statd()) {
+ nfs_error(_("%s: rpc.statd is not running but is "
+ "required for remote locking.\n"
+ " Either use '-o nolock' to keep "
+ "locks local, or start statd."),
+ progname);
+ goto fail;
+ }
+ }
+
+ if (!fake) {
+ if (mount(spec, node, "nfs",
+ flags & ~(MS_USER|MS_USERS), &data)) {
+ mount_error(spec, node, errno);
+ goto fail;
+ }
+ }
+
+ return EX_SUCCESS;
+
+ /* abort */
+ fail:
+ if (fsock != -1)
+ close(fsock);
+ return retval;
+}
diff --git a/utils/mount/nfsmount.conf b/utils/mount/nfsmount.conf
new file mode 100644
index 0000000..c498eb8
--- /dev/null
+++ b/utils/mount/nfsmount.conf
@@ -0,0 +1,144 @@
+#
+# /etc/nfsmount.conf - see nfsmount.conf(5) for details
+#
+# This is an NFS mount configuration file. This file can be broken
+# up into three different sections: Mount, Server and Global
+#
+# [ MountPoint "Mount_point" ]
+# This section defines all the mount options that
+# should be used on a particular mount point. The '<Mount_Point>'
+# string need to be an exact match of the path in the mount
+# command. Example:
+# [ MountPoint "/export/home" ]
+# background=True
+# Would cause all mount to /export/home would be done in
+# the background
+#
+# [ Server "Server_Name" ]
+# This section defines all the mount options that
+# should be used on mounts to a particular NFS server.
+# Example:
+# [ Server "nfsserver.foo.com" ]
+# rsize=32k
+# wsize=32k
+# All reads and writes to the 'nfsserver.foo.com' server
+# will be done with 32k (32768 bytes) block sizes.
+#
+[ NFSMount_Global_Options ]
+# This statically named section defines global mount
+# options that can be applied on all NFS mount.
+#
+# Protocol Version [3,4]
+# This defines the default protocol version which will
+# be used to start the negotiation with the server.
+# Defaultvers=4
+#
+# Setting this option makes it mandatory the server supports the
+# given version. The mount will fail if the given version is
+# not support by the server.
+# Nfsvers=4
+#
+# Network Protocol [udp,tcp,rdma] (Note: values are case sensitive)
+# This defines the default network protocol which will
+# be used to start the negotiation with the server.
+# Defaultproto=tcp
+#
+# Setting this option makes it mandatory the server supports the
+# given network protocol. The mount will fail if the given network
+# protocol is not supported by the server.
+# Proto=tcp
+#
+# The number of times a request will be retired before
+# generating a timeout
+# Retrans=2
+#
+# The number of minutes that will retry mount
+# Retry=2
+#
+# The minimum time (in seconds) file attributes are cached
+# acregmin=30
+#
+# The Maximum time (in seconds) file attributes are cached
+# acregmax=60
+#
+# The minimum time (in seconds) directory attributes are cached
+# acdirmin=30
+#
+# The Maximum time (in seconds) directory attributes are cached
+# acdirmax=60
+#
+# Enable Access Control Lists
+# Acl=False
+#
+# Enable Attribute Caching
+# Ac=True
+#
+# Do mounts in background (i.e. asynchronously)
+# Background=False
+#
+# Close-To-Open cache coherence
+# Cto=True
+#
+# Do mounts in foreground (i.e. synchronously)
+# Foreground=True
+#
+# How to handle times out from servers (Hard is STRONGLY suggested)
+# Hard=True
+# Soft=False
+#
+# Enable File Locking
+# Lock=True
+#
+# Enable READDIRPLUS on NFS version 3 mounts
+# Rdirplus=True
+#
+# Maximum Read Size (in Bytes)
+# Rsize=8k
+#
+# Maximum Write Size (in Bytes)
+# Wsize=8k
+#
+# Maximum Server Block Size (in Bytes)
+# Bsize=8k
+#
+# Ignore unknown mount options
+# Sloppy=False
+#
+# Share Data and Attribute Caches
+# Sharecache=True
+#
+# The amount of time, in tenths of a seconds, the client
+# will wait for a response from the server before retransmitting
+# the request.
+# Timeo=600
+#
+# Sets all attributes times to the same time (in seconds)
+# actimeo=30
+#
+# Server Mountd port mountport
+# mountport=4001
+#
+# Server Mountd Protocol
+# mountproto=tcp
+#
+# Server Mountd Version
+# mountvers=3
+#
+# Server Mountd Host
+# mounthost=hostname
+#
+# Server Port
+# Port=2049
+#
+# RPCGSS security flavors
+# [none, sys, krb5, krb5i, krb5p ]
+# Sec=sys
+#
+# Allow Signals to interrupt file operations
+# Intr=True
+#
+# Specifies how the kernel manages its cache of directory
+# Lookupcache=all|none|pos|positive
+#
+# Turn of the caching of that access time
+# noatime=True
diff --git a/utils/mount/nfsmount.conf.man b/utils/mount/nfsmount.conf.man
new file mode 100644
index 0000000..34879c8
--- /dev/null
+++ b/utils/mount/nfsmount.conf.man
@@ -0,0 +1,131 @@
+.\" @(#)nfsmount.conf.5"
+.TH NFSMOUNT.CONF 5 "16 December 2020"
+.SH NAME
+nfsmount.conf - Configuration file for NFS mounts
+.SH SYNOPSIS
+Configuration file for NFS mounts that allows options
+to be set globally, per server or per mount point.
+.SH DESCRIPTION
+The configuration file is made up of multiple section headers
+followed by variable assignments associated with that section.
+A section header is defined by a string enclosed by
+.BR [
+and
+.BR ]
+brackets.
+Variable assignments are assignment statements that assign values
+to particular variables using the
+.BR =
+operator, as in
+.BR Proto=Tcp .
+The variables that can be assigned are the set of NFS specific
+mount options listed in
+.BR nfs (5)
+together with the filesystem-independant mount options listed in
+.BR mount (8)
+and three additions:
+.B Sloppy=True
+has the same effect as the
+.B -s
+option to
+.IR mount ,
+and
+.B Foreground=True
+and
+.B Background=True
+have the same effect as
+.B bg
+and
+.BR fg .
+.PP
+Options in the config file may be given in upper, lower, or mixed case
+and will be shifted to lower case before being passed to the filesystem.
+.PP
+Boolean mount options which do not need an equals sign must be given as
+.RI \[dq] option =True".
+Instead of preceeding such an option with
+.RB \[dq] no \[dq]
+its negation must be given as
+.RI \[dq] option =False".
+.PP
+Sections are broken up into three basic categories:
+Global options, Server options and Mount Point options.
+.HP
+.B [ NFSMount_Global_Options ]
+- This statically named section
+defines all of the global mount options that can be
+applied to every NFS mount.
+.HP
+.B [ Server \[dq]Server_Name\[dq] ]
+- This section defines all the mount options that should
+be used on mounts to a particular NFS server. The
+.I \[dq]Server_Name\[dq]
+strings needs to be surrounded by '\[dq]' and be an exact match
+(ignoring case) of the server name used in the
+.B mount
+command.
+.HP
+.B [ MountPoint \[dq]Mount_Point\[dq] ]
+- This section defines all the mount options that
+should be used on a particular mount point.
+The
+.I \[dq]Mount_Point\[dq]
+string needs to be surrounded by '\[dq]' and be an
+exact match of the mount point used in the
+.BR mount
+command. Though path names are usually case-sensitive, the Mount_Point
+name is matched insensitive to case.
+.PP
+The sections are processed in the reverse of the order listed above, and
+any options already seen, either in a previous section or on the
+command line, will be ignored when seen again.
+.SH EXAMPLES
+.PP
+These are some example lines of how sections and variables
+are defined in the configuration file.
+.PP
+[ NFSMount_Global_Options ]
+.br
+ Proto=Tcp
+.RS
+.PP
+The TCP/IPv4 protocol will be used on every NFS mount.
+.RE
+.PP
+[ Server \[dq]nfsserver.foo.com\[dq] ]
+.br
+ rsize=32k
+.br
+ wsize=32k
+.br
+ proto=udp6
+.RS
+.PP
+A 32k (32768 bytes) block size will be used as the read and write
+size on all mounts to the 'nfsserver.foo.com' server. UDP/IPv6
+is the protocol to be used.
+.RE
+.PP
+[ MountPoint \[dq]/export/home\[dq] ]
+.br
+ Background=True
+.RS
+.PP
+All mounts to the '/export/home' export will be performed in
+the background (i.e. done asynchronously).
+.RE
+.SH FILES
+.TP 10n
+.I /etc/nfsmount.conf
+Default NFS mount configuration file
+.TP 10n
+.I /etc/nfsmount.conf.d
+When this directory exists and files ending
+with ".conf" exist, those files will be
+used to set configuration variables. These
+files will override variables set
+in /etc/nfsmount.conf
+.PD
+.SH SEE ALSO
+.BR nfs (5),
+.BR mount (8),
diff --git a/utils/mount/nfsumount.c b/utils/mount/nfsumount.c
new file mode 100644
index 0000000..e16fb6a
--- /dev/null
+++ b/utils/mount/nfsumount.c
@@ -0,0 +1,351 @@
+/*
+ * nfsumount.c -- Linux NFS umount
+ * Copyright (C) 2006 Amit Gud <agud@redhat.com>
+ *
+ * - Basic code and wrapper around NFS umount code originally
+ * in util-linux/mount/nfsmount.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <errno.h>
+#include <getopt.h>
+#include <mntent.h>
+#include <sys/mount.h>
+#include <ctype.h>
+#include <pwd.h>
+
+#include "xcommon.h"
+#include "fstab.h"
+#include "nls.h"
+
+#include "mount_constants.h"
+#include "nfs_mount.h"
+#include "mount.h"
+#include "error.h"
+#include "network.h"
+#include "parse_opt.h"
+#include "parse_dev.h"
+#include "utils.h"
+
+#define MOUNTSFILE "/proc/mounts"
+#define LINELEN (4096)
+
+#if !defined(MNT_FORCE)
+/* dare not try to include <linux/mount.h> -- lots of errors */
+#define MNT_FORCE 1
+#endif
+
+#if !defined(MNT_DETACH)
+#define MNT_DETACH 2
+#endif
+
+extern char *progname;
+extern int nomtab;
+extern int verbose;
+int force;
+int lazy;
+int remount;
+
+
+static int try_remount(const char *spec, const char *node)
+{
+ int res;
+
+ res = mount(spec, node, NULL,
+ MS_MGC_VAL | MS_REMOUNT | MS_RDONLY, NULL);
+ if (res == 0) {
+ struct mntent remnt;
+ nfs_error(_("%s: %s busy - remounted read-only"),
+ progname, spec);
+ remnt.mnt_type = remnt.mnt_fsname = NULL;
+ remnt.mnt_dir = xstrdup(node);
+ remnt.mnt_opts = xstrdup("ro");
+ if (!nomtab)
+ update_mtab(node, &remnt);
+ } else if (errno != EBUSY) { /* hmm ... */
+ perror(_("remount"));
+ nfs_error(_("%s: could not remount %s read-only"),
+ progname, spec);
+ }
+ return res;
+}
+
+static int del_mtab(const char *spec, const char *node)
+{
+ int umnt_err, res;
+
+ umnt_err = 0;
+ if (lazy) {
+ res = umount2 (node, MNT_DETACH);
+ if (res < 0)
+ umnt_err = errno;
+ goto writemtab;
+ }
+
+ if (force) {
+ res = umount2 (node, MNT_FORCE);
+ if (res == -1) {
+ int errsv = errno;
+ perror(_("umount2"));
+ errno = errsv;
+ if (errno == ENOSYS) {
+ if (verbose)
+ printf(_("no umount2, trying umount...\n"));
+ res = umount (node);
+ }
+ }
+ } else
+ res = umount (node);
+
+ if (res < 0) {
+ if (remount && errno == EBUSY && spec) {
+ res = try_remount(spec, node);
+ if (res)
+ goto writemtab;
+ return EX_SUCCESS;
+ } else
+ umnt_err = errno;
+ }
+
+ if (res >= 0) {
+ /* Umount succeeded */
+ if (verbose)
+ printf(_("%s umounted\n"), spec ? spec : node);
+ }
+
+ writemtab:
+ if (!nomtab &&
+ (umnt_err == 0 || umnt_err == EINVAL || umnt_err == ENOENT)) {
+ update_mtab(node, NULL);
+ }
+
+ if (res >= 0)
+ return EX_SUCCESS;
+
+ if (umnt_err)
+ umount_error(umnt_err, node);
+ return EX_FILEIO;
+}
+
+/*
+ * Detect NFSv4 mounts.
+ *
+ * Consult /proc/mounts to determine if the mount point
+ * is an NFSv4 mount. The kernel is authoritative about
+ * what type of mount this is.
+ *
+ * Returns 1 if "mc" is an NFSv4 mount, zero if not, and
+ * -1 if some error occurred.
+ */
+static int nfs_umount_is_vers4(const struct mntentchn *mc)
+{
+ struct mntentchn *pmc;
+ struct mount_options *options;
+ int retval;
+
+ retval = -1;
+ pmc = getprocmntdirbackward(mc->m.mnt_dir, NULL);
+ if (!pmc)
+ goto not_found;
+
+ do {
+ size_t nlen = strlen(pmc->m.mnt_fsname);
+
+ /*
+ * It's possible the mount location string in /proc/mounts
+ * ends with a '/'. In this case, if the entry came from
+ * /etc/mtab, it won't have the trailing '/' so deal with
+ * it.
+ */
+ while (pmc->m.mnt_fsname[nlen - 1] == '/')
+ nlen--;
+ if (strncmp(pmc->m.mnt_fsname, mc->m.mnt_fsname, nlen) != 0)
+ continue;
+
+ if (strcmp(pmc->m.mnt_type, "nfs4") == 0)
+ goto out_nfs4;
+
+ options = po_split(pmc->m.mnt_opts);
+ if (options != NULL) {
+ struct nfs_version version;
+ int rc = nfs_nfs_version("nfs", options, &version);
+ po_destroy(options);
+ if (rc && version.major == 4)
+ goto out_nfs4;
+ }
+
+ if (strcmp(pmc->m.mnt_type, "nfs") == 0)
+ goto out_nfs;
+ } while ((pmc = getprocmntdirbackward(mc->m.mnt_dir, pmc)) != NULL);
+
+ if (retval == -1) {
+not_found:
+ fprintf(stderr, "%s was not found in %s\n",
+ mc->m.mnt_dir, MOUNTSFILE);
+ goto out;
+ }
+
+out_nfs4:
+ if (verbose)
+ fprintf(stderr, "NFSv4 mount point detected\n");
+ retval = 1;
+ goto out;
+
+out_nfs:
+ if (verbose)
+ fprintf(stderr, "Legacy NFS mount point detected\n");
+ retval = 0;
+
+out:
+ return retval;
+}
+
+static struct option umount_longopts[] =
+{
+ { "force", 0, 0, 'f' },
+ { "help", 0, 0, 'h' },
+ { "no-mtab", 0, 0, 'n' },
+ { "verbose", 0, 0, 'v' },
+ { "read-only", 0, 0, 'r' },
+ { NULL, 0, 0, 0 }
+};
+
+int nfsumount(int argc, char *argv[])
+{
+ int c, ret;
+ char *spec;
+ struct mntentchn *mc;
+
+ if (argc < 2) {
+ umount_usage();
+ return EX_USAGE;
+ }
+
+ spec = argv[1];
+
+ argv += 1;
+ argc -= 1;
+
+ argv[0] = argv[-1]; /* So that getopt error messages are correct */
+ while ((c = getopt_long (argc, argv, "fvnrlh",
+ umount_longopts, NULL)) != -1) {
+
+ switch (c) {
+ case 'f':
+ ++force;
+ break;
+ case 'v':
+ ++verbose;
+ break;
+ case 'n':
+ ++nomtab;
+ break;
+ case 'r':
+ ++remount;
+ break;
+ case 'l':
+ ++lazy;
+ break;
+ case 'h':
+ default:
+ umount_usage();
+ return EX_USAGE;
+ }
+ }
+ if (optind != argc) {
+ umount_usage();
+ return EX_USAGE;
+ }
+
+ if (spec == NULL || (*spec != '/' && strchr(spec,':') == NULL)) {
+ nfs_error(_("%s: %s: not found\n"), progname, spec);
+ return EX_USAGE;
+ }
+
+ if (*spec == '/')
+ mc = getmntdirbackward(spec, NULL);
+ else
+ mc = getmntdevbackward(spec, NULL);
+ if (!mc && verbose)
+ printf(_("Could not find %s in mtab\n"), spec);
+
+ if (mc && strcmp(mc->m.mnt_type, "nfs") != 0 &&
+ strcmp(mc->m.mnt_type, "nfs4") != 0) {
+ nfs_error(_("%s: %s on %s is not an NFS filesystem"),
+ progname, mc->m.mnt_fsname, mc->m.mnt_dir);
+ return EX_USAGE;
+ }
+
+ if (getuid() != 0) {
+ /* only permitted if "user=" or "users" is in mount options */
+ if (!mc) {
+ /* umount might call us twice. The second time there will
+ * be no entry in mtab and we should just exit quietly
+ */
+ return EX_SUCCESS;
+
+ only_root:
+ nfs_error(_("%s: You are not permitted to unmount %s"),
+ progname, spec);
+ return EX_USAGE;
+ }
+ if (hasmntopt(&mc->m, "users") == NULL) {
+ char *opt = hasmntopt(&mc->m, "user");
+ struct passwd *pw;
+ char *comma;
+ size_t len;
+ if (!opt)
+ goto only_root;
+ if (opt[4] != '=')
+ goto only_root;
+ comma = strchr(opt, ',');
+ if (comma)
+ len = comma - (opt + 5);
+ else
+ len = strlen(opt+5);
+ pw = getpwuid(getuid());
+ if (pw == NULL || strlen(pw->pw_name) != len
+ || strncmp(pw->pw_name, opt+5, len) != 0)
+ goto only_root;
+ }
+ }
+
+ ret = EX_SUCCESS;
+ if (mc) {
+ if (!lazy) {
+ switch (nfs_umount_is_vers4(mc)) {
+ case 0:
+ /* We ignore the error from nfs_umount23.
+ * If the actual umount succeeds (in del_mtab),
+ * we don't want to signal an error, as that
+ * could cause /sbin/mount to retry!
+ */
+ nfs_umount23(mc->m.mnt_fsname, mc->m.mnt_opts);
+ break;
+ case 1:
+ break;
+ default:
+ return EX_FAIL;
+ }
+ }
+ ret = del_mtab(mc->m.mnt_fsname, mc->m.mnt_dir);
+ } else if (*spec != '/') {
+ if (!lazy)
+ ret = nfs_umount23(spec, "tcp,v3");
+ } else
+ ret = del_mtab(NULL, spec);
+
+ return ret;
+}
diff --git a/utils/mount/parse_dev.c b/utils/mount/parse_dev.c
new file mode 100644
index 0000000..2ade5d5
--- /dev/null
+++ b/utils/mount/parse_dev.c
@@ -0,0 +1,233 @@
+/*
+ * parse_dev.c -- parse device name into hostname and export path
+ *
+ * Copyright (C) 2008 Oracle. 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 "xcommon.h"
+#include "nls.h"
+#include "parse_dev.h"
+
+#ifndef NFS_MAXHOSTNAME
+#define NFS_MAXHOSTNAME (255)
+#endif
+
+#ifndef NFS_MAXPATHNAME
+#define NFS_MAXPATHNAME (1024)
+#endif
+
+extern char *progname;
+extern int verbose;
+
+static int nfs_pdn_no_devname_err(void)
+{
+ nfs_error(_("%s: no device name was provided"), progname);
+ return 0;
+}
+
+static int nfs_pdn_hostname_too_long_err(void)
+{
+ nfs_error(_("%s: server hostname is too long"), progname);
+ return 0;
+}
+
+static int nfs_pdn_pathname_too_long_err(void)
+{
+ nfs_error(_("%s: export pathname is too long"), progname);
+ return 0;
+}
+
+static int nfs_pdn_bad_format_err(void)
+{
+ nfs_error(_("%s: remote share not in 'host:dir' format"), progname);
+ return 0;
+}
+
+static int nfs_pdn_nomem_err(void)
+{
+ nfs_error(_("%s: no memory available to parse devname"), progname);
+ return 0;
+}
+
+static int nfs_pdn_missing_brace_err(void)
+{
+ nfs_error(_("%s: closing bracket missing from server address"),
+ progname);
+ return 0;
+}
+
+/*
+ * Standard hostname:path format
+ */
+static int nfs_parse_simple_hostname(const char *dev,
+ char **hostname, char **pathname)
+{
+ size_t host_len, path_len;
+ char *colon, *comma;
+
+ /* Must have a colon */
+ colon = strchr(dev, ':');
+ if (colon == NULL)
+ return nfs_pdn_bad_format_err();
+ *colon = '\0';
+ host_len = colon - dev;
+
+ if (host_len > NFS_MAXHOSTNAME)
+ return nfs_pdn_hostname_too_long_err();
+
+ /* If there's a comma before the colon, take only the
+ * first name in list */
+ comma = strchr(dev, ',');
+ if (comma != NULL) {
+ *comma = '\0';
+ host_len = comma - dev;
+ nfs_error(_("%s: warning: multiple hostnames not supported"),
+ progname);
+ } else
+
+ colon++;
+ path_len = strlen(colon);
+ if (path_len > NFS_MAXPATHNAME)
+ return nfs_pdn_pathname_too_long_err();
+
+ if (hostname) {
+ *hostname = strndup(dev, host_len);
+ if (*hostname == NULL)
+ return nfs_pdn_nomem_err();
+ }
+ if (pathname) {
+ *pathname = strndup(colon, path_len);
+ if (*pathname == NULL) {
+ if (hostname)
+ free(*hostname);
+ return nfs_pdn_nomem_err();
+ }
+ }
+ return 1;
+}
+
+/*
+ * To handle raw IPv6 addresses (which contain colons), the
+ * server's address is enclosed in square brackets. Return
+ * what's between the brackets.
+ *
+ * There could be anything in between the brackets, but we'll
+ * let DNS resolution sort it out later.
+ */
+static int nfs_parse_square_bracket(const char *dev,
+ char **hostname, char **pathname)
+{
+ size_t host_len, path_len;
+ char *cbrace;
+
+ dev++;
+
+ /* Must have a closing square bracket */
+ cbrace = strchr(dev, ']');
+ if (cbrace == NULL)
+ return nfs_pdn_missing_brace_err();
+ *cbrace = '\0';
+ host_len = cbrace - dev;
+
+ /* Must have a colon just after the closing bracket */
+ cbrace++;
+ if (*cbrace != ':')
+ return nfs_pdn_bad_format_err();
+
+ if (host_len > NFS_MAXHOSTNAME)
+ return nfs_pdn_hostname_too_long_err();
+
+ cbrace++;
+ path_len = strlen(cbrace);
+ if (path_len > NFS_MAXPATHNAME)
+ return nfs_pdn_pathname_too_long_err();
+
+ if (hostname) {
+ *hostname = strndup(dev, host_len);
+ if (*hostname == NULL)
+ return nfs_pdn_nomem_err();
+ }
+ if (pathname) {
+ *pathname = strndup(cbrace, path_len);
+ if (*pathname == NULL) {
+ if (hostname)
+ free(*hostname);
+ return nfs_pdn_nomem_err();
+ }
+ }
+ return 1;
+}
+
+/*
+ * RFC 2224 says an NFS client must grok "public file handles" to
+ * support NFS URLs. Linux doesn't do that yet. Print a somewhat
+ * helpful error message in this case instead of pressing forward
+ * with the mount request and failing with a cryptic error message
+ * later.
+ */
+static int nfs_parse_nfs_url(__attribute__((unused)) const char *dev,
+ __attribute__((unused)) char **hostname,
+ __attribute__((unused)) char **pathname)
+{
+ nfs_error(_("%s: NFS URLs are not supported"), progname);
+ return 0;
+}
+
+/**
+ * nfs_parse_devname - Determine the server's hostname by looking at "devname".
+ * @devname: pointer to mounted device name (first argument of mount command)
+ * @hostname: OUT: pointer to server's hostname
+ * @pathname: OUT: pointer to export path on server
+ *
+ * Returns 1 if succesful, or zero if some error occurred. On success,
+ * @hostname and @pathname point to dynamically allocated buffers containing
+ * the hostname of the server and the export pathname (both '\0'-terminated).
+ *
+ * @hostname or @pathname may be NULL if caller doesn't want a copy of those
+ * parts of @devname.
+ *
+ * Note that this will not work if @devname is a wide-character string.
+ */
+int nfs_parse_devname(const char *devname,
+ char **hostname, char **pathname)
+{
+ char *dev;
+ int result;
+
+ if (devname == NULL)
+ return nfs_pdn_no_devname_err();
+
+ /* Parser is destructive, so operate on a copy of the device name. */
+ dev = strdup(devname);
+ if (dev == NULL)
+ return nfs_pdn_nomem_err();
+ if (*dev == '[')
+ result = nfs_parse_square_bracket(dev, hostname, pathname);
+ else if (strncmp(dev, "nfs://", 6) == 0)
+ result = nfs_parse_nfs_url(dev, hostname, pathname);
+ else
+ result = nfs_parse_simple_hostname(dev, hostname, pathname);
+
+ free(dev);
+ return result;
+}
diff --git a/utils/mount/parse_dev.h b/utils/mount/parse_dev.h
new file mode 100644
index 0000000..f9857bc
--- /dev/null
+++ b/utils/mount/parse_dev.h
@@ -0,0 +1,28 @@
+/*
+ * parse_dev.c -- parse device name into hostname and export path
+ *
+ * Copyright (C) 2008 Oracle. 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
+ *
+ */
+
+#ifndef __NFS_UTILS_PARSE_DEV_HEADER
+#define __NFS_UTILS_PARSE_DEV_HEADER
+
+extern int nfs_parse_devname(const char *, char **, char **);
+
+#endif /* __NFS_UTILS_PARSE_DEV */
diff --git a/utils/mount/parse_opt.c b/utils/mount/parse_opt.c
new file mode 100644
index 0000000..d2d0b65
--- /dev/null
+++ b/utils/mount/parse_opt.c
@@ -0,0 +1,610 @@
+/*
+ * parse_opt.c -- mount option string parsing helpers
+ *
+ * Copyright (C) 2007 Oracle. All rights reserved.
+ * Copyright (C) 2007 Chuck Lever <chuck.lever@oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ *
+ */
+
+/*
+ * Converting a C string containing mount options to a data object
+ * and manipulating that object is cleaner in C than manipulating
+ * the C string itself. This is similar to the way Python handles
+ * string manipulation.
+ *
+ * The current implementation uses a linked list as the data object
+ * since lists are simple, and we don't need to worry about more
+ * than ten or twenty options at a time.
+ *
+ * Hopefully the interface is abstract enough that the underlying
+ * data structure can be replaced if needed without changing the API.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <ctype.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "parse_opt.h"
+#include "token.h"
+
+
+struct mount_option {
+ struct mount_option *next, *prev;
+ char *keyword;
+ char *value;
+};
+
+struct mount_options {
+ struct mount_option *head, *tail;
+ unsigned int count;
+};
+
+static struct mount_option *option_create(char *str)
+{
+ struct mount_option *option;
+ char *opteq;
+
+ if (!str)
+ return NULL;
+
+ option = malloc(sizeof(*option));
+ if (!option)
+ return NULL;
+
+ option->next = NULL;
+ option->prev = NULL;
+
+ opteq = strchr(str, '=');
+ if (opteq) {
+ option->keyword = strndup(str, opteq - str);
+ if (!option->keyword)
+ goto fail;
+ option->value = strdup(opteq + 1);
+ if (!option->value) {
+ free(option->keyword);
+ goto fail;
+ }
+ } else {
+ option->keyword = strdup(str);
+ if (!option->keyword)
+ goto fail;
+ option->value = NULL;
+ }
+
+ return option;
+
+fail:
+ free(option);
+ return NULL;
+}
+
+static struct mount_option *option_dup(const struct mount_option *option)
+{
+ struct mount_option *new;
+
+ new = malloc(sizeof(*new));
+ if (!new)
+ return NULL;
+
+ new->next = NULL;
+ new->prev = NULL;
+
+ new->keyword = strdup(option->keyword);
+ if (!new->keyword)
+ goto fail;
+
+ new->value = NULL;
+ if (option->value) {
+ new->value = strdup(option->value);
+ if (!new->value) {
+ free(new->keyword);
+ goto fail;
+ }
+ }
+
+ return new;
+
+fail:
+ free(new);
+ return NULL;
+}
+
+static void option_destroy(struct mount_option *option)
+{
+ free(option->keyword);
+ free(option->value);
+ free(option);
+}
+
+static void options_init(struct mount_options *options)
+{
+ options->head = options->tail = NULL;
+ options->count = 0;
+}
+
+static struct mount_options *options_create(void)
+{
+ struct mount_options *options;
+
+ options = malloc(sizeof(*options));
+ if (options)
+ options_init(options);
+
+ return options;
+}
+
+static int options_empty(struct mount_options *options)
+{
+ return options->count == 0;
+}
+
+static void options_tail_insert(struct mount_options *options,
+ struct mount_option *option)
+{
+ struct mount_option *prev = options->tail;
+
+ option->next = NULL;
+ option->prev = prev;
+
+ if (prev)
+ prev->next = option;
+ else
+ options->head = option;
+ options->tail = option;
+
+ options->count++;
+}
+
+static void options_head_insert(struct mount_options *options,
+ struct mount_option *option)
+{
+ struct mount_option *ohead = options->head;
+
+ option->prev = NULL;
+ option->next = ohead;
+ if (ohead)
+ ohead->prev = option;
+ else
+ options->tail = option;
+ options->head = option;
+
+ options->count++;
+}
+
+static void options_delete(struct mount_options *options,
+ struct mount_option *option)
+{
+ struct mount_option *prev = option->prev;
+ struct mount_option *next = option->next;
+
+ if (!options_empty(options)) {
+ if (prev)
+ prev->next = option->next;
+ if (next)
+ next->prev = option->prev;
+
+ if (options->head == option)
+ options->head = option->next;
+ if (options->tail == option)
+ options->tail = prev;
+
+ options->count--;
+
+ option_destroy(option);
+ }
+}
+
+
+/**
+ * po_destroy - deallocate a group of mount options
+ * @options: pointer to mount options to free
+ *
+ */
+void po_destroy(struct mount_options *options)
+{
+ if (options) {
+ while (!options_empty(options))
+ options_delete(options, options->head);
+ free(options);
+ }
+}
+
+/**
+ * po_split - split options string into group of options
+ * @options: pointer to C string containing zero or more comma-delimited options
+ *
+ * Convert our mount options string to a list to make it easier
+ * to adjust the options as we go. This is just an exercise in
+ * lexical parsing -- this function doesn't pay attention to the
+ * meaning of the options themselves.
+ *
+ * Returns a new group of mount options if successful; otherwise NULL
+ * is returned if some failure occurred.
+ */
+struct mount_options *po_split(char *str)
+{
+ struct mount_options *options;
+ struct tokenizer_state *tstate;
+ char *opt;
+
+ if (!str)
+ return options_create();
+
+ options = options_create();
+ if (options) {
+ tstate = init_tokenizer(str, ',');
+ for (opt = next_token(tstate); opt; opt = next_token(tstate)) {
+ struct mount_option *option = option_create(opt);
+ free(opt);
+ if (!option)
+ goto fail;
+ options_tail_insert(options, option);
+ }
+ if (tokenizer_error(tstate))
+ goto fail;
+ end_tokenizer(tstate);
+ }
+ return options;
+
+fail:
+ end_tokenizer(tstate);
+ po_destroy(options);
+ return NULL;
+}
+
+/**
+ * po_dup - duplicate an existing list of options
+ * @options: pointer to mount options
+ *
+ */
+struct mount_options *po_dup(struct mount_options *source)
+{
+ struct mount_options *target;
+ struct mount_option *current;
+
+ if (!source)
+ return NULL;
+
+ target = options_create();
+ if (options_empty(source) || target == NULL)
+ return target;
+
+ current = source->head;
+ while (target->count < source->count) {
+ struct mount_option *option;
+
+ option = option_dup(current);
+ if (!option) {
+ po_destroy(target);
+ return NULL;
+ }
+
+ options_tail_insert(target, option);
+ current = current->next;
+ }
+
+ return target;
+}
+
+/**
+ * po_replace - replace mount options in one mount_options object with another
+ * @target: pointer to previously instantiated object to replace
+ * @source: pointer to object containing source mount options
+ *
+ * Side effect: the object referred to by source is emptied.
+ */
+void po_replace(struct mount_options *target, struct mount_options *source)
+{
+ if (target) {
+ while (!options_empty(target))
+ options_delete(target, target->head);
+
+ if (source) {
+ target->head = source->head;
+ target->tail = source->tail;
+ target->count = source->count;
+
+ options_init(source);
+ }
+ }
+}
+
+/**
+ * po_join - recombine group of mount options into a C string
+ * @options: pointer to mount options to recombine
+ * @str: handle on string to replace (input and output)
+ *
+ * Convert our mount options object back into a string that the
+ * rest of the world can use.
+ *
+ * Upon return, @string contains the address of a replacement
+ * C string containing a comma-delimited list of mount options
+ * and values; or the passed-in string is freed and NULL is
+ * returned if some failure occurred.
+ */
+po_return_t po_join(struct mount_options *options, char **str)
+{
+ size_t len = 0;
+ struct mount_option *option;
+
+ if (!str || !options)
+ return PO_FAILED;
+
+ free(*str);
+ *str = NULL;
+
+ if (options_empty(options)) {
+ *str = strdup("");
+ return *str ? PO_SUCCEEDED : PO_FAILED;
+ }
+
+ for (option = options->head; option; option = option->next) {
+ len += strlen(option->keyword);
+ if (option->value)
+ len +=strlen(option->value) + 1; /* equals sign */
+ if (option->next)
+ len++; /* comma */
+ }
+
+ len++; /* NULL on the end */
+
+ *str = malloc(len);
+ if (!*str)
+ return PO_FAILED;
+ *str[0] = '\0';
+
+ for (option = options->head; option; option = option->next) {
+ strcat(*str, option->keyword);
+ if (option->value) {
+ strcat(*str, "=");
+ strcat(*str, option->value);
+ }
+ if (option->next)
+ strcat(*str, ",");
+ }
+
+ return PO_SUCCEEDED;
+}
+
+/**
+ * po_insert - insert an option into a group of options
+ * @options: pointer to mount options
+ * @option: pointer to a C string containing the option to add
+ *
+ */
+po_return_t po_insert(struct mount_options *options, char *str)
+{
+ struct mount_option *option = option_create(str);
+
+ if (option) {
+ options_head_insert(options, option);
+ return PO_SUCCEEDED;
+ }
+ return PO_FAILED;
+}
+
+/**
+ * po_append - concatenate an option onto a group of options
+ * @options: pointer to mount options
+ * @option: pointer to a C string containing the option to add
+ *
+ */
+po_return_t po_append(struct mount_options *options, char *str)
+{
+ struct mount_option *option = option_create(str);
+
+ if (option) {
+ options_tail_insert(options, option);
+ return PO_SUCCEEDED;
+ }
+ return PO_FAILED;
+}
+
+/**
+ * po_contains - check for presence of an option in a group
+ * @options: pointer to mount options
+ * @keyword: pointer to a C string containing option keyword for which to search
+ *
+ */
+po_found_t po_contains(struct mount_options *options, char *keyword)
+{
+ struct mount_option *option;
+
+ if (options && keyword) {
+ for (option = options->head; option; option = option->next)
+ if (strcmp(option->keyword, keyword) == 0)
+ return PO_FOUND;
+ }
+
+ return PO_NOT_FOUND;
+}
+
+/**
+ * po_contains_prefix - check for presence of an option matching a prefix
+ * @options: pointer to mount options
+ * @prefix: pointer to prefix to match against a keyword
+ * @keyword: pointer to a C string containing the option keyword if found
+ * @n: number of instances to skip, so '0' returns the first.
+ *
+ * On success, *keyword contains the pointer of the matching option's keyword.
+ */
+po_found_t po_contains_prefix(struct mount_options *options,
+ const char *prefix, char **keyword, int n)
+{
+ struct mount_option *option;
+
+ if (options && prefix) {
+ for (option = options->head; option; option = option->next)
+ if (strncmp(option->keyword, prefix, strlen(prefix)) == 0) {
+ if (n > 0) {
+ n -= 1;
+ } else {
+ if (keyword)
+ *keyword = option->keyword;
+ return PO_FOUND;
+ }
+ }
+ }
+
+ return PO_NOT_FOUND;
+}
+
+/**
+ * po_get - return the value of the rightmost instance of an option
+ * @options: pointer to mount options
+ * @keyword: pointer to a C string containing option keyword for which to search
+ *
+ * If multiple instances of the same option are present in a mount option
+ * list, the rightmost instance is always the effective one.
+ *
+ * Returns pointer to C string containing the value of the option.
+ * Returns NULL if the option isn't found, or if the option doesn't
+ * have a value.
+ */
+char *po_get(struct mount_options *options, char *keyword)
+{
+ struct mount_option *option;
+
+ if (options && keyword) {
+ for (option = options->tail; option; option = option->prev)
+ if (strcmp(option->keyword, keyword) == 0)
+ return option->value;
+ }
+
+ return NULL;
+}
+
+/**
+ * po_get_numeric - return numeric value of rightmost instance of keyword option
+ * @options: pointer to mount options
+ * @keyword: pointer to a C string containing option keyword for which to search
+ * @value: OUT: set to the value of the keyword
+ *
+ * This is specifically for parsing keyword options that take only a numeric
+ * value. If multiple instances of the same option are present in a mount
+ * option list, the rightmost instance is always the effective one.
+ *
+ * Returns:
+ * * PO_FOUND if the keyword was found and the value is numeric; @value is
+ * set to the keyword's value
+ * * PO_NOT_FOUND if the keyword was not found
+ * * PO_BAD_VALUE if the keyword was found, but the value is not numeric
+ *
+ * These last two are separate in case the caller wants to warn about bad mount
+ * options instead of silently using a default.
+ */
+#ifdef HAVE_STRTOL
+po_found_t po_get_numeric(struct mount_options *options, char *keyword, long *value)
+{
+ char *option, *endptr;
+ long tmp;
+
+ option = po_get(options, keyword);
+ if (option == NULL)
+ return PO_NOT_FOUND;
+
+ errno = 0;
+ tmp = strtol(option, &endptr, 10);
+ if (errno == 0 && endptr != option) {
+ *value = tmp;
+ return PO_FOUND;
+ }
+ return PO_BAD_VALUE;
+}
+#else /* HAVE_STRTOL */
+po_found_t po_get_numeric(struct mount_options *options, char *keyword, long *value)
+{
+ char *option;
+
+ option = po_get(options, keyword);
+ if (option == NULL)
+ return PO_NOT_FOUND;
+
+ *value = atoi(option);
+ return PO_FOUND;
+}
+#endif /* HAVE_STRTOL */
+
+/**
+ * po_rightmost - determine the relative position of several options
+ * @options: pointer to mount options
+ * @keys: pointer to an array of C strings containing option keywords
+ *
+ * This function can be used to determine which of several similar
+ * options will be the one to take effect.
+ *
+ * The kernel parses the mount option string from left to right.
+ * If an option is specified more than once (for example, "intr"
+ * and "nointr", the rightmost option is the last to be parsed,
+ * and it therefore takes precedence over previous similar options.
+ *
+ * This can also distinguish among multiple synonymous options, such
+ * as "proto=," "udp" and "tcp."
+ *
+ * Returns the index into @keys of the option that is rightmost.
+ * If none of the options listed in @keys is present in @options, or
+ * if @options is NULL, returns -1.
+ */
+int po_rightmost(struct mount_options *options, const char *keys[])
+{
+ struct mount_option *option;
+ int i;
+
+ if (options) {
+ for (option = options->tail; option; option = option->prev) {
+ for (i = 0; keys[i] != NULL; i++)
+ if (strcmp(option->keyword, keys[i]) == 0)
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+/**
+ * po_remove_all - remove instances of an option from a group
+ * @options: pointer to mount options
+ * @keyword: pointer to a C string containing an option keyword to remove
+ *
+ * Side-effect: the passed-in list is truncated on success.
+ */
+po_found_t po_remove_all(struct mount_options *options, char *keyword)
+{
+ struct mount_option *option, *next;
+ int found = PO_NOT_FOUND;
+
+ if (options && keyword) {
+ for (option = options->head; option; option = next) {
+ next = option->next;
+ if (strcmp(option->keyword, keyword) == 0) {
+ options_delete(options, option);
+ found = PO_FOUND;
+ }
+ }
+ }
+
+ return found;
+}
diff --git a/utils/mount/parse_opt.h b/utils/mount/parse_opt.h
new file mode 100644
index 0000000..181e7bf
--- /dev/null
+++ b/utils/mount/parse_opt.h
@@ -0,0 +1,60 @@
+/*
+ * parse_opt.h -- mount option string parsing helpers
+ *
+ * Copyright (C) 2007 Oracle. All rights reserved.
+ * Copyright (C) 2007 Chuck Lever <chuck.lever@oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ *
+ */
+
+#ifndef _NFS_UTILS_PARSE_OPT_H
+#define _NFS_UTILS_PARSE_OPT_H
+
+typedef enum {
+ PO_FAILED = 0,
+ PO_SUCCEEDED = 1,
+} po_return_t;
+
+typedef enum {
+ PO_NOT_FOUND = 0,
+ PO_FOUND = 1,
+ PO_BAD_VALUE = 2,
+} po_found_t;
+
+struct mount_options;
+
+struct mount_options * po_split(char *);
+struct mount_options * po_dup(struct mount_options *);
+void po_replace(struct mount_options *,
+ struct mount_options *);
+po_return_t po_join(struct mount_options *, char **);
+
+po_return_t po_insert(struct mount_options *, char *);
+po_return_t po_append(struct mount_options *, char *);
+po_found_t po_contains(struct mount_options *, char *);
+po_found_t po_contains_prefix(struct mount_options *options,
+ const char *prefix, char **keyword,
+ int n);
+char * po_get(struct mount_options *, char *);
+po_found_t po_get_numeric(struct mount_options *,
+ char *, long *);
+int po_rightmost(struct mount_options *,
+ const char *keys[]);
+po_found_t po_remove_all(struct mount_options *, char *);
+void po_destroy(struct mount_options *);
+
+#endif /* _NFS_UTILS_PARSE_OPT_H */
diff --git a/utils/mount/stropts.c b/utils/mount/stropts.c
new file mode 100644
index 0000000..dbdd11e
--- /dev/null
+++ b/utils/mount/stropts.c
@@ -0,0 +1,1310 @@
+/*
+ * stropts.c -- NFS mount using C string to pass options to kernel
+ *
+ * Copyright (C) 2007 Oracle. All rights reserved.
+ * Copyright (C) 2007 Chuck Lever <chuck.lever@oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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 <unistd.h>
+#include <errno.h>
+#include <netdb.h>
+#include <time.h>
+
+#include <sys/socket.h>
+#include <sys/mount.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "nfslib.h"
+#include "sockaddr.h"
+#include "xcommon.h"
+#include "mount.h"
+#include "nls.h"
+#include "nfsrpc.h"
+#include "mount_constants.h"
+#include "stropts.h"
+#include "error.h"
+#include "network.h"
+#include "parse_opt.h"
+#include "version.h"
+#include "parse_dev.h"
+#include "conffile.h"
+#include "misc.h"
+
+#ifndef NFS_PROGRAM
+#define NFS_PROGRAM (100003)
+#endif
+
+#ifndef NFS_PORT
+#define NFS_PORT (2049)
+#endif
+
+#ifndef NFS_MAXHOSTNAME
+#define NFS_MAXHOSTNAME (255)
+#endif
+
+#ifndef NFS_MAXPATHNAME
+#define NFS_MAXPATHNAME (1024)
+#endif
+
+#ifndef NFS_DEF_FG_TIMEOUT_MINUTES
+#define NFS_DEF_FG_TIMEOUT_MINUTES (2u)
+#endif
+
+#ifndef NFS_DEF_BG_TIMEOUT_MINUTES
+#define NFS_DEF_BG_TIMEOUT_MINUTES (10000u)
+#endif
+
+#ifndef NFS_DEFAULT_MAJOR
+#define NFS_DEFAULT_MAJOR 4
+#endif
+#ifndef NFS_DEFAULT_MINOR
+#define NFS_DEFAULT_MINOR 2
+#endif
+
+extern int nfs_mount_data_version;
+extern char *progname;
+extern int verbose;
+extern int sloppy;
+
+struct nfsmount_info {
+ const char *spec, /* server:/path */
+ *node; /* mounted-on dir */
+ char *type; /* "nfs" or "nfs4" */
+ char *hostname; /* server's hostname */
+ struct addrinfo *address; /* server's addresses */
+ sa_family_t family; /* Address family */
+
+ struct mount_options *options; /* parsed mount options */
+ char **extra_opts; /* string for /etc/mtab */
+
+ struct nfs_version version; /* NFS version */
+ int flags, /* MS_ flags */
+ fake, /* actually do the mount? */
+ child; /* forked bg child? */
+};
+
+
+static void nfs_default_version(struct nfsmount_info *mi)
+{
+#ifdef MOUNT_CONFIG
+ extern struct nfs_version config_default_vers;
+ /*
+ * Use the default value set in the config file when
+ * the version has not been explicitly set.
+ */
+ if (config_default_vers.v_mode == V_PARSE_ERR) {
+ mi->version.v_mode = V_PARSE_ERR;
+ return;
+ }
+
+ if (mi->version.v_mode == V_DEFAULT &&
+ config_default_vers.v_mode != V_DEFAULT) {
+ mi->version.major = config_default_vers.major;
+ if (config_default_vers.v_mode == V_SPECIFIC)
+ mi->version.minor = config_default_vers.minor;
+ else
+ mi->version.minor = NFS_DEFAULT_MINOR;
+ return;
+ }
+
+ if (mi->version.v_mode == V_GENERAL) {
+ if (config_default_vers.v_mode != V_DEFAULT &&
+ mi->version.major == config_default_vers.major) {
+ if (config_default_vers.v_mode == V_SPECIFIC)
+ mi->version.minor = config_default_vers.minor;
+ else
+ mi->version.minor = NFS_DEFAULT_MINOR;
+ } else
+ mi->version.minor = NFS_DEFAULT_MINOR;
+ return;
+ }
+
+#endif /* MOUNT_CONFIG */
+ mi->version.major = NFS_DEFAULT_MAJOR;
+ mi->version.minor = NFS_DEFAULT_MINOR;
+}
+
+/*
+ * Obtain a retry timeout value based on the value of the "retry=" option.
+ *
+ * Returns a time_t timeout timestamp, in seconds.
+ */
+static time_t nfs_parse_retry_option(struct mount_options *options,
+ const time_t default_timeout)
+{
+ time_t timeout_minutes;
+ long tmp;
+
+ timeout_minutes = default_timeout;
+ switch (po_get_numeric(options, "retry", &tmp)) {
+ case PO_NOT_FOUND:
+ break;
+ case PO_FOUND:
+ if (tmp >= 0) {
+ timeout_minutes = tmp;
+ break;
+ }
+ /*FALLTHROUGH*/
+ case PO_BAD_VALUE:
+ if (verbose)
+ nfs_error(_("%s: invalid retry timeout was specified; "
+ "using default timeout"), progname);
+ break;
+ }
+
+ return time(NULL) + (timeout_minutes * 60);
+}
+
+/*
+ * Convert the passed-in sockaddr-style address to presentation
+ * format, then append an option of the form "keyword=address".
+ *
+ * Returns 1 if the option was appended successfully; otherwise zero.
+ */
+static int nfs_append_generic_address_option(const struct sockaddr *sap,
+ const socklen_t salen,
+ const char *keyword,
+ struct mount_options *options)
+{
+ char address[NI_MAXHOST];
+ char new_option[512];
+ int len;
+
+ if (!nfs_present_sockaddr(sap, salen, address, sizeof(address)))
+ goto out_err;
+
+ len = snprintf(new_option, sizeof(new_option), "%s=%s",
+ keyword, address);
+ if (len < 0 || (size_t)len >= sizeof(new_option))
+ goto out_err;
+
+ if (po_append(options, new_option) != PO_SUCCEEDED)
+ goto out_err;
+
+ return 1;
+
+out_err:
+ nfs_error(_("%s: failed to construct %s option"), progname, keyword);
+ return 0;
+}
+
+/*
+ * Append the 'addr=' option to the options string to pass a resolved
+ * server address to the kernel. After a successful mount, this address
+ * is also added to /etc/mtab for use when unmounting.
+ *
+ * If 'addr=' is already present, we strip it out. This prevents users
+ * from setting a bogus 'addr=' option themselves, and also allows bg
+ * retries to recompute the server's address, in case it has changed.
+ *
+ * Returns 1 if 'addr=' option appended successfully;
+ * otherwise zero.
+ */
+static int nfs_append_addr_option(const struct sockaddr *sap,
+ socklen_t salen,
+ struct mount_options *options)
+{
+ po_remove_all(options, "addr");
+ return nfs_append_generic_address_option(sap, salen, "addr", options);
+}
+
+/*
+ * Called to discover our address and append an appropriate 'clientaddr='
+ * option to the options string. If the supplied 'clientaddr=' value does
+ * not match either IPV4/IPv6 any or a local address, then fail the mount.
+ *
+ * Returns 1 if 'clientaddr=' option created successfully or if
+ * 'clientaddr=' option is already present; otherwise zero.
+ */
+static int nfs_append_clientaddr_option(const struct sockaddr *sap,
+ socklen_t salen,
+ struct mount_options *options)
+{
+ union nfs_sockaddr address;
+ struct sockaddr *my_addr = &address.sa;
+ socklen_t my_len = sizeof(address);
+
+ if (po_contains(options, "clientaddr") == PO_FOUND) {
+ char *addr = po_get(options, "clientaddr");
+ union nfs_sockaddr nfs_address;
+ struct sockaddr *nfs_saddr = &nfs_address.sa;
+ socklen_t nfs_salen = sizeof(nfs_address);
+
+ /* translate the input for clientaddr to nfs_sockaddr */
+ if (!nfs_string_to_sockaddr(addr, nfs_saddr, &nfs_salen))
+ return 0;
+
+ /* check for IPV4_ANY and IPV6_ANY */
+ if (nfs_is_inaddr_any(nfs_saddr))
+ return 1;
+
+ /* check if ip matches local network addresses */
+ if (!nfs_addr_matches_localips(nfs_saddr))
+ nfs_error(_("%s: [warning] supplied clientaddr=%s "
+ "does not match any existing network "
+ "addresses"), progname, addr);
+ return 1;
+ }
+
+ nfs_callback_address(sap, salen, my_addr, &my_len);
+
+ return nfs_append_generic_address_option(my_addr, my_len,
+ "clientaddr", options);
+}
+
+/*
+ * Determine whether to append a 'mountaddr=' option. The option is needed if:
+ *
+ * 1. "mounthost=" was specified, or
+ * 2. The address families for proto= and mountproto= are different.
+ */
+static int nfs_fix_mounthost_option(struct mount_options *options,
+ const char *nfs_hostname)
+{
+ union nfs_sockaddr address;
+ struct sockaddr *sap = &address.sa;
+ socklen_t salen = sizeof(address);
+ sa_family_t nfs_family, mnt_family;
+ char *mounthost;
+
+ if (!nfs_nfs_proto_family(options, &nfs_family))
+ return 0;
+ if (!nfs_mount_proto_family(options, &mnt_family))
+ return 0;
+
+ mounthost = po_get(options, "mounthost");
+ if (mounthost == NULL) {
+ if (nfs_family == mnt_family)
+ return 1;
+ mounthost = (char *)nfs_hostname;
+ }
+
+ if (!nfs_lookup(mounthost, mnt_family, sap, &salen)) {
+ nfs_error(_("%s: unable to determine mount server's address"),
+ progname);
+ return 0;
+ }
+
+ return nfs_append_generic_address_option(sap, salen,
+ "mountaddr", options);
+}
+
+/*
+ * Returns zero if the "lock" option is in effect, but statd
+ * can't be started. Otherwise, returns 1.
+ */
+static const char *nfs_lock_opttbl[] = {
+ "nolock",
+ "lock",
+ NULL,
+};
+
+static int nfs_verify_lock_option(struct mount_options *options)
+{
+ if (po_rightmost(options, nfs_lock_opttbl) == 0)
+ return 1;
+
+ if (!start_statd()) {
+ nfs_error(_("%s: rpc.statd is not running but is "
+ "required for remote locking."), progname);
+ nfs_error(_("%s: Either use '-o nolock' to keep "
+ "locks local, or start statd."), progname);
+ errno = EALREADY; /* Don't print further error message */
+ return 0;
+ }
+
+ return 1;
+}
+
+static int nfs_insert_sloppy_option(struct mount_options *options)
+{
+ if (linux_version_code() < MAKE_VERSION(2, 6, 27))
+ return 1;
+
+ if (po_contains(options, "sloppy")) {
+ po_remove_all(options, "sloppy");
+ sloppy++;
+ }
+
+ if (sloppy) {
+ if (po_insert(options, "sloppy") == PO_FAILED)
+ return 0;
+ }
+
+ return 1;
+}
+
+static int nfs_set_version(struct nfsmount_info *mi)
+{
+
+ if (!nfs_nfs_version(mi->type, mi->options, &mi->version))
+ return 0;
+
+ /*
+ * Before 2.6.32, the kernel NFS client didn't
+ * support "-t nfs vers=4" mounts, so NFS version
+ * 4 cannot be included when autonegotiating
+ * while running on those kernels.
+ */
+ if (mi->version.v_mode == V_DEFAULT &&
+ linux_version_code() <= MAKE_VERSION(2, 6, 31)) {
+ mi->version.major = 3;
+ mi->version.v_mode = V_SPECIFIC;
+ }
+
+ /*
+ * If we still don't know, check for version-specific
+ * mount options.
+ */
+ if (mi->version.v_mode == V_DEFAULT) {
+ if (po_contains(mi->options, "mounthost") ||
+ po_contains(mi->options, "mountaddr") ||
+ po_contains(mi->options, "mountvers") ||
+ po_contains(mi->options, "mountproto")) {
+ mi->version.major = 3;
+ mi->version.v_mode = V_SPECIFIC;
+ }
+ }
+
+ /*
+ * If enabled, see if the default version was
+ * set in the config file
+ */
+ if (mi->version.v_mode != V_SPECIFIC) {
+ nfs_default_version(mi);
+ /*
+ * If the version was not specifically set, it will
+ * be set by autonegotiation later, so remove it now:
+ */
+ po_remove_all(mi->options, "v4");
+ po_remove_all(mi->options, "vers");
+ po_remove_all(mi->options, "nfsvers");
+ }
+
+ if (mi->version.v_mode == V_PARSE_ERR)
+ return 0;
+
+ return 1;
+}
+
+/*
+ * Set up mandatory non-version specific NFS mount options.
+ *
+ * Returns 1 if successful; otherwise zero.
+ */
+static int nfs_validate_options(struct nfsmount_info *mi)
+{
+ /* For remount, ignore mi->spec: the kernel will. */
+ if (!(mi->flags & MS_REMOUNT) &&
+ !nfs_parse_devname(mi->spec, &mi->hostname, NULL))
+ return 0;
+
+ if (!nfs_nfs_proto_family(mi->options, &mi->family))
+ return 0;
+
+ /*
+ * A remount is not going to be able to change the server's address,
+ * nor should we try to resolve another address for the server as we
+ * may end up with a different address.
+ * A non-remount will set 'addr' from ->hostname
+ */
+ po_remove_all(mi->options, "addr");
+
+ if (!nfs_set_version(mi))
+ return 0;
+
+ if (!nfs_insert_sloppy_option(mi->options))
+ return 0;
+
+ return 1;
+}
+
+/*
+ * Get NFS/mnt server addresses from mount options
+ *
+ * Returns 1 and fills in @nfs_saddr, @nfs_salen, @mnt_saddr, and @mnt_salen
+ * if all goes well; otherwise zero.
+ */
+static int nfs_extract_server_addresses(struct mount_options *options,
+ struct sockaddr *nfs_saddr,
+ socklen_t *nfs_salen,
+ struct sockaddr *mnt_saddr,
+ socklen_t *mnt_salen)
+{
+ char *option;
+
+ option = po_get(options, "addr");
+ if (option == NULL)
+ return 0;
+ if (!nfs_string_to_sockaddr(option, nfs_saddr, nfs_salen))
+ return 0;
+
+ option = po_get(options, "mountaddr");
+ if (option == NULL) {
+ memcpy(mnt_saddr, nfs_saddr, *nfs_salen);
+ *mnt_salen = *nfs_salen;
+ } else if (!nfs_string_to_sockaddr(option, mnt_saddr, mnt_salen))
+ return 0;
+
+ return 1;
+}
+
+static int nfs_construct_new_options(struct mount_options *options,
+ struct sockaddr *nfs_saddr,
+ struct pmap *nfs_pmap,
+ struct sockaddr *mnt_saddr,
+ struct pmap *mnt_pmap)
+{
+ char new_option[64];
+ char *netid;
+
+ po_remove_all(options, "nfsprog");
+ po_remove_all(options, "mountprog");
+
+ po_remove_all(options, "v2");
+ po_remove_all(options, "v3");
+ po_remove_all(options, "vers");
+ po_remove_all(options, "nfsvers");
+ snprintf(new_option, sizeof(new_option) - 1,
+ "vers=%lu", nfs_pmap->pm_vers);
+ if (po_append(options, new_option) == PO_FAILED)
+ return 0;
+
+ po_remove_all(options, "proto");
+ po_remove_all(options, "udp");
+ po_remove_all(options, "tcp");
+ netid = nfs_get_netid(nfs_saddr->sa_family, nfs_pmap->pm_prot);
+ if (netid == NULL)
+ return 0;
+ snprintf(new_option, sizeof(new_option) - 1,
+ "proto=%s", netid);
+ free(netid);
+ if (po_append(options, new_option) == PO_FAILED)
+ return 0;
+
+ if(po_remove_all(options, "port") == PO_FOUND ||
+ nfs_pmap->pm_port != NFS_PORT) {
+ snprintf(new_option, sizeof(new_option) - 1,
+ "port=%lu", nfs_pmap->pm_port);
+ if (po_append(options, new_option) == PO_FAILED)
+ return 0;
+ }
+
+ po_remove_all(options, "mountvers");
+ snprintf(new_option, sizeof(new_option) - 1,
+ "mountvers=%lu", mnt_pmap->pm_vers);
+ if (po_append(options, new_option) == PO_FAILED)
+ return 0;
+
+ po_remove_all(options, "mountproto");
+ netid = nfs_get_netid(mnt_saddr->sa_family, mnt_pmap->pm_prot);
+ if (netid == NULL)
+ return 0;
+ snprintf(new_option, sizeof(new_option) - 1,
+ "mountproto=%s", netid);
+ free(netid);
+ if (po_append(options, new_option) == PO_FAILED)
+ return 0;
+
+ po_remove_all(options, "mountport");
+ snprintf(new_option, sizeof(new_option) - 1,
+ "mountport=%lu", mnt_pmap->pm_port);
+ if (po_append(options, new_option) == PO_FAILED)
+ return 0;
+
+ return 1;
+}
+
+/*
+ * Reconstruct the mount option string based on a portmapper probe
+ * of the server. Returns one if the server's portmapper returned
+ * something we can use, otherwise zero.
+ *
+ * To handle version and transport protocol fallback properly, we
+ * need to parse some of the mount options in order to set up a
+ * portmap probe. Mount options that nfs_rewrite_pmap_mount_options()
+ * doesn't recognize are left alone.
+ *
+ * Returns TRUE if rewriting was successful; otherwise
+ * FALSE is returned if some failure occurred.
+ */
+static int
+nfs_rewrite_pmap_mount_options(struct mount_options *options, int checkv4)
+{
+ union nfs_sockaddr nfs_address;
+ struct sockaddr *nfs_saddr = &nfs_address.sa;
+ socklen_t nfs_salen = sizeof(nfs_address);
+ struct pmap nfs_pmap;
+ union nfs_sockaddr mnt_address;
+ struct sockaddr *mnt_saddr = &mnt_address.sa;
+ socklen_t mnt_salen = sizeof(mnt_address);
+ unsigned long protocol;
+ struct pmap mnt_pmap;
+
+ /* initialize structs */
+ memset(&nfs_pmap, 0, sizeof(struct pmap));
+ memset(&mnt_pmap, 0, sizeof(struct pmap));
+
+ /*
+ * Version and transport negotiation is not required
+ * and does not work for RDMA mounts.
+ */
+ if (!nfs_nfs_protocol(options, &protocol)) {
+ errno = EINVAL;
+ return 0;
+ }
+ if (protocol == NFSPROTO_RDMA)
+ goto out;
+
+ /*
+ * Extract just the options needed to contact server.
+ * Bail now if any of these have bad values.
+ */
+ if (!nfs_extract_server_addresses(options, nfs_saddr, &nfs_salen,
+ mnt_saddr, &mnt_salen)) {
+ errno = EINVAL;
+ return 0;
+ }
+ if (!nfs_options2pmap(options, &nfs_pmap, &mnt_pmap)) {
+ errno = EINVAL;
+ return 0;
+ }
+
+ /*
+ * The kernel NFS client doesn't support changing the RPC
+ * program number for these services, so force the value of
+ * these fields before probing the server's ports.
+ */
+ nfs_pmap.pm_prog = NFS_PROGRAM;
+ mnt_pmap.pm_prog = MOUNTPROG;
+
+ /*
+ * If the server's rpcbind service isn't available, we can't
+ * negotiate. Bail now if we can't contact it.
+ */
+ if (!nfs_probe_bothports(mnt_saddr, mnt_salen, &mnt_pmap,
+ nfs_saddr, nfs_salen, &nfs_pmap, checkv4)) {
+ errno = ESPIPE;
+ if (rpc_createerr.cf_stat == RPC_PROGNOTREGISTERED)
+ errno = EOPNOTSUPP;
+ else if (rpc_createerr.cf_stat == RPC_AUTHERROR)
+ errno = EACCES;
+ else if (rpc_createerr.cf_stat == RPC_TIMEDOUT)
+ errno = ETIMEDOUT;
+ else if (rpc_createerr.cf_stat == RPC_PROGVERSMISMATCH)
+ errno = EPROTONOSUPPORT;
+ else if (rpc_createerr.cf_error.re_errno != 0)
+ errno = rpc_createerr.cf_error.re_errno;
+ return 0;
+ }
+
+ if (!nfs_construct_new_options(options, nfs_saddr, &nfs_pmap,
+ mnt_saddr, &mnt_pmap)) {
+ if (rpc_createerr.cf_stat == RPC_UNKNOWNPROTO)
+ errno = EPROTONOSUPPORT;
+ else
+ errno = EINVAL;
+ return 0;
+ }
+
+out:
+ errno = 0;
+ return 1;
+}
+
+/*
+ * Do the mount(2) system call.
+ *
+ * Returns TRUE if successful, otherwise FALSE.
+ * "errno" is set to reflect the individual error.
+ */
+static int nfs_sys_mount(struct nfsmount_info *mi, struct mount_options *opts)
+{
+ char *options = NULL;
+ int result;
+
+ if (mi->fake)
+ return 1;
+
+ if (po_join(opts, &options) == PO_FAILED) {
+ errno = EIO;
+ return 0;
+ }
+
+ result = mount(mi->spec, mi->node, mi->type,
+ mi->flags & ~(MS_USER|MS_USERS), options);
+ free(options);
+
+ if (verbose && result) {
+ int save = errno;
+ nfs_error(_("%s: mount(2): %s"), progname, strerror(save));
+ errno = save;
+ }
+ return !result;
+}
+
+static int nfs_do_mount_v3v2(struct nfsmount_info *mi,
+ struct sockaddr *sap, socklen_t salen,
+ int checkv4)
+{
+ struct mount_options *options = po_dup(mi->options);
+ int result = 0;
+
+ if (!options) {
+ errno = ENOMEM;
+ return result;
+ }
+ errno = 0;
+ if (!nfs_append_addr_option(sap, salen, options)) {
+ if (errno == 0)
+ errno = EINVAL;
+ goto out_fail;
+ }
+
+ if (!nfs_fix_mounthost_option(options, mi->hostname)) {
+ if (errno == 0)
+ errno = EINVAL;
+ goto out_fail;
+ }
+ if (!mi->fake && !nfs_verify_lock_option(options)) {
+ if (errno == 0)
+ errno = EINVAL;
+ goto out_fail;
+ }
+
+ /*
+ * Options we negotiate below may be stale by the time this
+ * file system is unmounted. In order to force umount.nfs
+ * to renegotiate with the server, only write the user-
+ * specified options, and not negotiated options, to /etc/mtab.
+ */
+ if (po_join(options, mi->extra_opts) == PO_FAILED) {
+ errno = ENOMEM;
+ goto out_fail;
+ }
+
+ if (verbose)
+ printf(_("%s: trying text-based options '%s'\n"),
+ progname, *mi->extra_opts);
+
+ if (!nfs_rewrite_pmap_mount_options(options, checkv4))
+ goto out_fail;
+
+ result = nfs_sys_mount(mi, options);
+
+out_fail:
+ po_destroy(options);
+ return result;
+}
+
+/*
+ * Attempt a "-t nfs vers=2" or "-t nfs vers=3" mount.
+ *
+ * Returns TRUE if successful, otherwise FALSE.
+ * "errno" is set to reflect the individual error.
+ */
+static int nfs_try_mount_v3v2(struct nfsmount_info *mi, int checkv4)
+{
+ struct addrinfo *ai;
+ int ret = 0;
+
+ for (ai = mi->address; ai != NULL; ai = ai->ai_next) {
+ ret = nfs_do_mount_v3v2(mi, ai->ai_addr, ai->ai_addrlen, checkv4);
+ if (ret != 0)
+ return ret;
+
+ switch (errno) {
+ case ECONNREFUSED:
+ case EOPNOTSUPP:
+ case EHOSTUNREACH:
+ case ETIMEDOUT:
+ case EACCES:
+ continue;
+ default:
+ goto out;
+ }
+ }
+out:
+ return ret;
+}
+
+static int nfs_do_mount_v4(struct nfsmount_info *mi,
+ struct sockaddr *sap, socklen_t salen)
+{
+ struct mount_options *options = po_dup(mi->options);
+ int result = 0;
+ char version_opt[32];
+ char *extra_opts = NULL;
+
+ if (!options) {
+ errno = ENOMEM;
+ return result;
+ }
+
+ if (po_contains(options, "mounthost") ||
+ po_contains(options, "mountaddr") ||
+ po_contains(options, "mountvers") ||
+ po_contains(options, "mountport") ||
+ po_contains(options, "mountproto")) {
+ /*
+ * Since these mountd options are set assume version 3
+ * is wanted so error out with EPROTONOSUPPORT so the
+ * protocol negation starts with v3.
+ */
+ if (verbose) {
+ printf(_("%s: Unsupported nfs4 mount option(s) passed '%s'\n"),
+ progname, *mi->extra_opts);
+ }
+ errno = EPROTONOSUPPORT;
+ goto out_fail;
+ }
+
+ if (mi->version.v_mode != V_SPECIFIC) {
+ char *fmt;
+ switch (mi->version.minor) {
+ /* Old kernels don't support the new "vers=x.y"
+ * option, but do support old versions of NFS4.
+ * So use the format that is most widely understood.
+ */
+ case 0:
+ fmt = "vers=%lu";
+ break;
+ case 1:
+ fmt = "vers=%lu,minorversion=%lu";
+ break;
+ default:
+ fmt = "vers=%lu.%lu";
+ break;
+ }
+#pragma GCC diagnostic ignored "-Wformat-nonliteral"
+ snprintf(version_opt, sizeof(version_opt) - 1,
+ fmt, mi->version.major,
+ mi->version.minor);
+#pragma GCC diagnostic warning "-Wformat-nonliteral"
+
+ if (po_append(options, version_opt) == PO_FAILED) {
+ errno = EINVAL;
+ goto out_fail;
+ }
+ } else if (po_get(options, "minorversion") &&
+ linux_version_code() > MAKE_VERSION(3, 4, 0)) {
+ /*
+ * convert minorversion= into vers=4.x
+ */
+ po_remove_all(options, "minorversion");
+
+ snprintf(version_opt, sizeof(version_opt) - 1,
+ "vers=%lu.%lu", mi->version.major,
+ mi->version.minor);
+
+ if (po_append(options, version_opt) == PO_FAILED) {
+ errno = EINVAL;
+ goto out_fail;
+ }
+ }
+
+ if (!nfs_append_addr_option(sap, salen, options)) {
+ errno = EINVAL;
+ goto out_fail;
+ }
+
+ if (!nfs_append_clientaddr_option(sap, salen, options)) {
+ errno = EINVAL;
+ goto out_fail;
+ }
+
+ if (po_join(options, &extra_opts) == PO_FAILED) {
+ errno = ENOMEM;
+ goto out_fail;
+ }
+
+ if (verbose)
+ printf(_("%s: trying text-based options '%s'\n"),
+ progname, extra_opts);
+
+ result = nfs_sys_mount(mi, options);
+
+ /*
+ * If success, update option string to be recorded in /etc/mtab.
+ */
+ if (result) {
+ free(*mi->extra_opts);
+ *mi->extra_opts = extra_opts;
+ } else
+ free(extra_opts);
+
+out_fail:
+ po_destroy(options);
+ return result;
+}
+
+/*
+ * Attempt a "-t nfs -o vers=4" or "-t nfs4" mount.
+ *
+ * Returns TRUE if successful, otherwise FALSE.
+ * "errno" is set to reflect the individual error.
+ */
+static int nfs_try_mount_v4(struct nfsmount_info *mi)
+{
+ struct addrinfo *ai;
+ int ret = 0;
+
+ for (ai = mi->address; ai != NULL; ai = ai->ai_next) {
+ ret = nfs_do_mount_v4(mi, ai->ai_addr, ai->ai_addrlen);
+ if (ret != 0)
+ return ret;
+
+ switch (errno) {
+ case ECONNREFUSED:
+ case EHOSTUNREACH:
+ case ETIMEDOUT:
+ case EACCES:
+ continue;
+ default:
+ goto out;
+ }
+ }
+out:
+ return ret;
+}
+
+/*
+ * Handle NFS version and transport protocol
+ * autonegotiation.
+ *
+ * When no version or protocol is specified on the
+ * command line, mount.nfs negotiates with the server
+ * to determine appropriate settings for the new
+ * mount point.
+ *
+ * Returns TRUE if successful, otherwise FALSE.
+ * "errno" is set to reflect the individual error.
+ */
+static int nfs_autonegotiate(struct nfsmount_info *mi)
+{
+ int result, olderrno;
+
+ result = nfs_try_mount_v4(mi);
+check_result:
+ if (result)
+ return result;
+
+ switch (errno) {
+ case EPROTONOSUPPORT:
+ /* A clear indication that the server or our
+ * client does not support NFS version 4 and minor */
+ case EINVAL:
+ /* A less clear indication that our client
+ * does not support NFSv4 minor version. */
+ case EACCES:
+ /* An unclear indication that the server
+ * may not support NFSv4 minor version. */
+ if (mi->version.v_mode != V_SPECIFIC) {
+ if (mi->version.minor > 0) {
+ mi->version.minor--;
+ result = nfs_try_mount_v4(mi);
+ goto check_result;
+ }
+ }
+
+ goto fall_back;
+ case ENOENT:
+ /* Legacy Linux servers don't export an NFS
+ * version 4 pseudoroot. */
+ goto fall_back;
+ case EPERM:
+ /* Linux servers prior to 2.6.25 may return
+ * EPERM when NFS version 4 is not supported. */
+ goto fall_back;
+ case ECONNREFUSED:
+ /* UDP-Only servers won't support v4, but maybe it
+ * just isn't ready yet. So try v3, but double-check
+ * with rpcbind for v4. */
+ if (mi->version.v_mode == V_GENERAL)
+ /* Mustn't try v2,v3 */
+ return result;
+ result = nfs_try_mount_v3v2(mi, TRUE);
+ if (result == 0 && errno == EAGAIN) {
+ /* v4 server seems to be registered now. */
+ result = nfs_try_mount_v4(mi);
+ if (result == 0 && errno != ECONNREFUSED)
+ goto check_result;
+ } else if (result == 0)
+ /* Restore original errno with v3 failures */
+ errno = ECONNREFUSED;
+
+ return result;
+ default:
+ return result;
+ }
+
+fall_back:
+ if (mi->version.v_mode == V_GENERAL)
+ /* v2,3 fallback not allowed */
+ return result;
+
+ /*
+ * Save the original errno in case the v3
+ * mount fails from one of the fall_back cases.
+ * Report the first failure not the v3 mount failure
+ */
+ olderrno = errno;
+ if ((result = nfs_try_mount_v3v2(mi, FALSE)))
+ return result;
+
+ if (errno != EBUSY && errno != EACCES)
+ errno = olderrno;
+
+ return result;
+}
+
+/*
+ * This is a single pass through the fg/bg loop.
+ *
+ * Returns TRUE if successful, otherwise FALSE.
+ * "errno" is set to reflect the individual error.
+ */
+static int nfs_try_mount(struct nfsmount_info *mi)
+{
+ int result = 0;
+
+ if (mi->address == NULL) {
+ struct addrinfo hint = {
+ .ai_protocol = (int)IPPROTO_UDP,
+ };
+ int error;
+ struct addrinfo *address;
+
+ hint.ai_family = (int)mi->family;
+ error = getaddrinfo(mi->hostname, NULL, &hint, &address);
+ if (error != 0) {
+ if (error == EAI_AGAIN)
+ errno = EAGAIN;
+ else {
+ nfs_error(_("%s: Failed to resolve server %s: %s"),
+ progname, mi->hostname, gai_strerror(error));
+ errno = EALREADY;
+ }
+ return 0;
+ }
+
+ if (!nfs_append_addr_option(address->ai_addr,
+ address->ai_addrlen, mi->options)) {
+ nfs_freeaddrinfo(address);
+ errno = ENOMEM;
+ return 0;
+ }
+ mi->address = address;
+ }
+
+ switch (mi->version.major) {
+ case 3:
+ result = nfs_try_mount_v3v2(mi, FALSE);
+ break;
+ case 4:
+ if (mi->version.v_mode != V_SPECIFIC)
+ result = nfs_autonegotiate(mi);
+ else
+ result = nfs_try_mount_v4(mi);
+ break;
+ default:
+ errno = EIO;
+ }
+
+ return result;
+}
+
+/*
+ * Distinguish between permanent and temporary errors.
+ *
+ * Basically, we retry if communication with the server has
+ * failed so far, but fail immediately if there is a local
+ * error (like a bad mount option).
+ *
+ * If there is a remote error, like ESTALE or RPC_PROGNOTREGISTERED
+ * then it is probably permanent, but there is a small chance
+ * the it is temporary can we caught the server at an awkward
+ * time during start-up. So require that we see three of those
+ * before treating them as permanent.
+ * For ECONNREFUSED, wait a bit longer as there is often a longer
+ * gap between the network being ready and the NFS server starting.
+ *
+ * Returns 1 if we should fail immediately, or 0 if we
+ * should retry.
+ */
+static int nfs_is_permanent_error(int error)
+{
+ static int prev_error;
+ static int rpt_cnt;
+
+ if (error == prev_error)
+ rpt_cnt += 1;
+ else
+ rpt_cnt = 1;
+ prev_error = error;
+
+ switch (error) {
+ case ESTALE:
+ case EOPNOTSUPP: /* aka RPC_PROGNOTREGISTERED */
+ /* If two in a row, assume permanent */
+ return rpt_cnt >= 3;
+ case ECONNREFUSED:
+ /* Like the above, this can be temporary during a
+ * small window. However it is typically a larger
+ * window than for the others, and we have historically
+ * treated this as a temporary (i.e. long timeout)
+ * error with no complaints, so continue to treat
+ * it as temporary.
+ */
+ return 0; /* temporary */
+ case ETIMEDOUT:
+ case EHOSTUNREACH:
+ case EAGAIN:
+ return 0; /* temporary */
+ default:
+ return 1; /* permanent */
+ }
+}
+
+/*
+ * Handle "foreground" NFS mounts.
+ *
+ * Retry the mount request for as long as the 'retry=' option says.
+ *
+ * Returns a valid mount command exit code.
+ */
+static int nfsmount_fg(struct nfsmount_info *mi)
+{
+ unsigned int secs = 1;
+ time_t timeout;
+
+ timeout = nfs_parse_retry_option(mi->options,
+ NFS_DEF_FG_TIMEOUT_MINUTES);
+ if (verbose)
+ printf(_("%s: timeout set for %s"),
+ progname, ctime(&timeout));
+
+ for (;;) {
+ if (nfs_try_mount(mi))
+ return EX_SUCCESS;
+
+ if (errno == EBUSY && is_mountpoint(mi->node)) {
+ /*
+ * EBUSY can happen when mounting a filesystem that
+ * is already mounted or when the context= are
+ * different when using the -o sharecache
+ *
+ * Only error out in the latter case.
+ */
+ return EX_SUCCESS;
+ }
+
+ if (nfs_is_permanent_error(errno))
+ break;
+
+ if (time(NULL) > timeout)
+ break;
+
+ if (errno != ETIMEDOUT) {
+ if (sleep(secs))
+ break;
+ secs <<= 1;
+ if (secs > 10)
+ secs = 10;
+ }
+ }
+
+ mount_error(mi->spec, mi->node, errno);
+ return EX_FAIL;
+}
+
+/*
+ * Handle "background" NFS mount [first try]
+ *
+ * Returns a valid mount command exit code.
+ *
+ * EX_BG should cause the caller to fork and invoke nfsmount_child.
+ */
+static int nfsmount_parent(struct nfsmount_info *mi)
+{
+ if (nfs_try_mount(mi))
+ return EX_SUCCESS;
+
+ if (nfs_is_permanent_error(errno)) {
+ mount_error(mi->spec, mi->node, errno);
+ return EX_FAIL;
+ }
+
+ sys_mount_errors(mi->hostname, errno, 1, 1);
+ return EX_BG;
+}
+
+/*
+ * Handle "background" NFS mount [retry daemon]
+ *
+ * Returns a valid mount command exit code: EX_SUCCESS if successful,
+ * EX_FAIL if a failure occurred. There's nothing to catch the
+ * error return, though, so we use sys_mount_errors to log the
+ * failure.
+ */
+static int nfsmount_child(struct nfsmount_info *mi)
+{
+ unsigned int secs = 1;
+ time_t timeout;
+
+ timeout = nfs_parse_retry_option(mi->options,
+ NFS_DEF_BG_TIMEOUT_MINUTES);
+
+ for (;;) {
+ if (sleep(secs))
+ break;
+ secs <<= 1;
+ if (secs > 120)
+ secs = 120;
+
+ if (nfs_try_mount(mi))
+ return EX_SUCCESS;
+
+ if (nfs_is_permanent_error(errno))
+ break;
+
+ if (time(NULL) > timeout)
+ break;
+
+ sys_mount_errors(mi->hostname, errno, 1, 1);
+ };
+
+ sys_mount_errors(mi->hostname, errno, 1, 0);
+ return EX_FAIL;
+}
+
+/*
+ * Handle "background" NFS mount
+ *
+ * Returns a valid mount command exit code.
+ */
+static int nfsmount_bg(struct nfsmount_info *mi)
+{
+ if (!mi->child)
+ return nfsmount_parent(mi);
+ else
+ return nfsmount_child(mi);
+}
+
+/*
+ * Usually all that is needed for an NFS remount is to change
+ * generic mount options like "sync" or "ro". These generic
+ * options are controlled by mi->flags, not by text-based
+ * options, and no contact with the server is needed.
+ *
+ * Take care with the /etc/mtab entry for this mount; just
+ * calling update_mtab() will change an "-t nfs -o vers=4"
+ * mount to an "-t nfs -o remount" mount, and that will
+ * confuse umount.nfs.
+ *
+ * Returns a valid mount command exit code.
+ */
+static int nfs_remount(struct nfsmount_info *mi)
+{
+ if (nfs_sys_mount(mi, mi->options))
+ return EX_SUCCESS;
+ mount_error(mi->spec, mi->node, errno);
+ return EX_FAIL;
+}
+
+/*
+ * Process mount options and try a mount system call.
+ *
+ * Returns a valid mount command exit code.
+ */
+static const char *nfs_background_opttbl[] = {
+ "bg",
+ "fg",
+ NULL,
+};
+
+static int nfsmount_start(struct nfsmount_info *mi)
+{
+ if (!nfs_validate_options(mi))
+ return EX_FAIL;
+
+ /*
+ * NFS v2 has been deprecated
+ */
+ if (mi->version.major == 2) {
+ mount_error(mi->spec, mi->node, EOPNOTSUPP);
+ return EX_FAIL;
+ }
+
+ /*
+ * Avoid retry and negotiation logic when remounting
+ */
+ if (mi->flags & MS_REMOUNT)
+ return nfs_remount(mi);
+
+ if (po_rightmost(mi->options, nfs_background_opttbl) == 0)
+ return nfsmount_bg(mi);
+ else
+ return nfsmount_fg(mi);
+}
+
+/**
+ * nfsmount_string - Mount an NFS file system using C string options
+ * @spec: C string specifying remote share to mount ("hostname:path")
+ * @node: C string pathname of local mounted-on directory
+ * @type: C string that represents file system type ("nfs" or "nfs4")
+ * @flags: MS_ style mount flags
+ * @extra_opts: pointer to C string containing fs-specific mount options
+ * (input and output argument)
+ * @fake: flag indicating whether to carry out the whole operation
+ * @child: one if this is a mount daemon (bg)
+ *
+ * Returns a valid mount command exit code.
+ */
+int nfsmount_string(const char *spec, const char *node, char *type,
+ int flags, char **extra_opts, int fake, int child)
+{
+ struct nfsmount_info mi = {
+ .spec = spec,
+ .node = node,
+ .address = NULL,
+ .type = type,
+ .extra_opts = extra_opts,
+ .flags = flags,
+ .fake = fake,
+ .child = child,
+ };
+ int retval = EX_FAIL;
+
+ mi.options = po_split(*extra_opts);
+ if (mi.options) {
+ retval = nfsmount_start(&mi);
+ po_destroy(mi.options);
+ } else
+ nfs_error(_("%s: internal option parsing error"), progname);
+
+ nfs_freeaddrinfo(mi.address);
+ free(mi.hostname);
+ return retval;
+}
diff --git a/utils/mount/stropts.h b/utils/mount/stropts.h
new file mode 100644
index 0000000..6acd2ac
--- /dev/null
+++ b/utils/mount/stropts.h
@@ -0,0 +1,30 @@
+/*
+ * stropts.h -- Provide common network functions for NFS mount/umount
+ *
+ * Copyright (C) 2007 Oracle. All rights reserved.
+ * Copyright (C) 2007 Chuck Lever <chuck.lever@oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ *
+ */
+
+#ifndef _NFS_UTILS_MOUNT_STROPTS_H
+#define _NFS_UTILS_MOUNT_STROPTS_H
+
+int nfsmount_string(const char *, const char *, char *, int,
+ char **, int, int);
+
+#endif /* _NFS_UTILS_MOUNT_STROPTS_H */
diff --git a/utils/mount/token.c b/utils/mount/token.c
new file mode 100644
index 0000000..d7e2f4a
--- /dev/null
+++ b/utils/mount/token.c
@@ -0,0 +1,157 @@
+/*
+ * token.c -- tokenize strings, a la strtok(3)
+ *
+ * Copyright (C) 2007 Oracle. All rights reserved.
+ * Copyright (C) 2007 Chuck Lever <chuck.lever@oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ *
+ */
+
+/*
+ * We've constructed a simple string tokenizer that is better than
+ * strtok(3) in several ways:
+ *
+ * 1. It doesn't interfere with ongoing tokenizations using strtok(3).
+ * 2. It's re-entrant so we can nest tokenizations, if needed.
+ * 3. It can handle double-quoted delimiters (needed for 'context="sd,fslj"').
+ * 4. It doesn't alter the string we're tokenizing, so it can work
+ * on write-protected strings as well as writable strings.
+ */
+
+
+#include <ctype.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "token.h"
+
+
+struct tokenizer_state {
+ char *pos;
+ char delimiter;
+ int error;
+};
+
+static void find_next_nondelimiter(struct tokenizer_state *tstate)
+{
+ while (*tstate->pos != '\0' && *tstate->pos == tstate->delimiter)
+ tstate->pos++;
+}
+
+static size_t find_next_delimiter(struct tokenizer_state *tstate)
+{
+ size_t len = 0;
+ int quote_seen = 0;
+
+ while (*tstate->pos != '\0') {
+ if (*tstate->pos == '"')
+ quote_seen ^= 1;
+
+ if (!quote_seen && *tstate->pos == tstate->delimiter)
+ break;
+
+ len++;
+ tstate->pos++;
+ }
+
+ /* did the string terminate before the close quote? */
+ if (quote_seen) {
+ tstate->error = EINVAL;
+ return 0;
+ }
+
+ return len;
+}
+
+/**
+ * next_token - find the next token in a string and return it
+ * @tstate: pointer to tokenizer context object
+ *
+ * Returns the next token found in the current string.
+ * Returns NULL if there are no more tokens in the string,
+ * or if an error occurs.
+ *
+ * Side effect: tstate is updated
+ */
+char *next_token(struct tokenizer_state *tstate)
+{
+ char *token;
+ size_t len;
+
+ if (!tstate || !tstate->pos || tstate->error)
+ return NULL;
+
+ find_next_nondelimiter(tstate);
+ if (*tstate->pos == '\0')
+ goto fail;
+ token = tstate->pos;
+
+ len = find_next_delimiter(tstate);
+ if (len) {
+ token = strndup(token, len);
+ if (token)
+ return token;
+ tstate->error = ENOMEM;
+ }
+
+fail:
+ tstate->pos = NULL;
+ return NULL; /* no tokens found in this string */
+}
+
+/**
+ * init_tokenizer - return an initialized tokenizer context object
+ * @string: pointer to C string
+ * @delimiter: single character that delimits tokens in @string
+ *
+ * Returns an initialized tokenizer context object
+ */
+struct tokenizer_state *init_tokenizer(char *string, char delimiter)
+{
+ struct tokenizer_state *tstate;
+
+ tstate = malloc(sizeof(*tstate));
+ if (tstate) {
+ tstate->pos = string;
+ tstate->delimiter = delimiter;
+ tstate->error = 0;
+ }
+ return tstate;
+}
+
+/**
+ * tokenizer_error - digs error value out of tokenizer context
+ * @tstate: pointer to tokenizer context object
+ *
+ */
+int tokenizer_error(struct tokenizer_state *tstate)
+{
+ return tstate ? tstate->error : 0;
+}
+
+/**
+ * end_tokenizer - free a tokenizer context object
+ * @tstate: pointer to tokenizer context object
+ *
+ */
+void end_tokenizer(struct tokenizer_state *tstate)
+{
+ free(tstate);
+}
diff --git a/utils/mount/token.h b/utils/mount/token.h
new file mode 100644
index 0000000..17a9c15
--- /dev/null
+++ b/utils/mount/token.h
@@ -0,0 +1,34 @@
+/*
+ * token.h -- tokenize strings, a la strtok(3)
+ *
+ * Copyright (C) 2007 Oracle. All rights reserved.
+ * Copyright (C) 2007 Chuck Lever <chuck.lever@oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ *
+ */
+
+#ifndef _NFS_UTILS_MOUNT_TOKEN_H
+#define _NFS_UTILS_MOUNT_TOKEN_H
+
+struct tokenizer_state;
+
+char *next_token(struct tokenizer_state *);
+struct tokenizer_state *init_tokenizer(char *, char);
+int tokenizer_error(struct tokenizer_state *);
+void end_tokenizer(struct tokenizer_state *);
+
+#endif /* _NFS_UTILS_MOUNT_TOKEN_H */
diff --git a/utils/mount/umount.nfs.man b/utils/mount/umount.nfs.man
new file mode 100644
index 0000000..15addfa
--- /dev/null
+++ b/utils/mount/umount.nfs.man
@@ -0,0 +1,70 @@
+.\"@(#)umount.nfs.8"
+.TH UMOUNT.NFS 8 "6 Jun 2006"
+.SH NAME
+umount.nfs, umount.nfs4 \- unmount a Network File System
+.SH SYNOPSIS
+.BI "umount.nfs" " dir" " [\-fvnrlh ]"
+.SH DESCRIPTION
+.BR umount.nfs
+and
+.BR umount.nfs4
+are a part of
+.BR nfs (5)
+utilities package, which provides NFS client functionality.
+
+.BR umount.nfs4
+and
+.BR umount.nfs
+are meant to be used by the
+.BR umount (8)
+command for unmounting NFS shares. This subcommand, however, can also be used as a standalone command with limited functionality.
+
+.I dir
+is the directory on which the file system is mounted.
+
+.SH OPTIONS
+.TP
+.BI "\-f"
+Force unmount the file system in case of unreachable NFS system.
+.TP
+.BI "\-v"
+Be verbose.
+.TP
+.BI "\-n"
+Do not update
+.I /etc/mtab.
+By default, an entry is created in
+.I /etc/mtab
+for every mounted file system. Use this option to skip deleting an entry.
+.TP
+.BI "\-r"
+In case unmounting fails, try to mount read-only.
+.TP
+.BI "\-l"
+Lazy unmount. Detach the file system from the file system hierarchy now, and cleanup all references to the file system as soon as it is not busy anymore.
+.TP
+.BI "\-h"
+Print help message.
+
+.SH NOTE
+For further information please refer
+.BR nfs (5)
+and
+.BR umount (8)
+manual pages.
+
+.SH FILES
+.TP 18n
+.I /etc/fstab
+file system table
+.TP
+.I /etc/mtab
+table of mounted file systems
+
+.PD
+.SH "SEE ALSO"
+.BR nfs (5),
+.BR umount (8),
+
+.SH "AUTHOR"
+Amit Gud <agud@redhat.com>
diff --git a/utils/mount/utils.c b/utils/mount/utils.c
new file mode 100644
index 0000000..865a4a0
--- /dev/null
+++ b/utils/mount/utils.c
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2010 Karel Zak <kzak@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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 <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "sockaddr.h"
+#include "nfs_mount.h"
+#include "nls.h"
+#include "xcommon.h"
+#include "version.h"
+#include "error.h"
+#include "utils.h"
+#include "mount.h"
+#include "network.h"
+#include "parse_dev.h"
+
+extern int verbose;
+extern char *progname;
+
+/*
+ * Choose the version of the nfs_mount_data structure that is appropriate
+ * for the kernel that is doing the mount.
+ *
+ * NFS_MOUNT_VERSION: maximum version supported by these sources
+ * nfs_mount_data_version: maximum version supported by the running kernel
+ */
+int discover_nfs_mount_data_version(int *string_ver)
+{
+ unsigned int kernel_version = linux_version_code();
+ int ver = 0;
+
+ *string_ver = 0;
+
+ if (kernel_version) {
+ if (kernel_version < MAKE_VERSION(2, 1, 32))
+ ver = 1;
+ else if (kernel_version < MAKE_VERSION(2, 2, 18))
+ ver = 3;
+ else if (kernel_version < MAKE_VERSION(2, 3, 0))
+ ver = 4;
+ else if (kernel_version < MAKE_VERSION(2, 3, 99))
+ ver = 3;
+ else if (kernel_version < MAKE_VERSION(2, 6, 3))
+ ver = 4;
+ else
+ ver = 6;
+ }
+ if (ver > NFS_MOUNT_VERSION)
+ ver = NFS_MOUNT_VERSION;
+ else
+ if (kernel_version > MAKE_VERSION(2, 6, 22))
+ (*string_ver)++;
+
+ return ver;
+}
+
+void print_one(char *spec, char *node, char *type, char *opts)
+{
+ if (!verbose)
+ return;
+
+ if (opts)
+ printf(_("%s on %s type %s (%s)\n"), spec, node, type, opts);
+ else
+ printf(_("%s on %s type %s\n"), spec, node, type);
+}
+
+void mount_usage(void)
+{
+ printf(_("usage: %s remotetarget dir [-rvVwfnsh] [-o nfsoptions]\n"),
+ progname);
+ printf(_("options:\n"));
+ printf(_("\t-r\t\tMount file system readonly\n"));
+ printf(_("\t-v\t\tVerbose\n"));
+ printf(_("\t-V\t\tPrint version\n"));
+ printf(_("\t-w\t\tMount file system read-write\n"));
+ printf(_("\t-f\t\tFake mount, do not actually mount\n"));
+ printf(_("\t-n\t\tDo not update /etc/mtab\n"));
+ printf(_("\t-s\t\tTolerate sloppy mount options rather than fail\n"));
+ printf(_("\t-h\t\tPrint this help\n"));
+ printf(_("\tnfsoptions\tRefer to mount.nfs(8) or nfs(5)\n\n"));
+}
+
+void umount_usage(void)
+{
+ printf(_("usage: %s dir [-fvnrlh]\n"), progname);
+ printf(_("options:\n\t-f\tforce unmount\n"));
+ printf(_("\t-v\tverbose\n"));
+ printf(_("\t-n\tDo not update /etc/mtab\n"));
+ printf(_("\t-r\tremount\n"));
+ printf(_("\t-l\tlazy unmount\n"));
+ printf(_("\t-h\tprint this help\n\n"));
+}
+
+int chk_mountpoint(const char *mount_point)
+{
+ struct stat sb;
+
+ if (stat(mount_point, &sb) < 0){
+ mount_error(NULL, mount_point, errno);
+ return 1;
+ }
+ if (S_ISDIR(sb.st_mode) == 0){
+ mount_error(NULL, mount_point, ENOTDIR);
+ return 1;
+ }
+ if (getuid() != 0 && geteuid() != 0 && access(mount_point, X_OK) < 0) {
+ mount_error(NULL, mount_point, errno);
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * Pick up certain mount options used during the original mount
+ * from /etc/mtab. The basics include the server's IP address and
+ * the server pathname of the share to unregister.
+ *
+ * These options might also describe the mount port, mount protocol
+ * version, and transport protocol used to punch through a firewall.
+ * We will need this information to get through the firewall again
+ * to do the umount.
+ *
+ * Note that option parsing failures won't necessarily cause the
+ * umount request to fail. Those values will be left zero in the
+ * pmap tuple. If the GETPORT call later fails to disambiguate them,
+ * then we fail.
+ */
+int nfs_umount23(const char *devname, char *string)
+{
+ char *hostname = NULL, *dirname = NULL;
+ struct mount_options *options;
+ int result = EX_FAIL;
+
+ if (!nfs_parse_devname(devname, &hostname, &dirname))
+ return EX_USAGE;
+
+ options = po_split(string);
+ if (options) {
+ result = nfs_umount_do_umnt(options, &hostname, &dirname);
+ po_destroy(options);
+ } else
+ nfs_error(_("%s: option parsing error"), progname);
+
+ free(hostname);
+ free(dirname);
+ return result;
+}
diff --git a/utils/mount/utils.h b/utils/mount/utils.h
new file mode 100644
index 0000000..224918a
--- /dev/null
+++ b/utils/mount/utils.h
@@ -0,0 +1,36 @@
+/*
+ * utils.h -- misc utils for mount and umount
+ *
+ * Copyright (C) 2010 Karel Zak <kzak@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ *
+ */
+
+#ifndef _NFS_UTILS_MOUNT_UTILS_H
+#define _NFS_UTILS_MOUNT_UTILS_H
+
+#include "parse_opt.h"
+
+int discover_nfs_mount_data_version(int *string_ver);
+void print_one(char *spec, char *node, char *type, char *opts);
+void mount_usage(void);
+void umount_usage(void);
+int chk_mountpoint(const char *mount_point);
+
+int nfs_umount23(const char *devname, char *string);
+
+#endif /* !_NFS_UTILS_MOUNT_UTILS_H */
diff --git a/utils/mount/version.h b/utils/mount/version.h
new file mode 100644
index 0000000..d7cf680
--- /dev/null
+++ b/utils/mount/version.h
@@ -0,0 +1,53 @@
+/*
+ * version.h -- get running kernel version
+ *
+ * Copyright (C) 2008 Oracle. 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
+ *
+ */
+
+#ifndef _NFS_UTILS_MOUNT_VERSION_H
+#define _NFS_UTILS_MOUNT_VERSION_H
+
+#include <stdio.h>
+#include <limits.h>
+
+#include <sys/utsname.h>
+
+static inline unsigned int MAKE_VERSION(unsigned int p, unsigned int q,
+ unsigned int r)
+{
+ return (65536 * p) + (256 * q) + r;
+}
+
+static inline unsigned int linux_version_code(void)
+{
+ struct utsname my_utsname;
+ unsigned int p, q = 0, r = 0;
+
+ /* UINT_MAX as backward compatibility code should not be run */
+ if (uname(&my_utsname))
+ return UINT_MAX;
+
+ /* UINT_MAX as future versions might not start with an integer */
+ if (sscanf(my_utsname.release, "%u.%u.%u", &p, &q, &r) < 1)
+ return UINT_MAX;
+
+ return MAKE_VERSION(p, q, r);
+}
+
+#endif /* _NFS_UTILS_MOUNT_VERSION_H */
diff --git a/utils/mountd/Makefile.am b/utils/mountd/Makefile.am
new file mode 100644
index 0000000..197ef29
--- /dev/null
+++ b/utils/mountd/Makefile.am
@@ -0,0 +1,70 @@
+## Process this file with automake to produce Makefile.in
+
+OPTLIBS =
+if CONFIG_JUNCTION
+OPTLIBS += ../../support/junction/libjunction.la $(LIBXML2)
+endif
+
+man8_MANS = mountd.man
+EXTRA_DIST = $(man8_MANS)
+
+RPCPREFIX = rpc.
+KPREFIX = @kprefix@
+sbin_PROGRAMS = mountd
+
+mountd_SOURCES = mountd.c mount_dispatch.c rmtab.c \
+ svc_run.c mountd.h
+mountd_LDADD = ../../support/export/libexport.a \
+ ../../support/nfs/libnfs.la \
+ ../../support/misc/libmisc.a \
+ ../../support/reexport/libreexport.a \
+ $(OPTLIBS) \
+ $(LIBBSD) $(LIBWRAP) $(LIBNSL) $(LIBBLKID) -luuid $(LIBTIRPC) \
+ $(LIBPTHREAD)
+
+mountd_CPPFLAGS = $(AM_CPPFLAGS) $(CPPFLAGS) \
+ -I$(top_builddir)/support/include \
+ -I$(top_srcdir)/support/export
+
+MAINTAINERCLEANFILES = Makefile.in
+
+#######################################################################
+# The following allows the current practice of having
+# daemons renamed during the install to include RPCPREFIX
+# and the KPREFIX
+# This could all be done much easier with program_transform_name
+# ( program_transform_name = s/^/$(RPCPREFIX)$(KPREFIX)/ )
+# but that also renames the man pages, which the current
+# practice does not do.
+install-exec-hook:
+ (cd $(DESTDIR)$(sbindir) && \
+ for p in $(sbin_PROGRAMS); do \
+ mv -f $$p$(EXEEXT) $(RPCPREFIX)$(KPREFIX)$$p$(EXEEXT) ;\
+ done)
+uninstall-hook:
+ (cd $(DESTDIR)$(sbindir) && \
+ for p in $(sbin_PROGRAMS); do \
+ rm -f $(RPCPREFIX)$(KPREFIX)$$p$(EXEEXT) ;\
+ done)
+
+
+# XXX This makes some assumptions about what automake does.
+# XXX But there is no install-man-hook or install-man-local.
+install-man: install-man8 install-man-links
+uninstall-man: uninstall-man8 uninstall-man-links
+
+install-man-links:
+ (cd $(DESTDIR)$(man8dir) && \
+ for m in $(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS); do \
+ inst=`echo $$m | sed -e 's/man$$/8/'`; \
+ rm -f $(RPCPREFIX)$$inst ; \
+ $(LN_S) $$inst $(RPCPREFIX)$$inst ; \
+ done)
+
+uninstall-man-links:
+ (cd $(DESTDIR)$(man8dir) && \
+ for m in $(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS); do \
+ inst=`echo $$m | sed -e 's/man$$/8/'`; \
+ rm -f $(RPCPREFIX)$$inst ; \
+ done)
+
diff --git a/utils/mountd/Makefile.in b/utils/mountd/Makefile.in
new file mode 100644
index 0000000..7754575
--- /dev/null
+++ b/utils/mountd/Makefile.in
@@ -0,0 +1,941 @@
+# 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@
+@CONFIG_JUNCTION_TRUE@am__append_1 = ../../support/junction/libjunction.la $(LIBXML2)
+sbin_PROGRAMS = mountd$(EXEEXT)
+subdir = utils/mountd
+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 =
+am__installdirs = "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man8dir)"
+PROGRAMS = $(sbin_PROGRAMS)
+am_mountd_OBJECTS = mountd-mountd.$(OBJEXT) \
+ mountd-mount_dispatch.$(OBJEXT) mountd-rmtab.$(OBJEXT) \
+ mountd-svc_run.$(OBJEXT)
+mountd_OBJECTS = $(am_mountd_OBJECTS)
+am__DEPENDENCIES_1 =
+@CONFIG_JUNCTION_TRUE@am__DEPENDENCIES_2 = \
+@CONFIG_JUNCTION_TRUE@ ../../support/junction/libjunction.la \
+@CONFIG_JUNCTION_TRUE@ $(am__DEPENDENCIES_1)
+am__DEPENDENCIES_3 = $(am__DEPENDENCIES_2)
+mountd_DEPENDENCIES = ../../support/export/libexport.a \
+ ../../support/nfs/libnfs.la ../../support/misc/libmisc.a \
+ ../../support/reexport/libreexport.a $(am__DEPENDENCIES_3) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+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 =
+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)/mountd-mount_dispatch.Po \
+ ./$(DEPDIR)/mountd-mountd.Po ./$(DEPDIR)/mountd-rmtab.Po \
+ ./$(DEPDIR)/mountd-svc_run.Po
+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 = $(mountd_SOURCES)
+DIST_SOURCES = $(mountd_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+man8dir = $(mandir)/man8
+NROFF = nroff
+MANS = $(man8_MANS)
+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@
+OPTLIBS = $(am__append_1)
+man8_MANS = mountd.man
+EXTRA_DIST = $(man8_MANS)
+RPCPREFIX = rpc.
+KPREFIX = @kprefix@
+mountd_SOURCES = mountd.c mount_dispatch.c rmtab.c \
+ svc_run.c mountd.h
+
+mountd_LDADD = ../../support/export/libexport.a \
+ ../../support/nfs/libnfs.la \
+ ../../support/misc/libmisc.a \
+ ../../support/reexport/libreexport.a \
+ $(OPTLIBS) \
+ $(LIBBSD) $(LIBWRAP) $(LIBNSL) $(LIBBLKID) -luuid $(LIBTIRPC) \
+ $(LIBPTHREAD)
+
+mountd_CPPFLAGS = $(AM_CPPFLAGS) $(CPPFLAGS) \
+ -I$(top_builddir)/support/include \
+ -I$(top_srcdir)/support/export
+
+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 utils/mountd/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu utils/mountd/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):
+install-sbinPROGRAMS: $(sbin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(sbindir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-sbinPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(sbindir)" && rm -f $$files
+
+clean-sbinPROGRAMS:
+ @list='$(sbin_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+mountd$(EXEEXT): $(mountd_OBJECTS) $(mountd_DEPENDENCIES) $(EXTRA_mountd_DEPENDENCIES)
+ @rm -f mountd$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(mountd_OBJECTS) $(mountd_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mountd-mount_dispatch.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mountd-mountd.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mountd-rmtab.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mountd-svc_run.Po@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 $@ $<
+
+mountd-mountd.o: mountd.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mountd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mountd-mountd.o -MD -MP -MF $(DEPDIR)/mountd-mountd.Tpo -c -o mountd-mountd.o `test -f 'mountd.c' || echo '$(srcdir)/'`mountd.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mountd-mountd.Tpo $(DEPDIR)/mountd-mountd.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mountd.c' object='mountd-mountd.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mountd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mountd-mountd.o `test -f 'mountd.c' || echo '$(srcdir)/'`mountd.c
+
+mountd-mountd.obj: mountd.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mountd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mountd-mountd.obj -MD -MP -MF $(DEPDIR)/mountd-mountd.Tpo -c -o mountd-mountd.obj `if test -f 'mountd.c'; then $(CYGPATH_W) 'mountd.c'; else $(CYGPATH_W) '$(srcdir)/mountd.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mountd-mountd.Tpo $(DEPDIR)/mountd-mountd.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mountd.c' object='mountd-mountd.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mountd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mountd-mountd.obj `if test -f 'mountd.c'; then $(CYGPATH_W) 'mountd.c'; else $(CYGPATH_W) '$(srcdir)/mountd.c'; fi`
+
+mountd-mount_dispatch.o: mount_dispatch.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mountd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mountd-mount_dispatch.o -MD -MP -MF $(DEPDIR)/mountd-mount_dispatch.Tpo -c -o mountd-mount_dispatch.o `test -f 'mount_dispatch.c' || echo '$(srcdir)/'`mount_dispatch.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mountd-mount_dispatch.Tpo $(DEPDIR)/mountd-mount_dispatch.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mount_dispatch.c' object='mountd-mount_dispatch.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mountd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mountd-mount_dispatch.o `test -f 'mount_dispatch.c' || echo '$(srcdir)/'`mount_dispatch.c
+
+mountd-mount_dispatch.obj: mount_dispatch.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mountd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mountd-mount_dispatch.obj -MD -MP -MF $(DEPDIR)/mountd-mount_dispatch.Tpo -c -o mountd-mount_dispatch.obj `if test -f 'mount_dispatch.c'; then $(CYGPATH_W) 'mount_dispatch.c'; else $(CYGPATH_W) '$(srcdir)/mount_dispatch.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mountd-mount_dispatch.Tpo $(DEPDIR)/mountd-mount_dispatch.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mount_dispatch.c' object='mountd-mount_dispatch.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mountd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mountd-mount_dispatch.obj `if test -f 'mount_dispatch.c'; then $(CYGPATH_W) 'mount_dispatch.c'; else $(CYGPATH_W) '$(srcdir)/mount_dispatch.c'; fi`
+
+mountd-rmtab.o: rmtab.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mountd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mountd-rmtab.o -MD -MP -MF $(DEPDIR)/mountd-rmtab.Tpo -c -o mountd-rmtab.o `test -f 'rmtab.c' || echo '$(srcdir)/'`rmtab.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mountd-rmtab.Tpo $(DEPDIR)/mountd-rmtab.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rmtab.c' object='mountd-rmtab.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mountd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mountd-rmtab.o `test -f 'rmtab.c' || echo '$(srcdir)/'`rmtab.c
+
+mountd-rmtab.obj: rmtab.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mountd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mountd-rmtab.obj -MD -MP -MF $(DEPDIR)/mountd-rmtab.Tpo -c -o mountd-rmtab.obj `if test -f 'rmtab.c'; then $(CYGPATH_W) 'rmtab.c'; else $(CYGPATH_W) '$(srcdir)/rmtab.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mountd-rmtab.Tpo $(DEPDIR)/mountd-rmtab.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rmtab.c' object='mountd-rmtab.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mountd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mountd-rmtab.obj `if test -f 'rmtab.c'; then $(CYGPATH_W) 'rmtab.c'; else $(CYGPATH_W) '$(srcdir)/rmtab.c'; fi`
+
+mountd-svc_run.o: svc_run.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mountd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mountd-svc_run.o -MD -MP -MF $(DEPDIR)/mountd-svc_run.Tpo -c -o mountd-svc_run.o `test -f 'svc_run.c' || echo '$(srcdir)/'`svc_run.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mountd-svc_run.Tpo $(DEPDIR)/mountd-svc_run.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='svc_run.c' object='mountd-svc_run.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mountd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mountd-svc_run.o `test -f 'svc_run.c' || echo '$(srcdir)/'`svc_run.c
+
+mountd-svc_run.obj: svc_run.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mountd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mountd-svc_run.obj -MD -MP -MF $(DEPDIR)/mountd-svc_run.Tpo -c -o mountd-svc_run.obj `if test -f 'svc_run.c'; then $(CYGPATH_W) 'svc_run.c'; else $(CYGPATH_W) '$(srcdir)/svc_run.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mountd-svc_run.Tpo $(DEPDIR)/mountd-svc_run.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='svc_run.c' object='mountd-svc_run.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mountd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mountd-svc_run.obj `if test -f 'svc_run.c'; then $(CYGPATH_W) 'svc_run.c'; else $(CYGPATH_W) '$(srcdir)/svc_run.c'; fi`
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-man8: $(man8_MANS)
+ @$(NORMAL_INSTALL)
+ @list1='$(man8_MANS)'; \
+ list2=''; \
+ test -n "$(man8dir)" \
+ && test -n "`echo $$list1$$list2`" \
+ || exit 0; \
+ echo " $(MKDIR_P) '$(DESTDIR)$(man8dir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(man8dir)" || exit 1; \
+ { for i in $$list1; do echo "$$i"; done; \
+ if test -n "$$list2"; then \
+ for i in $$list2; do echo "$$i"; done \
+ | sed -n '/\.8[a-z]*$$/p'; \
+ fi; \
+ } | while read p; do \
+ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; echo "$$p"; \
+ done | \
+ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \
+ sed 'N;N;s,\n, ,g' | { \
+ list=; while read file base inst; do \
+ if test "$$base" = "$$inst"; then list="$$list $$file"; else \
+ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man8dir)/$$inst'"; \
+ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man8dir)/$$inst" || exit $$?; \
+ fi; \
+ done; \
+ for i in $$list; do echo "$$i"; done | $(am__base_list) | \
+ while read files; do \
+ test -z "$$files" || { \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man8dir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(man8dir)" || exit $$?; }; \
+ done; }
+
+uninstall-man8:
+ @$(NORMAL_UNINSTALL)
+ @list='$(man8_MANS)'; test -n "$(man8dir)" || exit 0; \
+ files=`{ for i in $$list; do echo "$$i"; done; \
+ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
+ dir='$(DESTDIR)$(man8dir)'; $(am__uninstall_files_from_dir)
+
+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 $(PROGRAMS) $(MANS)
+installdirs:
+ for dir in "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man8dir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+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-sbinPROGRAMS \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/mountd-mount_dispatch.Po
+ -rm -f ./$(DEPDIR)/mountd-mountd.Po
+ -rm -f ./$(DEPDIR)/mountd-rmtab.Po
+ -rm -f ./$(DEPDIR)/mountd-svc_run.Po
+ -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-man
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-sbinPROGRAMS
+ @$(NORMAL_INSTALL)
+ $(MAKE) $(AM_MAKEFLAGS) install-exec-hook
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+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)/mountd-mount_dispatch.Po
+ -rm -f ./$(DEPDIR)/mountd-mountd.Po
+ -rm -f ./$(DEPDIR)/mountd-rmtab.Po
+ -rm -f ./$(DEPDIR)/mountd-svc_run.Po
+ -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: uninstall-man uninstall-sbinPROGRAMS
+ @$(NORMAL_INSTALL)
+ $(MAKE) $(AM_MAKEFLAGS) uninstall-hook
+.MAKE: install-am install-exec-am install-strip uninstall-am
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-sbinPROGRAMS 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-exec-hook install-html install-html-am \
+ install-info install-info-am install-man install-man8 \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-sbinPROGRAMS 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 uninstall-hook \
+ uninstall-man uninstall-man8 uninstall-sbinPROGRAMS
+
+.PRECIOUS: Makefile
+
+
+#######################################################################
+# The following allows the current practice of having
+# daemons renamed during the install to include RPCPREFIX
+# and the KPREFIX
+# This could all be done much easier with program_transform_name
+# ( program_transform_name = s/^/$(RPCPREFIX)$(KPREFIX)/ )
+# but that also renames the man pages, which the current
+# practice does not do.
+install-exec-hook:
+ (cd $(DESTDIR)$(sbindir) && \
+ for p in $(sbin_PROGRAMS); do \
+ mv -f $$p$(EXEEXT) $(RPCPREFIX)$(KPREFIX)$$p$(EXEEXT) ;\
+ done)
+uninstall-hook:
+ (cd $(DESTDIR)$(sbindir) && \
+ for p in $(sbin_PROGRAMS); do \
+ rm -f $(RPCPREFIX)$(KPREFIX)$$p$(EXEEXT) ;\
+ done)
+
+# XXX This makes some assumptions about what automake does.
+# XXX But there is no install-man-hook or install-man-local.
+install-man: install-man8 install-man-links
+uninstall-man: uninstall-man8 uninstall-man-links
+
+install-man-links:
+ (cd $(DESTDIR)$(man8dir) && \
+ for m in $(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS); do \
+ inst=`echo $$m | sed -e 's/man$$/8/'`; \
+ rm -f $(RPCPREFIX)$$inst ; \
+ $(LN_S) $$inst $(RPCPREFIX)$$inst ; \
+ done)
+
+uninstall-man-links:
+ (cd $(DESTDIR)$(man8dir) && \
+ for m in $(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS); do \
+ inst=`echo $$m | sed -e 's/man$$/8/'`; \
+ rm -f $(RPCPREFIX)$$inst ; \
+ done)
+
+# 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/utils/mountd/mount_dispatch.c b/utils/mountd/mount_dispatch.c
new file mode 100644
index 0000000..ba6981d
--- /dev/null
+++ b/utils/mountd/mount_dispatch.c
@@ -0,0 +1,84 @@
+/*
+ * mount_dispatch This file contains the function dispatch table.
+ *
+ * Copyright (C) 1995 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_TCP_WRAPPER
+#include "tcpwrapper.h"
+#endif
+
+#include "mountd.h"
+#include "rpcmisc.h"
+
+/*
+ * Procedures for MNTv1
+ */
+static struct rpc_dentry mnt_1_dtable[] = {
+ dtable_ent(mount_null,1,void,void), /* NULL */
+ dtable_ent(mount_mnt,1,dirpath,fhstatus), /* MNT */
+ dtable_ent(mount_dump,1,void,mountlist), /* DUMP */
+ dtable_ent(mount_umnt,1,dirpath,void), /* UMNT */
+ dtable_ent(mount_umntall,1,void,void), /* UMNTALL */
+ dtable_ent(mount_export,1,void,exports), /* EXPORT */
+ dtable_ent(mount_exportall,1,void,exports), /* EXPORTALL */
+};
+
+/*
+ * Procedures for MNTv2
+ */
+static struct rpc_dentry mnt_2_dtable[] = {
+ dtable_ent(mount_null,1,void,void), /* NULL */
+ dtable_ent(mount_mnt,1,dirpath,fhstatus), /* MNT */
+ dtable_ent(mount_dump,1,void,mountlist), /* DUMP */
+ dtable_ent(mount_umnt,1,dirpath,void), /* UMNT */
+ dtable_ent(mount_umntall,1,void,void), /* UMNTALL */
+ dtable_ent(mount_export,1,void,exports), /* EXPORT */
+ dtable_ent(mount_exportall,1,void,exports), /* EXPORTALL */
+ dtable_ent(mount_pathconf,2,dirpath,ppathcnf), /* PATHCONF */
+};
+
+/*
+ * Procedures for MNTv3
+ */
+static struct rpc_dentry mnt_3_dtable[] = {
+ dtable_ent(mount_null,1,void,void), /* NULL */
+ dtable_ent(mount_mnt,3,dirpath,mountres3), /* MNT */
+ dtable_ent(mount_dump,1,void,mountlist), /* DUMP */
+ dtable_ent(mount_umnt,1,dirpath,void), /* UMNT */
+ dtable_ent(mount_umntall,1,void,void), /* UMNTALL */
+ dtable_ent(mount_export,1,void,exports), /* EXPORT */
+};
+
+#define number_of(x) (sizeof(x)/sizeof(x[0]))
+
+static struct rpc_dtable dtable[] = {
+ { mnt_1_dtable, number_of(mnt_1_dtable) },
+ { mnt_2_dtable, number_of(mnt_2_dtable) },
+ { mnt_3_dtable, number_of(mnt_3_dtable) },
+};
+
+/*
+ * The main dispatch routine.
+ */
+void
+mount_dispatch(struct svc_req *rqstp, SVCXPRT *transp)
+{
+ union mountd_arguments argument;
+ union mountd_results result;
+
+#ifdef HAVE_TCP_WRAPPER
+ /* remote host authorization check */
+ if (!check_default("mountd", nfs_getrpccaller(transp), MOUNTPROG)) {
+ svcerr_auth (transp, AUTH_FAILED);
+ return;
+ }
+#endif
+
+ rpc_dispatch(rqstp, transp, dtable, number_of(dtable),
+ &argument, &result);
+}
diff --git a/utils/mountd/mountd.c b/utils/mountd/mountd.c
new file mode 100644
index 0000000..dbd5546
--- /dev/null
+++ b/utils/mountd/mountd.c
@@ -0,0 +1,887 @@
+/*
+ * utils/mountd/mountd.c
+ *
+ * Authenticate mount requests and retrieve file handle.
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <signal.h>
+#include <sys/stat.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/resource.h>
+
+#include "conffile.h"
+#include "xmalloc.h"
+#include "misc.h"
+#include "mountd.h"
+#include "rpcmisc.h"
+#include "pseudoflavors.h"
+#include "nfsd_path.h"
+#include "nfslib.h"
+#include "export.h"
+
+extern void my_svc_run(void);
+
+static void usage(const char *, int exitcode);
+static exports get_exportlist(void);
+static struct nfs_fh_len *get_rootfh(struct svc_req *, dirpath *, nfs_export **, mountstat3 *, int v3);
+
+int reverse_resolve = 0;
+int manage_gids;
+int use_ipaddr = -1;
+
+/* PRC: a high-availability callout program can be specified with -H
+ * When this is done, the program will receive callouts whenever clients
+ * send mount or unmount requests -- the callout is not needed for 2.6 kernel */
+char *ha_callout_prog = NULL;
+
+/* Number of mountd threads to start. Default is 1 and
+ * that's probably enough unless you need hundreds of
+ * clients to be able to mount at once. */
+static int num_threads = 1;
+/* Arbitrary limit on number of threads */
+#define MAX_THREADS 64
+
+static struct option longopts[] =
+{
+ { "foreground", 0, 0, 'F' },
+ { "descriptors", 1, 0, 'o' },
+ { "debug", 1, 0, 'd' },
+ { "help", 0, 0, 'h' },
+ { "nfs-version", 1, 0, 'V' },
+ { "no-nfs-version", 1, 0, 'N' },
+ { "version", 0, 0, 'v' },
+ { "port", 1, 0, 'p' },
+ { "no-tcp", 0, 0, 'n' },
+ { "ha-callout", 1, 0, 'H' },
+ { "state-directory-path", 1, 0, 's' },
+ { "num-threads", 1, 0, 't' },
+ { "reverse-lookup", 0, 0, 'r' },
+ { "manage-gids", 0, 0, 'g' },
+ { "no-udp", 0, 0, 'u' },
+ { "log-auth", 0, 0, 'l'},
+ { "cache-use-ipaddr", 0, 0, 'i'},
+ { "ttl", 1, 0, 'T'},
+ { NULL, 0, 0, 0 }
+};
+static char shortopts[] = "o:nFd:p:P:hH:N:V:vurs:t:gliT:";
+
+#define NFSVERSBIT(vers) (0x1 << (vers - 1))
+#define NFSVERSBIT_ALL (NFSVERSBIT(2) | NFSVERSBIT(3) | NFSVERSBIT(4))
+
+static int nfs_version = NFSVERSBIT_ALL;
+
+static int version2(void)
+{
+ return nfs_version & NFSVERSBIT(2);
+}
+
+static int version3(void)
+{
+ return nfs_version & NFSVERSBIT(3);
+}
+
+static int version23(void)
+{
+ return nfs_version & (NFSVERSBIT(2) | NFSVERSBIT(3));
+}
+
+static int version_any(void)
+{
+ return nfs_version & NFSVERSBIT_ALL;
+}
+
+static void
+unregister_services (void)
+{
+ nfs_svc_unregister(MOUNTPROG, MOUNTVERS);
+ nfs_svc_unregister(MOUNTPROG, MOUNTVERS_POSIX);
+ nfs_svc_unregister(MOUNTPROG, MOUNTVERS_NFSV3);
+}
+
+static void
+cleanup_lockfiles (void)
+{
+ unlink(etab.lockfn);
+ unlink(rmtab.lockfn);
+}
+
+/*
+ * Signal handler.
+ */
+static void
+killer (int sig)
+{
+ unregister_services();
+ if (num_threads > 1) {
+ /* play Kronos and eat our children */
+ kill(0, SIGTERM);
+ cache_wait_for_workers("mountd");
+ }
+ cleanup_lockfiles();
+ free_state_path_names(&etab);
+ free_state_path_names(&rmtab);
+ xlog (L_NOTICE, "Caught signal %d, un-registering and exiting.", sig);
+ exit(0);
+}
+
+static void
+sig_hup (int UNUSED(sig))
+{
+ /* don't exit on SIGHUP */
+ xlog (L_NOTICE, "Received SIGHUP... Ignoring.\n");
+ return;
+}
+
+bool_t
+mount_null_1_svc(struct svc_req *rqstp, void *UNUSED(argp),
+ void *UNUSED(resp))
+{
+ struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt);
+ char buf[INET6_ADDRSTRLEN];
+
+ xlog(D_CALL, "Received NULL request from %s",
+ host_ntop(sap, buf, sizeof(buf)));
+
+ return 1;
+}
+
+bool_t
+mount_mnt_1_svc(struct svc_req *rqstp, dirpath *path, fhstatus *res)
+{
+ struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt);
+ char buf[INET6_ADDRSTRLEN];
+ struct nfs_fh_len *fh;
+
+ xlog(D_CALL, "Received MNT1(%s) request from %s", *path,
+ host_ntop(sap, buf, sizeof(buf)));
+
+ fh = get_rootfh(rqstp, path, NULL, &res->fhs_status, 0);
+ if (fh)
+ memcpy(&res->fhstatus_u.fhs_fhandle, fh->fh_handle, 32);
+ return 1;
+}
+
+bool_t
+mount_dump_1_svc(struct svc_req *rqstp, void *UNUSED(argp), mountlist *res)
+{
+ struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt);
+ char buf[INET6_ADDRSTRLEN];
+
+ xlog(D_CALL, "Received DUMP request from %s",
+ host_ntop(sap, buf, sizeof(buf)));
+
+ *res = mountlist_list();
+
+ return 1;
+}
+
+bool_t
+mount_umnt_1_svc(struct svc_req *rqstp, dirpath *argp, void *UNUSED(resp))
+{
+ struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt);
+ nfs_export *exp;
+ char *p = *argp;
+ char rpath[MAXPATHLEN+1];
+ char buf[INET6_ADDRSTRLEN];
+
+ if (*p == '\0')
+ p = "/";
+
+ if (nfsd_realpath(p, rpath) != NULL) {
+ rpath[sizeof (rpath) - 1] = '\0';
+ p = rpath;
+ }
+
+ xlog(D_CALL, "Received UMNT(%s) request from %s", p,
+ host_ntop(sap, buf, sizeof(buf)));
+
+ exp = auth_authenticate("unmount", sap, p);
+ if (exp == NULL)
+ return 1;
+
+ mountlist_del(host_ntop(sap, buf, sizeof(buf)), p);
+ return 1;
+}
+
+bool_t
+mount_umntall_1_svc(struct svc_req *rqstp, void *UNUSED(argp),
+ void *UNUSED(resp))
+{
+ struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt);
+ char buf[INET6_ADDRSTRLEN];
+
+ xlog(D_CALL, "Received UMNTALL request from %s",
+ host_ntop(sap, buf, sizeof(buf)));
+
+ /* Reload /etc/exports if necessary */
+ auth_reload();
+
+ mountlist_del_all(nfs_getrpccaller(rqstp->rq_xprt));
+ return 1;
+}
+
+bool_t
+mount_export_1_svc(struct svc_req *rqstp, void *UNUSED(argp), exports *resp)
+{
+ struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt);
+ char buf[INET6_ADDRSTRLEN];
+
+ xlog(D_CALL, "Received EXPORT request from %s.",
+ host_ntop(sap, buf, sizeof(buf)));
+
+ *resp = get_exportlist();
+
+ return 1;
+}
+
+bool_t
+mount_exportall_1_svc(struct svc_req *rqstp, void *UNUSED(argp), exports *resp)
+{
+ struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt);
+ char buf[INET6_ADDRSTRLEN];
+
+ xlog(D_CALL, "Received EXPORTALL request from %s.",
+ host_ntop(sap, buf, sizeof(buf)));
+
+ *resp = get_exportlist();
+
+ return 1;
+}
+
+/*
+ * MNTv2 pathconf procedure
+ *
+ * The protocol doesn't include a status field, so Sun apparently considers
+ * it good practice to let anyone snoop on your system, even if it's
+ * pretty harmless data such as pathconf. We don't.
+ *
+ * Besides, many of the pathconf values don't make much sense on NFS volumes.
+ * FIFOs and tty device files represent devices on the *client*, so there's
+ * no point in getting the server's buffer sizes etc.
+ */
+bool_t
+mount_pathconf_2_svc(struct svc_req *rqstp, dirpath *path, ppathcnf *res)
+{
+ struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt);
+ struct stat stb;
+ nfs_export *exp;
+ char rpath[MAXPATHLEN+1];
+ char *p = *path;
+ char buf[INET6_ADDRSTRLEN];
+
+ memset(res, 0, sizeof(*res));
+
+ if (*p == '\0')
+ p = "/";
+
+ /* Reload /etc/exports if necessary */
+ auth_reload();
+
+ /* Resolve symlinks */
+ if (nfsd_realpath(p, rpath) != NULL) {
+ rpath[sizeof (rpath) - 1] = '\0';
+ p = rpath;
+ }
+
+ xlog(D_CALL, "Received PATHCONF(%s) request from %s", p,
+ host_ntop(sap, buf, sizeof(buf)));
+
+ /* Now authenticate the intruder... */
+ exp = auth_authenticate("pathconf", sap, p);
+ if (exp == NULL)
+ return 1;
+ else if (nfsd_path_stat(p, &stb) < 0) {
+ xlog(L_WARNING, "can't stat exported dir %s: %s",
+ p, strerror(errno));
+ return 1;
+ }
+
+ res->pc_link_max = pathconf(p, _PC_LINK_MAX);
+ res->pc_max_canon = pathconf(p, _PC_MAX_CANON);
+ res->pc_max_input = pathconf(p, _PC_MAX_INPUT);
+ res->pc_name_max = pathconf(p, _PC_NAME_MAX);
+ res->pc_path_max = pathconf(p, _PC_PATH_MAX);
+ res->pc_pipe_buf = pathconf(p, _PC_PIPE_BUF);
+ res->pc_vdisable = pathconf(p, _PC_VDISABLE);
+
+ /* Can't figure out what to do with pc_mask */
+ res->pc_mask[0] = 0;
+ res->pc_mask[1] = 0;
+
+ return 1;
+}
+
+/*
+ * We should advertise the preferred flavours first. (See RFC 2623
+ * section 2.7.) We leave that to the administrator, by advertising
+ * flavours in the order they were listed in /etc/exports. AUTH_NULL is
+ * dropped from the list to avoid backward compatibility issue with
+ * older Linux clients, who inspect the list in reversed order.
+ *
+ * XXX: It might be more helpful to rearrange these so that flavors
+ * giving more access (as determined from readonly and id-squashing
+ * options) come first. (If we decide to do that we should probably do
+ * that when reading the exports rather than here.)
+ */
+static void set_authflavors(struct mountres3_ok *ok, nfs_export *exp)
+{
+ struct sec_entry *s;
+ static int flavors[SECFLAVOR_COUNT];
+ int i = 0;
+
+ for (s = exp->m_export.e_secinfo; s->flav; s++) {
+ if (s->flav->fnum == AUTH_NULL)
+ continue;
+ flavors[i] = s->flav->fnum;
+ i++;
+ }
+ if (i == 0) {
+ /* default when there is no sec= option: */
+ i = 1;
+ flavors[0] = AUTH_UNIX;
+ }
+ ok->auth_flavors.auth_flavors_val = flavors;
+ ok->auth_flavors.auth_flavors_len = i;
+}
+
+/*
+ * NFSv3 MOUNT procedure
+ */
+bool_t
+mount_mnt_3_svc(struct svc_req *rqstp, dirpath *path, mountres3 *res)
+{
+ struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt);
+ struct mountres3_ok *ok = &res->mountres3_u.mountinfo;
+ char buf[INET6_ADDRSTRLEN];
+ nfs_export *exp;
+ struct nfs_fh_len *fh;
+
+ xlog(D_CALL, "Received MNT3(%s) request from %s", *path,
+ host_ntop(sap, buf, sizeof(buf)));
+
+ fh = get_rootfh(rqstp, path, &exp, &res->fhs_status, 1);
+ if (!fh)
+ return 1;
+
+ ok->fhandle.fhandle3_len = fh->fh_size;
+ ok->fhandle.fhandle3_val = (char *)fh->fh_handle;
+ set_authflavors(ok, exp);
+ return 1;
+}
+
+static struct nfs_fh_len *
+get_rootfh(struct svc_req *rqstp, dirpath *path, nfs_export **expret,
+ mountstat3 *error, int v3)
+{
+ struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt);
+ struct stat stb, estb;
+ nfs_export *exp;
+ struct nfs_fh_len *fh;
+ char rpath[MAXPATHLEN+1];
+ char *p = *path;
+ char buf[INET6_ADDRSTRLEN];
+
+ if (*p == '\0')
+ p = "/";
+
+ /* Reload /var/lib/nfs/etab if necessary */
+ auth_reload();
+
+ /* Resolve symlinks */
+ if (nfsd_realpath(p, rpath) != NULL) {
+ rpath[sizeof (rpath) - 1] = '\0';
+ p = rpath;
+ }
+
+ /* Now authenticate the intruder... */
+ exp = auth_authenticate("mount", sap, p);
+ if (exp == NULL) {
+ *error = MNT3ERR_ACCES;
+ return NULL;
+ }
+ if (nfsd_path_stat(p, &stb) < 0) {
+ xlog(L_WARNING, "can't stat exported dir %s: %s",
+ p, strerror(errno));
+ if (errno == ENOENT)
+ *error = MNT3ERR_NOENT;
+ else
+ *error = MNT3ERR_ACCES;
+ return NULL;
+ }
+ if (!S_ISDIR(stb.st_mode) && !S_ISREG(stb.st_mode)) {
+ xlog(L_WARNING, "%s is not a directory or regular file", p);
+ *error = MNT3ERR_NOTDIR;
+ return NULL;
+ }
+ if (nfsd_path_stat(exp->m_export.e_path, &estb) < 0) {
+ xlog(L_WARNING, "can't stat export point %s: %s",
+ p, strerror(errno));
+ *error = MNT3ERR_NOENT;
+ return NULL;
+ }
+ if (estb.st_dev != stb.st_dev
+ && !(exp->m_export.e_flags & NFSEXP_CROSSMOUNT)) {
+ xlog(L_WARNING, "request to export directory %s below nearest filesystem %s",
+ p, exp->m_export.e_path);
+ *error = MNT3ERR_ACCES;
+ return NULL;
+ }
+ if (exp->m_export.e_mountpoint &&
+ !check_is_mountpoint(exp->m_export.e_mountpoint[0]?
+ exp->m_export.e_mountpoint:
+ exp->m_export.e_path,
+ nfsd_path_lstat)) {
+ xlog(L_WARNING, "request to export an unmounted filesystem: %s",
+ p);
+ *error = MNT3ERR_NOENT;
+ return NULL;
+ }
+
+ /* This will be a static private nfs_export with just one
+ * address. We feed it to kernel then extract the filehandle,
+ */
+
+ if (cache_export(exp, p)) {
+ *error = MNT3ERR_ACCES;
+ return NULL;
+ }
+ fh = cache_get_filehandle(exp, v3?64:32, p);
+ if (fh == NULL) {
+ *error = MNT3ERR_ACCES;
+ return NULL;
+ }
+ *error = MNT_OK;
+ mountlist_add(host_ntop(sap, buf, sizeof(buf)), p);
+ if (expret)
+ *expret = exp;
+ return fh;
+}
+
+static void remove_all_clients(exportnode *e)
+{
+ struct groupnode *g, *ng;
+
+ for (g = e->ex_groups; g; g = ng) {
+ ng = g->gr_next;
+ xfree(g->gr_name);
+ xfree(g);
+ }
+ e->ex_groups = NULL;
+}
+
+static void free_exportlist(exports *elist)
+{
+ struct exportnode *e, *ne;
+
+ for (e = *elist; e != NULL; e = ne) {
+ ne = e->ex_next;
+ remove_all_clients(e);
+ xfree(e->ex_dir);
+ xfree(e);
+ }
+ *elist = NULL;
+}
+
+static void prune_clients(nfs_export *exp, struct exportnode *e)
+{
+ struct addrinfo *ai = NULL;
+ struct groupnode *c, **cp;
+
+ cp = &e->ex_groups;
+ while ((c = *cp) != NULL) {
+ if (client_gettype(c->gr_name) == MCL_FQDN
+ && (ai = host_addrinfo(c->gr_name))) {
+ if (client_check(exp->m_client, ai)) {
+ *cp = c->gr_next;
+ xfree(c->gr_name);
+ xfree(c);
+ nfs_freeaddrinfo(ai);
+ continue;
+ }
+ nfs_freeaddrinfo(ai);
+ }
+ cp = &(c->gr_next);
+ }
+}
+
+static exportnode *lookup_or_create_elist_entry(exports *elist, nfs_export *exp)
+{
+ exportnode *e;
+
+ for (e = *elist; e != NULL; e = e->ex_next) {
+ if (!strcmp(exp->m_export.e_path, e->ex_dir))
+ return e;
+ }
+ e = xmalloc(sizeof(*e));
+ e->ex_next = *elist;
+ e->ex_groups = NULL;
+ e->ex_dir = xstrdup(exp->m_export.e_path);
+ *elist = e;
+ return e;
+}
+
+static void insert_group(struct exportnode *e, char *newname)
+{
+ struct groupnode *g;
+
+ for (g = e->ex_groups; g; g = g->gr_next)
+ if (!strcmp(g->gr_name, newname))
+ return;
+
+ g = xmalloc(sizeof(*g));
+ g->gr_name = xstrdup(newname);
+ g->gr_next = e->ex_groups;
+ e->ex_groups = g;
+}
+
+static exports
+get_exportlist(void)
+{
+ static exports elist = NULL;
+ struct exportnode *e;
+ nfs_export *exp;
+ int i;
+ static unsigned int ecounter;
+ unsigned int acounter;
+
+ acounter = auth_reload();
+ if (elist && acounter == ecounter)
+ return elist;
+
+ ecounter = acounter;
+
+ free_exportlist(&elist);
+
+ for (i = 0; i < MCL_MAXTYPES; i++) {
+ for (exp = exportlist[i].p_head; exp; exp = exp->m_next) {
+ /* Don't show pseudo exports */
+ if (exp->m_export.e_flags & NFSEXP_V4ROOT)
+ continue;
+ e = lookup_or_create_elist_entry(&elist, exp);
+
+ /* exports to "*" absorb any others */
+ if (i == MCL_ANONYMOUS && e->ex_groups) {
+ remove_all_clients(e);
+ continue;
+ }
+ /* non-FQDN's absorb FQDN's they contain: */
+ if (i != MCL_FQDN && e->ex_groups)
+ prune_clients(exp, e);
+
+ if (exp->m_export.e_hostname[0] != '\0')
+ insert_group(e, exp->m_export.e_hostname);
+ }
+ }
+
+ return elist;
+}
+
+int vers;
+int port = 0;
+int descriptors = 0;
+
+inline static void
+read_mountd_conf(char **argv)
+{
+ char *s;
+ int ttl;
+
+ conf_init_file(NFS_CONFFILE);
+
+ xlog_set_debug("mountd");
+ manage_gids = conf_get_bool("mountd", "manage-gids", manage_gids);
+ descriptors = conf_get_num("mountd", "descriptors", descriptors);
+ port = conf_get_num("mountd", "port", port);
+ num_threads = conf_get_num("mountd", "threads", num_threads);
+ reverse_resolve = conf_get_bool("mountd", "reverse-lookup", reverse_resolve);
+ ha_callout_prog = conf_get_str("mountd", "ha-callout");
+ if (conf_get_bool("mountd", "cache-use-ipaddr", 0))
+ use_ipaddr = 2;
+
+ s = conf_get_str("mountd", "state-directory-path");
+ if (s && !state_setup_basedir(argv[0], s))
+ exit(1);
+
+ /* NOTE: following uses "nfsd" section of nfs.conf !!!! */
+ if (conf_get_bool("nfsd", "udp", NFSCTL_UDPISSET(_rpcprotobits)))
+ NFSCTL_UDPSET(_rpcprotobits);
+ else
+ NFSCTL_UDPUNSET(_rpcprotobits);
+ if (conf_get_bool("nfsd", "tcp", NFSCTL_TCPISSET(_rpcprotobits)))
+ NFSCTL_TCPSET(_rpcprotobits);
+ else
+ NFSCTL_TCPUNSET(_rpcprotobits);
+ for (vers = 2; vers <= 4; vers++) {
+ char tag[20];
+ sprintf(tag, "vers%d", vers);
+ if (conf_get_bool("nfsd", tag, NFSCTL_VERISSET(nfs_version, vers)))
+ NFSCTL_VERSET(nfs_version, vers);
+ else
+ NFSCTL_VERUNSET(nfs_version, vers);
+ }
+
+ ttl = conf_get_num("mountd", "ttl", default_ttl);
+ if (ttl > 0)
+ default_ttl = ttl;
+}
+
+int
+main(int argc, char **argv)
+{
+ char *progname;
+ unsigned int listeners = 0;
+ int foreground = 0;
+ int c;
+ int ttl;
+ struct sigaction sa;
+ struct rlimit rlim;
+
+ /* Set the basename */
+ if ((progname = strrchr(argv[0], '/')) != NULL)
+ progname++;
+ else
+ progname = argv[0];
+
+ /* Initialize logging. */
+ xlog_open(progname);
+
+ /* Read in config setting */
+ read_mountd_conf(argv);
+
+ /* Parse the command line options and arguments. */
+ opterr = 0;
+ while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != EOF)
+ switch (c) {
+ case 'g':
+ manage_gids = 1;
+ break;
+ case 'o':
+ descriptors = atoi(optarg);
+ if (descriptors <= 0) {
+ fprintf(stderr, "%s: bad descriptors: %s\n",
+ progname, optarg);
+ usage(progname, 1);
+ }
+ break;
+ case 'F':
+ foreground = 1;
+ break;
+ case 'd':
+ xlog_sconfig(optarg, 1);
+ break;
+ case 'H': /* PRC: specify a high-availability callout program */
+ ha_callout_prog = optarg;
+ break;
+ case 'h':
+ usage(progname, 0);
+ break;
+ case 'P': /* XXX for nfs-server compatibility */
+ case 'p':
+ port = atoi(optarg);
+ if (port <= 0 || port > 65535) {
+ fprintf(stderr, "%s: bad port number: %s\n",
+ progname, optarg);
+ usage(progname, 1);
+ }
+ break;
+ case 'N':
+ vers = atoi(optarg);
+ if (vers < 2 || vers > 4) {
+ fprintf(stderr, "%s: bad version number: %s\n",
+ argv[0], optarg);
+ usage(argv[0], 1);
+ }
+ nfs_version &= ~NFSVERSBIT(vers);
+ break;
+ case 'n':
+ NFSCTL_TCPUNSET(_rpcprotobits);
+ break;
+ case 'r':
+ reverse_resolve = 1;
+ break;
+ case 's':
+ if (!state_setup_basedir(argv[0], optarg))
+ exit(1);
+ break;
+ case 't':
+ num_threads = atoi (optarg);
+ break;
+ case 'V':
+ vers = atoi(optarg);
+ if (vers < 2 || vers > 4) {
+ fprintf(stderr, "%s: bad version number: %s\n",
+ argv[0], optarg);
+ usage(argv[0], 1);
+ }
+ nfs_version |= NFSVERSBIT(vers);
+ break;
+ case 'v':
+ printf("%s version " VERSION "\n", progname);
+ exit(0);
+ case 'u':
+ NFSCTL_UDPUNSET(_rpcprotobits);
+ break;
+ case 'l':
+ xlog_sconfig("auth", 1);
+ break;
+ case 'i':
+ use_ipaddr = 2;
+ break;
+ case 'T':
+ ttl = atoi(optarg);
+ if (ttl <= 0) {
+ fprintf(stderr, "%s: bad ttl number of seconds: %s\n",
+ argv[0], optarg);
+ usage(argv[0], 1);
+ }
+ default_ttl = ttl;
+ break;
+ case 0:
+ break;
+ case '?':
+ default:
+ usage(progname, 1);
+ }
+
+ /* No more arguments allowed. */
+ if (optind != argc || !version_any()) {
+ fprintf(stderr, "%s: No protocol versions specified!\n", progname);
+ usage(progname, 1);
+ }
+ if (!setup_state_path_names(progname, ETAB, ETABTMP, ETABLCK, &etab))
+ return 1;
+ if (!setup_state_path_names(progname, RMTAB, RMTABTMP, RMTABLCK, &rmtab))
+ return 1;
+
+ if (getrlimit (RLIMIT_NOFILE, &rlim) != 0)
+ fprintf(stderr, "%s: getrlimit (RLIMIT_NOFILE) failed: %s\n",
+ progname, strerror(errno));
+ else {
+ /* glibc sunrpc code dies if getdtablesize > FD_SETSIZE */
+ if ((descriptors == 0 && rlim.rlim_cur > FD_SETSIZE) ||
+ descriptors > FD_SETSIZE)
+ descriptors = FD_SETSIZE;
+ if (descriptors) {
+ rlim.rlim_cur = descriptors;
+ if (setrlimit (RLIMIT_NOFILE, &rlim) != 0) {
+ fprintf(stderr, "%s: setrlimit (RLIMIT_NOFILE) failed: %s\n",
+ progname, strerror(errno));
+ exit(1);
+ }
+ }
+ }
+ if (!foreground) xlog_stderr(0);
+
+ sa.sa_handler = SIG_IGN;
+ sa.sa_flags = 0;
+ sigemptyset(&sa.sa_mask);
+ sigaction(SIGHUP, &sa, NULL);
+ sigaction(SIGINT, &sa, NULL);
+ sigaction(SIGTERM, &sa, NULL);
+ sigaction(SIGPIPE, &sa, NULL);
+ /* WARNING: the following works on Linux and SysV, but not BSD! */
+ sigaction(SIGCHLD, &sa, NULL);
+
+ unregister_services();
+ if (version2()) {
+ listeners += nfs_svc_create("mountd", MOUNTPROG,
+ MOUNTVERS, mount_dispatch, port);
+ listeners += nfs_svc_create("mountd", MOUNTPROG,
+ MOUNTVERS_POSIX, mount_dispatch, port);
+ }
+ if (version3())
+ listeners += nfs_svc_create("mountd", MOUNTPROG,
+ MOUNTVERS_NFSV3, mount_dispatch, port);
+ if (version23() && listeners == 0)
+ xlog(L_WARNING, "mountd: No V2 or V3 listeners created!");
+
+ sa.sa_handler = killer;
+ sigaction(SIGINT, &sa, NULL);
+ sigaction(SIGTERM, &sa, NULL);
+ sa.sa_handler = sig_hup;
+ sigaction(SIGHUP, &sa, NULL);
+
+ if (!foreground) {
+ /* We first fork off a child. */
+ if ((c = fork()) > 0)
+ exit(0);
+ if (c < 0) {
+ xlog(L_FATAL, "mountd: cannot fork: %s\n",
+ strerror(errno));
+ }
+ /* Now we remove ourselves from the foreground.
+ Redirect stdin/stdout/stderr first. */
+ {
+ int fd = open("/dev/null", O_RDWR);
+ (void) dup2(fd, 0);
+ (void) dup2(fd, 1);
+ (void) dup2(fd, 2);
+ if (fd > 2) (void) close(fd);
+ }
+ setsid();
+ }
+
+ /* silently bounds check num_threads */
+ if (foreground)
+ num_threads = 1;
+ else if (num_threads < 1)
+ num_threads = 1;
+ else if (num_threads > MAX_THREADS)
+ num_threads = MAX_THREADS;
+
+ /* Open cache channel files BEFORE forking so each upcall is
+ * only handled by one thread. Kernel provides locking for both
+ * read and write.
+ */
+ cache_open();
+
+ if (cache_fork_workers("mountd", num_threads) == 0) {
+ /* We forked, waited, and now need to clean up */
+ unregister_services();
+ cleanup_lockfiles();
+ free_state_path_names(&etab);
+ free_state_path_names(&rmtab);
+ xlog(L_NOTICE, "mountd: no more workers, exiting\n");
+ exit(0);
+ }
+
+ nfsd_path_init();
+ v4clients_init();
+
+ xlog(L_NOTICE, "Version " VERSION " starting");
+ my_svc_run();
+
+ xlog(L_ERROR, "RPC service loop terminated unexpectedly. Exiting...\n");
+ unregister_services();
+ free_state_path_names(&etab);
+ free_state_path_names(&rmtab);
+ exit(1);
+}
+
+static void
+usage(const char *prog, int n)
+{
+ fprintf(stderr,
+"Usage: %s [-F|--foreground] [-h|--help] [-v|--version] [-d kind|--debug kind]\n"
+" [-l|--log-auth] [-i|--cache-use-ipaddr] [-T|--ttl ttl]\n"
+" [-o num|--descriptors num]\n"
+" [-p|--port port] [-V version|--nfs-version version]\n"
+" [-N version|--no-nfs-version version] [-n|--no-tcp]\n"
+" [-H prog |--ha-callout prog] [-r |--reverse-lookup]\n"
+" [-s|--state-directory-path path] [-g|--manage-gids]\n"
+" [-t num|--num-threads=num] [-u|--no-udp]\n", prog);
+ exit(n);
+}
diff --git a/utils/mountd/mountd.h b/utils/mountd/mountd.h
new file mode 100644
index 0000000..bd5c957
--- /dev/null
+++ b/utils/mountd/mountd.h
@@ -0,0 +1,54 @@
+/*
+ * utils/mountd/mountd.h
+ *
+ * Declarations for mountd.
+ *
+ * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
+ */
+
+#ifndef MOUNTD_H
+#define MOUNTD_H
+
+#include <rpc/rpc.h>
+#include <rpc/svc.h>
+#include "nfslib.h"
+#include "exportfs.h"
+#include "mount.h"
+
+union mountd_arguments {
+ dirpath dirpath;
+};
+
+union mountd_results {
+ fhstatus fstatus;
+ mountlist mountlist;
+ exports exports;
+};
+
+/*
+ * Global Function prototypes.
+ */
+bool_t mount_null_1_svc(struct svc_req *, void *, void *);
+bool_t mount_mnt_1_svc(struct svc_req *, dirpath *, fhstatus *);
+bool_t mount_dump_1_svc(struct svc_req *, void *, mountlist *);
+bool_t mount_umnt_1_svc(struct svc_req *, dirpath *, void *);
+bool_t mount_umntall_1_svc(struct svc_req *, void *, void *);
+bool_t mount_export_1_svc(struct svc_req *, void *, exports *);
+bool_t mount_exportall_1_svc(struct svc_req *, void *, exports *);
+bool_t mount_pathconf_2_svc(struct svc_req *, dirpath *, ppathcnf *);
+bool_t mount_mnt_3_svc(struct svc_req *, dirpath *, mountres3 *);
+
+void mount_dispatch(struct svc_req *, SVCXPRT *);
+void auth_init(void);
+unsigned int auth_reload(void);
+nfs_export * auth_authenticate(const char *what,
+ const struct sockaddr *caller,
+ const char *path);
+void auth_export(nfs_export *exp);
+
+void mountlist_add(char *host, const char *path);
+void mountlist_del(char *host, const char *path);
+void mountlist_del_all(const struct sockaddr *sap);
+mountlist mountlist_list(void);
+
+#endif /* MOUNTD_H */
diff --git a/utils/mountd/mountd.man b/utils/mountd/mountd.man
new file mode 100644
index 0000000..a206a3e
--- /dev/null
+++ b/utils/mountd/mountd.man
@@ -0,0 +1,362 @@
+.\"@(#)rpc.mountd.8"
+.\"
+.\" Copyright (C) 1999 Olaf Kirch <okir@monad.swb.de>
+.\" Modified by Paul Clements, 2004.
+.\"
+.TH rpc.mountd 8 "31 Dec 2009"
+.SH NAME
+rpc.mountd \- NFS mount daemon
+.SH SYNOPSIS
+.BI "/usr/sbin/rpc.mountd [" options "]"
+.SH DESCRIPTION
+The
+.B rpc.mountd
+daemon implements the server side of the NFS MOUNT protocol,
+an NFS side protocol used by NFS version 2 [RFC1094] and NFS version 3 [RFC1813].
+It also responds to requests from the Linux kernel to authenticate
+clients and provides details of access permissions.
+.PP
+The NFS server
+.RI ( nfsd )
+maintains a cache of authentication and authorization information which
+is used to identify the source of each request, and then what access
+permissions that source has to any local filesystem. When required
+information is not found in the cache, the server sends a request to
+.B mountd
+to fill in the missing information. Mountd uses a table of information
+stored in
+.B /var/lib/nfs/etab
+and maintained by
+.BR exportfs (8),
+possibly based on the contents of
+.BR exports (5),
+to respond to each request.
+.SS Mounting exported NFS File Systems
+The NFS MOUNT protocol has several procedures.
+The most important of these are
+MNT (mount an export) and
+UMNT (unmount an export).
+.PP
+A MNT request has two arguments: an explicit argument that
+contains the pathname of the root directory of the export to be mounted,
+and an implicit argument that is the sender's IP address.
+.PP
+When receiving a MNT request from an NFS client,
+.B rpc.mountd
+checks both the pathname and the sender's IP address against its export table.
+If the sender is permitted to access the requested export,
+.B rpc.mountd
+returns an NFS file handle for the export's root directory to the client.
+The client can then use the root file handle and NFS LOOKUP requests
+to navigate the directory structure of the export.
+.SS The rmtab File
+The
+.B rpc.mountd
+daemon registers every successful MNT request by adding an entry to the
+.I /var/lib/nfs/rmtab
+file.
+When receivng a UMNT request from an NFS client,
+.B rpc.mountd
+simply removes the matching entry from
+.IR /var/lib/nfs/rmtab ,
+as long as the access control list for that export allows that sender
+to access the export.
+.PP
+Clients can discover the list of file systems an NFS server is
+currently exporting, or the list of other clients that have mounted
+its exports, by using the
+.BR showmount (8)
+command.
+.BR showmount (8)
+uses other procedures in the NFS MOUNT protocol to report information
+about the server's exported file systems.
+.PP
+Note, however, that there is little to guarantee that the contents of
+.I /var/lib/nfs/rmtab
+are accurate.
+A client may continue accessing an export even after invoking UMNT.
+If the client reboots without sending a UMNT request, stale entries
+remain for that client in
+.IR /var/lib/nfs/rmtab .
+.SS Mounting File Systems with NFSv4
+Version 4 (and later) of NFS does not use a separate NFS MOUNT
+protocol. Instead mounting is performed using regular NFS requests
+handled by the NFS server in the Linux kernel
+.RI ( nfsd ).
+Consequently
+.I /var/lib/nfs/rmtab
+is not updated to reflect any NFSv4 activity.
+.SH OPTIONS
+.TP
+.B \-d kind " or " \-\-debug kind
+Turn on debugging. Valid kinds are: all, auth, call, general and parse.
+.TP
+.BR \-l " or " \-\-log\-auth
+Enable logging of responses to authentication and access requests from
+nfsd. Each response is then cached by the kernel for 30 minutes (or as set by
+.B \-\-ttl
+below), and will be refreshed after 15 minutes (half the ttl time) if
+the relevant client remains active.
+Note that
+.B -l
+is equivalent to
+.B "-d auth"
+and so can be enabled in
+.B /etc/nfs.conf
+with
+.B "\[dq]debug = auth\[dq]"
+in the
+.B "[mountd]"
+section.
+.IP
+.B rpc.mountd
+will always log authentication responses to MOUNT requests when NFSv3 is
+used, but to get similar logs for NFSv4, this option is required.
+.TP
+.BR \-i " or " \-\-cache\-use\-ipaddr
+Normally each client IP address is matched against each host identifier
+(name, wildcard, netgroup etc) found in
+.B /etc/exports
+and a combined identity is formed from all matching identifiers.
+Often many clients will map to the same combined identity so performing
+this mapping reduces the number of distinct access details that the
+kernel needs to store.
+Specifying the
+.B \-i
+option suppresses this mapping so that access to each filesystem is
+requested and cached separately for each client IP address. Doing this
+can increase the burden of updating the cache slightly, but can make the
+log messages produced by the
+.B -l
+option easier to read.
+.TP
+.B \-T " or " \-\-ttl
+Provide a time-to-live (TTL) for cached information given to the kernel.
+The kernel will normally request an update if the information is needed
+after half of this time has expired. Increasing the provided number,
+which is in seconds, reduces the rate of cache update requests, and this
+is particularly noticeable when these requests are logged with
+.BR \-l .
+However increasing also means that changes to hostname to address
+mappings can take longer to be noticed.
+The default TTL is 1800 (30 minutes).
+.TP
+.B \-F " or " \-\-foreground
+Run in foreground (do not daemonize)
+.TP
+.B \-h " or " \-\-help
+Display usage message.
+.TP
+.B \-o num " or " \-\-descriptors num
+Set the limit of the number of open file descriptors to num. The
+default is to leave the limit unchanged.
+.TP
+.B \-N mountd-version " or " \-\-no-nfs-version mountd-version
+This option can be used to request that
+.B rpc.mountd
+do not offer certain versions of NFS. The current version of
+.B rpc.mountd
+can support both NFS version 2, 3 and 4. If the
+either one of these version should not be offered,
+.B rpc.mountd
+must be invoked with the option
+.B "\-\-no-nfs-version <vers>" .
+.TP
+.B \-n " or " \-\-no-tcp
+Don't advertise TCP for mount.
+.TP
+.B \-p num " or " \-P num " or " \-\-port num
+Specifies the port number used for RPC listener sockets.
+If this option is not specified,
+.B rpc.mountd
+will try to consult
+.IR /etc/services ,
+if gets port succeed, set the same port for all listener socket,
+otherwise chooses a random ephemeral port for each listener socket.
+.IP
+This option can be used to fix the port value of
+.BR rpc.mountd 's
+listeners when NFS MOUNT requests must traverse a firewall
+between clients and servers.
+.TP
+.B \-H " prog or " \-\-ha-callout prog
+Specify a high availability callout program.
+This program receives callouts for all MOUNT and UNMOUNT requests.
+This allows
+.B rpc.mountd
+to be used in a High Availability NFS (HA-NFS) environment.
+.IP
+The callout program is run with 4 arguments.
+The first is
+.B mount
+or
+.B unmount
+depending on the reason for the callout.
+The second will be the name of the client performing the mount.
+The third will be the path that the client is mounting.
+The last is the number of concurrent mounts that we believe the client
+has of that path.
+.IP
+This callout is not needed with 2.6 and later kernels.
+Instead, mount the nfsd filesystem on
+.IR /proc/fs/nfsd .
+.TP
+.BI "\-s," "" " \-\-state\-directory\-path " directory
+Specify a directory in which to place state information (etab and rmtab).
+If this option is not specified the default of
+.I /var/lib/nfs
+is used.
+.TP
+.BI "\-r," "" " \-\-reverse\-lookup"
+.B rpc.mountd
+tracks IP addresses in the
+.I rmtab
+file. When a DUMP request is made (by
+someone running
+.BR "showmount -a" ,
+for instance), it returns IP addresses instead
+of hostnames by default. This option causes
+.B rpc.mountd
+to perform a reverse lookup on each IP address and return that hostname instead.
+Enabling this can have a substantial negative effect on performance
+in some situations.
+.TP
+.BR "\-t N" " or " "\-\-num\-threads=N " or " \-\-num\-threads N "
+This option specifies the number of worker threads that rpc.mountd
+spawns. The default is 1 thread, which is probably enough. More
+threads are usually only needed for NFS servers which need to handle
+mount storms of hundreds of NFS mounts in a few seconds, or when
+your DNS server is slow or unreliable.
+.TP
+.B \-u " or " \-\-no-udp
+Don't advertise UDP for mounting
+.TP
+.B \-V version " or " \-\-nfs-version version
+This option can be used to request that
+.B rpc.mountd
+offer certain versions of NFS. The current version of
+.B rpc.mountd
+can support both NFS version 2 and the newer version 3.
+.TP
+.B \-v " or " \-\-version
+Print the version of
+.B rpc.mountd
+and exit.
+.TP
+.B \-g " or " \-\-manage-gids
+Accept requests from the kernel to map user id numbers into lists of
+group id numbers for use in access control. An NFS request will
+normally (except when using Kerberos or other cryptographic
+authentication) contains a user-id and a list of group-ids. Due to a
+limitation in the NFS protocol, at most 16 groups ids can be listed.
+If you use the
+.B \-g
+flag, then the list of group ids received from the client will be
+replaced by a list of group ids determined by an appropriate lookup on
+the server. Note that the 'primary' group id is not affected so a
+.B newgroup
+command on the client will still be effective. This function requires
+a Linux Kernel with version at least 2.6.21.
+
+.SH CONFIGURATION FILE
+Many of the options that can be set on the command line can also be
+controlled through values set in the
+.B [mountd]
+or, in some cases, the
+.B [nfsd]
+sections of the
+.I /etc/nfs.conf
+configuration file.
+Values recognized in the
+.B [mountd]
+section include
+.BR manage-gids ,
+.BR cache\-use\-ipaddr ,
+.BR descriptors ,
+.BR port ,
+.BR threads ,
+.BR ttl ,
+.BR reverse-lookup ", and"
+.BR state-directory-path ,
+.B ha-callout
+which each have the same effect as the option with the same name.
+
+The values recognized in the
+.B [nfsd]
+section include
+.BR TCP ,
+.BR UDP ,
+.BR vers3 ", and"
+.B vers4
+which each have the same meaning as given by
+.BR rpc.nfsd (8).
+
+.SH TCP_WRAPPERS SUPPORT
+You can protect your
+.B rpc.mountd
+listeners using the
+.B tcp_wrapper
+library or
+.BR iptables (8).
+.PP
+Note that the
+.B tcp_wrapper
+library supports only IPv4 networking.
+.PP
+Add the hostnames of NFS peers that are allowed to access
+.B rpc.mountd
+to
+.IR /etc/hosts.allow .
+Use the daemon name
+.B mountd
+even if the
+.B rpc.mountd
+binary has a different name.
+.PP
+Hostnames used in either access file will be ignored when
+they can not be resolved into IP addresses.
+For further information see the
+.BR tcpd (8)
+and
+.BR hosts_access (5)
+man pages.
+.SS IPv6 and TI-RPC support
+TI-RPC is a pre-requisite for supporting NFS on IPv6.
+If TI-RPC support is built into
+.BR rpc.mountd ,
+it attempts to start listeners on network transports marked 'visible' in
+.IR /etc/netconfig .
+As long as at least one network transport listener starts successfully,
+.B rpc.mountd
+will operate.
+.SH FILES
+.TP 2.5i
+.I /etc/exports
+input file for
+.BR exportfs ,
+listing exports, export options, and access control lists
+.TP 2.5i
+.I /var/lib/nfs/rmtab
+table of clients accessing server's exports
+.SH SEE ALSO
+.BR exportfs (8),
+.BR exports (5),
+.BR showmount (8),
+.BR rpc.nfsd (8),
+.BR rpc.rquotad (8),
+.BR nfs (5),
+.BR nfs.conf (5),
+.BR tcpd (8),
+.BR hosts_access (5),
+.BR iptables (8),
+.BR netconfig (5)
+.sp
+RFC 1094 - "NFS: Network File System Protocol Specification"
+.br
+RFC 1813 - "NFS Version 3 Protocol Specification"
+.br
+RFC 7530 - "Network File System (NFS) Version 4 Protocol"
+.br
+RFC 8881 - "Network File System (NFS) Version 4 Minor Version 1 Protocol"
+.SH AUTHOR
+Olaf Kirch, H. J. Lu, G. Allan Morris III, and a host of others.
diff --git a/utils/mountd/rmtab.c b/utils/mountd/rmtab.c
new file mode 100644
index 0000000..752fdb6
--- /dev/null
+++ b/utils/mountd/rmtab.c
@@ -0,0 +1,254 @@
+/*
+ * utils/mountd/rmtab.c
+ *
+ * Manage the rmtab file for mountd.
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include "misc.h"
+#include "exportfs.h"
+#include "xio.h"
+#include "mountd.h"
+#include "ha-callout.h"
+
+#include <limits.h> /* PATH_MAX */
+#include <errno.h>
+
+extern int reverse_resolve;
+
+/* If new path is a link do not destroy it but place the
+ * file where the link points.
+ */
+
+static int
+slink_safe_rename(const char * oldpath, const char * newpath)
+{
+ int r;
+ struct stat s;
+ char slink_path[PATH_MAX];
+ const char *real_newpath = newpath;
+
+ if ((lstat(newpath, &s) == 0) && S_ISLNK(s.st_mode)) {
+ /* New path is a symbolic link, do not destroy but follow */
+ if ((r = readlink(newpath, slink_path, PATH_MAX - 1)) == -1)
+ return -1;
+ slink_path[r] = '\0';
+ real_newpath = slink_path;
+ }
+
+ return rename(oldpath, real_newpath);
+}
+
+void
+mountlist_add(char *host, const char *path)
+{
+ struct rmtabent xe;
+ struct rmtabent *rep;
+ int lockid;
+ long pos;
+
+ if ((lockid = xflock(rmtab.lockfn, "a")) < 0)
+ return;
+ setrmtabent("r+");
+ while ((rep = getrmtabent(1, &pos)) != NULL) {
+ if (strcmp (rep->r_client,
+ host) == 0
+ && strcmp(rep->r_path, path) == 0) {
+ rep->r_count++;
+ /* PRC: do the HA callout: */
+ ha_callout("mount", rep->r_client, rep->r_path, rep->r_count);
+ putrmtabent(rep, &pos);
+ endrmtabent();
+ xfunlock(lockid);
+ return;
+ }
+ }
+ endrmtabent();
+ strncpy(xe.r_client, host,
+ sizeof (xe.r_client) - 1);
+ xe.r_client [sizeof (xe.r_client) - 1] = '\0';
+ strncpy(xe.r_path, path, sizeof (xe.r_path) - 1);
+ xe.r_path [sizeof (xe.r_path) - 1] = '\0';
+ xe.r_count = 1;
+ if (setrmtabent("a")) {
+ /* PRC: do the HA callout: */
+ ha_callout("mount", xe.r_client, xe.r_path, xe.r_count);
+ putrmtabent(&xe, NULL);
+ endrmtabent();
+ }
+ xfunlock(lockid);
+}
+
+void
+mountlist_del(char *hname, const char *path)
+{
+ struct rmtabent *rep;
+ FILE *fp;
+ int lockid;
+ int match;
+
+ if ((lockid = xflock(rmtab.lockfn, "w")) < 0)
+ return;
+ if (!setrmtabent("r")) {
+ xfunlock(lockid);
+ return;
+ }
+ if (!(fp = fsetrmtabent(rmtab.tmpfn, "w"))) {
+ endrmtabent();
+ xfunlock(lockid);
+ return;
+ }
+ while ((rep = getrmtabent(1, NULL)) != NULL) {
+ match = !strcmp (rep->r_client, hname)
+ && !strcmp(rep->r_path, path);
+ if (match) {
+ rep->r_count--;
+ /* PRC: do the HA callout: */
+ ha_callout("unmount", rep->r_client, rep->r_path, rep->r_count);
+ }
+ if (!match || rep->r_count)
+ fputrmtabent(fp, rep, NULL);
+ }
+ if (slink_safe_rename(rmtab.tmpfn, rmtab.statefn) < 0) {
+ xlog(L_ERROR, "couldn't rename %s to %s",
+ rmtab.tmpfn, rmtab.statefn);
+ }
+ endrmtabent(); /* close & unlink */
+ fendrmtabent(fp);
+ xfunlock(lockid);
+}
+
+void
+mountlist_del_all(const struct sockaddr *sap)
+{
+ char *hostname;
+ struct rmtabent *rep;
+ FILE *fp;
+ int lockid;
+
+ if ((lockid = xflock(rmtab.lockfn, "w")) < 0)
+ return;
+ hostname = host_canonname(sap);
+ if (hostname == NULL) {
+ char buf[INET6_ADDRSTRLEN];
+ xlog(L_ERROR, "can't get hostname of %s",
+ host_ntop(sap, buf, sizeof(buf)));
+ goto out_unlock;
+ }
+
+ if (!setrmtabent("r"))
+ goto out_free;
+
+ if (!(fp = fsetrmtabent(rmtab.tmpfn, "w")))
+ goto out_close;
+
+ while ((rep = getrmtabent(1, NULL)) != NULL) {
+ if (strcmp(rep->r_client, hostname) == 0 &&
+ auth_authenticate("umountall", sap, rep->r_path) != NULL)
+ continue;
+ fputrmtabent(fp, rep, NULL);
+ }
+ if (slink_safe_rename(rmtab.tmpfn, rmtab.statefn) < 0) {
+ xlog(L_ERROR, "couldn't rename %s to %s",
+ rmtab.tmpfn, rmtab.statefn);
+ }
+ fendrmtabent(fp);
+out_close:
+ endrmtabent(); /* close & unlink */
+out_free:
+ free(hostname);
+out_unlock:
+ xfunlock(lockid);
+}
+
+static void
+mountlist_freeall(mountlist list)
+{
+ while (list != NULL) {
+ mountlist m = list;
+ list = m->ml_next;
+ free(m->ml_hostname);
+ free(m->ml_directory);
+ free(m);
+ }
+}
+
+mountlist
+mountlist_list(void)
+{
+ static mountlist mlist = NULL;
+ static time_t last_mtime = 0;
+ mountlist m;
+ struct rmtabent *rep;
+ struct stat stb;
+ int lockid;
+
+ if ((lockid = xflock(rmtab.lockfn, "r")) < 0)
+ return NULL;
+ if (stat(rmtab.statefn, &stb) < 0) {
+ xlog(L_ERROR, "can't stat %s: %s",
+ rmtab.statefn, strerror(errno));
+ xfunlock(lockid);
+ return NULL;
+ }
+ if (stb.st_mtime != last_mtime) {
+ mountlist_freeall(mlist);
+ mlist = NULL;
+ last_mtime = stb.st_mtime;
+
+ setrmtabent("r");
+ while ((rep = getrmtabent(1, NULL)) != NULL) {
+ m = calloc(1, sizeof(*m));
+ if (m == NULL) {
+ mountlist_freeall(mlist);
+ mlist = NULL;
+ xlog(L_ERROR, "%s: memory allocation failed",
+ __func__);
+ break;
+ }
+
+ if (reverse_resolve) {
+ struct addrinfo *ai;
+ ai = host_pton(rep->r_client);
+ if (ai != NULL) {
+ m->ml_hostname = host_canonname(ai->ai_addr);
+ nfs_freeaddrinfo(ai);
+ }
+ }
+ if (m->ml_hostname == NULL)
+ m->ml_hostname = strdup(rep->r_client);
+
+ m->ml_directory = strdup(rep->r_path);
+
+ if (m->ml_hostname == NULL || m->ml_directory == NULL) {
+ free(m->ml_hostname);
+ free(m->ml_directory);
+ free(m);
+ mountlist_freeall(mlist);
+ mlist = NULL;
+ xlog(L_ERROR, "%s: memory allocation failed",
+ __func__);
+ break;
+ }
+
+ m->ml_next = mlist;
+ mlist = m;
+ }
+ endrmtabent();
+ }
+ xfunlock(lockid);
+
+ return mlist;
+}
diff --git a/utils/mountd/svc_run.c b/utils/mountd/svc_run.c
new file mode 100644
index 0000000..2aaf375
--- /dev/null
+++ b/utils/mountd/svc_run.c
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 1984 Sun Microsystems, Inc.
+ * Based on svc_run.c from statd which claimed:
+ * Modified by Jeffrey A. Uphoff, 1995, 1997-1999.
+ * Modified by Olaf Kirch, 1996.
+ *
+ */
+
+/*
+ * Copyright (c) 2009, Sun Microsystems, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * - 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.
+ * - Neither the name of Sun Microsystems, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER 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.
+ */
+
+/*
+ * Allow svc_run to listen to other file descriptors as well
+ */
+
+/*
+ * This is the RPC server side idle loop.
+ * Wait for input, call server program.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <rpc/rpc.h>
+#include "xlog.h"
+#include <errno.h>
+#include <time.h>
+
+#ifdef HAVE_LIBTIRPC
+#include <rpc/rpc_com.h>
+#endif
+#include "export.h"
+
+void my_svc_run(void);
+
+#if defined(__GLIBC__) && LONG_MAX != INT_MAX
+/* bug in glibc 2.3.6 and earlier, we need
+ * our own svc_getreqset
+ */
+static void
+my_svc_getreqset (fd_set *readfds)
+{
+ fd_mask mask;
+ fd_mask *maskp;
+ int setsize;
+ int sock;
+ int bit;
+
+ setsize = _rpc_dtablesize ();
+ if (setsize > FD_SETSIZE)
+ setsize = FD_SETSIZE;
+ maskp = readfds->fds_bits;
+ for (sock = 0; sock < setsize; sock += NFDBITS)
+ for (mask = *maskp++;
+ (bit = ffsl (mask));
+ mask ^= (1L << (bit - 1)))
+ svc_getreq_common (sock + bit - 1);
+}
+#define svc_getreqset my_svc_getreqset
+
+#endif
+
+/*
+ * The heart of the server. A crib from libc for the most part...
+ */
+void
+my_svc_run(void)
+{
+ fd_set readfds;
+ int selret;
+
+ for (;;) {
+ readfds = svc_fdset;
+ selret = cache_process(&readfds);
+ if (selret < 0) {
+ xlog(L_ERROR, "my_svc_run() - select: %m");
+ return;
+ }
+ if (selret)
+ svc_getreqset(&readfds);
+ }
+}
diff --git a/utils/nfsd/Makefile.am b/utils/nfsd/Makefile.am
new file mode 100644
index 0000000..8acc9a0
--- /dev/null
+++ b/utils/nfsd/Makefile.am
@@ -0,0 +1,55 @@
+## Process this file with automake to produce Makefile.in
+
+man8_MANS = nfsd.man
+EXTRA_DIST = $(man8_MANS)
+
+RPCPREFIX = rpc.
+KPREFIX = @kprefix@
+sbin_PROGRAMS = nfsd
+
+noinst_HEADERS = nfssvc.h
+nfsd_SOURCES = nfsd.c nfssvc.c
+nfsd_LDADD = ../../support/nfs/libnfs.la $(LIBTIRPC)
+
+MAINTAINERCLEANFILES = Makefile.in
+
+#######################################################################
+# The following allows the current practice of having
+# daemons renamed during the install to include RPCPREFIX
+# and the KPREFIX
+# This could all be done much easier with program_transform_name
+# ( program_transform_name = s/^/$(RPCPREFIX)$(KPREFIX)/ )
+# but that also renames the man pages, which the current
+# practice does not do.
+install-exec-hook:
+ (cd $(DESTDIR)$(sbindir) && \
+ for p in $(sbin_PROGRAMS); do \
+ mv -f $$p$(EXEEXT) $(RPCPREFIX)$(KPREFIX)$$p$(EXEEXT) ;\
+ done)
+uninstall-hook:
+ (cd $(DESTDIR)$(sbindir) && \
+ for p in $(sbin_PROGRAMS); do \
+ rm -f $(RPCPREFIX)$(KPREFIX)$$p$(EXEEXT) ;\
+ done)
+
+
+# XXX This makes some assumptions about what automake does.
+# XXX But there is no install-man-hook or install-man-local.
+install-man: install-man8 install-man-links
+uninstall-man: uninstall-man8 uninstall-man-links
+
+install-man-links:
+ (cd $(DESTDIR)$(man8dir) && \
+ for m in $(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS); do \
+ inst=`echo $$m | sed -e 's/man$$/8/'`; \
+ rm -f $(RPCPREFIX)$$inst ; \
+ $(LN_S) $$inst $(RPCPREFIX)$$inst ; \
+ done)
+
+uninstall-man-links:
+ (cd $(DESTDIR)$(man8dir) && \
+ for m in $(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS); do \
+ inst=`echo $$m | sed -e 's/man$$/8/'`; \
+ rm -f $(RPCPREFIX)$$inst ; \
+ done)
+
diff --git a/utils/nfsd/Makefile.in b/utils/nfsd/Makefile.in
new file mode 100644
index 0000000..853bf55
--- /dev/null
+++ b/utils/nfsd/Makefile.in
@@ -0,0 +1,855 @@
+# 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@
+sbin_PROGRAMS = nfsd$(EXEEXT)
+subdir = utils/nfsd
+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 $(noinst_HEADERS) \
+ $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/support/include/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man8dir)"
+PROGRAMS = $(sbin_PROGRAMS)
+am_nfsd_OBJECTS = nfsd.$(OBJEXT) nfssvc.$(OBJEXT)
+nfsd_OBJECTS = $(am_nfsd_OBJECTS)
+am__DEPENDENCIES_1 =
+nfsd_DEPENDENCIES = ../../support/nfs/libnfs.la $(am__DEPENDENCIES_1)
+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 =
+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)/nfsd.Po ./$(DEPDIR)/nfssvc.Po
+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 = $(nfsd_SOURCES)
+DIST_SOURCES = $(nfsd_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+man8dir = $(mandir)/man8
+NROFF = nroff
+MANS = $(man8_MANS)
+HEADERS = $(noinst_HEADERS)
+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@
+man8_MANS = nfsd.man
+EXTRA_DIST = $(man8_MANS)
+RPCPREFIX = rpc.
+KPREFIX = @kprefix@
+noinst_HEADERS = nfssvc.h
+nfsd_SOURCES = nfsd.c nfssvc.c
+nfsd_LDADD = ../../support/nfs/libnfs.la $(LIBTIRPC)
+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 utils/nfsd/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu utils/nfsd/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):
+install-sbinPROGRAMS: $(sbin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(sbindir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-sbinPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(sbindir)" && rm -f $$files
+
+clean-sbinPROGRAMS:
+ @list='$(sbin_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+nfsd$(EXEEXT): $(nfsd_OBJECTS) $(nfsd_DEPENDENCIES) $(EXTRA_nfsd_DEPENDENCIES)
+ @rm -f nfsd$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(nfsd_OBJECTS) $(nfsd_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nfsd.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nfssvc.Po@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 $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-man8: $(man8_MANS)
+ @$(NORMAL_INSTALL)
+ @list1='$(man8_MANS)'; \
+ list2=''; \
+ test -n "$(man8dir)" \
+ && test -n "`echo $$list1$$list2`" \
+ || exit 0; \
+ echo " $(MKDIR_P) '$(DESTDIR)$(man8dir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(man8dir)" || exit 1; \
+ { for i in $$list1; do echo "$$i"; done; \
+ if test -n "$$list2"; then \
+ for i in $$list2; do echo "$$i"; done \
+ | sed -n '/\.8[a-z]*$$/p'; \
+ fi; \
+ } | while read p; do \
+ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; echo "$$p"; \
+ done | \
+ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \
+ sed 'N;N;s,\n, ,g' | { \
+ list=; while read file base inst; do \
+ if test "$$base" = "$$inst"; then list="$$list $$file"; else \
+ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man8dir)/$$inst'"; \
+ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man8dir)/$$inst" || exit $$?; \
+ fi; \
+ done; \
+ for i in $$list; do echo "$$i"; done | $(am__base_list) | \
+ while read files; do \
+ test -z "$$files" || { \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man8dir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(man8dir)" || exit $$?; }; \
+ done; }
+
+uninstall-man8:
+ @$(NORMAL_UNINSTALL)
+ @list='$(man8_MANS)'; test -n "$(man8dir)" || exit 0; \
+ files=`{ for i in $$list; do echo "$$i"; done; \
+ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
+ dir='$(DESTDIR)$(man8dir)'; $(am__uninstall_files_from_dir)
+
+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 $(PROGRAMS) $(MANS) $(HEADERS)
+installdirs:
+ for dir in "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man8dir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+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-sbinPROGRAMS \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/nfsd.Po
+ -rm -f ./$(DEPDIR)/nfssvc.Po
+ -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-man
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-sbinPROGRAMS
+ @$(NORMAL_INSTALL)
+ $(MAKE) $(AM_MAKEFLAGS) install-exec-hook
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+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)/nfsd.Po
+ -rm -f ./$(DEPDIR)/nfssvc.Po
+ -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: uninstall-man uninstall-sbinPROGRAMS
+ @$(NORMAL_INSTALL)
+ $(MAKE) $(AM_MAKEFLAGS) uninstall-hook
+.MAKE: install-am install-exec-am install-strip uninstall-am
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-sbinPROGRAMS 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-exec-hook install-html install-html-am \
+ install-info install-info-am install-man install-man8 \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-sbinPROGRAMS 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 uninstall-hook \
+ uninstall-man uninstall-man8 uninstall-sbinPROGRAMS
+
+.PRECIOUS: Makefile
+
+
+#######################################################################
+# The following allows the current practice of having
+# daemons renamed during the install to include RPCPREFIX
+# and the KPREFIX
+# This could all be done much easier with program_transform_name
+# ( program_transform_name = s/^/$(RPCPREFIX)$(KPREFIX)/ )
+# but that also renames the man pages, which the current
+# practice does not do.
+install-exec-hook:
+ (cd $(DESTDIR)$(sbindir) && \
+ for p in $(sbin_PROGRAMS); do \
+ mv -f $$p$(EXEEXT) $(RPCPREFIX)$(KPREFIX)$$p$(EXEEXT) ;\
+ done)
+uninstall-hook:
+ (cd $(DESTDIR)$(sbindir) && \
+ for p in $(sbin_PROGRAMS); do \
+ rm -f $(RPCPREFIX)$(KPREFIX)$$p$(EXEEXT) ;\
+ done)
+
+# XXX This makes some assumptions about what automake does.
+# XXX But there is no install-man-hook or install-man-local.
+install-man: install-man8 install-man-links
+uninstall-man: uninstall-man8 uninstall-man-links
+
+install-man-links:
+ (cd $(DESTDIR)$(man8dir) && \
+ for m in $(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS); do \
+ inst=`echo $$m | sed -e 's/man$$/8/'`; \
+ rm -f $(RPCPREFIX)$$inst ; \
+ $(LN_S) $$inst $(RPCPREFIX)$$inst ; \
+ done)
+
+uninstall-man-links:
+ (cd $(DESTDIR)$(man8dir) && \
+ for m in $(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS); do \
+ inst=`echo $$m | sed -e 's/man$$/8/'`; \
+ rm -f $(RPCPREFIX)$$inst ; \
+ done)
+
+# 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/utils/nfsd/nfsd.c b/utils/nfsd/nfsd.c
new file mode 100644
index 0000000..249df00
--- /dev/null
+++ b/utils/nfsd/nfsd.c
@@ -0,0 +1,441 @@
+/*
+ * nfsd
+ *
+ * This is the user level part of nfsd. This is very primitive, because
+ * all the work is now done in the kernel module.
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <getopt.h>
+#include <netdb.h>
+#include <libgen.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sched.h>
+
+#include "conffile.h"
+#include "nfslib.h"
+#include "nfssvc.h"
+#include "xlog.h"
+#include "xcommon.h"
+
+#ifndef NFSD_NPROC
+#define NFSD_NPROC 8
+#endif
+
+static void usage(const char *);
+
+static struct option longopts[] =
+{
+ { "host", 1, 0, 'H' },
+ { "scope", 1, 0, 'S'},
+ { "help", 0, 0, 'h' },
+ { "no-nfs-version", 1, 0, 'N' },
+ { "nfs-version", 1, 0, 'V' },
+ { "tcp", 0, 0, 't' },
+ { "no-tcp", 0, 0, 'T' },
+ { "udp", 0, 0, 'u' },
+ { "no-udp", 0, 0, 'U' },
+ { "port", 1, 0, 'P' },
+ { "port", 1, 0, 'p' },
+ { "debug", 0, 0, 'd' },
+ { "syslog", 0, 0, 's' },
+ { "rdma", 2, 0, 'R' },
+ { "grace-time", 1, 0, 'G'},
+ { "lease-time", 1, 0, 'L'},
+ { NULL, 0, 0, 0 }
+};
+
+inline static void
+read_nfsd_conf(void)
+{
+ conf_init_file(NFS_CONFFILE);
+ xlog_set_debug("nfsd");
+}
+
+int
+main(int argc, char **argv)
+{
+ int count = NFSD_NPROC, c, i, error = 0, portnum, fd, found_one;
+ char *p, *progname, *port, *rdma_port = NULL;
+ char **haddr = NULL;
+ char *scope = NULL;
+ int hcounter = 0;
+ struct conf_list *hosts;
+ int socket_up = 0;
+ unsigned int minorvers = NFSCTL_MINDEFAULT;
+ unsigned int minorversset = NFSCTL_MINDEFAULT;
+ unsigned int minormask = 0;
+ unsigned int versbits = NFSCTL_VERDEFAULT;
+ unsigned int protobits = NFSCTL_PROTODEFAULT;
+ int grace = -1;
+ int lease = -1;
+ int force4dot0 = 0;
+
+ progname = basename(argv[0]);
+ haddr = xmalloc(sizeof(char *));
+ haddr[0] = NULL;
+
+ xlog_syslog(0);
+ xlog_stderr(1);
+
+ /* Read in config setting */
+ read_nfsd_conf();
+
+ nfssvc_get_minormask(&minormask);
+
+ count = conf_get_num("nfsd", "threads", count);
+ grace = conf_get_num("nfsd", "grace-time", grace);
+ lease = conf_get_num("nfsd", "lease-time", lease);
+ port = conf_get_str("nfsd", "port");
+ if (!port)
+ port = "nfs";
+ if (conf_get_bool("nfsd", "rdma", false)) {
+ rdma_port = conf_get_str("nfsd", "rdma-port");
+ if (!rdma_port)
+ rdma_port = "nfsrdma";
+ }
+ /* backward compatibility - nfs.conf used to set rdma port directly */
+ if (!rdma_port)
+ rdma_port = conf_get_str("nfsd", "rdma");
+ if (conf_get_bool("nfsd", "udp", NFSCTL_UDPISSET(protobits)))
+ NFSCTL_UDPSET(protobits);
+ else
+ NFSCTL_UDPUNSET(protobits);
+ if (conf_get_bool("nfsd", "tcp", NFSCTL_TCPISSET(protobits)))
+ NFSCTL_TCPSET(protobits);
+ else
+ NFSCTL_TCPUNSET(protobits);
+ for (i = 2; i <= 4; i++) {
+ char tag[20];
+ sprintf(tag, "vers%d", i);
+ if (conf_get_bool("nfsd", tag, NFSCTL_VERISSET(versbits, i))) {
+ NFSCTL_VERSET(versbits, i);
+ if (i == 4)
+ minorvers = minorversset = minormask;
+ } else {
+ NFSCTL_VERUNSET(versbits, i);
+ if (i == 4) {
+ minorvers = 0;
+ minorversset = minormask;
+ }
+ }
+ }
+
+ /* We assume the kernel will default all minor versions to 'on',
+ * and allow the config file to disable some.
+ */
+ for (i = NFS4_MINMINOR; i <= NFS4_MAXMINOR; i++) {
+ char tag[20];
+ sprintf(tag, "vers4.%d", i);
+ /* The default for minor version support is to let the
+ * kernel decide. We could ask the kernel what that choice
+ * will be, but that is needlessly complex.
+ * Instead, perform a config-file lookup using each of the
+ * two possible default. If the result is different from the
+ * default, then impose that value, else don't make a change
+ * (i.e. don't set the bit in minorversset).
+ */
+ if (!conf_get_bool("nfsd", tag, 1)) {
+ NFSCTL_MINORSET(minorversset, i);
+ NFSCTL_MINORUNSET(minorvers, i);
+ if (i == 0)
+ force4dot0 = 1;
+ }
+ if (conf_get_bool("nfsd", tag, 0)) {
+ NFSCTL_MINORSET(minorversset, i);
+ NFSCTL_MINORSET(minorvers, i);
+ if (i == 0)
+ force4dot0 = 1;
+ }
+ }
+
+ hosts = conf_get_list("nfsd", "host");
+ if (hosts && hosts->cnt) {
+ struct conf_list_node *n;
+ haddr = realloc(haddr, sizeof(char*) * hosts->cnt);
+ TAILQ_FOREACH(n, &(hosts->fields), link) {
+ haddr[hcounter] = n->field;
+ hcounter++;
+ }
+ }
+ scope = conf_get_str("nfsd", "scope");
+
+ while ((c = getopt_long(argc, argv, "dH:S:hN:V:p:P:stTuUrG:L:", longopts, NULL)) != EOF) {
+ switch(c) {
+ case 'd':
+ xlog_config(D_ALL, 1);
+ break;
+ case 'H':
+ if (hosts) {
+ hosts = NULL;
+ hcounter = 0;
+ }
+ if (hcounter) {
+ haddr = realloc(haddr, sizeof(char*) * hcounter+1);
+ if(!haddr) {
+ fprintf(stderr, "%s: unable to allocate "
+ "memory.\n", progname);
+ exit(1);
+ }
+ }
+ haddr[hcounter] = optarg;
+ hcounter++;
+ break;
+ case 'S':
+ scope = optarg;
+ break;
+ case 'P': /* XXX for nfs-server compatibility */
+ case 'p':
+ /* only the last -p option has any effect */
+ port = optarg;
+ break;
+ case 'r':
+ rdma_port = "nfsrdma";
+ break;
+ case 'R': /* --rdma */
+ if (optarg)
+ rdma_port = optarg;
+ else
+ rdma_port = "nfsrdma";
+ break;
+
+ case 'N':
+ switch((c = strtol(optarg, &p, 0))) {
+ case 4:
+ if (*p == '.') {
+ int i = atoi(p+1);
+ if (i < 0 || i > NFS4_MAXMINOR) {
+ fprintf(stderr, "%s: unsupported minor version\n", optarg);
+ exit(1);
+ }
+ NFSCTL_MINORSET(minorversset, i);
+ NFSCTL_MINORUNSET(minorvers, i);
+ if (i == 0)
+ force4dot0 = 1;
+ if (minorvers != 0)
+ break;
+ } else {
+ minorvers = 0;
+ minorversset = minormask;
+ }
+ /* FALLTHRU */
+ case 3:
+ NFSCTL_VERUNSET(versbits, c);
+ break;
+ default:
+ fprintf(stderr, "%s: Unsupported version\n", optarg);
+ exit(1);
+ }
+ break;
+ case 'V':
+ switch((c = strtol(optarg, &p, 0))) {
+ case 4:
+ if (*p == '.') {
+ int i = atoi(p+1);
+ if (i < 0 || i > NFS4_MAXMINOR) {
+ fprintf(stderr, "%s: unsupported minor version\n", optarg);
+ exit(1);
+ }
+ NFSCTL_MINORSET(minorversset, i);
+ NFSCTL_MINORSET(minorvers, i);
+ if (i == 0)
+ force4dot0 = 1;
+ } else
+ minorvers = minorversset = minormask;
+ /* FALLTHRU */
+ case 3:
+ NFSCTL_VERSET(versbits, c);
+ break;
+ default:
+ fprintf(stderr, "%s: Unsupported version\n", optarg);
+ exit(1);
+ }
+ break;
+ case 's':
+ xlog_syslog(1);
+ xlog_stderr(0);
+ break;
+ case 't':
+ NFSCTL_TCPSET(protobits);
+ break;
+ case 'T':
+ NFSCTL_TCPUNSET(protobits);
+ break;
+ case 'u':
+ NFSCTL_UDPSET(protobits);
+ break;
+ case 'U':
+ NFSCTL_UDPUNSET(protobits);
+ break;
+ case 'G':
+ grace = strtol(optarg, &p, 0);
+ if (*p || grace <= 0) {
+ fprintf(stderr, "%s: Unrecognized grace time.\n", optarg);
+ exit(1);
+ }
+ break;
+ case 'L':
+ lease = strtol(optarg, &p, 0);
+ if (*p || lease <= 0) {
+ fprintf(stderr, "%s: Unrecognized lease time.\n", optarg);
+ exit(1);
+ }
+ break;
+ default:
+ fprintf(stderr, "Invalid argument: '%c'\n", c);
+ /* FALLTHRU */
+ case 'h':
+ usage(progname);
+ }
+ }
+
+ if (optind < argc) {
+ if ((count = atoi(argv[optind])) < 0) {
+ /* insane # of servers */
+ fprintf(stderr,
+ "%s: invalid server count (%d), using 1\n",
+ argv[0], count);
+ count = 1;
+ } else if (count == 0) {
+ /*
+ * don't bother setting anything else if the threads
+ * are coming down anyway.
+ */
+ socket_up = 1;
+ goto set_threads;
+ }
+ }
+
+ xlog_open(progname);
+
+ portnum = strtol(port, &p, 0);
+ if (!*p && (portnum <= 0 || portnum > 65535)) {
+ /* getaddrinfo will catch other errors, but not
+ * out-of-range numbers.
+ */
+ xlog(L_ERROR, "invalid port number: %s", port);
+ exit(1);
+ }
+
+ /* make sure that at least one version is enabled */
+ found_one = 0;
+ for (c = NFSD_MINVERS; c <= NFSD_MAXVERS; c++) {
+ if (NFSCTL_VERISSET(versbits, c))
+ found_one = 1;
+ }
+ if (!found_one) {
+ xlog(L_ERROR, "no version specified");
+ exit(1);
+ }
+
+ if (NFSCTL_VERISSET(versbits, 4) &&
+ !NFSCTL_TCPISSET(protobits)) {
+ xlog(L_ERROR, "version 4 requires the TCP protocol");
+ exit(1);
+ }
+
+ if (chdir(NFS_STATEDIR)) {
+ xlog(L_ERROR, "chdir(%s) failed: %m", NFS_STATEDIR);
+ exit(1);
+ }
+
+ /* make sure nfsdfs is mounted if it's available */
+ nfssvc_mount_nfsdfs(progname);
+
+ /* can only change number of threads if nfsd is already up */
+ if (nfssvc_inuse()) {
+ socket_up = 1;
+ goto set_threads;
+ }
+
+ /*
+ * Must set versions before the fd's so that the right versions get
+ * registered with rpcbind. Note that on older kernels w/o the right
+ * interfaces, these are a no-op.
+ * Timeouts must also be set before ports are created else we get
+ * EBUSY.
+ */
+ nfssvc_setvers(versbits, minorvers, minorversset, force4dot0);
+ if (grace > 0)
+ nfssvc_set_time("grace", grace);
+ if (lease > 0)
+ nfssvc_set_time("lease", lease);
+
+ if (scope) {
+ if (unshare(CLONE_NEWUTS) < 0 ||
+ sethostname(scope, strlen(scope)) < 0) {
+ xlog(L_ERROR, "Unable to set server scope: %m");
+ error = -1;
+ goto out;
+ }
+ }
+ i = 0;
+ do {
+ error = nfssvc_set_sockets(protobits, haddr[i], port);
+ if (!error)
+ socket_up = 1;
+ } while (++i < hcounter);
+
+ if (rdma_port) {
+ error = nfssvc_set_rdmaport(rdma_port);
+ if (!error)
+ socket_up = 1;
+ }
+set_threads:
+ /* don't start any threads if unable to hand off any sockets */
+ if (!socket_up) {
+ xlog(L_ERROR, "unable to set any sockets for nfsd");
+ goto out;
+ }
+ error = 0;
+
+ /*
+ * KLUDGE ALERT:
+ * Some kernels let nfsd kernel threads inherit open files
+ * from the program that spawns them (i.e. us). So close
+ * everything before spawning kernel threads. --Chip
+ */
+ fd = open("/dev/null", O_RDWR);
+ if (fd == -1)
+ xlog(L_ERROR, "Unable to open /dev/null: %m");
+ else {
+ /* switch xlog output to syslog since stderr is being closed */
+ xlog_syslog(1);
+ xlog_stderr(0);
+ (void) dup2(fd, 0);
+ (void) dup2(fd, 1);
+ (void) dup2(fd, 2);
+ }
+ closeall(3);
+
+ if ((error = nfssvc_threads(count)) < 0)
+ xlog(L_ERROR, "error starting threads: errno %d (%m)", errno);
+out:
+ free(haddr);
+ return (error != 0);
+}
+
+static void
+usage(const char *prog)
+{
+ fprintf(stderr, "Usage:\n"
+ "%s [-d|--debug] [-H hostname] [-p|-P|--port port]\n"
+ " [-N|--no-nfs-version version] [-V|--nfs-version version]\n"
+ " [-s|--syslog] [-t|--tcp] [-T|--no-tcp] [-u|--udp] [-U|--no-udp]\n"
+ " [-r|--rdma=] [-G|--grace-time secs] [-L|--leasetime secs] nrservs\n",
+ prog);
+ exit(2);
+}
diff --git a/utils/nfsd/nfsd.man b/utils/nfsd/nfsd.man
new file mode 100644
index 0000000..6f4fc1d
--- /dev/null
+++ b/utils/nfsd/nfsd.man
@@ -0,0 +1,203 @@
+.\"
+.\" nfsd(8)
+.\"
+.\" Copyright (C) 1999 Olaf Kirch <okir@monad.swb.de>
+.TH rpc.nfsd 8 "20 Feb 2014"
+.SH NAME
+rpc.nfsd \- NFS server process
+.SH SYNOPSIS
+.BI "/usr/sbin/rpc.nfsd [" options "]" " "nproc
+.SH DESCRIPTION
+The
+.B rpc.nfsd
+program implements the user level part of the NFS service. The
+main functionality is handled by the
+.B nfsd
+kernel module. The user space program merely specifies what sort of sockets
+the kernel service should listen on, what NFS versions it should support, and
+how many kernel threads it should use.
+.P
+The
+.B rpc.mountd
+server provides an ancillary service needed to satisfy mount requests
+by NFS clients.
+.SH OPTIONS
+.TP
+.B \-d " or " \-\-debug
+enable logging of debugging messages
+.TP
+.B \-H " or " \-\-host hostname
+specify a particular hostname (or address) that NFS requests will
+be accepted on. By default,
+.B rpc.nfsd
+will accept NFS requests on all known network addresses.
+Note that
+.B lockd
+(which performs file locking services for NFS) may still accept
+request on all known network addresses. This may change in future
+releases of the Linux Kernel. This option can be used multiple times
+to listen to more than one interface.
+.TP
+.B \-S " or " \-\-scope scope
+NFSv4.1 and later require the server to report a "scope" which is used
+by the clients to detect if two connections are to the same server.
+By default Linux NFSD uses the host name as the scope.
+.sp
+It is particularly important for high-availablity configurations to ensure
+that all potential server nodes report the same server scope.
+.TP
+.B \-p " or " \-\-port port
+specify a different port to listen on for NFS requests. By default,
+.B rpc.nfsd
+will listen on port 2049.
+.TP
+.B \-r " or " \-\-rdma
+specify that NFS requests on the standard RDMA port ("nfsrdma", port
+20049) should be honored.
+.TP
+.BI \-\-rdma= port
+Listen for RDMA requests on an alternate port - may be a number or a
+name listed in
+.BR /etc/services .
+.TP
+.B \-N " or " \-\-no-nfs-version vers
+This option can be used to request that
+.B rpc.nfsd
+does not offer certain versions of NFS. The current version of
+.B rpc.nfsd
+can support major NFS versions 3,4 and the minor versions 4.0, 4.1 and 4.2.
+.TP
+.B \-s " or " \-\-syslog
+By default,
+.B rpc.nfsd
+logs error messages (and debug messages, if enabled) to stderr. This option makes
+.B rpc.nfsd
+log these messages to syslog instead. Note that errors encountered during
+option processing will still be logged to stderr regardless of this option.
+.TP
+.B \-t " or " \-\-tcp
+Instruct the kernel nfs server to open and listen on a TCP socket. This is the default.
+.TP
+.B \-T " or " \-\-no-tcp
+Instruct the kernel nfs server not to open and listen on a TCP socket.
+.TP
+.B \-u " or " \-\-udp
+Instruct the kernel nfs server to open and listen on a UDP socket.
+.TP
+.B \-U " or " \-\-no-udp
+Instruct the kernel nfs server not to open and listen on a UDP socket. This is the default.
+.TP
+.B \-V " or " \-\-nfs-version vers
+This option can be used to request that
+.B rpc.nfsd
+offer certain versions of NFS. The current version of
+.B rpc.nfsd
+can support major NFS versions 3,4 and the minor versions 4.0, 4.1 and 4.2.
+.TP
+.B \-L " or " \-\-lease-time seconds
+Set the lease-time used for NFSv4. This corresponds to how often
+clients need to confirm their state with the server. Valid range is
+from 10 to 3600 seconds.
+.TP
+.B \-G " or " \-\-grace-time seconds
+Set the grace-time used for NFSv4 and NLM (for NFSv2 and NFSv3).
+New file open requests (NFSv4) and new file locks (NLM) will not be
+allowed until after this time has passed to allow clients to recover state.
+.TP
+.I nproc
+specify the number of NFS server threads. By default, eight
+threads are started. However, for optimum performance several threads
+should be used. The actual figure depends on the number of and the work
+load created by the NFS clients, but a useful starting point is
+eight threads. Effects of modifying that number can be checked using
+the
+.BR nfsstat (8)
+program.
+.P
+Note that if the NFS server is already running, then the options for
+specifying host, port, and protocol will be ignored. The number of
+processes given will be the only option considered, and the number of
+active
+.B nfsd
+processes will be increased or decreased to match this number.
+In particular
+.B rpc.nfsd 0
+will stop all threads and thus close any open connections.
+
+.SH CONFIGURATION FILE
+Many of the options that can be set on the command line can also be
+controlled through values set in the
+.B [nfsd]
+section of the
+.I /etc/nfs.conf
+configuration file. Values recognized include:
+.TP
+.B threads
+The number of threads to start.
+.TP
+.B host
+A host name, or comma separated list of host names, that
+.I rpc.nfsd
+will listen on. Use of the
+.B --host
+option replaces all host names listed here.
+.TP
+.B scope
+Set the server scope.
+.TP
+.B grace-time
+The grace time, for both NFSv4 and NLM, in seconds.
+.TP
+.B lease-time
+The lease time for NFSv4, in seconds.
+.TP
+.B port
+Set the port for TCP/UDP to bind to.
+.TP
+.B rdma
+Enable RDMA port (with "on" or "yes" etc) on the standard port
+("nfsrdma", port 20049).
+.TP
+.B rdma-port
+Set an alternate RDMA port.
+.TP
+.B UDP
+Enable (with "on" or "yes" etc) or disable ("off", "no") UDP support.
+.TP
+.B TCP
+Enable or disable TCP support.
+.TP
+.B vers3
+.TP
+.B vers4
+Enable or disable
+.B all
+NFSv4 versions. All versions are normally enabled
+by default.
+.TP
+.B vers4.1
+.TP
+.B vers4.2
+Setting these to "off" or similar will disable the selected minor
+versions. Setting to "on" will enable them. The default values
+are determined by the kernel, and usually minor versions default to
+being enabled once the implementation is sufficiently complete.
+
+.SH NOTES
+If the program is built with TI-RPC support, it will enable any protocol and
+address family combinations that are marked visible in the
+.B netconfig
+database.
+
+.SH SEE ALSO
+.BR nfsd (7),
+.BR rpc.mountd (8),
+.BR exports (5),
+.BR exportfs (8),
+.BR nfs.conf (5),
+.BR rpc.rquotad (8),
+.BR nfsstat (8),
+.BR netconfig(5).
+.SH AUTHOR
+Olaf Kirch, Bill Hawes, H. J. Lu, G. Allan Morris III,
+and a host of others.
diff --git a/utils/nfsd/nfssvc.c b/utils/nfsd/nfssvc.c
new file mode 100644
index 0000000..46452d9
--- /dev/null
+++ b/utils/nfsd/nfssvc.c
@@ -0,0 +1,438 @@
+/*
+ * utils/nfsd/nfssvc.c
+ *
+ * Run an NFS daemon.
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "nfslib.h"
+#include "xlog.h"
+#include "nfssvc.h"
+#include "version.h"
+
+#ifndef NFSD_FS_DIR
+#define NFSD_FS_DIR "/proc/fs/nfsd"
+#endif
+
+#define NFSD_PORTS_FILE NFSD_FS_DIR "/portlist"
+#define NFSD_VERS_FILE NFSD_FS_DIR "/versions"
+#define NFSD_THREAD_FILE NFSD_FS_DIR "/threads"
+
+/*
+ * declaring a common static scratch buffer here keeps us from having to
+ * continually thrash the stack. The value of 128 bytes here is really just a
+ * SWAG and can be increased if necessary. It ought to be enough for the
+ * routines below however.
+ */
+char buf[128];
+
+/*
+ * Using the "new" interfaces for nfsd requires that /proc/fs/nfsd is
+ * actually mounted. Make an attempt to mount it here if it doesn't appear
+ * to be.
+ */
+void
+nfssvc_mount_nfsdfs(char *progname)
+{
+ int err;
+ struct stat statbuf;
+
+ err = stat(NFSD_THREAD_FILE, &statbuf);
+ if (err == 0)
+ return;
+
+ if (errno != ENOENT) {
+ xlog(L_ERROR, "Unable to stat %s: errno %d (%m)",
+ NFSD_THREAD_FILE, errno);
+ return;
+ }
+
+ /*
+ * this call can return an error if modprobe is set up to automatically
+ * mount nfsdfs when nfsd.ko is plugged in. So, ignore the return
+ * code from it and just check for the "threads" file afterward.
+ */
+ err = system("/bin/mount -t nfsd nfsd " NFSD_FS_DIR " >/dev/null 2>&1");
+
+ err = stat(NFSD_THREAD_FILE, &statbuf);
+ if (err == 0)
+ return;
+
+ xlog(L_WARNING, "Unable to access " NFSD_FS_DIR " errno %d (%m)."
+ "\nPlease try, as root, 'mount -t nfsd nfsd " NFSD_FS_DIR
+ "' and then restart %s to correct the problem", errno, progname);
+
+ return;
+}
+
+/*
+ * Are there already sockets configured? If not, then it is safe to try to
+ * open some and pass them through.
+ *
+ * Note: If the user explicitly asked for 'udp', then we should probably check
+ * if that is open, and should open it if not. However we don't yet. All
+ * sockets have to be opened when the first daemon is started.
+ */
+int
+nfssvc_inuse(void)
+{
+ int fd, n;
+
+ fd = open(NFSD_PORTS_FILE, O_RDONLY);
+
+ /* problem opening file, assume that nothing is configured */
+ if (fd < 0)
+ return 0;
+
+ n = read(fd, buf, sizeof(buf));
+ close(fd);
+
+ xlog(D_GENERAL, "knfsd is currently %s", (n > 0) ? "up" : "down");
+
+ return (n > 0);
+}
+
+static int
+nfssvc_setfds(const struct addrinfo *hints, const char *node, const char *port)
+{
+ int fd, on = 1, fac = L_ERROR;
+ int sockfd = -1, rc = 0, bounded = 0;
+ struct addrinfo *addrhead = NULL, *addr;
+ char *proto, *family;
+
+ /*
+ * if file can't be opened, fail.
+ */
+ fd = open(NFSD_PORTS_FILE, O_WRONLY);
+ if (fd < 0)
+ return 0;
+
+ rc = getaddrinfo(node, port, hints, &addrhead);
+ if (rc == EAI_NONAME && !strcmp(port, "nfs")) {
+ snprintf(buf, sizeof(buf), "%d", NFS_PORT);
+ rc = getaddrinfo(node, buf, hints, &addrhead);
+ }
+
+ if (rc != 0) {
+ xlog(L_ERROR, "unable to resolve %s:%s: %s",
+ node ? node : "ANYADDR", port,
+ rc == EAI_SYSTEM ? strerror(errno) :
+ gai_strerror(rc));
+ goto error;
+ }
+
+ addr = addrhead;
+ while(addr) {
+ /* skip non-TCP / non-UDP sockets */
+ switch(addr->ai_protocol) {
+ case IPPROTO_UDP:
+ proto = "UDP";
+ break;
+ case IPPROTO_TCP:
+ proto = "TCP";
+ break;
+ default:
+ addr = addr->ai_next;
+ continue;
+ }
+
+ switch(addr->ai_addr->sa_family) {
+ case AF_INET:
+ family = "AF_INET";
+ break;
+#ifdef IPV6_SUPPORTED
+ case AF_INET6:
+ family = "AF_INET6";
+ break;
+#endif /* IPV6_SUPPORTED */
+ default:
+ addr = addr->ai_next;
+ continue;
+ }
+
+ /* open socket and prepare to hand it off to kernel */
+ sockfd = socket(addr->ai_family, addr->ai_socktype,
+ addr->ai_protocol);
+ if (sockfd < 0) {
+ if (errno != EAFNOSUPPORT) {
+ xlog(L_ERROR, "unable to create %s %s socket: "
+ "errno %d (%m)", family, proto, errno);
+ rc = errno;
+ goto error;
+ }
+ addr = addr->ai_next;
+ continue;
+ }
+
+ xlog(D_GENERAL, "Created %s %s socket.", family, proto);
+
+#ifdef IPV6_SUPPORTED
+ if (addr->ai_family == AF_INET6 &&
+ setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on))) {
+ xlog(L_ERROR, "unable to set IPV6_V6ONLY: "
+ "errno %d (%m)\n", errno);
+ rc = errno;
+ goto error;
+ }
+#endif /* IPV6_SUPPORTED */
+ if (addr->ai_protocol == IPPROTO_TCP &&
+ setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) {
+ xlog(L_ERROR, "unable to set SO_REUSEADDR on %s "
+ "socket: errno %d (%m)", family, errno);
+ rc = errno;
+ goto error;
+ }
+ if (bind(sockfd, addr->ai_addr, addr->ai_addrlen)) {
+ xlog(L_ERROR, "unable to bind %s %s socket: "
+ "errno %d (%m)", family, proto, errno);
+ rc = errno;
+ goto error;
+ }
+ if (addr->ai_protocol == IPPROTO_TCP && listen(sockfd, 64)) {
+ xlog(L_ERROR, "unable to create listening socket: "
+ "errno %d (%m)", errno);
+ rc = errno;
+ goto error;
+ }
+
+ if (fd < 0)
+ fd = open(NFSD_PORTS_FILE, O_WRONLY);
+
+ if (fd < 0) {
+ xlog(L_ERROR, "couldn't open ports file: errno "
+ "%d (%m)", errno);
+ goto error;
+ }
+
+ snprintf(buf, sizeof(buf), "%d\n", sockfd);
+ if (write(fd, buf, strlen(buf)) != (ssize_t)strlen(buf)) {
+ /*
+ * this error may be common on older kernels that don't
+ * support IPv6, so turn into a debug message.
+ */
+ if (errno == EAFNOSUPPORT)
+ fac = D_ALL;
+ xlog(fac, "writing fd to kernel failed: errno %d (%m)",
+ errno);
+ rc = errno;
+ goto error;
+ }
+ bounded++;
+
+ close(fd);
+ close(sockfd);
+ sockfd = fd = -1;
+ addr = addr->ai_next;
+ }
+error:
+ if (fd >= 0)
+ close(fd);
+ if (sockfd >= 0)
+ close(sockfd);
+ nfs_freeaddrinfo(addrhead);
+ return (bounded ? 0 : rc);
+}
+
+int
+nfssvc_set_sockets(const unsigned int protobits,
+ const char *host, const char *port)
+{
+ struct addrinfo hints = { .ai_flags = AI_PASSIVE };
+
+#ifdef IPV6_SUPPORTED
+ hints.ai_family = AF_UNSPEC;
+#else /* IPV6_SUPPORTED */
+ hints.ai_family = AF_INET;
+#endif /* IPV6_SUPPORTED */
+
+ if (!NFSCTL_ANYPROTO(protobits))
+ return EPROTOTYPE;
+ else if (!NFSCTL_UDPISSET(protobits))
+ hints.ai_protocol = IPPROTO_TCP;
+ else if (!NFSCTL_TCPISSET(protobits))
+ hints.ai_protocol = IPPROTO_UDP;
+
+ return nfssvc_setfds(&hints, host, port);
+}
+
+int
+nfssvc_set_rdmaport(const char *port)
+{
+ struct servent *sv = getservbyname(port, "tcp");
+ int nport;
+ char buf[20];
+ int ret;
+ int fd;
+
+ if (sv)
+ nport = ntohs(sv->s_port);
+ else {
+ char *ep;
+ nport = strtol(port, &ep, 10);
+ if (!*port || *ep) {
+ xlog(L_ERROR, "unable to interpret port name %s",
+ port);
+ return 1;
+ }
+ }
+
+ fd = open(NFSD_PORTS_FILE, O_WRONLY);
+ if (fd < 0)
+ return 1;
+ snprintf(buf, sizeof(buf), "rdma %d", nport);
+ ret = 0;
+ if (write(fd, buf, strlen(buf)) != (ssize_t)strlen(buf)) {
+ ret= errno;
+ xlog(L_ERROR, "Unable to request RDMA services: %m");
+ }
+ close(fd);
+ return ret;
+}
+
+void
+nfssvc_set_time(const char *type, const int seconds)
+{
+ char pathbuf[40];
+ char nbuf[10];
+ int fd;
+
+ snprintf(pathbuf, sizeof(pathbuf), NFSD_FS_DIR "/nfsv4%stime", type);
+ snprintf(nbuf, sizeof(nbuf), "%d", seconds);
+ fd = open(pathbuf, O_WRONLY);
+ if (fd >= 0) {
+ if (write(fd, nbuf, strlen(nbuf)) != (ssize_t)strlen(nbuf))
+ xlog(L_ERROR, "Unable to set nfsv4%stime: %m", type);
+ close(fd);
+ }
+ if (strcmp(type, "grace") == 0) {
+ /* set same value for lockd */
+ fd = open("/proc/sys/fs/nfs/nlm_grace_period", O_WRONLY);
+ if (fd >= 0) {
+ if (write(fd, nbuf, strlen(nbuf)) != (ssize_t)strlen(nbuf))
+ xlog(L_ERROR, "Unable to write nlm_grace_period : %m");
+ close(fd);
+ }
+ }
+}
+
+void
+nfssvc_get_minormask(unsigned int *mask)
+{
+ int fd;
+ char *ptr = buf;
+ ssize_t size;
+
+ fd = open(NFSD_VERS_FILE, O_RDONLY);
+ if (fd < 0)
+ return;
+
+ size = read(fd, buf, sizeof(buf));
+ if (size < 0) {
+ xlog(L_ERROR, "Getting versions failed: errno %d (%m)", errno);
+ goto out;
+ }
+ ptr[size] = '\0';
+ for (;;) {
+ unsigned vers, minor = 0;
+ char *token = strtok(ptr, " ");
+
+ if (!token)
+ break;
+ ptr = NULL;
+ if (*token != '+' && *token != '-')
+ continue;
+ if (sscanf(++token, "%u.%u", &vers, &minor) > 0 &&
+ vers == 4 && minor <= NFS4_MAXMINOR)
+ NFSCTL_MINORSET(*mask, minor);
+ }
+out:
+ close(fd);
+ return;
+}
+
+static int
+nfssvc_print_vers(char *ptr, unsigned size, unsigned vers, unsigned minorvers,
+ int isset, int force4dot0)
+{
+ char sign = isset ? '+' : '-';
+ if (minorvers == 0)
+ if (linux_version_code() < MAKE_VERSION(4, 11, 0) || !force4dot0)
+ return snprintf(ptr, size, "%c%u ", sign, vers);
+ return snprintf(ptr, size, "%c%u.%u ", sign, vers, minorvers);
+}
+
+void
+nfssvc_setvers(unsigned int ctlbits, unsigned int minorvers, unsigned int minorversset,
+ int force4dot0)
+{
+ int fd, n, off;
+
+ off = 0;
+ fd = open(NFSD_VERS_FILE, O_WRONLY);
+ if (fd < 0)
+ return;
+
+ for (n = NFSD_MINVERS; n <= ((NFSD_MAXVERS < 3) ? NFSD_MAXVERS : 3); n++)
+ off += nfssvc_print_vers(&buf[off], sizeof(buf) - off,
+ n, 0, NFSCTL_VERISSET(ctlbits, n), 0);
+
+ for (n = 0; n <= NFS4_MAXMINOR; n++) {
+ if (!NFSCTL_MINORISSET(minorversset, n))
+ continue;
+ off += nfssvc_print_vers(&buf[off], sizeof(buf) - off,
+ 4, n, NFSCTL_MINORISSET(minorvers, n),
+ (n == 0) ? force4dot0 : 0);
+ }
+ if (!off--)
+ goto out;
+ buf[off] = '\0';
+ xlog(D_GENERAL, "Writing version string to kernel: %s", buf);
+ snprintf(&buf[off], sizeof(buf) - off, "\n");
+ if (write(fd, buf, strlen(buf)) != (ssize_t)strlen(buf))
+ xlog(L_ERROR, "Setting version failed: errno %d (%m)", errno);
+out:
+ close(fd);
+
+ return;
+}
+
+int
+nfssvc_threads(const int nrservs)
+{
+ ssize_t n;
+ int fd;
+
+ fd = open(NFSD_THREAD_FILE, O_WRONLY);
+ if (fd < 0)
+ fd = open("/proc/fs/nfs/threads", O_WRONLY);
+ if (fd >= 0) {
+ /* 2.5+ kernel with nfsd filesystem mounted.
+ * Just write the number of threads.
+ */
+ snprintf(buf, sizeof(buf), "%d\n", nrservs);
+ n = write(fd, buf, strlen(buf));
+ close(fd);
+ if (n != (ssize_t)strlen(buf))
+ return -1;
+ else
+ return 0;
+ }
+ return -1;
+}
diff --git a/utils/nfsd/nfssvc.h b/utils/nfsd/nfssvc.h
new file mode 100644
index 0000000..4d53af1
--- /dev/null
+++ b/utils/nfsd/nfssvc.h
@@ -0,0 +1,32 @@
+/*
+ * utils/nfsd/nfssvc.h -- nfs service control routines for rpc.nfsd
+ *
+ * Copyright (C) 2009 Red Hat, Inc <nfs@redhat.com>.
+ * Copyright (C) 2009 Jeff Layton <jlayton@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * 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
+ *
+ */
+
+void nfssvc_mount_nfsdfs(char *progname);
+int nfssvc_inuse(void);
+int nfssvc_set_sockets(const unsigned int protobits,
+ const char *host, const char *port);
+void nfssvc_set_time(const char *type, const int seconds);
+int nfssvc_set_rdmaport(const char *port);
+void nfssvc_setvers(unsigned int ctlbits, unsigned int minorvers4,
+ unsigned int minorvers4set, int force4dot0);
+int nfssvc_threads(int nrservs);
+void nfssvc_get_minormask(unsigned int *mask);
diff --git a/utils/nfsdcld/Makefile.am b/utils/nfsdcld/Makefile.am
new file mode 100644
index 0000000..273d64f
--- /dev/null
+++ b/utils/nfsdcld/Makefile.am
@@ -0,0 +1,15 @@
+## Process this file with automake to produce Makefile.in
+
+man8_MANS = nfsdcld.man
+EXTRA_DIST = $(man8_MANS)
+
+AM_CFLAGS += -D_LARGEFILE64_SOURCE
+sbin_PROGRAMS = nfsdcld
+
+nfsdcld_SOURCES = nfsdcld.c sqlite.c legacy.c
+nfsdcld_LDADD = ../../support/nfs/libnfs.la $(LIBEVENT) $(LIBSQLITE) $(LIBCAP)
+
+noinst_HEADERS = sqlite.h cld-internal.h legacy.h
+
+MAINTAINERCLEANFILES = Makefile.in
+
diff --git a/utils/nfsdcld/Makefile.in b/utils/nfsdcld/Makefile.in
new file mode 100644
index 0000000..5ada9cf
--- /dev/null
+++ b/utils/nfsdcld/Makefile.in
@@ -0,0 +1,822 @@
+# 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@
+sbin_PROGRAMS = nfsdcld$(EXEEXT)
+subdir = utils/nfsdcld
+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 $(noinst_HEADERS) \
+ $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/support/include/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man8dir)"
+PROGRAMS = $(sbin_PROGRAMS)
+am_nfsdcld_OBJECTS = nfsdcld.$(OBJEXT) sqlite.$(OBJEXT) \
+ legacy.$(OBJEXT)
+nfsdcld_OBJECTS = $(am_nfsdcld_OBJECTS)
+am__DEPENDENCIES_1 =
+nfsdcld_DEPENDENCIES = ../../support/nfs/libnfs.la \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+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 =
+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)/legacy.Po ./$(DEPDIR)/nfsdcld.Po \
+ ./$(DEPDIR)/sqlite.Po
+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 = $(nfsdcld_SOURCES)
+DIST_SOURCES = $(nfsdcld_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+man8dir = $(mandir)/man8
+NROFF = nroff
+MANS = $(man8_MANS)
+HEADERS = $(noinst_HEADERS)
+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@ -D_LARGEFILE64_SOURCE
+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@
+man8_MANS = nfsdcld.man
+EXTRA_DIST = $(man8_MANS)
+nfsdcld_SOURCES = nfsdcld.c sqlite.c legacy.c
+nfsdcld_LDADD = ../../support/nfs/libnfs.la $(LIBEVENT) $(LIBSQLITE) $(LIBCAP)
+noinst_HEADERS = sqlite.h cld-internal.h legacy.h
+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 utils/nfsdcld/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu utils/nfsdcld/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):
+install-sbinPROGRAMS: $(sbin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(sbindir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-sbinPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(sbindir)" && rm -f $$files
+
+clean-sbinPROGRAMS:
+ @list='$(sbin_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+nfsdcld$(EXEEXT): $(nfsdcld_OBJECTS) $(nfsdcld_DEPENDENCIES) $(EXTRA_nfsdcld_DEPENDENCIES)
+ @rm -f nfsdcld$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(nfsdcld_OBJECTS) $(nfsdcld_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/legacy.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nfsdcld.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sqlite.Po@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 $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-man8: $(man8_MANS)
+ @$(NORMAL_INSTALL)
+ @list1='$(man8_MANS)'; \
+ list2=''; \
+ test -n "$(man8dir)" \
+ && test -n "`echo $$list1$$list2`" \
+ || exit 0; \
+ echo " $(MKDIR_P) '$(DESTDIR)$(man8dir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(man8dir)" || exit 1; \
+ { for i in $$list1; do echo "$$i"; done; \
+ if test -n "$$list2"; then \
+ for i in $$list2; do echo "$$i"; done \
+ | sed -n '/\.8[a-z]*$$/p'; \
+ fi; \
+ } | while read p; do \
+ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; echo "$$p"; \
+ done | \
+ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \
+ sed 'N;N;s,\n, ,g' | { \
+ list=; while read file base inst; do \
+ if test "$$base" = "$$inst"; then list="$$list $$file"; else \
+ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man8dir)/$$inst'"; \
+ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man8dir)/$$inst" || exit $$?; \
+ fi; \
+ done; \
+ for i in $$list; do echo "$$i"; done | $(am__base_list) | \
+ while read files; do \
+ test -z "$$files" || { \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man8dir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(man8dir)" || exit $$?; }; \
+ done; }
+
+uninstall-man8:
+ @$(NORMAL_UNINSTALL)
+ @list='$(man8_MANS)'; test -n "$(man8dir)" || exit 0; \
+ files=`{ for i in $$list; do echo "$$i"; done; \
+ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
+ dir='$(DESTDIR)$(man8dir)'; $(am__uninstall_files_from_dir)
+
+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 $(PROGRAMS) $(MANS) $(HEADERS)
+installdirs:
+ for dir in "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man8dir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+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-sbinPROGRAMS \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/legacy.Po
+ -rm -f ./$(DEPDIR)/nfsdcld.Po
+ -rm -f ./$(DEPDIR)/sqlite.Po
+ -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-man
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-sbinPROGRAMS
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man: install-man8
+
+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)/legacy.Po
+ -rm -f ./$(DEPDIR)/nfsdcld.Po
+ -rm -f ./$(DEPDIR)/sqlite.Po
+ -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: uninstall-man uninstall-sbinPROGRAMS
+
+uninstall-man: uninstall-man8
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-sbinPROGRAMS 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-man8 install-pdf \
+ install-pdf-am install-ps install-ps-am install-sbinPROGRAMS \
+ 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 \
+ uninstall-man uninstall-man8 uninstall-sbinPROGRAMS
+
+.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/utils/nfsdcld/cld-internal.h b/utils/nfsdcld/cld-internal.h
new file mode 100644
index 0000000..3576515
--- /dev/null
+++ b/utils/nfsdcld/cld-internal.h
@@ -0,0 +1,44 @@
+/*
+ * 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 02110-1301, USA.
+ */
+
+#ifndef _CLD_INTERNAL_H_
+#define _CLD_INTERNAL_H_
+
+#if CLD_UPCALL_VERSION >= 2
+#define UPCALL_VERSION 2
+#else
+#define UPCALL_VERSION 1
+#endif
+
+struct cld_client {
+ int cl_fd;
+ struct event *cl_event;
+ union {
+ struct cld_msg cl_msg;
+#if UPCALL_VERSION >= 2
+ struct cld_msg_v2 cl_msg_v2;
+#endif
+ } cl_u;
+};
+
+extern uint64_t current_epoch;
+extern uint64_t recovery_epoch;
+extern int first_time;
+extern int num_cltrack_records;
+extern int num_legacy_records;
+
+#endif /* _CLD_INTERNAL_H_ */
diff --git a/utils/nfsdcld/legacy.c b/utils/nfsdcld/legacy.c
new file mode 100644
index 0000000..b89374c
--- /dev/null
+++ b/utils/nfsdcld/legacy.c
@@ -0,0 +1,171 @@
+/*
+ * 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 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <dirent.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <limits.h>
+#include "cld.h"
+#include "sqlite.h"
+#include "xlog.h"
+#include "legacy.h"
+
+#define NFSD_RECDIR_FILE "/proc/fs/nfsd/nfsv4recoverydir"
+
+/*
+ * Loads client records from the v4recovery directory into the database.
+ * Records are prefixed with the string "hash:" and include the '\0' byte.
+ *
+ * Called during database initialization as part of a one-time "upgrade".
+ */
+void
+legacy_load_clients_from_recdir(int *num_records)
+{
+ int fd;
+ DIR *v4recovery;
+ struct dirent *entry;
+ char recdirname[PATH_MAX+1];
+ char buf[NFS4_OPAQUE_LIMIT];
+ char *nl;
+ ssize_t n;
+
+ fd = open(NFSD_RECDIR_FILE, O_RDONLY);
+ if (fd < 0) {
+ xlog(D_GENERAL, "Unable to open %s: %m", NFSD_RECDIR_FILE);
+ return;
+ }
+ n = read(fd, recdirname, PATH_MAX);
+ close(fd);
+ if (n < 0) {
+ xlog(D_GENERAL, "Unable to read from %s: %m", NFSD_RECDIR_FILE);
+ return;
+ }
+ /* the output from the proc file isn't null-terminated */
+ recdirname[PATH_MAX] = '\0';
+ nl = strchr(recdirname, '\n');
+ if (!nl)
+ return;
+ *nl = '\0';
+ v4recovery = opendir(recdirname);
+ if (!v4recovery)
+ return;
+ while ((entry = readdir(v4recovery))) {
+ int ret;
+
+ /* skip "." and ".." */
+ if (entry->d_name[0] == '.') {
+ switch (entry->d_name[1]) {
+ case '\0':
+ continue;
+ case '.':
+ if (entry->d_name[2] == '\0')
+ continue;
+ }
+ }
+ /* prefix legacy records with the string "hash:" */
+ ret = snprintf(buf, sizeof(buf), "hash:%s", entry->d_name);
+ /* if there's a problem, then skip this entry */
+ if (ret < 0 || (size_t)ret >= sizeof(buf)) {
+ xlog(L_WARNING, "%s: unable to build client string for %s!",
+ __func__, entry->d_name);
+ continue;
+ }
+ /* legacy client records need to include the null terminator */
+ ret = sqlite_insert_client((unsigned char *)buf, strlen(buf) + 1);
+ if (ret)
+ xlog(L_WARNING, "%s: unable to insert %s: %d", __func__,
+ entry->d_name, ret);
+ else
+ (*num_records)++;
+ }
+ closedir(v4recovery);
+}
+
+/*
+ * Cleans out the v4recovery directory.
+ *
+ * Called upon receipt of the first "GraceDone" upcall only.
+ */
+void
+legacy_clear_recdir(void)
+{
+ int fd;
+ DIR *v4recovery;
+ struct dirent *entry;
+ char recdirname[PATH_MAX+1];
+ char dirname[PATH_MAX];
+ char *nl;
+ ssize_t n;
+
+ fd = open(NFSD_RECDIR_FILE, O_RDONLY);
+ if (fd < 0) {
+ xlog(D_GENERAL, "Unable to open %s: %m", NFSD_RECDIR_FILE);
+ return;
+ }
+ n = read(fd, recdirname, PATH_MAX);
+ close(fd);
+ if (n < 0) {
+ xlog(D_GENERAL, "Unable to read from %s: %m", NFSD_RECDIR_FILE);
+ return;
+ }
+ /* the output from the proc file isn't null-terminated */
+ recdirname[PATH_MAX] = '\0';
+ nl = strchr(recdirname, '\n');
+ if (!nl)
+ return;
+ *nl = '\0';
+ v4recovery = opendir(recdirname);
+ if (!v4recovery)
+ return;
+ while ((entry = readdir(v4recovery))) {
+ int len;
+
+ /* skip "." and ".." */
+ if (entry->d_name[0] == '.') {
+ switch (entry->d_name[1]) {
+ case '\0':
+ continue;
+ case '.':
+ if (entry->d_name[2] == '\0')
+ continue;
+ }
+ }
+ len = snprintf(dirname, sizeof(dirname), "%s/%s", recdirname,
+ entry->d_name);
+ /* if there's a problem, then skip this entry */
+ if (len < 0 || (size_t)len >= sizeof(dirname)) {
+ xlog(L_WARNING, "%s: unable to build filename for %s!",
+ __func__, entry->d_name);
+ continue;
+ }
+ len = rmdir(dirname);
+ if (len)
+ xlog(L_WARNING, "%s: unable to rmdir %s: %d", __func__,
+ dirname, len);
+ }
+ closedir(v4recovery);
+}
diff --git a/utils/nfsdcld/legacy.h b/utils/nfsdcld/legacy.h
new file mode 100644
index 0000000..8988f6e
--- /dev/null
+++ b/utils/nfsdcld/legacy.h
@@ -0,0 +1,24 @@
+/*
+ * 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 02110-1301, USA.
+ */
+
+#ifndef _LEGACY_H_
+#define _LEGACY_H_
+
+void legacy_load_clients_from_recdir(int *);
+void legacy_clear_recdir(void);
+
+#endif /* _LEGACY_H_ */
diff --git a/utils/nfsdcld/nfsdcld.c b/utils/nfsdcld/nfsdcld.c
new file mode 100644
index 0000000..dbc7a57
--- /dev/null
+++ b/utils/nfsdcld/nfsdcld.c
@@ -0,0 +1,922 @@
+/*
+ * nfsdcld.c -- NFSv4 client name tracking daemon
+ *
+ * Copyright (C) 2011 Red Hat, Jeff Layton <jlayton@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * 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 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <errno.h>
+#include <event2/event.h>
+#include <stdbool.h>
+#include <getopt.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <libgen.h>
+#include <sys/inotify.h>
+#ifdef HAVE_SYS_CAPABILITY_H
+#include <sys/prctl.h>
+#include <sys/capability.h>
+#endif
+
+#include "xlog.h"
+#include "nfslib.h"
+#include "cld.h"
+#include "cld-internal.h"
+#include "sqlite.h"
+#include "version.h"
+#include "conffile.h"
+#include "legacy.h"
+
+#ifndef DEFAULT_PIPEFS_DIR
+#define DEFAULT_PIPEFS_DIR NFS_STATEDIR "/rpc_pipefs"
+#endif
+
+#define DEFAULT_CLD_PATH "/nfsd/cld"
+
+#ifndef CLD_DEFAULT_STORAGEDIR
+#define CLD_DEFAULT_STORAGEDIR NFS_STATEDIR "/nfsdcld"
+#endif
+
+#define NFSD_END_GRACE_FILE "/proc/fs/nfsd/v4_end_grace"
+
+/* private data structures */
+
+/* global variables */
+static char pipefs_dir[PATH_MAX] = DEFAULT_PIPEFS_DIR;
+static char pipepath[PATH_MAX];
+static int inotify_fd = -1;
+static struct event *pipedir_event;
+static struct event_base *evbase;
+static bool old_kernel = false;
+static bool signal_received = false;
+
+uint64_t current_epoch;
+uint64_t recovery_epoch;
+int first_time;
+int num_cltrack_records;
+int num_legacy_records;
+
+static struct option longopts[] =
+{
+ { "help", 0, NULL, 'h' },
+ { "foreground", 0, NULL, 'F' },
+ { "debug", 0, NULL, 'd' },
+ { "pipefsdir", 1, NULL, 'p' },
+ { "storagedir", 1, NULL, 's' },
+ { NULL, 0, 0, 0 },
+};
+
+/* forward declarations */
+static void cldcb(int UNUSED(fd), short which, void *data);
+
+static void
+sig_die(int signal)
+{
+ if (signal_received) {
+ xlog(D_GENERAL, "forced exiting on signal %d\n", signal);
+ exit(0);
+ }
+
+ signal_received = true;
+ xlog(D_GENERAL, "exiting on signal %d\n", signal);
+ event_base_loopexit(evbase, NULL);
+}
+
+static void
+usage(char *progname)
+{
+ printf("%s [ -hFd ] [ -p pipefsdir ] [ -s storagedir ]\n", progname);
+}
+
+static int
+cld_set_caps(void)
+{
+ int ret = 0;
+#ifdef HAVE_SYS_CAPABILITY_H
+ unsigned long i;
+ cap_t caps;
+
+ if (getuid() != 0) {
+ xlog(L_ERROR, "Not running as root. Daemon won't be able to "
+ "open the pipe after dropping capabilities!");
+ return -EINVAL;
+ }
+
+ /* prune the bounding set to nothing */
+ for (i = 0; prctl(PR_CAPBSET_READ, i, 0, 0, 0) >= 0 ; ++i) {
+ ret = prctl(PR_CAPBSET_DROP, i, 0, 0, 0);
+ if (ret) {
+ xlog(L_ERROR, "Unable to prune capability %lu from "
+ "bounding set: %m", i);
+ return -errno;
+ }
+ }
+
+ /* get a blank capset */
+ caps = cap_init();
+ if (caps == NULL) {
+ xlog(L_ERROR, "Unable to get blank capability set: %m");
+ return -errno;
+ }
+
+ /* reset the process capabilities */
+ if (cap_set_proc(caps) != 0) {
+ xlog(L_ERROR, "Unable to set process capabilities: %m");
+ ret = -errno;
+ }
+ cap_free(caps);
+#endif
+ return ret;
+}
+
+#define INOTIFY_EVENT_MAX (sizeof(struct inotify_event) + NAME_MAX)
+
+static int
+cld_pipe_open(struct cld_client *clnt)
+{
+ int fd;
+ struct event *ev;
+
+ xlog(D_GENERAL, "%s: opening upcall pipe %s", __func__, pipepath);
+ fd = open(pipepath, O_RDWR, 0);
+ if (fd < 0) {
+ xlog(D_GENERAL, "%s: open of %s failed: %m", __func__, pipepath);
+ return -errno;
+ }
+
+ ev = event_new(evbase, fd, EV_READ, cldcb, clnt);
+ if (ev == NULL) {
+ xlog(D_GENERAL, "%s: failed to create event for %s", __func__, pipepath);
+ close(fd);
+ return -ENOMEM;
+ }
+
+ if (clnt->cl_event && event_initialized(clnt->cl_event)) {
+ event_del(clnt->cl_event);
+ event_free(clnt->cl_event);
+ }
+ if (clnt->cl_fd >= 0)
+ close(clnt->cl_fd);
+
+ clnt->cl_fd = fd;
+ clnt->cl_event = ev;
+ /* event_add is done by the caller */
+ return 0;
+}
+
+static void
+cld_inotify_cb(int UNUSED(fd), short which, void *data)
+{
+ int ret;
+ size_t elen;
+ ssize_t rret;
+ char evbuf[INOTIFY_EVENT_MAX];
+ char *dirc = NULL, *pname;
+ struct inotify_event *event = (struct inotify_event *)evbuf;
+ struct cld_client *clnt = data;
+
+ if (which != EV_READ)
+ return;
+
+ xlog(D_GENERAL, "%s: called for EV_READ", __func__);
+
+ dirc = strndup(pipepath, PATH_MAX);
+ if (!dirc) {
+ xlog(L_ERROR, "%s: unable to allocate memory", __func__);
+ goto out;
+ }
+
+ rret = read(inotify_fd, evbuf, INOTIFY_EVENT_MAX);
+ if (rret < 0) {
+ xlog(L_ERROR, "%s: read from inotify fd failed: %m", __func__);
+ goto out;
+ }
+
+ /* check to see if we have a filename in the evbuf */
+ if (!event->len) {
+ xlog(D_GENERAL, "%s: no filename in inotify event", __func__);
+ goto out;
+ }
+
+ pname = basename(dirc);
+ elen = strnlen(event->name, event->len);
+
+ /* does the filename match our pipe? */
+ if (strlen(pname) != elen || memcmp(pname, event->name, elen)) {
+ xlog(D_GENERAL, "%s: wrong filename (%s)", __func__,
+ event->name);
+ goto out;
+ }
+
+ ret = cld_pipe_open(clnt);
+ switch (ret) {
+ case 0:
+ /* readd the event for the cl_event pipe */
+ event_add(clnt->cl_event, NULL);
+ break;
+ case -ENOENT:
+ /* pipe must have disappeared, wait for it to come back */
+ goto out;
+ default:
+ /* anything else is fatal */
+ xlog(L_FATAL, "%s: unable to open new pipe (%d). Aborting.",
+ __func__, ret);
+ exit(ret);
+ }
+
+out:
+ event_add(pipedir_event, NULL);
+ free(dirc);
+}
+
+static int
+cld_inotify_setup(void)
+{
+ int ret;
+ char *dirc, *dname;
+
+ dirc = strndup(pipepath, PATH_MAX);
+ if (!dirc) {
+ xlog_err("%s: unable to allocate memory", __func__);
+ ret = -ENOMEM;
+ goto out_free;
+ }
+
+ dname = dirname(dirc);
+
+ inotify_fd = inotify_init();
+ if (inotify_fd < 0) {
+ xlog_err("%s: inotify_init failed: %m", __func__);
+ ret = -errno;
+ goto out_free;
+ }
+
+ ret = inotify_add_watch(inotify_fd, dname, IN_CREATE);
+ if (ret < 0) {
+ xlog_err("%s: inotify_add_watch failed: %m", __func__);
+ ret = -errno;
+ goto out_err;
+ } else
+ ret = 0;
+
+out_free:
+ free(dirc);
+ return ret;
+out_err:
+ close(inotify_fd);
+ goto out_free;
+}
+
+/*
+ * Set an inotify watch on the directory that should contain the pipe, and then
+ * try to open it. If it fails with anything but -ENOENT, return the error
+ * immediately.
+ *
+ * If it succeeds, then set up the pipe event handler. At that point, set up
+ * the inotify event handler and go ahead and return success.
+ */
+static int
+cld_pipe_init(struct cld_client *clnt)
+{
+ int ret;
+
+ xlog(D_GENERAL, "%s: init pipe handlers", __func__);
+
+ ret = cld_inotify_setup();
+ if (ret != 0)
+ goto out;
+
+ clnt->cl_fd = -1;
+ ret = cld_pipe_open(clnt);
+ switch (ret) {
+ case 0:
+ /* add the event and we're good to go */
+ event_add(clnt->cl_event, NULL);
+ break;
+ case -ENOENT:
+ /* ignore this error -- cld_inotify_cb will handle it */
+ ret = 0;
+ break;
+ default:
+ /* anything else is fatal */
+ close(inotify_fd);
+ goto out;
+ }
+
+ /* set event for inotify read */
+ pipedir_event = event_new(evbase, inotify_fd, EV_READ, cld_inotify_cb, clnt);
+ if (pipedir_event == NULL) {
+ close(inotify_fd);
+ return -ENOMEM;
+ }
+ event_add(pipedir_event, NULL);
+out:
+ return ret;
+}
+
+/*
+ * Older kernels will not tell nfsdcld when a grace period has started.
+ * Therefore we have to peek at the /proc/fs/nfsd/v4_end_grace file to
+ * see if nfsd is in grace. We have to do this for create and remove
+ * upcalls to ensure that the correct table is being updated - otherwise
+ * we could lose client records when the grace period is lifted.
+ */
+static int
+cld_check_grace_period(void)
+{
+ int fd, ret = 0;
+ char c;
+
+ if (!old_kernel)
+ return 0;
+ if (recovery_epoch != 0)
+ return 0;
+ fd = open(NFSD_END_GRACE_FILE, O_RDONLY);
+ if (fd < 0) {
+ xlog(L_WARNING, "Unable to open %s: %m",
+ NFSD_END_GRACE_FILE);
+ return 1;
+ }
+ if (read(fd, &c, 1) < 0) {
+ xlog(L_WARNING, "Unable to read from %s: %m",
+ NFSD_END_GRACE_FILE);
+ close(fd);
+ return 1;
+ }
+ close(fd);
+ if (c == 'N') {
+ xlog(L_WARNING, "nfsd is in grace but didn't send a gracestart upcall, "
+ "please update the kernel");
+ ret = sqlite_grace_start();
+ }
+ return ret;
+}
+
+#if UPCALL_VERSION >= 2
+static ssize_t cld_message_size(void *msg)
+{
+ struct cld_msg_hdr *hdr = (struct cld_msg_hdr *)msg;
+
+ switch (hdr->cm_vers) {
+ case 1:
+ return sizeof(struct cld_msg);
+ case 2:
+ return sizeof(struct cld_msg_v2);
+ default:
+ xlog(L_FATAL, "%s invalid upcall version %d", __func__,
+ hdr->cm_vers);
+ exit(-EINVAL);
+ }
+}
+#else
+static ssize_t cld_message_size(void *UNUSED(msg))
+{
+ return sizeof(struct cld_msg);
+}
+#endif
+
+static void
+cld_not_implemented(struct cld_client *clnt)
+{
+ int ret;
+ ssize_t bsize, wsize;
+#if UPCALL_VERSION >= 2
+ struct cld_msg_v2 *cmsg = &clnt->cl_u.cl_msg_v2;
+#else
+ struct cld_msg *cmsg = &clnt->cl_u.cl_msg;
+#endif
+
+ xlog(D_GENERAL, "%s: downcalling with not implemented error", __func__);
+
+ /* set up reply */
+ cmsg->cm_status = -EOPNOTSUPP;
+
+ bsize = cld_message_size(cmsg);
+ wsize = atomicio((void *)write, clnt->cl_fd, cmsg, bsize);
+ if (wsize != bsize)
+ xlog(L_ERROR, "%s: problem writing to cld pipe (%zd): %m",
+ __func__, wsize);
+
+ /* reopen pipe, just to be sure */
+ ret = cld_pipe_open(clnt);
+ if (ret) {
+ xlog(L_FATAL, "%s: unable to reopen pipe: %d", __func__, ret);
+ exit(ret);
+ }
+}
+
+static void
+cld_get_version(struct cld_client *clnt)
+{
+ int ret;
+ ssize_t bsize, wsize;
+#if UPCALL_VERSION >= 2
+ struct cld_msg_v2 *cmsg = &clnt->cl_u.cl_msg_v2;
+#else
+ struct cld_msg *cmsg = &clnt->cl_u.cl_msg;
+#endif
+
+ xlog(D_GENERAL, "%s: version = %u.", __func__, UPCALL_VERSION);
+
+ cmsg->cm_u.cm_version = UPCALL_VERSION;
+ cmsg->cm_status = 0;
+
+ bsize = cld_message_size(cmsg);
+ xlog(D_GENERAL, "Doing downcall with status %d", cmsg->cm_status);
+ wsize = atomicio((void *)write, clnt->cl_fd, cmsg, bsize);
+ if (wsize != bsize) {
+ xlog(L_ERROR, "%s: problem writing to cld pipe (%zd): %m",
+ __func__, wsize);
+ ret = cld_pipe_open(clnt);
+ if (ret) {
+ xlog(L_FATAL, "%s: unable to reopen pipe: %d",
+ __func__, ret);
+ exit(ret);
+ }
+ }
+}
+
+static void
+cld_create(struct cld_client *clnt)
+{
+ int ret;
+ ssize_t bsize, wsize;
+#if UPCALL_VERSION >= 2
+ struct cld_msg_v2 *cmsg = &clnt->cl_u.cl_msg_v2;
+#else
+ struct cld_msg *cmsg = &clnt->cl_u.cl_msg;
+#endif
+
+ ret = cld_check_grace_period();
+ if (ret)
+ goto reply;
+
+ xlog(D_GENERAL, "%s: create client record.", __func__);
+
+#if UPCALL_VERSION >= 2
+ if (cmsg->cm_vers >= 2)
+ ret = sqlite_insert_client_and_princhash(
+ cmsg->cm_u.cm_clntinfo.cc_name.cn_id,
+ cmsg->cm_u.cm_clntinfo.cc_name.cn_len,
+ cmsg->cm_u.cm_clntinfo.cc_princhash.cp_data,
+ cmsg->cm_u.cm_clntinfo.cc_princhash.cp_len);
+ else
+ ret = sqlite_insert_client(cmsg->cm_u.cm_name.cn_id,
+ cmsg->cm_u.cm_name.cn_len);
+#else
+ ret = sqlite_insert_client(cmsg->cm_u.cm_name.cn_id,
+ cmsg->cm_u.cm_name.cn_len);
+#endif
+
+reply:
+ cmsg->cm_status = ret ? -EREMOTEIO : ret;
+
+ bsize = cld_message_size(cmsg);
+ xlog(D_GENERAL, "Doing downcall with status %d", cmsg->cm_status);
+ wsize = atomicio((void *)write, clnt->cl_fd, cmsg, bsize);
+ if (wsize != bsize) {
+ xlog(L_ERROR, "%s: problem writing to cld pipe (%zd): %m",
+ __func__, wsize);
+ ret = cld_pipe_open(clnt);
+ if (ret) {
+ xlog(L_FATAL, "%s: unable to reopen pipe: %d",
+ __func__, ret);
+ exit(ret);
+ }
+ }
+}
+
+static void
+cld_remove(struct cld_client *clnt)
+{
+ int ret;
+ ssize_t bsize, wsize;
+#if UPCALL_VERSION >= 2
+ struct cld_msg_v2 *cmsg = &clnt->cl_u.cl_msg_v2;
+#else
+ struct cld_msg *cmsg = &clnt->cl_u.cl_msg;
+#endif
+
+ ret = cld_check_grace_period();
+ if (ret)
+ goto reply;
+
+ xlog(D_GENERAL, "%s: remove client record.", __func__);
+
+ ret = sqlite_remove_client(cmsg->cm_u.cm_name.cn_id,
+ cmsg->cm_u.cm_name.cn_len);
+
+reply:
+ cmsg->cm_status = ret ? -EREMOTEIO : ret;
+
+ bsize = cld_message_size(cmsg);
+ xlog(D_GENERAL, "%s: downcall with status %d", __func__,
+ cmsg->cm_status);
+ wsize = atomicio((void *)write, clnt->cl_fd, cmsg, bsize);
+ if (wsize != bsize) {
+ xlog(L_ERROR, "%s: problem writing to cld pipe (%zd): %m",
+ __func__, wsize);
+ ret = cld_pipe_open(clnt);
+ if (ret) {
+ xlog(L_FATAL, "%s: unable to reopen pipe: %d",
+ __func__, ret);
+ exit(ret);
+ }
+ }
+}
+
+static void
+cld_check(struct cld_client *clnt)
+{
+ int ret;
+ ssize_t bsize, wsize;
+#if UPCALL_VERSION >= 2
+ struct cld_msg_v2 *cmsg = &clnt->cl_u.cl_msg_v2;
+#else
+ struct cld_msg *cmsg = &clnt->cl_u.cl_msg;
+#endif
+
+ /*
+ * If we get a check upcall at all, it means we're talking to an old
+ * kernel. Furthermore, if we're not in grace it means this is the
+ * first client to do a reclaim. Log a message and use
+ * sqlite_grace_start() to advance the epoch numbers.
+ */
+ if (recovery_epoch == 0) {
+ xlog(D_GENERAL, "%s: received a check upcall, please update the kernel",
+ __func__);
+ ret = sqlite_grace_start();
+ if (ret)
+ goto reply;
+ }
+
+ xlog(D_GENERAL, "%s: check client record", __func__);
+
+ ret = sqlite_check_client(cmsg->cm_u.cm_name.cn_id,
+ cmsg->cm_u.cm_name.cn_len);
+
+reply:
+ /* set up reply */
+ cmsg->cm_status = ret ? -EACCES : ret;
+
+ bsize = cld_message_size(cmsg);
+ xlog(D_GENERAL, "%s: downcall with status %d", __func__,
+ cmsg->cm_status);
+ wsize = atomicio((void *)write, clnt->cl_fd, cmsg, bsize);
+ if (wsize != bsize) {
+ xlog(L_ERROR, "%s: problem writing to cld pipe (%zd): %m",
+ __func__, wsize);
+ ret = cld_pipe_open(clnt);
+ if (ret) {
+ xlog(L_FATAL, "%s: unable to reopen pipe: %d",
+ __func__, ret);
+ exit(ret);
+ }
+ }
+}
+
+static void
+cld_gracedone(struct cld_client *clnt)
+{
+ int ret;
+ ssize_t bsize, wsize;
+#if UPCALL_VERSION >= 2
+ struct cld_msg_v2 *cmsg = &clnt->cl_u.cl_msg_v2;
+#else
+ struct cld_msg *cmsg = &clnt->cl_u.cl_msg;
+#endif
+
+ /*
+ * If we got a "gracedone" upcall while we're not in grace, then
+ * 1) we must be talking to an old kernel
+ * 2) no clients attempted to reclaim
+ * In that case, log a message and use sqlite_grace_start() to
+ * advance the epoch numbers, and then proceed as normal.
+ */
+ if (recovery_epoch == 0) {
+ xlog(D_GENERAL, "%s: received gracedone upcall "
+ "while not in grace, please update the kernel",
+ __func__);
+ ret = sqlite_grace_start();
+ if (ret)
+ goto reply;
+ }
+
+ xlog(D_GENERAL, "%s: grace done.", __func__);
+
+ ret = sqlite_grace_done();
+
+ if (first_time) {
+ if (num_cltrack_records > 0)
+ sqlite_delete_cltrack_records();
+ if (num_legacy_records > 0)
+ legacy_clear_recdir();
+ sqlite_first_time_done();
+ first_time = 0;
+ }
+
+reply:
+ /* set up reply: downcall with 0 status */
+ cmsg->cm_status = ret ? -EREMOTEIO : ret;
+
+ bsize = cld_message_size(cmsg);
+ xlog(D_GENERAL, "Doing downcall with status %d", cmsg->cm_status);
+ wsize = atomicio((void *)write, clnt->cl_fd, cmsg, bsize);
+ if (wsize != bsize) {
+ xlog(L_ERROR, "%s: problem writing to cld pipe (%zd): %m",
+ __func__, wsize);
+ ret = cld_pipe_open(clnt);
+ if (ret) {
+ xlog(L_FATAL, "%s: unable to reopen pipe: %d",
+ __func__, ret);
+ exit(ret);
+ }
+ }
+}
+
+static int
+gracestart_callback(struct cld_client *clnt) {
+ ssize_t bsize, wsize;
+#if UPCALL_VERSION >= 2
+ struct cld_msg_v2 *cmsg = &clnt->cl_u.cl_msg_v2;
+#else
+ struct cld_msg *cmsg = &clnt->cl_u.cl_msg;
+#endif
+
+ cmsg->cm_status = -EINPROGRESS;
+
+ bsize = cld_message_size(cmsg);
+ xlog(D_GENERAL, "Sending client %.*s",
+ cmsg->cm_u.cm_name.cn_len, cmsg->cm_u.cm_name.cn_id);
+ wsize = atomicio((void *)write, clnt->cl_fd, cmsg, bsize);
+ if (wsize != bsize)
+ return -EIO;
+ return 0;
+}
+
+static void
+cld_gracestart(struct cld_client *clnt)
+{
+ int ret;
+ ssize_t bsize, wsize;
+#if UPCALL_VERSION >= 2
+ struct cld_msg_v2 *cmsg = &clnt->cl_u.cl_msg_v2;
+#else
+ struct cld_msg *cmsg = &clnt->cl_u.cl_msg;
+#endif
+
+ xlog(D_GENERAL, "%s: updating grace epochs", __func__);
+
+ ret = sqlite_grace_start();
+ if (ret)
+ goto reply;
+
+ xlog(D_GENERAL, "%s: sending client records to the kernel", __func__);
+
+ ret = sqlite_iterate_recovery(&gracestart_callback, clnt);
+
+reply:
+ /* set up reply: downcall with 0 status */
+ cmsg->cm_status = ret ? -EREMOTEIO : ret;
+
+ bsize = cld_message_size(cmsg);
+ xlog(D_GENERAL, "Doing downcall with status %d", cmsg->cm_status);
+ wsize = atomicio((void *)write, clnt->cl_fd, cmsg, bsize);
+ if (wsize != bsize) {
+ xlog(L_ERROR, "%s: problem writing to cld pipe (%zd): %m",
+ __func__, wsize);
+ ret = cld_pipe_open(clnt);
+ if (ret) {
+ xlog(L_FATAL, "%s: unable to reopen pipe: %d",
+ __func__, ret);
+ exit(ret);
+ }
+ }
+}
+
+static void
+cldcb(int UNUSED(fd), short which, void *data)
+{
+ ssize_t len;
+ struct cld_client *clnt = data;
+#if UPCALL_VERSION >= 2
+ struct cld_msg_v2 *cmsg = &clnt->cl_u.cl_msg_v2;
+#else
+ struct cld_msg *cmsg = &clnt->cl_u.cl_msg;
+#endif
+
+ if (which != EV_READ)
+ goto out;
+
+ len = atomicio(read, clnt->cl_fd, cmsg, sizeof(*cmsg));
+ if (len <= 0) {
+ xlog(L_ERROR, "%s: pipe read failed: %m", __func__);
+ cld_pipe_open(clnt);
+ goto out;
+ }
+
+ if (cmsg->cm_vers > UPCALL_VERSION) {
+ xlog(L_ERROR, "%s: unsupported upcall version: %hu",
+ __func__, cmsg->cm_vers);
+ cld_pipe_open(clnt);
+ goto out;
+ }
+
+ switch(cmsg->cm_cmd) {
+ case Cld_Create:
+ cld_create(clnt);
+ break;
+ case Cld_Remove:
+ cld_remove(clnt);
+ break;
+ case Cld_Check:
+ cld_check(clnt);
+ break;
+ case Cld_GraceDone:
+ cld_gracedone(clnt);
+ break;
+ case Cld_GraceStart:
+ cld_gracestart(clnt);
+ break;
+ case Cld_GetVersion:
+ cld_get_version(clnt);
+ break;
+ default:
+ xlog(L_WARNING, "%s: command %u is not yet implemented",
+ __func__, cmsg->cm_cmd);
+ cld_not_implemented(clnt);
+ }
+out:
+ event_add(clnt->cl_event, NULL);
+}
+
+int
+main(int argc, char **argv)
+{
+ int arg;
+ int rc = 0;
+ bool foreground = false;
+ char *progname;
+ char *storagedir = CLD_DEFAULT_STORAGEDIR;
+ struct cld_client clnt;
+ char *s;
+ first_time = 0;
+ num_cltrack_records = 0;
+ num_legacy_records = 0;
+
+ memset(&clnt, 0, sizeof(clnt));
+
+ progname = strdup(basename(argv[0]));
+ if (!progname) {
+ fprintf(stderr, "%s: unable to allocate memory.\n", argv[0]);
+ return 1;
+ }
+
+ evbase = event_base_new();
+ if (evbase == NULL) {
+ fprintf(stderr, "%s: unable to allocate event base.\n", argv[0]);
+ return 1;
+ }
+ xlog_syslog(0);
+ xlog_stderr(1);
+
+ conf_init_file(NFS_CONFFILE);
+ s = conf_get_str("general", "pipefs-directory");
+ if (s)
+ strlcpy(pipefs_dir, s, sizeof(pipefs_dir));
+ s = conf_get_str("nfsdcld", "storagedir");
+ if (s)
+ storagedir = s;
+ rc = conf_get_num("nfsdcld", "debug", 0);
+ if (rc > 0)
+ xlog_config(D_ALL, 1);
+
+ /* process command-line options */
+ while ((arg = getopt_long(argc, argv, "hdFp:s:", longopts,
+ NULL)) != EOF) {
+ switch (arg) {
+ case 'd':
+ xlog_config(D_ALL, 1);
+ break;
+ case 'F':
+ foreground = true;
+ break;
+ case 'p':
+ strlcpy(pipefs_dir, optarg, sizeof(pipefs_dir));
+ break;
+ case 's':
+ storagedir = optarg;
+ break;
+ default:
+ usage(progname);
+ free(progname);
+ return 0;
+ }
+ }
+
+ strlcpy(pipepath, pipefs_dir, sizeof(pipepath));
+ strlcat(pipepath, DEFAULT_CLD_PATH, sizeof(pipepath));
+
+ xlog_open(progname);
+ if (!foreground) {
+ xlog_syslog(1);
+ xlog_stderr(0);
+ rc = daemon(0, 0);
+ if (rc) {
+ xlog(L_ERROR, "Unable to daemonize: %m");
+ goto out;
+ }
+ }
+
+ /* drop all capabilities */
+ rc = cld_set_caps();
+ if (rc)
+ goto out;
+
+ /*
+ * now see if the storagedir is writable by root w/o CAP_DAC_OVERRIDE.
+ * If it isn't then give the user a warning but proceed as if
+ * everything is OK. If the DB has already been created, then
+ * everything might still work. If it doesn't exist at all, then
+ * assume that the maindb init will be able to create it. Fail on
+ * anything else.
+ */
+ if (access(storagedir, W_OK) == -1) {
+ switch (errno) {
+ case EACCES:
+ xlog(L_WARNING, "Storage directory %s is not writable. "
+ "Should be owned by root and writable "
+ "by owner!", storagedir);
+ break;
+ case ENOENT:
+ /* ignore and assume that we can create dir as root */
+ break;
+ default:
+ xlog(L_ERROR, "Unexpected error when checking access "
+ "on %s: %m", storagedir);
+ rc = -errno;
+ goto out;
+ }
+ }
+
+ if (linux_version_code() < MAKE_VERSION(4, 20, 0))
+ old_kernel = true;
+
+ /* set up storage db */
+ rc = sqlite_prepare_dbh(storagedir);
+ if (rc) {
+ xlog(L_ERROR, "Failed to open main database: %d", rc);
+ goto out;
+ }
+
+ /* set up event handler */
+ rc = cld_pipe_init(&clnt);
+ if (rc)
+ goto out;
+
+ signal(SIGINT, sig_die);
+ signal(SIGTERM, sig_die);
+
+ xlog(D_GENERAL, "%s: Starting event dispatch handler.", __func__);
+ rc = event_base_dispatch(evbase);
+ if (rc < 0)
+ xlog(L_ERROR, "%s: event_dispatch failed: %m", __func__);
+
+out:
+ if (clnt.cl_event)
+ event_free(clnt.cl_event);
+ if (clnt.cl_fd != -1)
+ close(clnt.cl_fd);
+ if (pipedir_event)
+ event_free(pipedir_event);
+ if (inotify_fd != -1)
+ close(inotify_fd);
+
+ event_base_free(evbase);
+ sqlite_shutdown();
+
+ free(progname);
+ return rc;
+}
diff --git a/utils/nfsdcld/nfsdcld.man b/utils/nfsdcld/nfsdcld.man
new file mode 100644
index 0000000..861f1c4
--- /dev/null
+++ b/utils/nfsdcld/nfsdcld.man
@@ -0,0 +1,221 @@
+.\" Automatically generated by Pod::Man 2.22 (Pod::Simple 3.13)
+.\"
+.\" Standard preamble:
+.\" ========================================================================
+.de Sp \" Vertical space (when we can't use .PP)
+.if t .sp .5v
+.if n .sp
+..
+.de Vb \" Begin verbatim text
+.ft CW
+.nf
+.ne \\$1
+..
+.de Ve \" End verbatim text
+.ft R
+.fi
+..
+.\" Set up some character translations and predefined strings. \*(-- will
+.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left
+.\" double quote, and \*(R" will give a right double quote. \*(C+ will
+.\" give a nicer C++. Capital omega is used to do unbreakable dashes and
+.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff,
+.\" nothing in troff, for use with C<>.
+.tr \(*W-
+.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p'
+.ie n \{\
+. ds -- \(*W-
+. ds PI pi
+. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch
+. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch
+. ds L" ""
+. ds R" ""
+. ds C` ""
+. ds C' ""
+'br\}
+.el\{\
+. ds -- \|\(em\|
+. ds PI \(*p
+. ds L" ``
+. ds R" ''
+'br\}
+.\"
+.\" Escape single quotes in literal strings from groff's Unicode transform.
+.ie \n(.g .ds Aq \(aq
+.el .ds Aq '
+.\"
+.\" If the F register is turned on, we'll generate index entries on stderr for
+.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index
+.\" entries marked with X<> in POD. Of course, you'll have to process the
+.\" output yourself in some meaningful fashion.
+.ie \nF \{\
+. de IX
+. tm Index:\\$1\t\\n%\t"\\$2"
+..
+. nr % 0
+. rr F
+.\}
+.el \{\
+. de IX
+..
+.\}
+.\"
+.\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2).
+.\" Fear. Run. Save yourself. No user-serviceable parts.
+. \" fudge factors for nroff and troff
+.if n \{\
+. ds #H 0
+. ds #V .8m
+. ds #F .3m
+. ds #[ \f1
+. ds #] \fP
+.\}
+.if t \{\
+. ds #H ((1u-(\\\\n(.fu%2u))*.13m)
+. ds #V .6m
+. ds #F 0
+. ds #[ \&
+. ds #] \&
+.\}
+. \" simple accents for nroff and troff
+.if n \{\
+. ds ' \&
+. ds ` \&
+. ds ^ \&
+. ds , \&
+. ds ~ ~
+. ds /
+.\}
+.if t \{\
+. ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u"
+. ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u'
+. ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u'
+. ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u'
+. ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u'
+. ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u'
+.\}
+. \" troff and (daisy-wheel) nroff accents
+.ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V'
+.ds 8 \h'\*(#H'\(*b\h'-\*(#H'
+.ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#]
+.ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H'
+.ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u'
+.ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#]
+.ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#]
+.ds ae a\h'-(\w'a'u*4/10)'e
+.ds Ae A\h'-(\w'A'u*4/10)'E
+. \" corrections for vroff
+.if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u'
+.if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u'
+. \" for low resolution devices (crt and lpr)
+.if \n(.H>23 .if \n(.V>19 \
+\{\
+. ds : e
+. ds 8 ss
+. ds o a
+. ds d- d\h'-1'\(ga
+. ds D- D\h'-1'\(hy
+. ds th \o'bp'
+. ds Th \o'LP'
+. ds ae ae
+. ds Ae AE
+.\}
+.rm #[ #] #H #V #F C
+.\" ========================================================================
+.\"
+.IX Title "NFSDCLD 8"
+.TH NFSDCLD 8 "2011-12-21" "" ""
+.\" For nroff, turn off justification. Always turn off hyphenation; it makes
+.\" way too many mistakes in technical documents.
+.if n .ad l
+.nh
+.SH "NAME"
+nfsdcld \- NFSv4 Client Tracking Daemon
+.SH "SYNOPSIS"
+.IX Header "SYNOPSIS"
+nfsdcld [\-d] [\-F] [\-p path] [\-s stable storage dir]
+.SH "DESCRIPTION"
+.IX Header "DESCRIPTION"
+nfsdcld is the NFSv4 client tracking daemon. It is not necessary to run
+this daemon on machines that are not acting as NFSv4 servers.
+.PP
+When a network partition is combined with a server reboot, there are
+edge conditions that can cause the server to grant lock reclaims when
+other clients have taken conflicting locks in the interim. A more detailed
+explanation of this issue is described in \s-1RFC\s0 3530, section 8.6.3.
+.PP
+In order to prevent these problems, the server must track a small amount
+of per-client information on stable storage. This daemon provides the
+userspace piece of that functionality.
+.SH "OPTIONS"
+.IX Header "OPTIONS"
+.IP "\fB\-d\fR, \fB\-\-debug\fR" 4
+.IX Item "-d, --debug"
+Enable debug level logging.
+.IP "\fB\-F\fR, \fB\-\-foreground\fR" 4
+.IX Item "-F, --foreground"
+Runs the daemon in the foreground and prints all output to stderr
+.IP "\fB\-p\fR \fIpath\fR, \fB\-\-pipefsdir\fR=\fIpath\fR" 4
+.IX Item "-p path, --pipefsdir=path"
+Location of the rpc_pipefs filesystem. The default value is
+\&\fI/var/lib/nfs/rpc_pipefs\fR.
+.IP "\fB\-s\fR \fIstorage_dir\fR, \fB\-\-storagedir\fR=\fIstorage_dir\fR" 4
+.IX Item "-s storagedir, --storagedir=storage_dir"
+Directory where stable storage information should be kept. The default
+value is \fI/var/lib/nfs/nfsdcld\fR.
+.SH "CONFIGURATION FILE"
+.IX Header "CONFIGURATION FILE"
+The following values are recognized in the \fB[nfsdcld]\fR section
+of the \fI/etc/nfs.conf\fR configuration file:
+.IP "\fBstoragedir\fR" 4
+.IX Item "storagedir"
+Equivalent to \fB\-s\fR/\fB\-\-storagedir\fR.
+.IP "\fBdebug\fR" 4
+.IX Item "debug"
+Setting "debug = 1" is equivalent to \fB\-d\fR/\fB\-\-debug\fR.
+.LP
+In addition, the following value is recognized from the \fB[general]\fR section:
+.IP "\fBpipefs\-directory\fR" 4
+.IX Item "pipefs-directory"
+Equivalent to \fB\-p\fR/\fB\-\-pipefsdir\fR.
+.SH "NOTES"
+.IX Header "NOTES"
+The Linux kernel NFSv4 server has historically tracked this information
+on stable storage by manipulating information on the filesystem
+directly, in the directory to which \fI/proc/fs/nfsd/nfsv4recoverydir\fR
+points.
+.PP
+This changed with the original introduction of \fBnfsdcld\fR upcall in kernel version 3.4,
+which was later deprecated in favor of the \fBnfsdcltrack\fR(8) usermodehelper
+program, support for which was added in kernel version 3.8. However, since the
+usermodehelper upcall does not work in containers, support for a new version of
+the \fBnfsdcld\fR upcall was added in kernel version 5.2.
+.PP
+This daemon requires a kernel that supports the \fBnfsdcld\fR upcall. On older kernels, if
+the legacy client name tracking code was in use, then the kernel would not create the
+pipe that \fBnfsdcld\fR uses to talk to the kernel. On newer kernels, nfsd attempts to
+initialize client tracking in the following order: First, the \fBnfsdcld\fR upcall. Second,
+the \fBnfsdcltrack\fR usermodehelper upcall. Finally, the legacy client tracking.
+.PP
+This daemon should be run as root, as the pipe that it uses to communicate
+with the kernel is only accessable by root. The daemon however does drop all
+superuser capabilities after starting. Because of this, the \fIstoragedir\fR
+should be owned by root, and be readable and writable by owner.
+.PP
+The daemon now supports different upcall versions to allow the kernel to pass additional
+data to be stored in the on-disk database. The kernel will query the supported upcall
+version from \fBnfsdcld\fR during client tracking initialization. A restart of \fBnfsd\fR is
+not necessary after upgrading \fBnfsdcld\fR, however \fBnfsd\fR will not use a later upcall
+version until restart. A restart of \fBnfsd is necessary\fR after downgrading \fBnfsdcld\fR,
+to ensure that \fBnfsd\fR does not use an upcall version that \fBnfsdcld\fR does not support.
+Additionally, a downgrade of \fBnfsdcld\fR requires the schema of the on-disk database to
+be downgraded as well. That can be accomplished using the \fBnfsdclddb\fR(8) utility.
+.SH FILES
+.TP
+.B /var/lib/nfs/nfsdcld/main.sqlite
+.SH SEE ALSO
+.BR nfsdcltrack "(8), " nfsdclddb (8)
+.SH "AUTHORS"
+.IX Header "AUTHORS"
+The nfsdcld daemon was developed by Jeff Layton <jlayton@redhat.com>
+with modifications from Scott Mayhew <smayhew@redhat.com>.
diff --git a/utils/nfsdcld/sqlite.c b/utils/nfsdcld/sqlite.c
new file mode 100644
index 0000000..03016fb
--- /dev/null
+++ b/utils/nfsdcld/sqlite.c
@@ -0,0 +1,1427 @@
+/*
+ * Copyright (C) 2011 Red Hat, Jeff Layton <jlayton@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * 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 02110-1301, USA.
+ */
+
+/*
+ * Explanation:
+ *
+ * This file contains the code to manage the sqlite backend database for the
+ * nfsdcld client tracking daemon.
+ *
+ * The main database is called main.sqlite and contains the following tables:
+ *
+ * parameters: simple key/value pairs for storing database info
+ *
+ * grace: a "current" column containing an INTEGER representing the current
+ * epoch (where should new values be stored) and a "recovery" column
+ * containing an INTEGER representing the recovery epoch (from what
+ * epoch are we allowed to recover). A recovery epoch of 0 means
+ * normal operation (grace period not in force). Note: sqlite stores
+ * integers as signed values, so these must be cast to a uint64_t when
+ * retrieving them from the database and back to an int64_t when storing
+ * them in the database.
+ *
+ * rec-CCCCCCCCCCCCCCCC (where C is the hex representation of the epoch value):
+ * an "id" column containing a BLOB with the long-form clientid
+ * as sent by the client, and a "princhash" column containing a BLOB
+ * with the sha256 hash of the kerberos principal (if available).
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <dirent.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <limits.h>
+#include <sqlite3.h>
+#include <linux/limits.h>
+#include <inttypes.h>
+
+#include "xlog.h"
+#include "sqlite.h"
+#include "cld.h"
+#include "cld-internal.h"
+#include "conffile.h"
+#include "legacy.h"
+#include "nfslib.h"
+
+#define CLD_SQLITE_LATEST_SCHEMA_VERSION 4
+#define CLTRACK_DEFAULT_STORAGEDIR NFS_STATEDIR "/nfsdcltrack"
+
+/* in milliseconds */
+#define CLD_SQLITE_BUSY_TIMEOUT 10000
+
+/* private data structures */
+
+/* global variables */
+static char *cltrack_storagedir = CLTRACK_DEFAULT_STORAGEDIR;
+
+/* reusable pathname and sql command buffer */
+static char buf[PATH_MAX];
+
+/* global database handle */
+static sqlite3 *dbh;
+
+/* forward declarations */
+
+/* make a directory, ignoring EEXIST errors unless it's not a directory */
+static int
+mkdir_if_not_exist(const char *dirname)
+{
+ int ret;
+ struct stat statbuf;
+
+ ret = mkdir(dirname, S_IRWXU);
+ if (ret && errno != EEXIST)
+ return -errno;
+
+ ret = stat(dirname, &statbuf);
+ if (ret)
+ return -errno;
+
+ if (!S_ISDIR(statbuf.st_mode))
+ ret = -ENOTDIR;
+
+ return ret;
+}
+
+static int
+sqlite_query_schema_version(void)
+{
+ int ret;
+ sqlite3_stmt *stmt = NULL;
+
+ /* prepare select query */
+ ret = sqlite3_prepare_v2(dbh,
+ "SELECT value FROM parameters WHERE key == \"version\";",
+ -1, &stmt, NULL);
+ if (ret != SQLITE_OK) {
+ xlog(D_GENERAL, "Unable to prepare select statement: %s",
+ sqlite3_errmsg(dbh));
+ ret = 0;
+ goto out;
+ }
+
+ /* query schema version */
+ ret = sqlite3_step(stmt);
+ if (ret != SQLITE_ROW) {
+ xlog(D_GENERAL, "Select statement execution failed: %s",
+ sqlite3_errmsg(dbh));
+ ret = 0;
+ goto out;
+ }
+
+ ret = sqlite3_column_int(stmt, 0);
+out:
+ sqlite3_finalize(stmt);
+ return ret;
+}
+
+static int
+sqlite_query_first_time(int *first_time)
+{
+ int ret;
+ sqlite3_stmt *stmt = NULL;
+
+ /* prepare select query */
+ ret = sqlite3_prepare_v2(dbh,
+ "SELECT value FROM parameters WHERE key == \"first_time\";",
+ -1, &stmt, NULL);
+ if (ret != SQLITE_OK) {
+ xlog(D_GENERAL, "Unable to prepare select statement: %s",
+ sqlite3_errmsg(dbh));
+ goto out;
+ }
+
+ /* query first_time */
+ ret = sqlite3_step(stmt);
+ if (ret != SQLITE_ROW) {
+ xlog(D_GENERAL, "Select statement execution failed: %s",
+ sqlite3_errmsg(dbh));
+ goto out;
+ }
+
+ *first_time = sqlite3_column_int(stmt, 0);
+ ret = 0;
+out:
+ sqlite3_finalize(stmt);
+ return ret;
+}
+
+static int
+sqlite_add_princ_col_cb(void *UNUSED(arg), int ncols, char **cols,
+ char **UNUSED(colnames))
+{
+ int ret;
+ char *err;
+
+ if (ncols > 1)
+ return -EINVAL;
+ ret = snprintf(buf, sizeof(buf), "ALTER TABLE \"%s\" "
+ "ADD COLUMN princhash BLOB;", cols[0]);
+ if (ret < 0) {
+ xlog(L_ERROR, "sprintf failed!");
+ return -EINVAL;
+ } else if ((size_t)ret >= sizeof(buf)) {
+ xlog(L_ERROR, "sprintf output too long! (%d chars)", ret);
+ return -EINVAL;
+ }
+ ret = sqlite3_exec(dbh, (const char *)buf, NULL, NULL, &err);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "Unable to add princhash column to table %s: %s",
+ cols[0], err);
+ goto out;
+ }
+ xlog(D_GENERAL, "Added princhash column to table %s", cols[0]);
+out:
+ sqlite3_free(err);
+ return ret;
+}
+
+static int
+sqlite_maindb_update_v3_to_v4(void)
+{
+ int ret;
+ char *err;
+
+ ret = sqlite3_exec(dbh, "SELECT name FROM sqlite_master "
+ "WHERE type=\"table\" AND name LIKE \"%rec-%\";",
+ sqlite_add_princ_col_cb, NULL, &err);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "%s: Failed to update tables!: %s", __func__, err);
+ }
+ sqlite3_free(err);
+ return ret;
+}
+
+static int
+sqlite_maindb_update_v1v2_to_v4(void)
+{
+ int ret;
+ char *err;
+
+ /* create grace table */
+ ret = sqlite3_exec(dbh, "CREATE TABLE grace "
+ "(current INTEGER , recovery INTEGER);",
+ NULL, NULL, &err);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "Unable to create grace table: %s", err);
+ goto out;
+ }
+
+ /* insert initial epochs into grace table */
+ ret = sqlite3_exec(dbh, "INSERT OR FAIL INTO grace "
+ "values (1, 0);",
+ NULL, NULL, &err);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "Unable to set initial epochs: %s", err);
+ goto out;
+ }
+
+ /* create recovery table for current epoch */
+ ret = sqlite3_exec(dbh, "CREATE TABLE \"rec-0000000000000001\" "
+ "(id BLOB PRIMARY KEY, princhash BLOB);",
+ NULL, NULL, &err);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "Unable to create recovery table "
+ "for current epoch: %s", err);
+ goto out;
+ }
+
+ /* copy records from old clients table */
+ ret = sqlite3_exec(dbh, "INSERT INTO \"rec-0000000000000001\" (id) "
+ "SELECT id FROM clients;",
+ NULL, NULL, &err);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "Unable to copy client records: %s", err);
+ goto out;
+ }
+
+ /* drop the old clients table */
+ ret = sqlite3_exec(dbh, "DROP TABLE clients;",
+ NULL, NULL, &err);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "Unable to drop old clients table: %s", err);
+ }
+out:
+ sqlite3_free(err);
+ return ret;
+}
+
+static int
+sqlite_maindb_update_schema(int oldversion)
+{
+ int ret, ret2;
+ char *err;
+
+ /* begin transaction */
+ ret = sqlite3_exec(dbh, "BEGIN EXCLUSIVE TRANSACTION;", NULL, NULL,
+ &err);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "Unable to begin transaction: %s", err);
+ goto rollback;
+ }
+
+ /*
+ * Check schema version again. This time, under an exclusive
+ * transaction to guard against racing DB setup attempts
+ */
+ ret = sqlite_query_schema_version();
+ if (ret != oldversion) {
+ if (ret == CLD_SQLITE_LATEST_SCHEMA_VERSION)
+ /* Someone else raced in and set it up */
+ ret = 0;
+ else
+ /* Something went wrong -- fail! */
+ ret = -EINVAL;
+ goto rollback;
+ }
+
+ /* Still at old version -- do conversion */
+
+ switch (oldversion) {
+ case 3:
+ case 2:
+ ret = sqlite_maindb_update_v3_to_v4();
+ break;
+ case 1:
+ ret = sqlite_maindb_update_v1v2_to_v4();
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ if (ret != SQLITE_OK)
+ goto rollback;
+
+ ret = snprintf(buf, sizeof(buf), "UPDATE parameters SET value = %d "
+ "WHERE key = \"version\";",
+ CLD_SQLITE_LATEST_SCHEMA_VERSION);
+ if (ret < 0) {
+ xlog(L_ERROR, "sprintf failed!");
+ goto rollback;
+ } else if ((size_t)ret >= sizeof(buf)) {
+ xlog(L_ERROR, "sprintf output too long! (%d chars)", ret);
+ ret = -EINVAL;
+ goto rollback;
+ }
+
+ ret = sqlite3_exec(dbh, (const char *)buf, NULL, NULL, &err);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "Unable to update schema version: %s", err);
+ goto rollback;
+ }
+
+ ret = sqlite_query_first_time(&first_time);
+ if (ret != SQLITE_OK) {
+ /* insert first_time into parameters table */
+ ret = sqlite3_exec(dbh, "INSERT OR FAIL INTO parameters "
+ "values (\"first_time\", \"1\");",
+ NULL, NULL, &err);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "Unable to insert into parameter table: %s", err);
+ goto rollback;
+ }
+ }
+
+ ret = sqlite3_exec(dbh, "COMMIT TRANSACTION;", NULL, NULL, &err);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "Unable to commit transaction: %s", err);
+ goto rollback;
+ }
+out:
+ sqlite3_free(err);
+ return ret;
+rollback:
+ ret2 = sqlite3_exec(dbh, "ROLLBACK TRANSACTION;", NULL, NULL, &err);
+ if (ret2 != SQLITE_OK)
+ xlog(L_ERROR, "Unable to rollback transaction: %s", err);
+ goto out;
+}
+
+/*
+ * Start an exclusive transaction and recheck the DB schema version. If it's
+ * still zero (indicating a new database) then set it up. If that all works,
+ * then insert schema version into the parameters table and commit the
+ * transaction. On any error, rollback the transaction.
+ */
+static int
+sqlite_maindb_init_v4(void)
+{
+ int ret, ret2;
+ char *err = NULL;
+
+ /* Start a transaction */
+ ret = sqlite3_exec(dbh, "BEGIN EXCLUSIVE TRANSACTION;", NULL, NULL,
+ &err);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "Unable to begin transaction: %s", err);
+ goto out;
+ }
+
+ /*
+ * Check schema version again. This time, under an exclusive
+ * transaction to guard against racing DB setup attempts
+ */
+ ret = sqlite_query_schema_version();
+ switch (ret) {
+ case 0:
+ /* Query failed again -- set up DB */
+ break;
+ case CLD_SQLITE_LATEST_SCHEMA_VERSION:
+ /* Someone else raced in and set it up */
+ ret = 0;
+ goto rollback;
+ default:
+ /* Something went wrong -- fail! */
+ ret = -EINVAL;
+ goto rollback;
+ }
+
+ ret = sqlite3_exec(dbh, "CREATE TABLE parameters "
+ "(key TEXT PRIMARY KEY, value TEXT);",
+ NULL, NULL, &err);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "Unable to create parameter table: %s", err);
+ goto rollback;
+ }
+
+ /* create grace table */
+ ret = sqlite3_exec(dbh, "CREATE TABLE grace "
+ "(current INTEGER , recovery INTEGER);",
+ NULL, NULL, &err);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "Unable to create grace table: %s", err);
+ goto rollback;
+ }
+
+ /* insert initial epochs into grace table */
+ ret = sqlite3_exec(dbh, "INSERT OR FAIL INTO grace "
+ "values (1, 0);",
+ NULL, NULL, &err);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "Unable to set initial epochs: %s", err);
+ goto rollback;
+ }
+
+ /* create recovery table for current epoch */
+ ret = sqlite3_exec(dbh, "CREATE TABLE \"rec-0000000000000001\" "
+ "(id BLOB PRIMARY KEY, princhash BLOB);",
+ NULL, NULL, &err);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "Unable to create recovery table "
+ "for current epoch: %s", err);
+ goto rollback;
+ }
+
+ /* insert version into parameters table */
+ ret = snprintf(buf, sizeof(buf), "INSERT OR FAIL INTO parameters "
+ "values (\"version\", \"%d\");",
+ CLD_SQLITE_LATEST_SCHEMA_VERSION);
+ if (ret < 0) {
+ xlog(L_ERROR, "sprintf failed!");
+ goto rollback;
+ } else if ((size_t)ret >= sizeof(buf)) {
+ xlog(L_ERROR, "sprintf output too long! (%d chars)", ret);
+ ret = -EINVAL;
+ goto rollback;
+ }
+
+ ret = sqlite3_exec(dbh, (const char *)buf, NULL, NULL, &err);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "Unable to insert into parameter table: %s", err);
+ goto rollback;
+ }
+
+ /* insert first_time into parameters table */
+ ret = sqlite3_exec(dbh, "INSERT OR FAIL INTO parameters "
+ "values (\"first_time\", \"1\");",
+ NULL, NULL, &err);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "Unable to insert into parameter table: %s", err);
+ goto rollback;
+ }
+
+ ret = sqlite3_exec(dbh, "COMMIT TRANSACTION;", NULL, NULL, &err);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "Unable to commit transaction: %s", err);
+ goto rollback;
+ }
+out:
+ sqlite3_free(err);
+ return ret;
+
+rollback:
+ /* Attempt to rollback the transaction */
+ ret2 = sqlite3_exec(dbh, "ROLLBACK TRANSACTION;", NULL, NULL, &err);
+ if (ret2 != SQLITE_OK)
+ xlog(L_ERROR, "Unable to rollback transaction: %s", err);
+ goto out;
+}
+
+static int
+sqlite_startup_query_grace(void)
+{
+ int ret;
+ uint64_t tcur;
+ uint64_t trec;
+ sqlite3_stmt *stmt = NULL;
+
+ /* prepare select query */
+ ret = sqlite3_prepare_v2(dbh, "SELECT * FROM grace;", -1, &stmt, NULL);
+ if (ret != SQLITE_OK) {
+ xlog(D_GENERAL, "Unable to prepare select statement: %s",
+ sqlite3_errmsg(dbh));
+ goto out;
+ }
+
+ ret = sqlite3_step(stmt);
+ if (ret != SQLITE_ROW) {
+ xlog(D_GENERAL, "Select statement execution failed: %s",
+ sqlite3_errmsg(dbh));
+ goto out;
+ }
+
+ tcur = (uint64_t)sqlite3_column_int64(stmt, 0);
+ trec = (uint64_t)sqlite3_column_int64(stmt, 1);
+
+ current_epoch = tcur;
+ recovery_epoch = trec;
+ ret = 0;
+ xlog(D_GENERAL, "%s: current_epoch=%"PRIu64" recovery_epoch=%"PRIu64,
+ __func__, current_epoch, recovery_epoch);
+out:
+ sqlite3_finalize(stmt);
+ return ret;
+}
+
+/*
+ * Helper for renaming a recovery table to fix the padding.
+ */
+static int
+sqlite_fix_table_name(const char *name)
+{
+ int ret;
+ uint64_t val;
+ char *err;
+
+ if (sscanf(name, "rec-%" PRIx64, &val) != 1)
+ return -EINVAL;
+ ret = snprintf(buf, sizeof(buf), "ALTER TABLE \"%s\" "
+ "RENAME TO \"rec-%016" PRIx64 "\";",
+ name, val);
+ if (ret < 0) {
+ xlog(L_ERROR, "sprintf failed!");
+ return -EINVAL;
+ } else if ((size_t)ret >= sizeof(buf)) {
+ xlog(L_ERROR, "sprintf output too long! (%d chars)", ret);
+ return -EINVAL;
+ }
+ ret = sqlite3_exec(dbh, (const char *)buf, NULL, NULL, &err);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "Unable to fix table for epoch %"PRIu64": %s",
+ val, err);
+ goto out;
+ }
+ xlog(D_GENERAL, "Renamed table %s to rec-%016" PRIx64, name, val);
+out:
+ sqlite3_free(err);
+ return ret;
+}
+
+/*
+ * Callback for the sqlite_exec statement in sqlite_check_table_names.
+ * If the epoch encoded in the table name matches either the current
+ * epoch or the recovery epoch, then try to fix the padding. Otherwise,
+ * we bail.
+ */
+static int
+sqlite_check_table_names_cb(void *UNUSED(arg), int ncols, char **cols,
+ char **UNUSED(colnames))
+{
+ int ret = SQLITE_OK;
+ uint64_t val;
+
+ if (ncols > 1)
+ return -EINVAL;
+ if (sscanf(cols[0], "rec-%" PRIx64, &val) != 1)
+ return -EINVAL;
+ if (val == current_epoch || val == recovery_epoch) {
+ xlog(D_GENERAL, "found invalid table name %s for %s epoch",
+ cols[0], val == current_epoch ? "current" : "recovery");
+ ret = sqlite_fix_table_name(cols[0]);
+ } else {
+ xlog(L_ERROR, "found invalid table name %s for unknown epoch %"
+ PRId64, cols[0], val);
+ return -EINVAL;
+ }
+ return ret;
+}
+
+/*
+ * Look for recovery table names where the epoch isn't zero-padded
+ */
+static int
+sqlite_check_table_names(void)
+{
+ int ret;
+ char *err;
+
+ ret = sqlite3_exec(dbh, "SELECT name FROM sqlite_master "
+ "WHERE type=\"table\" AND name LIKE \"%rec-%\" "
+ "AND length(name) < 20;",
+ sqlite_check_table_names_cb, NULL, &err);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "Table names check failed: %s", err);
+ }
+ sqlite3_free(err);
+ return ret;
+}
+
+/*
+ * Simple db health check. For now we're just making sure that the recovery
+ * table names are of the format "rec-CCCCCCCCCCCCCCCC" (where C is the hex
+ * representation of the epoch value) and that epoch value matches either
+ * the current epoch or the recovery epoch.
+ */
+static int
+sqlite_check_db_health(void)
+{
+ int ret, ret2;
+ char *err;
+
+ ret = sqlite3_exec(dbh, "BEGIN EXCLUSIVE TRANSACTION;", NULL, NULL,
+ &err);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "Unable to begin transaction: %s", err);
+ goto rollback;
+ }
+
+ ret = sqlite_check_table_names();
+ if (ret != SQLITE_OK)
+ goto rollback;
+
+ ret = sqlite3_exec(dbh, "COMMIT TRANSACTION;", NULL, NULL, &err);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "Unable to commit transaction: %s", err);
+ goto rollback;
+ }
+
+cleanup:
+ sqlite3_free(err);
+ xlog(D_GENERAL, "%s: returning %d", __func__, ret);
+ return ret;
+rollback:
+ ret2 = sqlite3_exec(dbh, "ROLLBACK TRANSACTION;", NULL, NULL, &err);
+ if (ret2 != SQLITE_OK)
+ xlog(L_ERROR, "Unable to rollback transaction: %s", err);
+ goto cleanup;
+}
+
+static int
+sqlite_attach_db(const char *path)
+{
+ int ret;
+ char dbpath[PATH_MAX];
+ struct stat stb;
+ sqlite3_stmt *stmt = NULL;
+
+ ret = snprintf(dbpath, PATH_MAX - 1, "%s/main.sqlite", path);
+ if (ret < 0)
+ return ret;
+
+ dbpath[PATH_MAX - 1] = '\0';
+ ret = stat(dbpath, &stb);
+ if (ret < 0)
+ return ret;
+
+ xlog(D_GENERAL, "attaching %s", dbpath);
+ ret = sqlite3_prepare_v2(dbh, "ATTACH DATABASE ? AS attached;",
+ -1, &stmt, NULL);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "%s: unable to prepare attach statement: %s",
+ __func__, sqlite3_errmsg(dbh));
+ return ret;
+ }
+
+ ret = sqlite3_bind_text(stmt, 1, dbpath, strlen(dbpath), SQLITE_STATIC);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "%s: bind text failed: %s",
+ __func__, sqlite3_errmsg(dbh));
+ return ret;
+ }
+
+ ret = sqlite3_step(stmt);
+ if (ret == SQLITE_DONE)
+ ret = SQLITE_OK;
+ else
+ xlog(L_ERROR, "%s: unexpected return code from attach: %s",
+ __func__, sqlite3_errmsg(dbh));
+
+ sqlite3_finalize(stmt);
+ stmt = NULL;
+ return ret;
+}
+
+static int
+sqlite_detach_db(void)
+{
+ int ret;
+ char *err = NULL;
+
+ xlog(D_GENERAL, "detaching database");
+ ret = sqlite3_exec(dbh, "DETACH DATABASE attached;", NULL, NULL, &err);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "Unable to detach attached db: %s", err);
+ }
+
+ sqlite3_free(err);
+ return ret;
+}
+
+/*
+ * Copies client records from the nfsdcltrack database as part of a one-time
+ * "upgrade".
+ *
+ * Returns a non-zero sqlite error code, or SQLITE_OK (aka 0).
+ * Returns the number of records copied via "num_rec".
+ */
+static int
+sqlite_copy_cltrack_records(int *num_rec)
+{
+ int ret, ret2;
+ char *s;
+ char *err = NULL;
+ sqlite3_stmt *stmt = NULL;
+
+ s = conf_get_str("nfsdcltrack", "storagedir");
+ if (s)
+ cltrack_storagedir = s;
+ ret = sqlite_attach_db(cltrack_storagedir);
+ if (ret)
+ goto out;
+ ret = sqlite3_exec(dbh, "BEGIN EXCLUSIVE TRANSACTION;", NULL, NULL,
+ &err);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "Unable to begin transaction: %s", err);
+ goto rollback;
+ }
+ ret = snprintf(buf, sizeof(buf), "DELETE FROM \"rec-%016" PRIx64 "\";",
+ current_epoch);
+ if (ret < 0) {
+ xlog(L_ERROR, "sprintf failed!");
+ goto rollback;
+ } else if ((size_t)ret >= sizeof(buf)) {
+ xlog(L_ERROR, "sprintf output too long! (%d chars)", ret);
+ ret = -EINVAL;
+ goto rollback;
+ }
+ ret = sqlite3_exec(dbh, (const char *)buf, NULL, NULL, &err);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "Unable to clear records from current epoch: %s", err);
+ goto rollback;
+ }
+ ret = snprintf(buf, sizeof(buf), "INSERT INTO \"rec-%016" PRIx64 "\" (id) "
+ "SELECT id FROM attached.clients;",
+ current_epoch);
+ if (ret < 0) {
+ xlog(L_ERROR, "sprintf failed!");
+ goto rollback;
+ } else if ((size_t)ret >= sizeof(buf)) {
+ xlog(L_ERROR, "sprintf output too long! (%d chars)", ret);
+ ret = -EINVAL;
+ goto rollback;
+ }
+ ret = sqlite3_prepare_v2(dbh, buf, -1, &stmt, NULL);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "%s: insert statement prepare failed: %s",
+ __func__, sqlite3_errmsg(dbh));
+ goto rollback;
+ }
+ ret = sqlite3_step(stmt);
+ if (ret != SQLITE_DONE) {
+ xlog(L_ERROR, "%s: unexpected return code from insert: %s",
+ __func__, sqlite3_errmsg(dbh));
+ goto rollback;
+ }
+ *num_rec = sqlite3_changes(dbh);
+ ret = sqlite3_exec(dbh, "COMMIT TRANSACTION;", NULL, NULL, &err);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "Unable to commit transaction: %s", err);
+ goto rollback;
+ }
+cleanup:
+ sqlite3_finalize(stmt);
+ sqlite3_free(err);
+ sqlite_detach_db();
+out:
+ xlog(D_GENERAL, "%s: returning %d", __func__, ret);
+ return ret;
+rollback:
+ *num_rec = 0;
+ ret2 = sqlite3_exec(dbh, "ROLLBACK TRANSACTION;", NULL, NULL, &err);
+ if (ret2 != SQLITE_OK)
+ xlog(L_ERROR, "Unable to rollback transaction: %s", err);
+ goto cleanup;
+}
+
+/* Open the database and set up the database handle for it */
+int
+sqlite_prepare_dbh(const char *topdir)
+{
+ int ret;
+
+ /* Do nothing if the database handle is already set up */
+ if (dbh)
+ return 0;
+
+ ret = snprintf(buf, PATH_MAX - 1, "%s/main.sqlite", topdir);
+ if (ret < 0)
+ return ret;
+
+ buf[PATH_MAX - 1] = '\0';
+
+ /* open a new DB handle */
+ ret = sqlite3_open(buf, &dbh);
+ if (ret != SQLITE_OK) {
+ /* try to create the dir */
+ ret = mkdir_if_not_exist(topdir);
+ if (ret)
+ goto out_close;
+
+ /* retry open */
+ ret = sqlite3_open(buf, &dbh);
+ if (ret != SQLITE_OK)
+ goto out_close;
+ }
+
+ /* set busy timeout */
+ ret = sqlite3_busy_timeout(dbh, CLD_SQLITE_BUSY_TIMEOUT);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "Unable to set sqlite busy timeout: %s",
+ sqlite3_errmsg(dbh));
+ goto out_close;
+ }
+
+ ret = sqlite_query_schema_version();
+ switch (ret) {
+ case CLD_SQLITE_LATEST_SCHEMA_VERSION:
+ /* DB is already set up. Do nothing */
+ break;
+ case 3:
+ /* Old DB -- update to new schema */
+ ret = sqlite_maindb_update_schema(3);
+ if (ret)
+ goto out_close;
+ break;
+ case 2:
+ /* Old DB -- update to new schema */
+ ret = sqlite_maindb_update_schema(2);
+ if (ret)
+ goto out_close;
+ break;
+
+ case 1:
+ /* Old DB -- update to new schema */
+ ret = sqlite_maindb_update_schema(1);
+ if (ret)
+ goto out_close;
+ break;
+ case 0:
+ /* Query failed -- try to set up new DB */
+ ret = sqlite_maindb_init_v4();
+ if (ret)
+ goto out_close;
+ break;
+ default:
+ /* Unknown DB version -- downgrade? Fail */
+ xlog(L_ERROR, "Unsupported database schema version! "
+ "Expected %d, got %d.",
+ CLD_SQLITE_LATEST_SCHEMA_VERSION, ret);
+ ret = -EINVAL;
+ goto out_close;
+ }
+
+ ret = sqlite_startup_query_grace();
+ if (ret)
+ goto out_close;
+
+ ret = sqlite_query_first_time(&first_time);
+ if (ret)
+ goto out_close;
+
+ ret = sqlite_check_db_health();
+ if (ret) {
+ xlog(L_ERROR, "Database health check failed! "
+ "Database must be fixed manually.");
+ goto out_close;
+ }
+
+ /* one-time "upgrade" from older client tracking methods */
+ if (first_time) {
+ sqlite_copy_cltrack_records(&num_cltrack_records);
+ xlog(D_GENERAL, "%s: num_cltrack_records = %d\n",
+ __func__, num_cltrack_records);
+ legacy_load_clients_from_recdir(&num_legacy_records);
+ xlog(D_GENERAL, "%s: num_legacy_records = %d\n",
+ __func__, num_legacy_records);
+ if (num_cltrack_records > 0 && num_legacy_records > 0)
+ xlog(L_WARNING, "%s: first-time upgrade detected "
+ "both cltrack and legacy records!\n", __func__);
+ }
+
+ return ret;
+out_close:
+ sqlite3_close(dbh);
+ dbh = NULL;
+ return ret;
+}
+
+/*
+ * Create a client record
+ *
+ * Returns a non-zero sqlite error code, or SQLITE_OK (aka 0)
+ */
+int
+sqlite_insert_client(const unsigned char *clname, const size_t namelen)
+{
+ int ret;
+ sqlite3_stmt *stmt = NULL;
+
+ ret = snprintf(buf, sizeof(buf), "INSERT OR REPLACE INTO \"rec-%016" PRIx64 "\" (id) "
+ "VALUES (?);", current_epoch);
+ if (ret < 0) {
+ xlog(L_ERROR, "sprintf failed!");
+ return ret;
+ } else if ((size_t)ret >= sizeof(buf)) {
+ xlog(L_ERROR, "sprintf output too long! (%d chars)", ret);
+ return -EINVAL;
+ }
+
+ ret = sqlite3_prepare_v2(dbh, buf, -1, &stmt, NULL);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "%s: insert statement prepare failed: %s",
+ __func__, sqlite3_errmsg(dbh));
+ return ret;
+ }
+
+ ret = sqlite3_bind_blob(stmt, 1, (const void *)clname, namelen,
+ SQLITE_STATIC);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "%s: bind blob failed: %s", __func__,
+ sqlite3_errmsg(dbh));
+ goto out_err;
+ }
+
+ ret = sqlite3_step(stmt);
+ if (ret == SQLITE_DONE)
+ ret = SQLITE_OK;
+ else
+ xlog(L_ERROR, "%s: unexpected return code from insert: %s",
+ __func__, sqlite3_errmsg(dbh));
+
+out_err:
+ xlog(D_GENERAL, "%s: returning %d", __func__, ret);
+ sqlite3_finalize(stmt);
+ return ret;
+}
+
+#if UPCALL_VERSION >= 2
+/*
+ * Create a client record including hash the kerberos principal
+ *
+ * Returns a non-zero sqlite error code, or SQLITE_OK (aka 0)
+ */
+int
+sqlite_insert_client_and_princhash(const unsigned char *clname, const size_t namelen,
+ const unsigned char *clprinchash, const size_t princhashlen)
+{
+ int ret;
+ sqlite3_stmt *stmt = NULL;
+
+ if (princhashlen > 0)
+ ret = snprintf(buf, sizeof(buf), "INSERT OR REPLACE INTO \"rec-%016" PRIx64 "\" "
+ "VALUES (?, ?);", current_epoch);
+ else
+ ret = snprintf(buf, sizeof(buf), "INSERT OR REPLACE INTO \"rec-%016" PRIx64 "\" (id) "
+ "VALUES (?);", current_epoch);
+ if (ret < 0) {
+ xlog(L_ERROR, "sprintf failed!");
+ return ret;
+ } else if ((size_t)ret >= sizeof(buf)) {
+ xlog(L_ERROR, "sprintf output too long! (%d chars)", ret);
+ return -EINVAL;
+ }
+
+ ret = sqlite3_prepare_v2(dbh, buf, -1, &stmt, NULL);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "%s: insert statement prepare failed: %s",
+ __func__, sqlite3_errmsg(dbh));
+ return ret;
+ }
+
+ ret = sqlite3_bind_blob(stmt, 1, (const void *)clname, namelen,
+ SQLITE_STATIC);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "%s: bind blob failed: %s", __func__,
+ sqlite3_errmsg(dbh));
+ goto out_err;
+ }
+
+ if (princhashlen > 0) {
+ ret = sqlite3_bind_blob(stmt, 2, (const void *)clprinchash, princhashlen,
+ SQLITE_STATIC);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "%s: bind blob failed: %s", __func__,
+ sqlite3_errmsg(dbh));
+ goto out_err;
+ }
+ }
+
+ ret = sqlite3_step(stmt);
+ if (ret == SQLITE_DONE)
+ ret = SQLITE_OK;
+ else
+ xlog(L_ERROR, "%s: unexpected return code from insert: %s",
+ __func__, sqlite3_errmsg(dbh));
+
+out_err:
+ xlog(D_GENERAL, "%s: returning %d", __func__, ret);
+ sqlite3_finalize(stmt);
+ return ret;
+}
+#else
+int
+sqlite_insert_client_and_princhash(const unsigned char *clname, const size_t namelen,
+ const unsigned char *clprinchash, const size_t princhashlen)
+{
+ return -EINVAL;
+}
+#endif
+
+/* Remove a client record */
+int
+sqlite_remove_client(const unsigned char *clname, const size_t namelen)
+{
+ int ret;
+ sqlite3_stmt *stmt = NULL;
+
+ ret = snprintf(buf, sizeof(buf), "DELETE FROM \"rec-%016" PRIx64 "\" "
+ "WHERE id==?;", current_epoch);
+ if (ret < 0) {
+ xlog(L_ERROR, "sprintf failed!");
+ return ret;
+ } else if ((size_t)ret >= sizeof(buf)) {
+ xlog(L_ERROR, "sprintf output too long! (%d chars)", ret);
+ return -EINVAL;
+ }
+
+ ret = sqlite3_prepare_v2(dbh, buf, -1, &stmt, NULL);
+
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "%s: statement prepare failed: %s",
+ __func__, sqlite3_errmsg(dbh));
+ goto out_err;
+ }
+
+ ret = sqlite3_bind_blob(stmt, 1, (const void *)clname, namelen,
+ SQLITE_STATIC);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "%s: bind blob failed: %s", __func__,
+ sqlite3_errmsg(dbh));
+ goto out_err;
+ }
+
+ ret = sqlite3_step(stmt);
+ if (ret == SQLITE_DONE)
+ ret = SQLITE_OK;
+ else
+ xlog(L_ERROR, "%s: unexpected return code from delete: %d",
+ __func__, ret);
+
+out_err:
+ xlog(D_GENERAL, "%s: returning %d", __func__, ret);
+ sqlite3_finalize(stmt);
+ return ret;
+}
+
+/*
+ * Is the given clname in the clients table? If so, then update its timestamp
+ * and return success. If the record isn't present, or the update fails, then
+ * return an error.
+ */
+int
+sqlite_check_client(const unsigned char *clname, const size_t namelen)
+{
+ int ret;
+ sqlite3_stmt *stmt = NULL;
+
+ ret = snprintf(buf, sizeof(buf), "SELECT count(*) FROM \"rec-%016" PRIx64 "\" "
+ "WHERE id==?;", recovery_epoch);
+ if (ret < 0) {
+ xlog(L_ERROR, "sprintf failed!");
+ return ret;
+ } else if ((size_t)ret >= sizeof(buf)) {
+ xlog(L_ERROR, "sprintf output too long! (%d chars)", ret);
+ return -EINVAL;
+ }
+
+ ret = sqlite3_prepare_v2(dbh, buf, -1, &stmt, NULL);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "%s: select statement prepare failed: %s",
+ __func__, sqlite3_errmsg(dbh));
+ return ret;
+ }
+
+ ret = sqlite3_bind_blob(stmt, 1, (const void *)clname, namelen,
+ SQLITE_STATIC);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "%s: bind blob failed: %s",
+ __func__, sqlite3_errmsg(dbh));
+ goto out_err;
+ }
+
+ ret = sqlite3_step(stmt);
+ if (ret != SQLITE_ROW) {
+ xlog(L_ERROR, "%s: unexpected return code from select: %d",
+ __func__, ret);
+ goto out_err;
+ }
+
+ ret = sqlite3_column_int(stmt, 0);
+ xlog(D_GENERAL, "%s: select returned %d rows", __func__, ret);
+ if (ret != 1) {
+ ret = -EACCES;
+ goto out_err;
+ }
+
+ sqlite3_finalize(stmt);
+
+ /* Now insert the client into the table for the current epoch */
+ return sqlite_insert_client(clname, namelen);
+
+out_err:
+ xlog(D_GENERAL, "%s: returning %d", __func__, ret);
+ sqlite3_finalize(stmt);
+ return ret;
+}
+
+int
+sqlite_grace_start(void)
+{
+ int ret, ret2;
+ char *err;
+ uint64_t tcur = current_epoch;
+ uint64_t trec = recovery_epoch;
+
+ /* begin transaction */
+ ret = sqlite3_exec(dbh, "BEGIN EXCLUSIVE TRANSACTION;", NULL, NULL,
+ &err);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "Unable to begin transaction: %s", err);
+ goto rollback;
+ }
+
+ if (trec == 0) {
+ /*
+ * A normal grace start - update the epoch values in the grace
+ * table and create a new table for the current reboot epoch.
+ */
+ trec = tcur;
+ tcur++;
+
+ ret = snprintf(buf, sizeof(buf), "UPDATE grace "
+ "SET current = %" PRId64 ", recovery = %" PRId64 ";",
+ (int64_t)tcur, (int64_t)trec);
+ if (ret < 0) {
+ xlog(L_ERROR, "sprintf failed!");
+ goto rollback;
+ } else if ((size_t)ret >= sizeof(buf)) {
+ xlog(L_ERROR, "sprintf output too long! (%d chars)",
+ ret);
+ ret = -EINVAL;
+ goto rollback;
+ }
+
+ ret = sqlite3_exec(dbh, (const char *)buf, NULL, NULL, &err);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "Unable to update epochs: %s", err);
+ goto rollback;
+ }
+
+ ret = snprintf(buf, sizeof(buf), "CREATE TABLE \"rec-%016" PRIx64 "\" "
+ "(id BLOB PRIMARY KEY, princhash blob);",
+ tcur);
+ if (ret < 0) {
+ xlog(L_ERROR, "sprintf failed!");
+ goto rollback;
+ } else if ((size_t)ret >= sizeof(buf)) {
+ xlog(L_ERROR, "sprintf output too long! (%d chars)",
+ ret);
+ ret = -EINVAL;
+ goto rollback;
+ }
+
+ ret = sqlite3_exec(dbh, (const char *)buf, NULL, NULL, &err);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "Unable to create table for current epoch: %s",
+ err);
+ goto rollback;
+ }
+ } else {
+ /* Server restarted while in grace - don't update the epoch
+ * values in the grace table, just clear out the records for
+ * the current reboot epoch.
+ */
+ ret = snprintf(buf, sizeof(buf), "DELETE FROM \"rec-%016" PRIx64 "\";",
+ tcur);
+ if (ret < 0) {
+ xlog(L_ERROR, "sprintf failed!");
+ goto rollback;
+ } else if ((size_t)ret >= sizeof(buf)) {
+ xlog(L_ERROR, "sprintf output too long! (%d chars)", ret);
+ ret = -EINVAL;
+ goto rollback;
+ }
+
+ ret = sqlite3_exec(dbh, (const char *)buf, NULL, NULL, &err);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "Unable to clear table for current epoch: %s",
+ err);
+ goto rollback;
+ }
+ }
+
+ ret = sqlite3_exec(dbh, "COMMIT TRANSACTION;", NULL, NULL, &err);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "Unable to commit transaction: %s", err);
+ goto rollback;
+ }
+
+ current_epoch = tcur;
+ recovery_epoch = trec;
+ xlog(D_GENERAL, "%s: current_epoch=%"PRIu64" recovery_epoch=%"PRIu64,
+ __func__, current_epoch, recovery_epoch);
+
+out:
+ sqlite3_free(err);
+ return ret;
+rollback:
+ ret2 = sqlite3_exec(dbh, "ROLLBACK TRANSACTION;", NULL, NULL, &err);
+ if (ret2 != SQLITE_OK)
+ xlog(L_ERROR, "Unable to rollback transaction: %s", err);
+ goto out;
+}
+
+int
+sqlite_grace_done(void)
+{
+ int ret, ret2;
+ char *err;
+
+ /* begin transaction */
+ ret = sqlite3_exec(dbh, "BEGIN EXCLUSIVE TRANSACTION;", NULL, NULL,
+ &err);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "Unable to begin transaction: %s", err);
+ goto rollback;
+ }
+
+ ret = sqlite3_exec(dbh, "UPDATE grace SET recovery = \"0\";",
+ NULL, NULL, &err);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "Unable to clear recovery epoch: %s", err);
+ goto rollback;
+ }
+
+ ret = snprintf(buf, sizeof(buf), "DROP TABLE \"rec-%016" PRIx64 "\";",
+ recovery_epoch);
+ if (ret < 0) {
+ xlog(L_ERROR, "sprintf failed!");
+ goto rollback;
+ } else if ((size_t)ret >= sizeof(buf)) {
+ xlog(L_ERROR, "sprintf output too long! (%d chars)", ret);
+ ret = -EINVAL;
+ goto rollback;
+ }
+
+ ret = sqlite3_exec(dbh, (const char *)buf, NULL, NULL, &err);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "Unable to drop table for recovery epoch: %s",
+ err);
+ goto rollback;
+ }
+
+ ret = sqlite3_exec(dbh, "COMMIT TRANSACTION;", NULL, NULL, &err);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "Unable to commit transaction: %s", err);
+ goto rollback;
+ }
+
+ recovery_epoch = 0;
+ xlog(D_GENERAL, "%s: current_epoch=%"PRIu64" recovery_epoch=%"PRIu64,
+ __func__, current_epoch, recovery_epoch);
+
+out:
+ sqlite3_free(err);
+ return ret;
+rollback:
+ ret2 = sqlite3_exec(dbh, "ROLLBACK TRANSACTION;", NULL, NULL, &err);
+ if (ret2 != SQLITE_OK)
+ xlog(L_ERROR, "Unable to rollback transaction: %s", err);
+ goto out;
+}
+
+
+int
+sqlite_iterate_recovery(int (*cb)(struct cld_client *clnt), struct cld_client *clnt)
+{
+ int ret;
+ sqlite3_stmt *stmt = NULL;
+#if UPCALL_VERSION >= 2
+ struct cld_msg_v2 *cmsg = &clnt->cl_u.cl_msg_v2;
+#else
+ struct cld_msg *cmsg = &clnt->cl_u.cl_msg;
+#endif
+
+ if (recovery_epoch == 0) {
+ xlog(D_GENERAL, "%s: not in grace!", __func__);
+ return -EINVAL;
+ }
+
+ ret = snprintf(buf, sizeof(buf), "SELECT * FROM \"rec-%016" PRIx64 "\";",
+ recovery_epoch);
+ if (ret < 0) {
+ xlog(L_ERROR, "sprintf failed!");
+ return ret;
+ } else if ((size_t)ret >= sizeof(buf)) {
+ xlog(L_ERROR, "sprintf output too long! (%d chars)", ret);
+ return -EINVAL;
+ }
+
+ ret = sqlite3_prepare_v2(dbh, buf, -1, &stmt, NULL);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "%s: select statement prepare failed: %s",
+ __func__, sqlite3_errmsg(dbh));
+ return ret;
+ }
+
+ while ((ret = sqlite3_step(stmt)) == SQLITE_ROW) {
+ const void *id;
+ int id_len;
+
+ id = sqlite3_column_blob(stmt, 0);
+ id_len = sqlite3_column_bytes(stmt, 0);
+ if (id_len > NFS4_OPAQUE_LIMIT)
+ id_len = NFS4_OPAQUE_LIMIT;
+
+ memset(&cmsg->cm_u, 0, sizeof(cmsg->cm_u));
+#if UPCALL_VERSION >= 2
+ memcpy(&cmsg->cm_u.cm_clntinfo.cc_name.cn_id, id, id_len);
+ cmsg->cm_u.cm_clntinfo.cc_name.cn_len = id_len;
+ if (sqlite3_column_bytes(stmt, 1) > 0) {
+ memcpy(&cmsg->cm_u.cm_clntinfo.cc_princhash.cp_data,
+ sqlite3_column_blob(stmt, 1), SHA256_DIGEST_SIZE);
+ cmsg->cm_u.cm_clntinfo.cc_princhash.cp_len = sqlite3_column_bytes(stmt, 1);
+ }
+#else
+ memcpy(&cmsg->cm_u.cm_name.cn_id, id, id_len);
+ cmsg->cm_u.cm_name.cn_len = id_len;
+#endif
+ cb(clnt);
+ }
+ if (ret == SQLITE_DONE)
+ ret = 0;
+ sqlite3_finalize(stmt);
+ return ret;
+}
+
+/*
+ * Cleans out the old nfsdcltrack database.
+ *
+ * Called upon receipt of the first "GraceDone" upcall only.
+ */
+int
+sqlite_delete_cltrack_records(void)
+{
+ int ret;
+ char *s;
+ char *err = NULL;
+
+ s = conf_get_str("nfsdcltrack", "storagedir");
+ if (s)
+ cltrack_storagedir = s;
+ ret = sqlite_attach_db(cltrack_storagedir);
+ if (ret)
+ goto out;
+ ret = sqlite3_exec(dbh, "DELETE FROM attached.clients;",
+ NULL, NULL, &err);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "Unable to clear records from cltrack db: %s",
+ err);
+ }
+ sqlite_detach_db();
+out:
+ sqlite3_free(err);
+ return ret;
+}
+
+/*
+ * Sets first_time to 0 in the parameters table to ensure we only
+ * copy old client tracking records into the database one time.
+ *
+ * Called upon receipt of the first "GraceDone" upcall only.
+ */
+int
+sqlite_first_time_done(void)
+{
+ int ret;
+ char *err = NULL;
+
+ ret = sqlite3_exec(dbh, "UPDATE parameters SET value = \"0\" "
+ "WHERE key = \"first_time\";",
+ NULL, NULL, &err);
+ if (ret != SQLITE_OK)
+ xlog(L_ERROR, "Unable to clear first_time: %s", err);
+
+ sqlite3_free(err);
+ return ret;
+}
+
+/*
+ * Closes all sqlite3 resources and shuts down the library.
+ *
+ */
+void
+sqlite_shutdown(void)
+{
+ if (dbh != NULL) {
+ sqlite3_close(dbh);
+ dbh = NULL;
+ }
+
+ sqlite3_shutdown();
+}
diff --git a/utils/nfsdcld/sqlite.h b/utils/nfsdcld/sqlite.h
new file mode 100644
index 0000000..044236c
--- /dev/null
+++ b/utils/nfsdcld/sqlite.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2011 Red Hat, Jeff Layton <jlayton@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * 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 02110-1301, USA.
+ */
+
+#ifndef _SQLITE_H_
+#define _SQLITE_H_
+
+struct cld_client;
+
+int sqlite_prepare_dbh(const char *topdir);
+int sqlite_insert_client(const unsigned char *clname, const size_t namelen);
+int sqlite_insert_client_and_princhash(const unsigned char *clname, const size_t namelen,
+ const unsigned char *clprinchash, const size_t princhashlen);
+int sqlite_remove_client(const unsigned char *clname, const size_t namelen);
+int sqlite_check_client(const unsigned char *clname, const size_t namelen);
+int sqlite_grace_start(void);
+int sqlite_grace_done(void);
+int sqlite_iterate_recovery(int (*cb)(struct cld_client *clnt), struct cld_client *clnt);
+int sqlite_delete_cltrack_records(void);
+int sqlite_first_time_done(void);
+
+void sqlite_shutdown(void);
+#endif /* _SQLITE_H */
diff --git a/utils/nfsdcltrack/Makefile.am b/utils/nfsdcltrack/Makefile.am
new file mode 100644
index 0000000..769e4a4
--- /dev/null
+++ b/utils/nfsdcltrack/Makefile.am
@@ -0,0 +1,22 @@
+## Process this file with automake to produce Makefile.in
+
+# These binaries go in /sbin (not /usr/sbin) as the kernel "knows" the
+# /sbin name. If /sbin is a symlink, CONFIG_SBIN_OVERRIDE can be
+# disabled to install in /usr/sbin anyway.
+# Note that we don't use "if CONFIG_SBIN_OVERRIDE" as that
+# causes autotools to notice the override and disable it.
+@CONFIG_SBIN_OVERRIDE_TRUE@sbindir = /sbin
+
+man8_MANS = nfsdcltrack.man
+EXTRA_DIST = $(man8_MANS)
+
+AM_CFLAGS += -D_LARGEFILE64_SOURCE
+sbin_PROGRAMS = nfsdcltrack
+
+noinst_HEADERS = sqlite.h
+
+nfsdcltrack_SOURCES = nfsdcltrack.c sqlite.c
+nfsdcltrack_LDADD = ../../support/nfs/libnfs.la $(LIBSQLITE) $(LIBCAP)
+
+MAINTAINERCLEANFILES = Makefile.in
+
diff --git a/utils/nfsdcltrack/Makefile.in b/utils/nfsdcltrack/Makefile.in
new file mode 100644
index 0000000..30836c8
--- /dev/null
+++ b/utils/nfsdcltrack/Makefile.in
@@ -0,0 +1,823 @@
+# 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@
+sbin_PROGRAMS = nfsdcltrack$(EXEEXT)
+subdir = utils/nfsdcltrack
+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 $(noinst_HEADERS) \
+ $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/support/include/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man8dir)"
+PROGRAMS = $(sbin_PROGRAMS)
+am_nfsdcltrack_OBJECTS = nfsdcltrack.$(OBJEXT) sqlite.$(OBJEXT)
+nfsdcltrack_OBJECTS = $(am_nfsdcltrack_OBJECTS)
+am__DEPENDENCIES_1 =
+nfsdcltrack_DEPENDENCIES = ../../support/nfs/libnfs.la \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+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 =
+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)/nfsdcltrack.Po ./$(DEPDIR)/sqlite.Po
+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 = $(nfsdcltrack_SOURCES)
+DIST_SOURCES = $(nfsdcltrack_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+man8dir = $(mandir)/man8
+NROFF = nroff
+MANS = $(man8_MANS)
+HEADERS = $(noinst_HEADERS)
+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@ -D_LARGEFILE64_SOURCE
+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@
+
+# These binaries go in /sbin (not /usr/sbin) as the kernel "knows" the
+# /sbin name. If /sbin is a symlink, CONFIG_SBIN_OVERRIDE can be
+# disabled to install in /usr/sbin anyway.
+# Note that we don't use "if CONFIG_SBIN_OVERRIDE" as that
+# causes autotools to notice the override and disable it.
+@CONFIG_SBIN_OVERRIDE_TRUE@sbindir = /sbin
+man8_MANS = nfsdcltrack.man
+EXTRA_DIST = $(man8_MANS)
+noinst_HEADERS = sqlite.h
+nfsdcltrack_SOURCES = nfsdcltrack.c sqlite.c
+nfsdcltrack_LDADD = ../../support/nfs/libnfs.la $(LIBSQLITE) $(LIBCAP)
+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 utils/nfsdcltrack/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu utils/nfsdcltrack/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):
+install-sbinPROGRAMS: $(sbin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(sbindir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-sbinPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(sbindir)" && rm -f $$files
+
+clean-sbinPROGRAMS:
+ @list='$(sbin_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+nfsdcltrack$(EXEEXT): $(nfsdcltrack_OBJECTS) $(nfsdcltrack_DEPENDENCIES) $(EXTRA_nfsdcltrack_DEPENDENCIES)
+ @rm -f nfsdcltrack$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(nfsdcltrack_OBJECTS) $(nfsdcltrack_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nfsdcltrack.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sqlite.Po@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 $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-man8: $(man8_MANS)
+ @$(NORMAL_INSTALL)
+ @list1='$(man8_MANS)'; \
+ list2=''; \
+ test -n "$(man8dir)" \
+ && test -n "`echo $$list1$$list2`" \
+ || exit 0; \
+ echo " $(MKDIR_P) '$(DESTDIR)$(man8dir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(man8dir)" || exit 1; \
+ { for i in $$list1; do echo "$$i"; done; \
+ if test -n "$$list2"; then \
+ for i in $$list2; do echo "$$i"; done \
+ | sed -n '/\.8[a-z]*$$/p'; \
+ fi; \
+ } | while read p; do \
+ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; echo "$$p"; \
+ done | \
+ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \
+ sed 'N;N;s,\n, ,g' | { \
+ list=; while read file base inst; do \
+ if test "$$base" = "$$inst"; then list="$$list $$file"; else \
+ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man8dir)/$$inst'"; \
+ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man8dir)/$$inst" || exit $$?; \
+ fi; \
+ done; \
+ for i in $$list; do echo "$$i"; done | $(am__base_list) | \
+ while read files; do \
+ test -z "$$files" || { \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man8dir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(man8dir)" || exit $$?; }; \
+ done; }
+
+uninstall-man8:
+ @$(NORMAL_UNINSTALL)
+ @list='$(man8_MANS)'; test -n "$(man8dir)" || exit 0; \
+ files=`{ for i in $$list; do echo "$$i"; done; \
+ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
+ dir='$(DESTDIR)$(man8dir)'; $(am__uninstall_files_from_dir)
+
+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 $(PROGRAMS) $(MANS) $(HEADERS)
+installdirs:
+ for dir in "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man8dir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+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-sbinPROGRAMS \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/nfsdcltrack.Po
+ -rm -f ./$(DEPDIR)/sqlite.Po
+ -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-man
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-sbinPROGRAMS
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man: install-man8
+
+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)/nfsdcltrack.Po
+ -rm -f ./$(DEPDIR)/sqlite.Po
+ -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: uninstall-man uninstall-sbinPROGRAMS
+
+uninstall-man: uninstall-man8
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-sbinPROGRAMS 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-man8 install-pdf \
+ install-pdf-am install-ps install-ps-am install-sbinPROGRAMS \
+ 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 \
+ uninstall-man uninstall-man8 uninstall-sbinPROGRAMS
+
+.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/utils/nfsdcltrack/nfsdcltrack.c b/utils/nfsdcltrack/nfsdcltrack.c
new file mode 100644
index 0000000..7c1c4bc
--- /dev/null
+++ b/utils/nfsdcltrack/nfsdcltrack.c
@@ -0,0 +1,639 @@
+/*
+ * nfsdcltrack.c -- NFSv4 client name tracking program
+ *
+ * Copyright (C) 2012 Jeff Layton <jlayton@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * 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 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <getopt.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <unistd.h>
+#include <libgen.h>
+#include <sys/inotify.h>
+#include <dirent.h>
+#include <limits.h>
+#ifdef HAVE_SYS_CAPABILITY_H
+#include <sys/prctl.h>
+#include <sys/capability.h>
+#endif
+
+#include "conffile.h"
+#include "xlog.h"
+#include "sqlite.h"
+
+#ifndef CLD_DEFAULT_STORAGEDIR
+#define CLD_DEFAULT_STORAGEDIR NFS_STATEDIR "/nfsdcltrack"
+#endif
+
+#define NFSD_END_GRACE_FILE "/proc/fs/nfsd/v4_end_grace"
+
+/* defined by RFC 3530 */
+#define NFS4_OPAQUE_LIMIT 1024
+
+/* private data structures */
+struct cltrack_cmd {
+ char *name;
+ bool needs_arg;
+ int (*func)(const char *arg);
+};
+
+/* forward declarations */
+static int cltrack_init(const char *unused);
+static int cltrack_create(const char *id);
+static int cltrack_remove(const char *id);
+static int cltrack_check(const char *id);
+static int cltrack_gracedone(const char *gracetime);
+
+/* global variables */
+static struct option longopts[] =
+{
+ { "help", 0, NULL, 'h' },
+ { "debug", 0, NULL, 'd' },
+ { "foreground", 0, NULL, 'f' },
+ { "storagedir", 1, NULL, 's' },
+ { NULL, 0, 0, 0 },
+};
+
+static struct cltrack_cmd commands[] =
+{
+ { "init", false, cltrack_init },
+ { "create", true, cltrack_create },
+ { "remove", true, cltrack_remove },
+ { "check", true, cltrack_check },
+ { "gracedone", true, cltrack_gracedone },
+ { NULL, false, NULL },
+};
+
+static char *storagedir = CLD_DEFAULT_STORAGEDIR;
+
+/* common buffer for holding id4 blobs */
+static unsigned char blob[NFS4_OPAQUE_LIMIT];
+
+static void
+usage(char *progname)
+{
+ printf("Usage: %s [ -hfd ] [ -s dir ] < cmd > < arg >\n", progname);
+ printf("Where < cmd > is one of the following and takes the following < arg >:\n");
+ printf(" init\n");
+ printf(" create <nfs_client_id4>\n");
+ printf(" remove <nfs_client_id4>\n");
+ printf(" check <nfs_client_id4>\n");
+ printf(" gracedone <epoch time>\n");
+}
+
+
+/**
+ * hex_to_bin - convert a hex digit to its real value
+ * @ch: ascii character represents hex digit
+ *
+ * hex_to_bin() converts one hex digit to its actual value or -1 in case of bad
+ * input.
+ *
+ * Note: borrowed from lib/hexdump.c in the Linux kernel sources.
+ */
+static int
+hex_to_bin(char ch)
+{
+ if ((ch >= '0') && (ch <= '9'))
+ return ch - '0';
+ ch = tolower(ch);
+ if ((ch >= 'a') && (ch <= 'f'))
+ return ch - 'a' + 10;
+ return -1;
+}
+
+/**
+ * hex_str_to_bin - convert a hexidecimal string into a binary blob
+ *
+ * @src: string of hex digit pairs
+ * @dst: destination buffer to hold binary data
+ * @dstsize: size of the destination buffer
+ *
+ * Walk a string of hex digit pairs and convert them into binary data. Returns
+ * the resulting length of the binary data or a negative error code. If the
+ * data will not fit in the buffer, it returns -ENOBUFS (but will likely have
+ * clobbered the dst buffer in the process of determining that). If there are
+ * non-hexidecimal characters in the src, or an odd number of them then it
+ * returns -EINVAL.
+ */
+static ssize_t
+hex_str_to_bin(const char *src, unsigned char *dst, ssize_t dstsize)
+{
+ unsigned char *tmpdst = dst;
+
+ while (*src) {
+ int hi, lo;
+
+ /* make sure we don't overrun the dst buffer */
+ if ((tmpdst - dst) >= dstsize)
+ return -ENOBUFS;
+
+ hi = hex_to_bin(*src++);
+
+ /* did we get an odd number of characters? */
+ if (!*src)
+ return -EINVAL;
+ lo = hex_to_bin(*src++);
+
+ /* one of the characters isn't a hex digit */
+ if (hi < 0 || lo < 0)
+ return -EINVAL;
+
+ /* now place it in the dst buffer */
+ *tmpdst++ = (hi << 4) | lo;
+ }
+
+ return (ssize_t)(tmpdst - dst);
+}
+
+/*
+ * This program will almost always be run with root privileges since the
+ * kernel will call out to run it. Drop all capabilities prior to doing
+ * anything important to limit the exposure to potential compromise.
+ *
+ * FIXME: should we setuid to a different user early on instead?
+ */
+static int
+cltrack_set_caps(void)
+{
+ int ret = 0;
+#ifdef HAVE_SYS_CAPABILITY_H
+ unsigned long i;
+ cap_t caps;
+
+ /* prune the bounding set to nothing */
+ for (i = 0; prctl(PR_CAPBSET_READ, i, 0, 0, 0) >= 0 ; ++i) {
+ ret = prctl(PR_CAPBSET_DROP, i, 0, 0, 0);
+ if (ret) {
+ xlog(L_ERROR, "Unable to prune capability %lu from "
+ "bounding set: %m", i);
+ return -errno;
+ }
+ }
+
+ /* get a blank capset */
+ caps = cap_init();
+ if (caps == NULL) {
+ xlog(L_ERROR, "Unable to get blank capability set: %m");
+ return -errno;
+ }
+
+ /* reset the process capabilities */
+ if (cap_set_proc(caps) != 0) {
+ xlog(L_ERROR, "Unable to set process capabilities: %m");
+ ret = -errno;
+ }
+ cap_free(caps);
+#endif
+ return ret;
+}
+
+/* Inform the kernel that it's OK to lift nfsd's grace period */
+static void
+cltrack_lift_grace_period(void)
+{
+ int fd;
+
+ fd = open(NFSD_END_GRACE_FILE, O_WRONLY);
+ if (fd < 0) {
+ /* Don't warn if file isn't present */
+ if (errno != ENOENT)
+ xlog(L_WARNING, "Unable to open %s: %m",
+ NFSD_END_GRACE_FILE);
+ return;
+ }
+
+ if (write(fd, "Y", 1) < 0)
+ xlog(L_WARNING, "Unable to write to %s: %m",
+ NFSD_END_GRACE_FILE);
+
+ close(fd);
+ return;
+}
+
+/*
+ * Fetch the contents of the NFSDCLTRACK_GRACE_START env var. If it's not set
+ * or there's an error converting it to time_t, then return LONG_MAX.
+ */
+static time_t
+cltrack_get_grace_start(void)
+{
+ time_t grace_start;
+ char *end;
+ char *grace_start_str = getenv("NFSDCLTRACK_GRACE_START");
+
+ if (!grace_start_str)
+ return LONG_MAX;
+
+ errno = 0;
+ grace_start = strtol(grace_start_str, &end, 0);
+ /* Problem converting or value is too large? */
+ if (errno)
+ return LONG_MAX;
+
+ return grace_start;
+}
+
+static bool
+cltrack_reclaims_complete(void)
+{
+ time_t grace_start = cltrack_get_grace_start();
+
+ /* Don't query DB if we didn't get a valid boot time */
+ if (grace_start == LONG_MAX)
+ return false;
+
+ return !sqlite_query_reclaiming(grace_start);
+}
+
+static int
+cltrack_init(const char __attribute__((unused)) *unused)
+{
+ int ret;
+
+ /*
+ * see if the storagedir is writable by root w/o CAP_DAC_OVERRIDE.
+ * If it isn't then give the user a warning but proceed as if
+ * everything is OK. If the DB has already been created, then
+ * everything might still work. If it doesn't exist at all, then
+ * assume that the maindb init will be able to create it. Fail on
+ * anything else.
+ */
+ if (access(storagedir, W_OK) == -1) {
+ switch (errno) {
+ case EACCES:
+ xlog(L_WARNING, "Storage directory %s is not writable. "
+ "Should be owned by root and writable "
+ "by owner!", storagedir);
+ break;
+ case ENOENT:
+ /* ignore and assume that we can create dir as root */
+ break;
+ default:
+ xlog(L_ERROR, "Unexpected error when checking access "
+ "on %s: %m", storagedir);
+ return -errno;
+ }
+ }
+
+ /* set up storage db */
+ ret = sqlite_prepare_dbh(storagedir);
+ if (ret) {
+ xlog(L_ERROR, "Failed to init database: %d", ret);
+ /*
+ * Convert any error here into -EACCES. It's not truly
+ * accurate in all cases, but it should cause the kernel to
+ * stop upcalling until the problem is resolved.
+ */
+ ret = -EACCES;
+ } else {
+ if (cltrack_reclaims_complete())
+ cltrack_lift_grace_period();
+ }
+
+ return ret;
+}
+
+/*
+ * Fetch the contents of the NFSDCLTRACK_CLIENT_HAS_SESSION env var. If
+ * it's set and the first character is 'Y' then return true. Otherwise
+ * return false.
+ */
+static bool
+cltrack_client_has_session(void)
+{
+ char *has_session = getenv("NFSDCLTRACK_CLIENT_HAS_SESSION");
+
+ if (has_session && *has_session == 'Y')
+ return true;
+
+ return false;
+}
+
+static int
+cltrack_create(const char *id)
+{
+ int ret;
+ ssize_t len;
+ bool has_session;
+
+ xlog(D_GENERAL, "%s: create client record.", __func__);
+
+ ret = sqlite_prepare_dbh(storagedir);
+ if (ret)
+ return ret;
+
+ len = hex_str_to_bin(id, blob, sizeof(blob));
+ if (len < 0)
+ return (int)len;
+
+ has_session = cltrack_client_has_session();
+
+ ret = sqlite_insert_client(blob, len, has_session, false);
+
+ if (!ret && has_session && cltrack_reclaims_complete())
+ cltrack_lift_grace_period();
+
+ return ret ? -EREMOTEIO : ret;
+}
+
+static int
+cltrack_remove(const char *id)
+{
+ int ret;
+ ssize_t len;
+
+ xlog(D_GENERAL, "%s: remove client record.", __func__);
+
+ ret = sqlite_prepare_dbh(storagedir);
+ if (ret)
+ return ret;
+
+ len = hex_str_to_bin(id, blob, sizeof(blob));
+ if (len < 0)
+ return (int)len;
+
+ ret = sqlite_remove_client(blob, len);
+
+ return ret ? -EREMOTEIO : ret;
+}
+
+static int
+cltrack_check_legacy(const unsigned char *blob, const ssize_t len,
+ bool has_session)
+{
+ int ret;
+ struct stat st;
+ char *recdir = getenv("NFSDCLTRACK_LEGACY_RECDIR");
+
+ if (!recdir) {
+ xlog(D_GENERAL, "No NFSDCLTRACK_LEGACY_RECDIR env var");
+ return -EOPNOTSUPP;
+ }
+
+ /* fail recovery on any stat failure */
+ ret = stat(recdir, &st);
+ if (ret) {
+ xlog(D_GENERAL, "Unable to stat %s: %d", recdir, errno);
+ return -errno;
+ }
+
+ /* fail if it isn't a directory */
+ if (!S_ISDIR(st.st_mode)) {
+ xlog(D_GENERAL, "%s is not a directory: mode=0%o", recdir
+ , st.st_mode);
+ return -ENOTDIR;
+ }
+
+ /* Dir exists, try to insert record into db */
+ ret = sqlite_insert_client(blob, len, has_session, has_session);
+ if (ret) {
+ xlog(D_GENERAL, "Failed to insert client: %d", ret);
+ return -EREMOTEIO;
+ }
+
+ /* remove the legacy recoverydir */
+ ret = rmdir(recdir);
+ if (ret) {
+ xlog(D_GENERAL, "Failed to rmdir %s: %d", recdir, errno);
+ return -errno;
+ }
+ return 0;
+}
+
+static int
+cltrack_check(const char *id)
+{
+ int ret;
+ ssize_t len;
+ bool has_session;
+
+ xlog(D_GENERAL, "%s: check client record", __func__);
+
+ ret = sqlite_prepare_dbh(storagedir);
+ if (ret)
+ return ret;
+
+ len = hex_str_to_bin(id, blob, sizeof(blob));
+ if (len < 0)
+ return (int)len;
+
+ has_session = cltrack_client_has_session();
+
+ ret = sqlite_check_client(blob, len, has_session);
+ if (ret)
+ ret = cltrack_check_legacy(blob, len, has_session);
+
+ return ret ? -EPERM : ret;
+}
+
+/* Clean out the v4recoverydir -- best effort here */
+static void
+cltrack_legacy_gracedone(void)
+{
+ DIR *v4recovery;
+ struct dirent *entry;
+ char *dirname = getenv("NFSDCLTRACK_LEGACY_TOPDIR");
+
+ if (!dirname)
+ return;
+
+ v4recovery = opendir(dirname);
+ if (!v4recovery)
+ return;
+
+ while ((entry = readdir(v4recovery))) {
+ int len;
+
+ /* skip "." and ".." */
+ if (entry->d_name[0] == '.') {
+ switch (entry->d_name[1]) {
+ case '\0':
+ continue;
+ case '.':
+ if (entry->d_name[2] == '\0')
+ continue;
+ }
+ }
+
+ /* borrow the clientid blob for this */
+ len = snprintf((char *)blob, sizeof(blob), "%s/%s", dirname,
+ entry->d_name);
+
+ /* if there's a problem, then skip this entry */
+ if (len < 0 || (size_t)len >= sizeof(blob)) {
+ xlog(L_WARNING, "%s: unable to build filename for %s!",
+ __func__, entry->d_name);
+ continue;
+ }
+
+ len = rmdir((char *)blob);
+ if (len)
+ xlog(L_WARNING, "%s: unable to rmdir %s: %d", __func__,
+ (char *)blob, len);
+ }
+
+ closedir(v4recovery);
+}
+
+static int
+cltrack_gracedone(const char *timestr)
+{
+ int ret;
+ char *tail;
+ uint64_t gracetime;
+
+
+ ret = sqlite_prepare_dbh(storagedir);
+ if (ret)
+ return ret;
+
+ errno = 0;
+ gracetime = strtol(timestr, &tail, 0);
+
+ /* did the resulting value overflow? (Probably -ERANGE here) */
+ if (errno)
+ return -errno;
+
+ /* string wasn't fully converted */
+ if (*tail)
+ return -EINVAL;
+
+ xlog(D_GENERAL, "%s: grace done. gracetime=%"PRIu64, __func__, gracetime);
+
+ ret = sqlite_remove_unreclaimed(gracetime);
+
+ cltrack_legacy_gracedone();
+
+ return ret ? -EREMOTEIO : ret;
+}
+
+static struct cltrack_cmd *
+find_cmd(char *cmdname)
+{
+ struct cltrack_cmd *current = &commands[0];
+
+ while (current->name) {
+ if (!strcmp(cmdname, current->name))
+ return current;
+ ++current;
+ }
+
+ xlog(L_ERROR, "%s: '%s' doesn't match any known command",
+ __func__, cmdname);
+ return NULL;
+}
+inline static void
+read_nfsdcltrack_conf(void)
+{
+ char *val;
+
+ conf_init_file(NFS_CONFFILE);
+ xlog_set_debug("nfsdcltrack");
+ val = conf_get_str("nfsdcltrack", "storagedir");
+ if (val)
+ storagedir = val;
+}
+int
+main(int argc, char **argv)
+{
+ int arg;
+ int rc = 0;
+ char *progname, *cmdarg = NULL;
+ struct cltrack_cmd *cmd;
+
+ progname = basename(argv[0]);
+
+ xlog_syslog(1);
+ xlog_stderr(0);
+
+ /* Read in config setting */
+ read_nfsdcltrack_conf();
+
+ /* process command-line options */
+ while ((arg = getopt_long(argc, argv, "hdfs:", longopts,
+ NULL)) != EOF) {
+ switch (arg) {
+ case 'd':
+ xlog_config(D_ALL, 1);
+ break;
+ case 'f':
+ xlog_syslog(0);
+ xlog_stderr(1);
+ break;
+ case 's':
+ storagedir = optarg;
+ break;
+ default:
+ usage(progname);
+ return 1;
+ }
+ }
+
+ xlog_open(progname);
+
+ /* we expect a command, at least */
+ if (optind >= argc) {
+ xlog(L_ERROR, "Missing command name\n");
+ rc = -EINVAL;
+ goto out;
+ }
+
+ /* drop all capabilities */
+ rc = cltrack_set_caps();
+ if (rc)
+ goto out;
+
+ cmd = find_cmd(argv[optind]);
+ if (!cmd) {
+ /*
+ * In the event that we get a command that we don't understand
+ * then return a distinct error. The kernel can use this to
+ * determine a new kernel/old userspace situation and cope
+ * with it.
+ */
+ rc = -ENOSYS;
+ goto out;
+ }
+
+ /* populate arg var if command needs it */
+ if (cmd->needs_arg) {
+ if (optind + 1 >= argc) {
+ xlog(L_ERROR, "Command %s requires an argument\n",
+ cmd->name);
+ rc = -EINVAL;
+ goto out;
+ }
+ cmdarg = argv[optind + 1];
+ }
+ rc = cmd->func(cmdarg);
+out:
+ return rc;
+}
diff --git a/utils/nfsdcltrack/nfsdcltrack.man b/utils/nfsdcltrack/nfsdcltrack.man
new file mode 100644
index 0000000..cc24b7a
--- /dev/null
+++ b/utils/nfsdcltrack/nfsdcltrack.man
@@ -0,0 +1,112 @@
+.ie \nF \{\
+. de IX
+. tm Index:\\$1\t\\n%\t"\\$2"
+..
+. nr % 0
+. rr F
+.\}
+.el \{\
+. de IX
+..
+.\}
+.IX Title "NFSDCLTRACK 8"
+.TH NFSDCLTRACK 8 "2012-10-24" "" ""
+.\" For nroff, turn off justification. Always turn off hyphenation; it makes
+.\" way too many mistakes in technical documents.
+.if n .ad l
+.nh
+.SH "NAME"
+nfsdcltrack \- NFSv4 Client Tracking Callout Program
+.SH "SYNOPSIS"
+.IX Header "SYNOPSIS"
+nfsdcltrack [\-d] [\-f] [\-s stable storage dir] <command> <args...>
+.SH "DESCRIPTION"
+.IX Header "DESCRIPTION"
+nfsdcltrack is the NFSv4 client tracking callout program. It is not necessary
+to install this program on machines that are not acting as NFSv4 servers.
+.PP
+When a network partition is combined with a server reboot, there are
+edge conditions that can cause the server to grant lock reclaims when
+other clients have taken conflicting locks in the interim. A more detailed
+explanation of this issue is described in \s-1RFC\s0 3530, section 8.6.3
+and in \s-1RFC\s0 5661, section 8.4.3.
+.PP
+In order to prevent these problems, the server must track a small amount
+of per-client information on stable storage. This program provides the
+userspace piece of that functionality. When the kernel needs to manipulate
+the database that stores this info, it will execute this program to handle
+it.
+.SH "OPTIONS"
+.IX Header "OPTIONS"
+.IP "\fB\-d\fR, \fB\-\-debug\fR" 4
+.IX Item "-d, --debug"
+Enable debug level logging.
+.IP "\fB\-f\fR, \fB\-\-foreground\fR" 4
+.IX Item "-f, --foreground"
+Log to stderr instead of syslog.
+.IP "\fB\-s\fR \fIstoragedir\fR, \fB\-\-storagedir\fR=\fIstorage_dir\fR" 4
+.IX Item "-s storagedir, --storagedir=storage_dir"
+Directory where stable storage information should be kept. The default
+value is \fI/var/lib/nfs/nfsdcltrack\fR.
+.SH "COMMANDS"
+.IX Header "COMMANDS"
+nfsdcltrack requires a command for each invocation. Supported commands
+are:
+.IP "\fBinit\fR" 4
+.IX Item "init"
+Initialize the database. This command requires no argument.
+.IP "\fBcreate\fR" 4
+.IX Item "create"
+Create a new client record (or update the timestamp on an existing one). This command requires a hex-encoded nfs_client_id4 as an argument.
+.IP "\fBremove\fR" 4
+.IX Item "remove"
+Remove a client record from the database. This command requires a hex-encoded nfs_client_id4 as an argument.
+.IP "\fBcheck\fR" 4
+.IX Item "check"
+Check to see if a nfs_client_id4 is allowed to reclaim. This command requires a hex-encoded nfs_client_id4 as an argument.
+.IP "\fBgracedone\fR" 4
+.IX Item "gracedone"
+Remove any unreclaimed client records from the database. This command requires a epoch boot time as an argument.
+.SH "EXTERNAL CONFIGURATION"
+The directory for stable storage information can be set via the file
+.B /etc/nfs.conf
+by setting the
+.B storagedir
+value in the
+.B nfsdcltrack
+section. For example:
+.in +5
+[nfsdcltrack]
+.br
+ storagedir = /shared/nfs/nfsdcltrack
+.in -5
+Debuging to syslog can also be enabled by setting "debug = 1" in this file.
+.SH "LEGACY TRANSITION MECHANISM"
+.IX Header "LEGACY TRANSITION MECHANISM"
+The Linux kernel NFSv4 server has historically tracked this information
+on stable storage by manipulating information on the filesystem
+directly, in the directory to which \fI/proc/fs/nfsd/nfsv4recoverydir\fR
+points. If the kernel passes the correct information, then nfsdcltrack
+can use it to allow a seamless transition from the old client tracking
+scheme to the new one.
+.PP
+On a \fBcheck\fR operation, if there is no record of the client in the
+database, nfsdcltrack will look to see if the \fB\s-1NFSDCLTRACK_LEGACY_RECDIR\s0\fR
+environment variable is set. If it is, then it will fetch that value and
+see if a directory exists by that name. If it does, then the check
+operation will succeed and the directory will be removed.
+.PP
+On a \fBgracedone\fR operation, nfsdcltrack will look to see if the
+\&\fB\s-1NFSDCLTRACK_LEGACY_TOPDIR\s0\fR environment variable is set. If it is, then
+it will attempt to clean out that directory prior to exiting.
+.PP
+Note that this transition is one-way. If the machine subsequently reboots
+back into an older kernel that does not support the nfsdcltrack upcall
+then the clients will not be able to recover their state.
+.SH "NOTES"
+.IX Header "NOTES"
+This program requires a kernel that supports the nfsdcltrack usermodehelper
+upcall. This support was first added to mainline kernels in 3.8.
+.SH "AUTHORS"
+.IX Header "AUTHORS"
+nfsdcltrack was developed by Jeff Layton <jlayton@redhat.com>.
diff --git a/utils/nfsdcltrack/sqlite.c b/utils/nfsdcltrack/sqlite.c
new file mode 100644
index 0000000..cf0c6a4
--- /dev/null
+++ b/utils/nfsdcltrack/sqlite.c
@@ -0,0 +1,604 @@
+/*
+ * Copyright (C) 2011 Red Hat, Jeff Layton <jlayton@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * 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 02110-1301, USA.
+ */
+
+/*
+ * Explanation:
+ *
+ * This file contains the code to manage the sqlite backend database for the
+ * nfsdcltrack usermodehelper upcall program.
+ *
+ * The main database is called main.sqlite and contains the following tables:
+ *
+ * parameters: simple key/value pairs for storing database info
+ *
+ * clients: an "id" column containing a BLOB with the long-form clientid as
+ * sent by the client, a "time" column containing a timestamp (in
+ * epoch seconds) of when the record was last updated, and a
+ * "has_session" column containing a boolean value indicating
+ * whether the client has sessions (v4.1+) or not (v4.0).
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <dirent.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <unistd.h>
+#include <sqlite3.h>
+#include <linux/limits.h>
+
+#include "xlog.h"
+#include "sqlite.h"
+
+#define CLTRACK_SQLITE_LATEST_SCHEMA_VERSION 2
+
+/* in milliseconds */
+#define CLTRACK_SQLITE_BUSY_TIMEOUT 10000
+
+/* private data structures */
+
+/* global variables */
+
+/* reusable pathname and sql command buffer */
+static char buf[PATH_MAX];
+
+/* global database handle */
+static sqlite3 *dbh;
+
+/* forward declarations */
+
+/* make a directory, ignoring EEXIST errors unless it's not a directory */
+static int
+mkdir_if_not_exist(const char *dirname)
+{
+ int ret;
+ struct stat statbuf;
+
+ ret = mkdir(dirname, S_IRWXU);
+ if (ret && errno != EEXIST)
+ return -errno;
+
+ ret = stat(dirname, &statbuf);
+ if (ret)
+ return -errno;
+
+ if (!S_ISDIR(statbuf.st_mode))
+ ret = -ENOTDIR;
+
+ return ret;
+}
+
+static int
+sqlite_query_schema_version(void)
+{
+ int ret;
+ sqlite3_stmt *stmt = NULL;
+
+ /* prepare select query */
+ ret = sqlite3_prepare_v2(dbh,
+ "SELECT value FROM parameters WHERE key == \"version\";",
+ -1, &stmt, NULL);
+ if (ret != SQLITE_OK) {
+ xlog(D_GENERAL, "Unable to prepare select statement: %s",
+ sqlite3_errmsg(dbh));
+ ret = 0;
+ goto out;
+ }
+
+ /* query schema version */
+ ret = sqlite3_step(stmt);
+ if (ret != SQLITE_ROW) {
+ xlog(D_GENERAL, "Select statement execution failed: %s",
+ sqlite3_errmsg(dbh));
+ ret = 0;
+ goto out;
+ }
+
+ ret = sqlite3_column_int(stmt, 0);
+out:
+ sqlite3_finalize(stmt);
+ return ret;
+}
+
+static int
+sqlite_maindb_update_v1_to_v2(void)
+{
+ int ret, ret2;
+ char *err;
+
+ /* begin transaction */
+ ret = sqlite3_exec(dbh, "BEGIN EXCLUSIVE TRANSACTION;", NULL, NULL,
+ &err);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "Unable to begin transaction: %s", err);
+ goto rollback;
+ }
+
+ /*
+ * Check schema version again. This time, under an exclusive
+ * transaction to guard against racing DB setup attempts
+ */
+ ret = sqlite_query_schema_version();
+ switch (ret) {
+ case 1:
+ /* Still at v1 -- do conversion */
+ break;
+ case CLTRACK_SQLITE_LATEST_SCHEMA_VERSION:
+ /* Someone else raced in and set it up */
+ ret = 0;
+ goto rollback;
+ default:
+ /* Something went wrong -- fail! */
+ ret = -EINVAL;
+ goto rollback;
+ }
+
+ /* create v2 clients table */
+ ret = sqlite3_exec(dbh, "ALTER TABLE clients ADD COLUMN "
+ "has_session INTEGER;",
+ NULL, NULL, &err);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "Unable to update clients table: %s", err);
+ goto rollback;
+ }
+
+ ret = snprintf(buf, sizeof(buf), "UPDATE parameters SET value = %d "
+ "WHERE key = \"version\";",
+ CLTRACK_SQLITE_LATEST_SCHEMA_VERSION);
+ if (ret < 0) {
+ xlog(L_ERROR, "sprintf failed!");
+ goto rollback;
+ } else if ((size_t)ret >= sizeof(buf)) {
+ xlog(L_ERROR, "sprintf output too long! (%d chars)", ret);
+ ret = -EINVAL;
+ goto rollback;
+ }
+
+ ret = sqlite3_exec(dbh, (const char *)buf, NULL, NULL, &err);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "Unable to update schema version: %s", err);
+ goto rollback;
+ }
+
+ ret = sqlite3_exec(dbh, "COMMIT TRANSACTION;", NULL, NULL, &err);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "Unable to commit transaction: %s", err);
+ goto rollback;
+ }
+out:
+ sqlite3_free(err);
+ return ret;
+rollback:
+ ret2 = sqlite3_exec(dbh, "ROLLBACK TRANSACTION;", NULL, NULL, &err);
+ if (ret2 != SQLITE_OK)
+ xlog(L_ERROR, "Unable to rollback transaction: %s", err);
+ goto out;
+}
+
+/*
+ * Start an exclusive transaction and recheck the DB schema version. If it's
+ * still zero (indicating a new database) then set it up. If that all works,
+ * then insert schema version into the parameters table and commit the
+ * transaction. On any error, rollback the transaction.
+ */
+static int
+sqlite_maindb_init_v2(void)
+{
+ int ret, ret2;
+ char *err = NULL;
+
+ /* Start a transaction */
+ ret = sqlite3_exec(dbh, "BEGIN EXCLUSIVE TRANSACTION;", NULL, NULL,
+ &err);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "Unable to begin transaction: %s", err);
+ if (err)
+ sqlite3_free(err);
+ return ret;
+ }
+
+ /*
+ * Check schema version again. This time, under an exclusive
+ * transaction to guard against racing DB setup attempts
+ */
+ ret = sqlite_query_schema_version();
+ switch (ret) {
+ case 0:
+ /* Query failed again -- set up DB */
+ break;
+ case CLTRACK_SQLITE_LATEST_SCHEMA_VERSION:
+ /* Someone else raced in and set it up */
+ ret = 0;
+ goto rollback;
+ default:
+ /* Something went wrong -- fail! */
+ ret = -EINVAL;
+ goto rollback;
+ }
+
+ ret = sqlite3_exec(dbh, "CREATE TABLE parameters "
+ "(key TEXT PRIMARY KEY, value TEXT);",
+ NULL, NULL, &err);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "Unable to create parameter table: %s", err);
+ goto rollback;
+ }
+
+ /* create the "clients" table */
+ ret = sqlite3_exec(dbh, "CREATE TABLE clients (id BLOB PRIMARY KEY, "
+ "time INTEGER, has_session INTEGER);",
+ NULL, NULL, &err);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "Unable to create clients table: %s", err);
+ goto rollback;
+ }
+
+
+ /* insert version into parameters table */
+ ret = snprintf(buf, sizeof(buf), "INSERT OR FAIL INTO parameters "
+ "values (\"version\", \"%d\");",
+ CLTRACK_SQLITE_LATEST_SCHEMA_VERSION);
+ if (ret < 0) {
+ xlog(L_ERROR, "sprintf failed!");
+ goto rollback;
+ } else if ((size_t)ret >= sizeof(buf)) {
+ xlog(L_ERROR, "sprintf output too long! (%d chars)", ret);
+ ret = -EINVAL;
+ goto rollback;
+ }
+
+ ret = sqlite3_exec(dbh, (const char *)buf, NULL, NULL, &err);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "Unable to insert into parameter table: %s", err);
+ goto rollback;
+ }
+
+ ret = sqlite3_exec(dbh, "COMMIT TRANSACTION;", NULL, NULL, &err);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "Unable to commit transaction: %s", err);
+ goto rollback;
+ }
+out:
+ sqlite3_free(err);
+ return ret;
+
+rollback:
+ /* Attempt to rollback the transaction */
+ ret2 = sqlite3_exec(dbh, "ROLLBACK TRANSACTION;", NULL, NULL, &err);
+ if (ret2 != SQLITE_OK)
+ xlog(L_ERROR, "Unable to rollback transaction: %s", err);
+ goto out;
+}
+
+/* Open the database and set up the database handle for it */
+int
+sqlite_prepare_dbh(const char *topdir)
+{
+ int ret;
+
+ /* Do nothing if the database handle is already set up */
+ if (dbh)
+ return 0;
+
+ ret = snprintf(buf, PATH_MAX - 1, "%s/main.sqlite", topdir);
+ if (ret < 0)
+ return ret;
+
+ buf[PATH_MAX - 1] = '\0';
+
+ /* open a new DB handle */
+ ret = sqlite3_open(buf, &dbh);
+ if (ret != SQLITE_OK) {
+ /* try to create the dir */
+ ret = mkdir_if_not_exist(topdir);
+ if (ret)
+ goto out_close;
+
+ /* retry open */
+ ret = sqlite3_open(buf, &dbh);
+ if (ret != SQLITE_OK)
+ goto out_close;
+ }
+
+ /* set busy timeout */
+ ret = sqlite3_busy_timeout(dbh, CLTRACK_SQLITE_BUSY_TIMEOUT);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "Unable to set sqlite busy timeout: %s",
+ sqlite3_errmsg(dbh));
+ goto out_close;
+ }
+
+ ret = sqlite_query_schema_version();
+ switch (ret) {
+ case CLTRACK_SQLITE_LATEST_SCHEMA_VERSION:
+ /* DB is already set up. Do nothing */
+ ret = 0;
+ break;
+ case 1:
+ /* Old DB -- update to new schema */
+ ret = sqlite_maindb_update_v1_to_v2();
+ if (ret)
+ goto out_close;
+ break;
+ case 0:
+ /* Query failed -- try to set up new DB */
+ ret = sqlite_maindb_init_v2();
+ if (ret)
+ goto out_close;
+ break;
+ default:
+ /* Unknown DB version -- downgrade? Fail */
+ xlog(L_ERROR, "Unsupported database schema version! "
+ "Expected %d, got %d.",
+ CLTRACK_SQLITE_LATEST_SCHEMA_VERSION, ret);
+ ret = -EINVAL;
+ goto out_close;
+ }
+
+ return ret;
+out_close:
+ sqlite3_close(dbh);
+ dbh = NULL;
+ return ret;
+}
+
+/*
+ * Create a client record
+ *
+ * Returns a non-zero sqlite error code, or SQLITE_OK (aka 0)
+ */
+int
+sqlite_insert_client(const unsigned char *clname, const size_t namelen,
+ const bool has_session, const bool zerotime)
+{
+ int ret;
+ sqlite3_stmt *stmt = NULL;
+
+ if (zerotime)
+ ret = sqlite3_prepare_v2(dbh, "INSERT OR REPLACE INTO clients "
+ "VALUES (?, 0, ?);", -1, &stmt, NULL);
+ else
+ ret = sqlite3_prepare_v2(dbh, "INSERT OR REPLACE INTO clients "
+ "VALUES (?, strftime('%s', 'now'), ?);", -1,
+ &stmt, NULL);
+
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "%s: insert statement prepare failed: %s",
+ __func__, sqlite3_errmsg(dbh));
+ return ret;
+ }
+
+ ret = sqlite3_bind_blob(stmt, 1, (const void *)clname, namelen,
+ SQLITE_STATIC);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "%s: bind blob failed: %s", __func__,
+ sqlite3_errmsg(dbh));
+ goto out_err;
+ }
+
+ ret = sqlite3_bind_int(stmt, 2, (int)has_session);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "%s: bind int failed: %s", __func__,
+ sqlite3_errmsg(dbh));
+ goto out_err;
+ }
+
+ ret = sqlite3_step(stmt);
+ if (ret == SQLITE_DONE)
+ ret = SQLITE_OK;
+ else
+ xlog(L_ERROR, "%s: unexpected return code from insert: %s",
+ __func__, sqlite3_errmsg(dbh));
+
+out_err:
+ xlog(D_GENERAL, "%s: returning %d", __func__, ret);
+ sqlite3_finalize(stmt);
+ return ret;
+}
+
+/* Remove a client record */
+int
+sqlite_remove_client(const unsigned char *clname, const size_t namelen)
+{
+ int ret;
+ sqlite3_stmt *stmt = NULL;
+
+ ret = sqlite3_prepare_v2(dbh, "DELETE FROM clients WHERE id==?", -1,
+ &stmt, NULL);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "%s: statement prepare failed: %s",
+ __func__, sqlite3_errmsg(dbh));
+ goto out_err;
+ }
+
+ ret = sqlite3_bind_blob(stmt, 1, (const void *)clname, namelen,
+ SQLITE_STATIC);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "%s: bind blob failed: %s", __func__,
+ sqlite3_errmsg(dbh));
+ goto out_err;
+ }
+
+ ret = sqlite3_step(stmt);
+ if (ret == SQLITE_DONE)
+ ret = SQLITE_OK;
+ else
+ xlog(L_ERROR, "%s: unexpected return code from delete: %d",
+ __func__, ret);
+
+out_err:
+ xlog(D_GENERAL, "%s: returning %d", __func__, ret);
+ sqlite3_finalize(stmt);
+ return ret;
+}
+
+/*
+ * Is the given clname in the clients table? If so, then update its timestamp
+ * and return success. If the record isn't present, or the update fails, then
+ * return an error.
+ */
+int
+sqlite_check_client(const unsigned char *clname, const size_t namelen,
+ const bool has_session)
+{
+ int ret;
+ sqlite3_stmt *stmt = NULL;
+
+ ret = sqlite3_prepare_v2(dbh, "SELECT count(*) FROM clients WHERE "
+ "id==?", -1, &stmt, NULL);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "%s: unable to prepare update statement: %s",
+ __func__, sqlite3_errmsg(dbh));
+ goto out_err;
+ }
+
+ ret = sqlite3_bind_blob(stmt, 1, (const void *)clname, namelen,
+ SQLITE_STATIC);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "%s: bind blob failed: %s",
+ __func__, sqlite3_errmsg(dbh));
+ goto out_err;
+ }
+
+ ret = sqlite3_step(stmt);
+ if (ret != SQLITE_ROW) {
+ xlog(L_ERROR, "%s: unexpected return code from select: %d",
+ __func__, ret);
+ goto out_err;
+ }
+
+ ret = sqlite3_column_int(stmt, 0);
+ xlog(D_GENERAL, "%s: select returned %d rows", __func__, ret);
+ if (ret != 1) {
+ ret = -EACCES;
+ goto out_err;
+ }
+
+ /* Only update timestamp for v4.0 clients */
+ if (has_session) {
+ ret = SQLITE_OK;
+ goto out_err;
+ }
+
+ sqlite3_finalize(stmt);
+ stmt = NULL;
+ ret = sqlite3_prepare_v2(dbh, "UPDATE OR FAIL clients SET "
+ "time=strftime('%s', 'now') WHERE id==?",
+ -1, &stmt, NULL);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "%s: unable to prepare update statement: %s",
+ __func__, sqlite3_errmsg(dbh));
+ goto out_err;
+ }
+
+ ret = sqlite3_bind_blob(stmt, 1, (const void *)clname, namelen,
+ SQLITE_STATIC);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "%s: bind blob failed: %s",
+ __func__, sqlite3_errmsg(dbh));
+ goto out_err;
+ }
+
+ ret = sqlite3_step(stmt);
+ if (ret == SQLITE_DONE)
+ ret = SQLITE_OK;
+ else
+ xlog(L_ERROR, "%s: unexpected return code from update: %s",
+ __func__, sqlite3_errmsg(dbh));
+
+out_err:
+ xlog(D_GENERAL, "%s: returning %d", __func__, ret);
+ sqlite3_finalize(stmt);
+ return ret;
+}
+
+/*
+ * remove any client records that were not reclaimed since grace_start.
+ */
+int
+sqlite_remove_unreclaimed(uint64_t grace_start)
+{
+ int ret;
+ char *err = NULL;
+
+ ret = snprintf(buf, sizeof(buf), "DELETE FROM clients WHERE time < %"PRIu64,
+ grace_start);
+ if (ret < 0) {
+ return ret;
+ } else if ((size_t)ret >= sizeof(buf)) {
+ ret = -EINVAL;
+ return ret;
+ }
+
+ ret = sqlite3_exec(dbh, buf, NULL, NULL, &err);
+ if (ret != SQLITE_OK)
+ xlog(L_ERROR, "%s: delete failed: %s", __func__, err);
+
+ xlog(D_GENERAL, "%s: returning %d", __func__, ret);
+ sqlite3_free(err);
+ return ret;
+}
+
+/*
+ * Are there any clients that are possibly still reclaiming? Return a positive
+ * integer (usually number of clients) if so. If not, then return 0. On any
+ * error, return non-zero.
+ */
+int
+sqlite_query_reclaiming(const time_t grace_start)
+{
+ int ret;
+ sqlite3_stmt *stmt = NULL;
+
+ ret = sqlite3_prepare_v2(dbh, "SELECT count(*) FROM clients WHERE "
+ "time < ? OR has_session != 1", -1, &stmt, NULL);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "%s: unable to prepare select statement: %s",
+ __func__, sqlite3_errmsg(dbh));
+ return ret;
+ }
+
+ ret = sqlite3_bind_int64(stmt, 1, (sqlite3_int64)grace_start);
+ if (ret != SQLITE_OK) {
+ xlog(L_ERROR, "%s: bind int64 failed: %s",
+ __func__, sqlite3_errmsg(dbh));
+ return ret;
+ }
+
+ ret = sqlite3_step(stmt);
+ if (ret != SQLITE_ROW) {
+ xlog(L_ERROR, "%s: unexpected return code from select: %s",
+ __func__, sqlite3_errmsg(dbh));
+ return ret;
+ }
+
+ ret = sqlite3_column_int(stmt, 0);
+ sqlite3_finalize(stmt);
+ xlog(D_GENERAL, "%s: there are %d clients that have not completed "
+ "reclaim", __func__, ret);
+ return ret;
+}
diff --git a/utils/nfsdcltrack/sqlite.h b/utils/nfsdcltrack/sqlite.h
new file mode 100644
index 0000000..ba8cdfa
--- /dev/null
+++ b/utils/nfsdcltrack/sqlite.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2011 Red Hat, Jeff Layton <jlayton@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * 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 02110-1301, USA.
+ */
+
+#ifndef _SQLITE_H_
+#define _SQLITE_H_
+
+int sqlite_prepare_dbh(const char *topdir);
+int sqlite_insert_client(const unsigned char *clname, const size_t namelen,
+ const bool has_session, const bool zerotime);
+int sqlite_remove_client(const unsigned char *clname, const size_t namelen);
+int sqlite_check_client(const unsigned char *clname, const size_t namelen,
+ const bool has_session);
+int sqlite_remove_unreclaimed(const uint64_t grace_start);
+int sqlite_query_reclaiming(const time_t grace_start);
+
+#endif /* _SQLITE_H */
diff --git a/utils/nfsidmap/Makefile.am b/utils/nfsidmap/Makefile.am
new file mode 100644
index 0000000..e5d7d04
--- /dev/null
+++ b/utils/nfsidmap/Makefile.am
@@ -0,0 +1,14 @@
+## Process this file with automake to produce Makefile.in
+
+man8_MANS = nfsidmap.man
+sbin_PROGRAMS = nfsidmap
+
+AM_CPPFLAGS += -I ../../support/nfsidmap
+
+nfsidmap_SOURCES = nfsidmap.c
+nfsidmap_LDADD = -lkeyutils \
+ ../../support/nfs/libnfs.la \
+ ../../support/nfsidmap/libnfsidmap.la
+
+MAINTAINERCLEANFILES = Makefile.in
+EXTRA_DIST = id_resolver.conf $(man8_MANS)
diff --git a/utils/nfsidmap/Makefile.in b/utils/nfsidmap/Makefile.in
new file mode 100644
index 0000000..dc0c7d9
--- /dev/null
+++ b/utils/nfsidmap/Makefile.in
@@ -0,0 +1,811 @@
+# 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@
+sbin_PROGRAMS = nfsidmap$(EXEEXT)
+subdir = utils/nfsidmap
+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 =
+am__installdirs = "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man8dir)"
+PROGRAMS = $(sbin_PROGRAMS)
+am_nfsidmap_OBJECTS = nfsidmap.$(OBJEXT)
+nfsidmap_OBJECTS = $(am_nfsidmap_OBJECTS)
+nfsidmap_DEPENDENCIES = ../../support/nfs/libnfs.la \
+ ../../support/nfsidmap/libnfsidmap.la
+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 =
+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)/nfsidmap.Po
+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 = $(nfsidmap_SOURCES)
+DIST_SOURCES = $(nfsidmap_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+man8dir = $(mandir)/man8
+NROFF = nroff
+MANS = $(man8_MANS)
+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@ -I ../../support/nfsidmap
+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@
+man8_MANS = nfsidmap.man
+nfsidmap_SOURCES = nfsidmap.c
+nfsidmap_LDADD = -lkeyutils \
+ ../../support/nfs/libnfs.la \
+ ../../support/nfsidmap/libnfsidmap.la
+
+MAINTAINERCLEANFILES = Makefile.in
+EXTRA_DIST = id_resolver.conf $(man8_MANS)
+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 utils/nfsidmap/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu utils/nfsidmap/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):
+install-sbinPROGRAMS: $(sbin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(sbindir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-sbinPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(sbindir)" && rm -f $$files
+
+clean-sbinPROGRAMS:
+ @list='$(sbin_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+nfsidmap$(EXEEXT): $(nfsidmap_OBJECTS) $(nfsidmap_DEPENDENCIES) $(EXTRA_nfsidmap_DEPENDENCIES)
+ @rm -f nfsidmap$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(nfsidmap_OBJECTS) $(nfsidmap_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nfsidmap.Po@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 $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-man8: $(man8_MANS)
+ @$(NORMAL_INSTALL)
+ @list1='$(man8_MANS)'; \
+ list2=''; \
+ test -n "$(man8dir)" \
+ && test -n "`echo $$list1$$list2`" \
+ || exit 0; \
+ echo " $(MKDIR_P) '$(DESTDIR)$(man8dir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(man8dir)" || exit 1; \
+ { for i in $$list1; do echo "$$i"; done; \
+ if test -n "$$list2"; then \
+ for i in $$list2; do echo "$$i"; done \
+ | sed -n '/\.8[a-z]*$$/p'; \
+ fi; \
+ } | while read p; do \
+ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; echo "$$p"; \
+ done | \
+ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \
+ sed 'N;N;s,\n, ,g' | { \
+ list=; while read file base inst; do \
+ if test "$$base" = "$$inst"; then list="$$list $$file"; else \
+ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man8dir)/$$inst'"; \
+ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man8dir)/$$inst" || exit $$?; \
+ fi; \
+ done; \
+ for i in $$list; do echo "$$i"; done | $(am__base_list) | \
+ while read files; do \
+ test -z "$$files" || { \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man8dir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(man8dir)" || exit $$?; }; \
+ done; }
+
+uninstall-man8:
+ @$(NORMAL_UNINSTALL)
+ @list='$(man8_MANS)'; test -n "$(man8dir)" || exit 0; \
+ files=`{ for i in $$list; do echo "$$i"; done; \
+ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
+ dir='$(DESTDIR)$(man8dir)'; $(am__uninstall_files_from_dir)
+
+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 $(PROGRAMS) $(MANS)
+installdirs:
+ for dir in "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man8dir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+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-sbinPROGRAMS \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/nfsidmap.Po
+ -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-man
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-sbinPROGRAMS
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man: install-man8
+
+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)/nfsidmap.Po
+ -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: uninstall-man uninstall-sbinPROGRAMS
+
+uninstall-man: uninstall-man8
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-sbinPROGRAMS 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-man8 install-pdf \
+ install-pdf-am install-ps install-ps-am install-sbinPROGRAMS \
+ 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 \
+ uninstall-man uninstall-man8 uninstall-sbinPROGRAMS
+
+.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/utils/nfsidmap/id_resolver.conf b/utils/nfsidmap/id_resolver.conf
new file mode 100644
index 0000000..2c156c6
--- /dev/null
+++ b/utils/nfsidmap/id_resolver.conf
@@ -0,0 +1 @@
+create id_resolver * * /usr/sbin/nfsidmap -t 600 %k %d
diff --git a/utils/nfsidmap/nfsidmap.c b/utils/nfsidmap/nfsidmap.c
new file mode 100644
index 0000000..cf7f65e
--- /dev/null
+++ b/utils/nfsidmap/nfsidmap.c
@@ -0,0 +1,478 @@
+#include "config.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libgen.h>
+
+#include <pwd.h>
+#include <grp.h>
+#include <keyutils.h>
+#include <nfsidmap.h>
+
+#include <unistd.h>
+#include "xlog.h"
+#include "conffile.h"
+#include "xcommon.h"
+
+int verbose = 0;
+#define USAGE "Usage: %s [-vh] [-c || [-u|-g|-r key] || -d || -l || [-t timeout] key desc]"
+
+#define MAX_ID_LEN 11
+#define IDMAP_NAMESZ 128
+#define USER 1
+#define GROUP 0
+
+#define PROCKEYS "/proc/keys"
+#ifndef DEFAULT_KEYRING
+#define DEFAULT_KEYRING ".id_resolver"
+#endif
+
+#ifndef PATH_IDMAPDCONF
+#define PATH_IDMAPDCONF "/etc/idmapd.conf"
+#endif
+
+#define UIDKEYS 0x1
+#define GIDKEYS 0x2
+
+#ifndef HAVE_FIND_KEY_BY_TYPE_AND_DESC
+static key_serial_t find_key_by_type_and_desc(const char *type,
+ const char *desc, key_serial_t destringid)
+{
+ char buf[BUFSIZ];
+ key_serial_t key;
+ FILE *fp;
+
+ if ((fp = fopen(PROCKEYS, "r")) == NULL) {
+ xlog_err("fopen(%s) failed: %m", PROCKEYS);
+ return -1;
+ }
+
+ key = -1;
+ while(fgets(buf, BUFSIZ, fp) != NULL) {
+ unsigned int id;
+
+ if (strstr(buf, type) == NULL)
+ continue;
+ if (strstr(buf, desc) == NULL)
+ continue;
+ if (sscanf(buf, "%x %*s", &id) != 1) {
+ xlog_err("Unparsable keyring entry in %s", PROCKEYS);
+ continue;
+ }
+
+ key = (key_serial_t)id;
+ break;
+ }
+
+ fclose(fp);
+ return key;
+}
+#endif
+
+/*
+ * Clear all the keys on the given keyring
+ */
+static int keyring_clear(const char *keyring)
+{
+ key_serial_t key;
+
+ key = find_key_by_type_and_desc("keyring", keyring, 0);
+ if (key == -1) {
+ if (verbose)
+ xlog_warn("'%s' keyring was not found.", keyring);
+ return EXIT_SUCCESS;
+ }
+
+ if (keyctl_clear(key) < 0) {
+ xlog_err("keyctl_clear(0x%x) failed: %m",
+ (unsigned int)key);
+ return EXIT_FAILURE;
+ }
+
+ if (verbose)
+ xlog_warn("'%s' cleared", keyring);
+ return EXIT_SUCCESS;
+}
+
+static int display_default_domain(void)
+{
+ char domain[NFS4_MAX_DOMAIN_LEN];
+ int rc;
+
+ rc = nfs4_get_default_domain(NULL, domain, NFS4_MAX_DOMAIN_LEN);
+ if (rc) {
+ xlog_errno(rc, "nfs4_get_default_domain failed: %m");
+ return EXIT_FAILURE;
+ }
+
+ printf("%s\n", domain);
+ return EXIT_SUCCESS;
+}
+
+static void list_key(key_serial_t key)
+{
+ char *buffer, *c;
+ int rc;
+
+ rc = keyctl_describe_alloc(key, &buffer);
+ if (rc < 0) {
+ switch (errno) {
+ case EKEYEXPIRED:
+ printf("Expired key not displayed\n");
+ break;
+ default:
+ xlog_err("Failed to describe key: %m");
+ }
+ return;
+ }
+
+ c = strrchr(buffer, ';');
+ if (!c) {
+ xlog_err("Unparsable key not displayed\n");
+ goto out_free;
+ }
+ printf(" %s\n", ++c);
+
+out_free:
+ free(buffer);
+}
+
+static void list_keys(const char *ring_name, key_serial_t ring_id)
+{
+ key_serial_t *key;
+ void *keylist;
+ int count;
+
+ count = keyctl_read_alloc(ring_id, &keylist);
+ if (count < 0) {
+ xlog_err("Failed to read keyring %s: %m", ring_name);
+ return;
+ }
+ count /= (int)sizeof(*key);
+
+ switch (count) {
+ case 0:
+ printf("No %s keys found.\n", ring_name);
+ break;
+ case 1:
+ printf("1 %s key found:\n", ring_name);
+ break;
+ default:
+ printf("%u %s keys found:\n", count, ring_name);
+ }
+
+ for (key = keylist; count--; key++)
+ list_key(*key);
+
+ free(keylist);
+}
+
+/*
+ * List all keys on a keyring
+ */
+static int list_keyring(const char *keyring)
+{
+ key_serial_t key;
+
+ key = find_key_by_type_and_desc("keyring", keyring, 0);
+ if (key == -1) {
+ xlog_err("'%s' keyring was not found.", keyring);
+ return EXIT_FAILURE;
+ }
+
+ list_keys(keyring, key);
+ return EXIT_SUCCESS;
+}
+
+/*
+ * Find either a user or group id based on the name@domain string
+ */
+static int id_lookup(char *name_at_domain, key_serial_t key, int type)
+{
+ char id[MAX_ID_LEN];
+ uid_t uid = 0;
+ gid_t gid = 0;
+ int rc;
+
+ if (type == USER) {
+ rc = nfs4_owner_to_uid(name_at_domain, &uid);
+ sprintf(id, "%u", uid);
+ } else {
+ rc = nfs4_group_owner_to_gid(name_at_domain, &gid);
+ sprintf(id, "%u", gid);
+ }
+ if (rc < 0) {
+ xlog_errno(rc, "id_lookup: %s: for %s failed: %m",
+ (type == USER ? "nfs4_owner_to_uid" : "nfs4_group_owner_to_gid"),
+ name_at_domain);
+ return EXIT_FAILURE;
+ }
+
+ rc = EXIT_SUCCESS;
+ if (keyctl_instantiate(key, id, strlen(id) + 1, 0)) {
+ switch (errno) {
+ case EDQUOT:
+ case ENFILE:
+ case ENOMEM:
+ /*
+ * The keyring is full. Clear the keyring and try again
+ */
+ rc = keyring_clear(DEFAULT_KEYRING);
+ if (rc)
+ break;
+ if (keyctl_instantiate(key, id, strlen(id) + 1, 0)) {
+ rc = EXIT_FAILURE;
+ xlog_err("id_lookup: keyctl_instantiate failed: %m");
+ }
+ break;
+ default:
+ rc = EXIT_FAILURE;
+ break;
+ }
+ }
+
+ return rc;
+}
+
+/*
+ * Find the name@domain string from either a user or group id
+ */
+static int name_lookup(char *id, key_serial_t key, int type)
+{
+ char name[IDMAP_NAMESZ];
+ char domain[NFS4_MAX_DOMAIN_LEN];
+ uid_t uid;
+ gid_t gid;
+ int rc;
+
+ rc = nfs4_get_default_domain(NULL, domain, NFS4_MAX_DOMAIN_LEN);
+ if (rc) {
+ xlog_errno(rc,
+ "name_lookup: nfs4_get_default_domain failed: %m");
+ return EXIT_FAILURE;
+ }
+
+ if (type == USER) {
+ uid = atoi(id);
+ rc = nfs4_uid_to_name(uid, domain, name, IDMAP_NAMESZ);
+ } else {
+ gid = atoi(id);
+ rc = nfs4_gid_to_name(gid, domain, name, IDMAP_NAMESZ);
+ }
+ if (rc) {
+ xlog_errno(rc, "name_lookup: %s: for %u failed: %m",
+ (type == USER ? "nfs4_uid_to_name" : "nfs4_gid_to_name"),
+ (type == USER ? uid : gid));
+ return EXIT_FAILURE;
+ }
+
+ rc = EXIT_SUCCESS;
+ if (keyctl_instantiate(key, &name, strlen(name), 0)) {
+ rc = EXIT_FAILURE;
+ xlog_err("name_lookup: keyctl_instantiate failed: %m");
+ }
+
+ return rc;
+}
+
+/*
+ * Revoke a key
+ */
+static int key_invalidate(char *keystr, int keymask)
+{
+ FILE *fp;
+ char buf[BUFSIZ], *ptr;
+ unsigned int key;
+ int mask;
+
+ xlog_syslog(0);
+
+ if ((fp = fopen(PROCKEYS, "r")) == NULL) {
+ xlog_err("fopen(%s) failed: %m", PROCKEYS);
+ return EXIT_FAILURE;
+ }
+
+ while(fgets(buf, BUFSIZ, fp) != NULL) {
+ if (strstr(buf, "keyring") != NULL)
+ continue;
+
+ mask = 0;
+ if ((ptr = strstr(buf, "uid:")) != NULL)
+ mask = UIDKEYS;
+ else if ((ptr = strstr(buf, "gid:")) != NULL)
+ mask = GIDKEYS;
+ else
+ continue;
+
+ if ((keymask & mask) == 0)
+ continue;
+
+ if (strncmp(ptr+4, keystr, strlen(keystr)) != 0)
+ continue;
+
+ if (verbose) {
+ *(strchr(buf, '\n')) = '\0';
+ xlog_warn("invalidating '%s'", buf);
+ }
+ /*
+ * The key is the first arugment in the string
+ */
+ *(strchr(buf, ' ')) = '\0';
+ sscanf(buf, "%x", &key);
+
+/* older libkeyutils compatibility */
+#ifndef KEYCTL_INVALIDATE
+#define KEYCTL_INVALIDATE 21 /* invalidate a key */
+#endif
+ if (keyctl(KEYCTL_INVALIDATE, key) < 0) {
+ if (errno != EOPNOTSUPP) {
+ xlog_err("keyctl_invalidate(0x%x) failed: %m", key);
+ fclose(fp);
+ return EXIT_FAILURE;
+ } else {
+ /* older kernel compatibility attempt: */
+ if (keyctl_revoke(key) < 0) {
+ xlog_err("keyctl_revoke(0x%x) failed: %m", key);
+ fclose(fp);
+ return EXIT_FAILURE;
+ }
+ }
+ }
+
+ keymask &= ~mask;
+ if (keymask == 0) {
+ fclose(fp);
+ return EXIT_SUCCESS;
+ }
+ }
+ xlog_err("'%s' key was not found.", keystr);
+ fclose(fp);
+ return EXIT_FAILURE;
+}
+
+int main(int argc, char **argv)
+{
+ char *arg;
+ char *value;
+ char *type;
+ int rc = 1, opt;
+ int timeout = 600;
+ key_serial_t key;
+ char *progname, *keystr = NULL;
+ int clearing = 0, keymask = 0, display = 0, list = 0;
+
+ /* Set the basename */
+ if ((progname = strrchr(argv[0], '/')) != NULL)
+ progname++;
+ else
+ progname = argv[0];
+
+ xlog_open(progname);
+
+ while ((opt = getopt(argc, argv, "hdu:g:r:ct:vl")) != -1) {
+ switch (opt) {
+ case 'd':
+ display++;
+ break;
+ case 'l':
+ list++;
+ break;
+ case 'u':
+ keymask = UIDKEYS;
+ keystr = strdup(optarg);
+ break;
+ case 'g':
+ keymask = GIDKEYS;
+ keystr = strdup(optarg);
+ break;
+ case 'r':
+ keymask = GIDKEYS|UIDKEYS;
+ keystr = strdup(optarg);
+ break;
+ case 'c':
+ clearing++;
+ break;
+ case 'v':
+ verbose++;
+ break;
+ case 't':
+ timeout = atoi(optarg);
+ break;
+ case 'h':
+ default:
+ xlog_warn(USAGE, progname);
+ exit(opt == 'h' ? 0 : 1);
+ }
+ }
+
+ if (geteuid() != 0) {
+ xlog_err("Must be run as root.");
+ return EXIT_FAILURE;
+ }
+
+ if ((rc = nfs4_init_name_mapping(PATH_IDMAPDCONF))) {
+ xlog_errno(rc, "Unable to create name to user id mappings.");
+ return EXIT_FAILURE;
+ }
+ if (!verbose)
+ verbose = conf_get_num("General", "Verbosity", 0);
+
+ if (display)
+ return display_default_domain();
+ if (list)
+ return list_keyring(DEFAULT_KEYRING);
+ if (keystr) {
+ return key_invalidate(keystr, keymask);
+ }
+ if (clearing) {
+ xlog_syslog(0);
+ return keyring_clear(DEFAULT_KEYRING);
+ }
+
+ xlog_stderr(verbose);
+ if ((argc - optind) != 2) {
+ xlog_warn("Bad arg count. Check /etc/request-key.conf");
+ xlog_warn(USAGE, progname);
+ return EXIT_FAILURE;
+ }
+
+ if (verbose)
+ nfs4_set_debug(verbose, NULL);
+
+ key = strtol(argv[optind++], NULL, 10);
+
+ arg = xstrdup(argv[optind]);
+ type = strtok(arg, ":");
+ value = strtok(NULL, ":");
+ if (value == NULL) {
+ free(arg);
+ xlog_err("Error: Null uid/gid value.");
+ return EXIT_FAILURE;
+ }
+ if (verbose) {
+ xlog_warn("key: 0x%x type: %s value: %s timeout %d",
+ key, type, value, timeout);
+ }
+
+ /* Become a possesor of the to-be-instantiated key to set the key's timeout */
+ request_key("keyring", DEFAULT_KEYRING, NULL, KEY_SPEC_THREAD_KEYRING);
+
+ if (strcmp(type, "uid") == 0)
+ rc = id_lookup(value, key, USER);
+ else if (strcmp(type, "gid") == 0)
+ rc = id_lookup(value, key, GROUP);
+ else if (strcmp(type, "user") == 0)
+ rc = name_lookup(value, key, USER);
+ else if (strcmp(type, "group") == 0)
+ rc = name_lookup(value, key, GROUP);
+
+ /* Set timeout to 10 (600 seconds) minutes */
+ if (rc == EXIT_SUCCESS)
+ keyctl_set_timeout(key, timeout);
+
+ free(arg);
+ return rc;
+}
diff --git a/utils/nfsidmap/nfsidmap.man b/utils/nfsidmap/nfsidmap.man
new file mode 100644
index 0000000..1911c41
--- /dev/null
+++ b/utils/nfsidmap/nfsidmap.man
@@ -0,0 +1,159 @@
+.\"
+.\"@(#)nfsidmap(8) - The NFS idmapper upcall program
+.\"
+.\" Copyright (C) 2010 Bryan Schumaker <bjschuma@netapp.com>
+.TH nfsidmap 8 "1 October 2010"
+.SH NAME
+nfsidmap \- The NFS idmapper upcall program
+.SH SYNOPSIS
+.B "nfsidmap [-v] [-t timeout] key desc"
+.br
+.B "nfsidmap [-v] [-c]"
+.br
+.B "nfsidmap [-v] [-u|-g|-r user]"
+.br
+.B "nfsidmap -d"
+.br
+.B "nfsidmap -l"
+.br
+.B "nfsidmap -h"
+.SH DESCRIPTION
+The NFSv4 protocol represents the local system's UID and GID values
+on the wire as strings of the form
+.IR user@domain .
+The process of translating from UID to string and string to UID is
+referred to as "ID mapping."
+.PP
+The system derives the
+.I user
+part of the string by performing a password or group lookup.
+The lookup mechanism is configured in
+.IR /etc/idmapd.conf .
+.PP
+By default, the
+.I domain
+part of the string is the system's DNS domain name.
+It can also be specified in
+.I /etc/idmapd.conf
+if the system is multi-homed,
+or if the system's DNS domain name does
+not match the name of the system's Kerberos realm.
+.PP
+When the domain is not specified in
+.I /etc/idmapd.conf
+the local DNS server will be queried for the
+.I _nfsv4idmapdomain
+text record. If the record exists
+that will be used as the domain. When the record
+does not exist, the domain part of the DNS domain
+will used.
+.PP
+The
+.I /usr/sbin/nfsidmap
+program performs translations on behalf of the kernel.
+The kernel uses the request-key mechanism to perform
+an upcall.
+.I /usr/sbin/nfsidmap
+is invoked by /sbin/request-key, performs the translation,
+and initializes a key with the resulting information.
+The kernel then caches the translation results in the key.
+.PP
+.I nfsidmap
+can also clear cached ID map results in the kernel,
+or revoke one particular key.
+An incorrect cached key can result in file and directory ownership
+reverting to "nobody" on NFSv4 mount points.
+.PP
+In addition, the
+.B -d
+and
+.B -l
+options are available to help diagnose misconfigurations.
+They have no effect on the keyring containing ID mapping results.
+.SH OPTIONS
+.TP
+.B -c
+Clear the keyring of all the keys.
+.TP
+.B -d
+Display the system's effective NFSv4 domain name on
+.IR stdout .
+.TP
+.B -g user
+Revoke the gid key of the given user.
+.TP
+.B -h
+Display usage message.
+.TP
+.B -l
+Display on
+.I stdout
+all keys currently in the keyring used to cache ID mapping results.
+These keys are visible only to the superuser.
+.TP
+.B -r user
+Revoke both the uid and gid key of the given user.
+.TP
+.B -t timeout
+Set the expiration timer, in seconds, on the key.
+The default is 600 seconds (10 mins).
+.TP
+.B -u user
+Revoke the uid key of the given user.
+.TP
+.B -v
+Increases the verbosity of the output to syslog
+(can be specified multiple times).
+.SH CONFIGURING
+The file
+.I /etc/request-key.conf
+will need to be modified so
+.I /sbin/request-key
+can properly direct the upcall. The following line should be added before a call
+to keyctl negate:
+.PP
+create id_resolver * * /usr/sbin/nfsidmap -t 600 %k %d
+.PP
+This will direct all id_resolver requests to the program
+.I /usr/sbin/nfsidmap.
+The
+.B -t 600
+defines how many seconds into the future the key will
+expire. This is an optional parameter for
+.I /usr/sbin/nfsidmap
+and will default to 600 seconds when not specified.
+.PP
+The idmapper system uses four key descriptions:
+.PP
+ uid: Find the UID for the given user
+.br
+ gid: Find the GID for the given group
+.br
+ user: Find the user name for the given UID
+.br
+ group: Find the group name for the given GID
+.PP
+You can choose to handle any of these individually, rather than using the
+generic upcall program. If you would like to use your own program for a uid
+lookup then you would edit your request-key.conf so it looks similar to this:
+.PP
+create id_resolver uid:* * /some/other/program %k %d
+.br
+create id_resolver * * /usr/sbin/nfsidmap %k %d
+.PP
+Notice that the new line was added above the line for the generic program.
+request-key will find the first matching line and run the corresponding program.
+In this case, /some/other/program will handle all uid lookups, and
+/usr/sbin/nfsidmap will handle gid, user, and group lookups.
+.SH FILES
+.TP
+.I /etc/idmapd.conf
+ID mapping configuration file
+.TP
+.I /etc/request-key.conf
+Request key configuration file
+.SH "SEE ALSO"
+.BR idmapd.conf (5),
+.BR request-key (8)
+.SH AUTHOR
+Bryan Schumaker, <bjschuma@netapp.com>
diff --git a/utils/nfsref/Makefile.am b/utils/nfsref/Makefile.am
new file mode 100644
index 0000000..2409dd0
--- /dev/null
+++ b/utils/nfsref/Makefile.am
@@ -0,0 +1,37 @@
+##
+## @file utils/nfsref/Makefile.am
+## @brief Process this file with automake to produce utils/nfsref/Makefile.in
+##
+
+##
+## Copyright 2011, 2018 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 version 2.0 as
+## published by the Free Software Foundation.
+##
+## 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 version 2.0 for more details.
+##
+## You should have received a copy of the GNU General Public License
+## version 2.0 along with nfs-utils. If not, see:
+##
+## http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+##
+
+noinst_HEADERS = nfsref.h
+
+sbin_PROGRAMS = nfsref
+nfsref_SOURCES = add.c lookup.c nfsref.c remove.c
+LDADD = ../../support/nfs/libnfs.la \
+ ../../support/junction/libjunction.la \
+ $(LIBXML2) $(LIBCAP)
+
+man8_MANS = nfsref.man
+
+MAINTAINERCLEANFILES = Makefile.in
+
diff --git a/utils/nfsref/Makefile.in b/utils/nfsref/Makefile.in
new file mode 100644
index 0000000..4080997
--- /dev/null
+++ b/utils/nfsref/Makefile.in
@@ -0,0 +1,828 @@
+# 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@
+sbin_PROGRAMS = nfsref$(EXEEXT)
+subdir = utils/nfsref
+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 $(noinst_HEADERS) \
+ $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/support/include/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man8dir)"
+PROGRAMS = $(sbin_PROGRAMS)
+am_nfsref_OBJECTS = add.$(OBJEXT) lookup.$(OBJEXT) nfsref.$(OBJEXT) \
+ remove.$(OBJEXT)
+nfsref_OBJECTS = $(am_nfsref_OBJECTS)
+nfsref_LDADD = $(LDADD)
+am__DEPENDENCIES_1 =
+nfsref_DEPENDENCIES = ../../support/nfs/libnfs.la \
+ ../../support/junction/libjunction.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+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 =
+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)/add.Po ./$(DEPDIR)/lookup.Po \
+ ./$(DEPDIR)/nfsref.Po ./$(DEPDIR)/remove.Po
+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 = $(nfsref_SOURCES)
+DIST_SOURCES = $(nfsref_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+man8dir = $(mandir)/man8
+NROFF = nroff
+MANS = $(man8_MANS)
+HEADERS = $(noinst_HEADERS)
+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_HEADERS = nfsref.h
+nfsref_SOURCES = add.c lookup.c nfsref.c remove.c
+LDADD = ../../support/nfs/libnfs.la \
+ ../../support/junction/libjunction.la \
+ $(LIBXML2) $(LIBCAP)
+
+man8_MANS = nfsref.man
+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 utils/nfsref/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu utils/nfsref/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):
+install-sbinPROGRAMS: $(sbin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(sbindir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-sbinPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(sbindir)" && rm -f $$files
+
+clean-sbinPROGRAMS:
+ @list='$(sbin_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+nfsref$(EXEEXT): $(nfsref_OBJECTS) $(nfsref_DEPENDENCIES) $(EXTRA_nfsref_DEPENDENCIES)
+ @rm -f nfsref$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(nfsref_OBJECTS) $(nfsref_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/add.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lookup.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nfsref.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/remove.Po@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 $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-man8: $(man8_MANS)
+ @$(NORMAL_INSTALL)
+ @list1='$(man8_MANS)'; \
+ list2=''; \
+ test -n "$(man8dir)" \
+ && test -n "`echo $$list1$$list2`" \
+ || exit 0; \
+ echo " $(MKDIR_P) '$(DESTDIR)$(man8dir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(man8dir)" || exit 1; \
+ { for i in $$list1; do echo "$$i"; done; \
+ if test -n "$$list2"; then \
+ for i in $$list2; do echo "$$i"; done \
+ | sed -n '/\.8[a-z]*$$/p'; \
+ fi; \
+ } | while read p; do \
+ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; echo "$$p"; \
+ done | \
+ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \
+ sed 'N;N;s,\n, ,g' | { \
+ list=; while read file base inst; do \
+ if test "$$base" = "$$inst"; then list="$$list $$file"; else \
+ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man8dir)/$$inst'"; \
+ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man8dir)/$$inst" || exit $$?; \
+ fi; \
+ done; \
+ for i in $$list; do echo "$$i"; done | $(am__base_list) | \
+ while read files; do \
+ test -z "$$files" || { \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man8dir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(man8dir)" || exit $$?; }; \
+ done; }
+
+uninstall-man8:
+ @$(NORMAL_UNINSTALL)
+ @list='$(man8_MANS)'; test -n "$(man8dir)" || exit 0; \
+ files=`{ for i in $$list; do echo "$$i"; done; \
+ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
+ dir='$(DESTDIR)$(man8dir)'; $(am__uninstall_files_from_dir)
+
+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 $(PROGRAMS) $(MANS) $(HEADERS)
+installdirs:
+ for dir in "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man8dir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+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-sbinPROGRAMS \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/add.Po
+ -rm -f ./$(DEPDIR)/lookup.Po
+ -rm -f ./$(DEPDIR)/nfsref.Po
+ -rm -f ./$(DEPDIR)/remove.Po
+ -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-man
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-sbinPROGRAMS
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man: install-man8
+
+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)/add.Po
+ -rm -f ./$(DEPDIR)/lookup.Po
+ -rm -f ./$(DEPDIR)/nfsref.Po
+ -rm -f ./$(DEPDIR)/remove.Po
+ -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: uninstall-man uninstall-sbinPROGRAMS
+
+uninstall-man: uninstall-man8
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-sbinPROGRAMS 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-man8 install-pdf \
+ install-pdf-am install-ps install-ps-am install-sbinPROGRAMS \
+ 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 \
+ uninstall-man uninstall-man8 uninstall-sbinPROGRAMS
+
+.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/utils/nfsref/add.c b/utils/nfsref/add.c
new file mode 100644
index 0000000..781aeee
--- /dev/null
+++ b/utils/nfsref/add.c
@@ -0,0 +1,272 @@
+/**
+ * @file utils/nfsref/add.c
+ * @brief Add junction metadata to a local file system object
+ */
+
+/*
+ * Copyright 2011, 2018 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 version 2.0 as
+ * published by the Free Software Foundation.
+ *
+ * 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 version 2.0 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2.0 along with nfs-utils. If not, see:
+ *
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+ */
+
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <uuid/uuid.h>
+
+#include "junction.h"
+#include "xlog.h"
+#include "nfsref.h"
+
+/**
+ * Default cache expiration for FSN information
+ */
+#define FSN_DEFAULT_TTL (300)
+
+/**
+ * Display help message for "add" subcommand
+ *
+ * @param progname NUL-terminated C string containing name of program
+ * @return program exit status
+ */
+int
+nfsref_add_help(const char *progname)
+{
+ fprintf(stderr, " \n");
+
+ fprintf(stderr, "Usage: %s [ -t type ] add <junction path> "
+ "<server> <export> [ <server> <export> ... ]\n\n",
+ progname);
+
+ fprintf(stderr, "Add a new junction containing the specified list "
+ "of fileset locations.\n");
+ fprintf(stderr, "<junction path> is the filename of the new junction. "
+ "<server> is the hostname\n");
+ fprintf(stderr, "or IP address of an NFS server where the fileset is "
+ "located. <export> is the\n");
+ fprintf(stderr, "export pathname of the fileset on that server.\n\n");
+
+ fprintf(stderr, "For NFS basic junctions, the location list is stored "
+ "locally in the junction.\n");
+ fprintf(stderr, "For FedFS junctions, the location list is stored "
+ "as new FSN and FSL records\n");
+ fprintf(stderr, "on an NSDB.\n");
+
+ return EXIT_SUCCESS;
+}
+
+/**
+ * Fill in default settings for NFSv4.0 fs_locations4
+ *
+ * @param new NFS location structure to fill in
+ *
+ * See section 5.1.3.2 of the NSDB protocol draft.
+ */
+static void
+nfsref_add_fsloc_defaults(struct nfs_fsloc *new)
+{
+ new->nfl_hostport = 0;
+ new->nfl_flags.nfl_varsub = false;
+ new->nfl_currency = -1;
+ new->nfl_validfor = 0;
+ new->nfl_genflags.nfl_writable = false;
+ new->nfl_genflags.nfl_going = false;
+ new->nfl_genflags.nfl_split = true;
+ new->nfl_transflags.nfl_rdma = true;
+ new->nfl_info.nfl_simul = 0;
+ new->nfl_info.nfl_handle = 0;
+ new->nfl_info.nfl_fileid = 0;
+ new->nfl_info.nfl_writever = 0;
+ new->nfl_info.nfl_change = 0;
+ new->nfl_info.nfl_readdir = 0;
+ new->nfl_info.nfl_readrank = 0;
+ new->nfl_info.nfl_readorder = 0;
+ new->nfl_info.nfl_writerank = 0;
+ new->nfl_info.nfl_writeorder = 0;
+}
+
+/**
+ * Convert a pair of command line arguments to one nfs_fsloc structure
+ *
+ * @param server NUL-terminated C string containing file server hostname
+ * @param rootpath NUL-terminated C string containing POSIX-style export path
+ * @param fsloc OUT: NFS location structure
+ * @return a FedFsStatus code
+ *
+ * If nfsref_add_build_fsloc() returns FEDFS_OK, caller must free the
+ * returned fsloc with nfs_free_location().
+ */
+static FedFsStatus
+nfsref_add_build_fsloc(const char *server, const char *rootpath,
+ struct nfs_fsloc **fsloc)
+{
+ struct nfs_fsloc *new;
+ FedFsStatus retval;
+
+ if (server == NULL || rootpath == NULL)
+ return FEDFS_ERR_INVAL;
+
+ xlog(D_GENERAL, "%s: Building fsloc for %s:%s",
+ __func__, server, rootpath);
+
+ new = nfs_new_location();
+ if (new == NULL) {
+ xlog(D_GENERAL, "%s: No memory", __func__);
+ return FEDFS_ERR_SVRFAULT;
+ }
+
+ new->nfl_hostname = strdup(server);
+ if (new->nfl_hostname == NULL) {
+ nfs_free_location(new);
+ xlog(D_GENERAL, "%s: No memory", __func__);
+ return FEDFS_ERR_SVRFAULT;
+ }
+
+ retval = nsdb_posix_to_path_array(rootpath, &new->nfl_rootpath);
+ if (retval != FEDFS_OK) {
+ nfs_free_location(new);
+ return retval;
+ }
+
+ nfsref_add_fsloc_defaults(new);
+ *fsloc = new;
+ return FEDFS_OK;
+}
+
+/**
+ * Convert array of command line arguments to list of nfs_fsloc structures
+ *
+ * @param argv array of pointers to NUL-terminated C strings contains arguments
+ * @param optind index of "argv" where "add" subcommand arguments start
+ * @param fslocs OUT: list of NFS locations
+ * @return a FedFsStatus code
+ *
+ * If nfsref_add_build_fsloc_list() returns FEDFS_OK, caller must free the
+ * returned list of fslocs with nfs_free_locations().
+ */
+static FedFsStatus
+nfsref_add_build_fsloc_list(char **argv, int optind, struct nfs_fsloc **fslocs)
+{
+ struct nfs_fsloc *fsloc, *result = NULL;
+ FedFsStatus retval;
+ int i;
+
+ for (i = optind + 2; argv[i] != NULL; i += 2) {
+ retval = nfsref_add_build_fsloc(argv[i], argv[i + 1], &fsloc);
+ if (retval != FEDFS_OK) {
+ nfs_free_locations(result);
+ return retval;
+ }
+ if (result == NULL)
+ result = fsloc;
+ else
+ result->nfl_next = fsloc;
+ }
+ if (result == NULL)
+ return FEDFS_ERR_INVAL;
+
+ *fslocs = result;
+ return FEDFS_OK;
+}
+
+/**
+ * Add NFS locations to a junction
+ *
+ * @param junct_path NUL-terminated C string containing pathname of junction
+ * @param argv array of pointers to NUL-terminated C strings contains arguments
+ * @param optind index of "argv" where "add" subcommand arguments start
+ * @return program exit status
+ */
+static int
+nfsref_add_nfs_basic(const char *junct_path, char **argv, int optind)
+{
+ struct nfs_fsloc *fslocs = NULL;
+ FedFsStatus retval;
+
+ xlog(D_GENERAL, "%s: Adding basic junction to %s",
+ __func__, junct_path);
+
+ retval = nfsref_add_build_fsloc_list(argv, optind, &fslocs);
+ switch (retval) {
+ case FEDFS_OK:
+ break;
+ case FEDFS_ERR_INVAL:
+ xlog(L_ERROR, "Missing arguments");
+ return EXIT_FAILURE;
+ case FEDFS_ERR_SVRFAULT:
+ xlog(L_ERROR, "No memory");
+ return EXIT_FAILURE;
+ default:
+ xlog(L_ERROR, "Failed to add NFS location metadata to %s: %s",
+ junct_path, nsdb_display_fedfsstatus(retval));
+ return EXIT_FAILURE;
+ }
+
+ retval = nfs_add_junction(junct_path, fslocs);
+ nfs_free_locations(fslocs);
+ switch (retval) {
+ case FEDFS_OK:
+ break;
+ case FEDFS_ERR_EXIST:
+ xlog(L_ERROR, "%s already contains junction metadata",
+ junct_path);
+ return EXIT_FAILURE;
+ default:
+ xlog(L_ERROR, "Failed to add NFS location metadata to %s: %s",
+ junct_path, nsdb_display_fedfsstatus(retval));
+ return EXIT_FAILURE;
+ }
+
+ printf("Created junction %s\n", junct_path);
+ return EXIT_SUCCESS;
+}
+
+/**
+ * Add locations to a junction
+ *
+ * @param type type of junction to add
+ * @param junct_path NUL-terminated C string containing pathname of junction
+ * @param argv array of pointers to NUL-terminated C strings contains arguments
+ * @param optind index of "argv" where "add" subcommand arguments start
+ * @return program exit status
+ */
+int
+nfsref_add(enum nfsref_type type, const char *junct_path, char **argv, int optind)
+{
+ if (mkdir(junct_path, 0755) == -1)
+ if (errno != EEXIST) {
+ xlog(L_ERROR, "Failed to create junction object: %m");
+ return EXIT_FAILURE;
+ }
+
+ switch (type) {
+ case NFSREF_TYPE_UNSPECIFIED:
+ case NFSREF_TYPE_NFS_BASIC:
+ return nfsref_add_nfs_basic(junct_path, argv, optind);
+ default:
+ xlog(L_ERROR, "Unrecognized junction type");
+ }
+ return EXIT_FAILURE;
+}
diff --git a/utils/nfsref/lookup.c b/utils/nfsref/lookup.c
new file mode 100644
index 0000000..16fca2e
--- /dev/null
+++ b/utils/nfsref/lookup.c
@@ -0,0 +1,211 @@
+/**
+ * @file utils/nfsref/lookup.c
+ * @brief Examine junction metadata from a local file system object
+ */
+
+/*
+ * Copyright 2011, 2018 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 version 2.0 as
+ * published by the Free Software Foundation.
+ *
+ * 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 version 2.0 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2.0 along with nfs-utils. If not, see:
+ *
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <rpcsvc/nfs_prot.h>
+
+#include "junction.h"
+#include "xlog.h"
+#include "nfsref.h"
+
+/**
+ * Display help message for "lookup" subcommand
+ *
+ * @param progname NUL-terminated C string containing name of program
+ * @return program exit status
+ */
+int
+nfsref_lookup_help(const char *progname)
+{
+ fprintf(stderr, " \n");
+
+ fprintf(stderr, "Usage: %s [ -t type ] lookup <junction path>\n\n",
+ progname);
+
+ fprintf(stderr, "Display the contents of the junction at "
+ "<junction path>. For NFS basic\n");
+ fprintf(stderr, "junctions, the local contents of the junction "
+ "are displayed. For FedFS\n");
+ fprintf(stderr, "junctions, FSL records are retrieved from the "
+ "NSDB and displayed.\n");
+
+ return EXIT_SUCCESS;
+}
+
+/**
+ * Convert a boolean value into a displayable string constant
+ *
+ * @param value boolean value
+ * @return NUL-terminated static constant C string
+ */
+static const char *
+nfsref_lookup_display_boolean(_Bool value)
+{
+ return value ? "true" : "false";
+}
+
+/**
+ * Display a single NFS location
+ *
+ * @param fsloc pointer to an NFS location structure
+ */
+static void
+nfsref_lookup_display_nfs_location(struct nfs_fsloc *fsloc)
+{
+ char *rootpath;
+
+ if (nsdb_path_array_to_posix(fsloc->nfl_rootpath, &rootpath) == FEDFS_OK) {
+ printf("%s:%s\n", fsloc->nfl_hostname, rootpath);
+ free(rootpath);
+ } else
+ printf("%s: - Invalid root path -\n", fsloc->nfl_hostname);
+ printf("\n");
+
+ printf("\tNFS port:\t%u\n", fsloc->nfl_hostport);
+ printf("\tValid for:\t%d\n", fsloc->nfl_validfor);
+ printf("\tCurrency:\t%d\n", fsloc->nfl_currency);
+ printf("\tFlags:\t\tvarsub(%s)\n",
+ nfsref_lookup_display_boolean(fsloc->nfl_flags.nfl_varsub));
+
+ printf("\tGenFlags:\twritable(%s), going(%s), split(%s)\n",
+ nfsref_lookup_display_boolean(fsloc->nfl_genflags.nfl_writable),
+ nfsref_lookup_display_boolean(fsloc->nfl_genflags.nfl_going),
+ nfsref_lookup_display_boolean(fsloc->nfl_genflags.nfl_split));
+ printf("\tTransFlags:\trdma(%s)\n",
+ nfsref_lookup_display_boolean(fsloc->nfl_transflags.nfl_rdma));
+
+ printf("\tClass:\t\tsimul(%u), handle(%u), fileid(%u)\n",
+ fsloc->nfl_info.nfl_simul,
+ fsloc->nfl_info.nfl_handle,
+ fsloc->nfl_info.nfl_fileid);
+ printf("\tClass:\t\twritever(%u), change(%u), readdir(%u)\n",
+ fsloc->nfl_info.nfl_writever,
+ fsloc->nfl_info.nfl_change,
+ fsloc->nfl_info.nfl_readdir);
+ printf("\tRead:\t\trank(%u), order(%u)\n",
+ fsloc->nfl_info.nfl_readrank, fsloc->nfl_info.nfl_readorder);
+ printf("\tWrite:\t\trank(%u), order(%u)\n",
+ fsloc->nfl_info.nfl_writerank, fsloc->nfl_info.nfl_writeorder);
+
+ printf("\n");
+}
+
+/**
+ * Display a list of NFS locations
+ *
+ * @param fslocs list of NFS locations to display
+ */
+static void
+nfsref_lookup_display_nfs_locations(struct nfs_fsloc *fslocs)
+{
+ struct nfs_fsloc *fsloc;
+
+ for (fsloc = fslocs; fsloc != NULL; fsloc = fsloc->nfl_next)
+ nfsref_lookup_display_nfs_location(fsloc);
+}
+
+/**
+ * List NFS locations in an nfs-basic junction
+ *
+ * @param junct_path NUL-terminated C string containing pathname of junction
+ * @return program exit status
+ */
+static int
+nfsref_lookup_nfs_basic(const char *junct_path)
+{
+ struct nfs_fsloc *fslocs = NULL;
+ FedFsStatus retval;
+
+ xlog(D_GENERAL, "%s: Looking up basic junction in %s",
+ __func__, junct_path);
+
+ retval = nfs_is_junction(junct_path);
+ switch (retval) {
+ case FEDFS_OK:
+ break;
+ case FEDFS_ERR_NOTJUNCT:
+ xlog(L_ERROR, "%s is not an nfs-basic junction", junct_path);
+ return EXIT_FAILURE;
+ default:
+ xlog(L_ERROR, "Failed to access %s: %s",
+ junct_path, nsdb_display_fedfsstatus(retval));
+ return EXIT_FAILURE;
+ }
+
+ retval = nfs_get_locations(junct_path, &fslocs);
+ if (retval != FEDFS_OK) {
+ xlog(L_ERROR, "Failed to access %s: %s",
+ junct_path, nsdb_display_fedfsstatus(retval));
+ return EXIT_FAILURE;
+ }
+
+ nfsref_lookup_display_nfs_locations(fslocs);
+
+ nfs_free_locations(fslocs);
+ return EXIT_SUCCESS;
+}
+
+/**
+ * Resolve either a FedFS or NFS basic junction
+ *
+ * @param junct_path NUL-terminated C string containing pathname of junction
+ * @return program exit status
+ */
+static int
+nfsref_lookup_unspecified(const char *junct_path)
+{
+ FedFsStatus retval;
+
+ retval = nfs_is_junction(junct_path);
+ if (retval == FEDFS_OK)
+ return nfsref_lookup_nfs_basic(junct_path);
+ xlog(L_ERROR, "%s is not a junction", junct_path);
+ return EXIT_FAILURE;
+}
+
+/**
+ * Enumerate metadata of a junction
+ *
+ * @param type type of junction to add
+ * @param junct_path NUL-terminated C string containing pathname of junction
+ * @return program exit status
+ */
+int
+nfsref_lookup(enum nfsref_type type, const char *junct_path)
+{
+ switch (type) {
+ case NFSREF_TYPE_UNSPECIFIED:
+ return nfsref_lookup_unspecified(junct_path);
+ case NFSREF_TYPE_NFS_BASIC:
+ return nfsref_lookup_nfs_basic(junct_path);
+ default:
+ xlog(L_ERROR, "Unrecognized junction type");
+ }
+ return EXIT_FAILURE;
+}
diff --git a/utils/nfsref/nfsref.c b/utils/nfsref/nfsref.c
new file mode 100644
index 0000000..7f97d01
--- /dev/null
+++ b/utils/nfsref/nfsref.c
@@ -0,0 +1,189 @@
+/**
+ * @file utils/nfsref/nfsref.c
+ * @brief Manage NFS referrals
+ */
+
+/*
+ * Copyright 2011, 2018 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 version 2.0 as
+ * published by the Free Software Foundation.
+ *
+ * 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 version 2.0 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2.0 along with nfs-utils. If not, see:
+ *
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+ */
+
+#include <sys/types.h>
+#include <sys/capability.h>
+#include <sys/prctl.h>
+#include <sys/stat.h>
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <time.h>
+
+#include <locale.h>
+#include <langinfo.h>
+
+#include "junction.h"
+#include "xlog.h"
+#include "nfsref.h"
+
+/**
+ * Short form command line options
+ */
+static const char nfsref_opts[] = "?dt:";
+
+/**
+ * Long form command line options
+ */
+static const struct option nfsref_longopts[] = {
+ { "debug", 0, NULL, 'd', },
+ { "help", 0, NULL, '?', },
+ { "type", 1, NULL, 't', },
+ { NULL, 0, NULL, 0, },
+};
+
+/**
+ * Display program synopsis
+ *
+ * @param progname NUL-terminated C string containing name of program
+ */
+static void
+nfsref_usage(const char *progname)
+{
+ fprintf(stderr, "Usage: %s [ -t type ] SUBCOMMAND [ ARGUMENTS ]\n\n",
+ progname);
+
+ fprintf(stderr, "SUBCOMMAND is one of:\n");
+ fprintf(stderr, "\tadd Add a new junction\n");
+ fprintf(stderr, "\tremove Remove an existing junction\n");
+ fprintf(stderr, "\tlookup Enumerate a junction\n");
+
+ fprintf(stderr, "\nUse \"%s SUBCOMMAND -?\" for details.\n", progname);
+}
+
+/**
+ * Program entry point
+ *
+ * @param argc count of command line arguments
+ * @param argv array of NUL-terminated C strings containing command line arguments
+ * @return program exit status
+ */
+int
+main(int argc, char **argv)
+{
+ char *progname, *subcommand, *junct_path;
+ enum nfsref_type type;
+ int arg, exit_status;
+ _Bool help;
+
+ (void)setlocale(LC_ALL, "");
+ (void)umask(S_IWGRP | S_IWOTH);
+
+ exit_status = EXIT_FAILURE;
+
+ /* Set the basename */
+ if ((progname = strrchr(argv[0], '/')) != NULL)
+ progname++;
+ else
+ progname = argv[0];
+
+ xlog_stderr(1);
+ xlog_syslog(0);
+ xlog_open(progname);
+
+ if (argc < 2) {
+ nfsref_usage(progname);
+ goto out;
+ }
+
+ help = false;
+ type = NFSREF_TYPE_UNSPECIFIED;
+ while ((arg = getopt_long(argc, argv, nfsref_opts,
+ nfsref_longopts, NULL)) != -1) {
+ switch (arg) {
+ case 'd':
+ xlog_config(D_ALL, 1);
+ break;
+ case 't':
+ if (strcmp(optarg, "nfs-basic") == 0)
+ type = NFSREF_TYPE_NFS_BASIC;
+ else if (strcmp(optarg, "nfs-fedfs") == 0)
+ type = NFSREF_TYPE_NFS_FEDFS;
+ else {
+ xlog(L_ERROR,
+ "Unrecognized junction type: %s",
+ optarg);
+ exit(EXIT_FAILURE);
+ }
+ break;
+ case '?':
+ help = true;
+ }
+ }
+
+ if (argc < optind + 1) {
+ nfsref_usage(progname);
+ goto out;
+ }
+
+ if (!help && geteuid() != 0) {
+ xlog(L_ERROR, "Root permission is required");
+ goto out;
+ }
+
+ subcommand = argv[optind];
+ junct_path = argv[optind + 1];
+
+ if (strcasecmp(subcommand, "add") == 0) {
+ if (help) {
+ exit_status = nfsref_add_help(progname);
+ goto out;
+ }
+ if (argc < optind + 3) {
+ xlog(L_ERROR, "Not enough positional parameters");
+ nfsref_usage(progname);
+ goto out;
+ }
+ exit_status = nfsref_add(type, junct_path, argv, optind);
+ if (exit_status == EXIT_SUCCESS)
+ (void)junction_flush_exports_cache();
+ } else if (strcasecmp(subcommand, "remove") == 0) {
+ if (help) {
+ exit_status = nfsref_remove_help(progname);
+ goto out;
+ }
+ exit_status = nfsref_remove(type, junct_path);
+ if (exit_status == EXIT_SUCCESS)
+ (void)junction_flush_exports_cache();
+ } else if (strcasecmp(subcommand, "lookup") == 0) {
+ if (help) {
+ exit_status = nfsref_lookup_help(progname);
+ goto out;
+ }
+ exit_status = nfsref_lookup(type, junct_path);
+ } else {
+ xlog(L_ERROR, "Unrecognized subcommand: %s", subcommand);
+ nfsref_usage(progname);
+ }
+
+out:
+ exit(exit_status);
+}
diff --git a/utils/nfsref/nfsref.h b/utils/nfsref/nfsref.h
new file mode 100644
index 0000000..bf0e70e
--- /dev/null
+++ b/utils/nfsref/nfsref.h
@@ -0,0 +1,47 @@
+/**
+ * @file support/nfsref/nfsref.h
+ * @brief Declarations and definitions for nfsref command line tool
+ */
+
+/*
+ * Copyright 2011, 2018 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 version 2.0 as
+ * published by the Free Software Foundation.
+ *
+ * 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 version 2.0 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2.0 along with nfs-utils. If not, see:
+ *
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+ */
+
+#ifndef UTILS_NFSREF_H
+#define UTILS_NFSREF_H
+
+/**
+ * Junction types supported by the "nfsref" command
+ */
+enum nfsref_type {
+ NFSREF_TYPE_UNSPECIFIED = 1,
+ NFSREF_TYPE_NFS_BASIC,
+ NFSREF_TYPE_NFS_FEDFS
+};
+
+int nfsref_add(enum nfsref_type type, const char *junct_path, char **argv,
+ int optind);
+int nfsref_remove(enum nfsref_type type, const char *junct_path);
+int nfsref_lookup(enum nfsref_type type, const char *junct_path);
+
+int nfsref_add_help(const char *progname);
+int nfsref_remove_help(const char *progname);
+int nfsref_lookup_help(const char *progname);
+
+#endif /* !UTILS_NFSREF_H */
diff --git a/utils/nfsref/nfsref.man b/utils/nfsref/nfsref.man
new file mode 100644
index 0000000..1261549
--- /dev/null
+++ b/utils/nfsref/nfsref.man
@@ -0,0 +1,180 @@
+.\"@(#)nfsref.8"
+.\"
+.\" @file utils/nfsref/nfsref.man
+.\" @brief man page for nfsref command
+.\"
+
+.\"
+.\" Copyright 2011, 2018 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 version 2.0 as
+.\" published by the Free Software Foundation.
+.\"
+.\" 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 version 2.0 for more details.
+.\"
+.\" You should have received a copy of the GNU General Public License
+.\" version 2.0 along with nfs-utils. If not, see:
+.\"
+.\" http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+.\"
+.TH NFSREF 8 "9 Jan 2018"
+.SH NAME
+nfsref \- manage NFS referrals
+.SH SYNOPSIS
+.B nfsref
+.RB [ \-?d ]
+.RB [ \-t
+.IB type ]
+.B add
+.I pathname server export
+.RI [ " server"
+.IR export " ... ]"
+.P
+.B nfsref
+.RB [ \-?d ]
+.RB [ \-t
+.IB type ]
+.B remove
+.I pathname
+.P
+.B nfsref
+.RB [ \-?d ]
+.RB [ \-t
+.IB type ]
+.B lookup
+.I pathname
+.SH INTRODUCTION
+NFS version 4 introduces the concept of
+.I file system referrals
+to NFS.
+A file system referral is like a symbolic link on a file server
+to another file system share, possibly on another file server.
+On an NFS client, a referral behaves like an automounted directory.
+The client, under the server's direction, mounts a new NFS export
+automatically when an application first accesses that directory.
+.P
+Referrals are typically used to construct a single file name space
+across multiple file servers.
+Because file servers control the shape of the name space,
+no client configuration is required,
+and all clients see the same referral information.
+.P
+The Linux NFS server supports NFS version 4 referrals.
+Administrators can specify the
+.B refer=
+export option in
+.I /etc/exports
+to configure a list of exports from which the client can choose.
+See
+.BR exports (5)
+for details.
+.P
+.SH DESCRIPTION
+The
+.BR nfsref (8)
+command is a simple way to get started managing junction metadata.
+Other administrative commands provide richer access to junction information.
+.SS Subcommands
+Valid
+.BR nfsref (8)
+subcommands are:
+.IP "\fBadd\fP"
+Adds junction information to the directory named by
+.IR pathname .
+The named directory must already exist,
+and must not already contain junction information.
+Regular directory contents are obscured to NFS clients by this operation.
+.IP
+A list of one or more file server and export path pairs
+is also specified on the command line.
+When creating an NFS basic junction, this list is
+stored in an extended attribute of the directory.
+.IP
+If junction creation is successful, the
+.BR nfsref (8)
+command flushes the kernel's export cache
+to remove previously cached junction information.
+.IP "\fBremove\fP"
+Removes junction information from the directory named by
+.IR pathname .
+The named directory must exist,
+and must contain junction information.
+Regular directory contents are made visible to NFS clients again by this operation.
+.IP
+If junction deletion is successful, the
+.BR nfsref (8)
+command flushes the kernel's export cache
+to remove previously cached junction information.
+.IP "\fBlookup\fP"
+Displays junction information stored in the directory named by
+.IR pathname .
+The named directory must exist,
+and must contain junction information.
+.IP
+When looking up an NFS basic junction, the junction information
+in the directory is listed on
+.IR stdout .
+.SS Command line options
+.IP "\fB\-d, \-\-debug"
+Enables debugging messages during operation.
+.IP "\fB\-t, \-\-type=\fIjunction-type\fP"
+Specifies the junction type for the operation. Valid values for
+.I junction-type
+are
+.B nfs-basic
+or
+.BR nfs-fedfs .
+.IP
+For the
+.B add
+subcommand, the default value if this option is not specified is
+.BR nfs-basic .
+For the
+.B remove
+and
+.B lookup
+subcommands, the
+.B \-\-type
+option is not required. The
+.BR nfsref (8)
+command operates on whatever junction contents are available.
+.SH EXAMPLES
+Suppose you have two file servers,
+.I top.example.net
+and
+.IR home.example.net .
+You want all your clients to mount
+.I top.example.net:/
+and then see the files under
+.I home.example.net:/
+automatically in
+.IR top:/home .
+.P
+On
+.IR top.example.net ,
+you might issue this command as root:
+.RS
+.sp
+# mkdir /home
+.br
+# nfsref --type=nfs-basic add /home home.example.net /
+.br
+Created junction /home.
+.sp
+.RE
+.SH FILES
+.TP
+.I /etc/exports
+NFS server export table
+.SH "SEE ALSO"
+.BR exports (5)
+.sp
+RFC 5661 for a description of NFS version 4 referrals
+.SH "AUTHOR"
+Chuck Lever <chuck.lever@oracle.com>
diff --git a/utils/nfsref/remove.c b/utils/nfsref/remove.c
new file mode 100644
index 0000000..1a4e371
--- /dev/null
+++ b/utils/nfsref/remove.c
@@ -0,0 +1,145 @@
+/**
+ * @file utils/nfsref/remove.c
+ * @brief Remove junction metadata from a local file system object
+ */
+
+/*
+ * Copyright 2011, 2018 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 version 2.0 as
+ * published by the Free Software Foundation.
+ *
+ * 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 version 2.0 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2.0 along with nfs-utils. If not, see:
+ *
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <unistd.h>
+#include <errno.h>
+
+#include "junction.h"
+#include "xlog.h"
+#include "nfsref.h"
+
+/**
+ * Display help message for "remove" subcommand
+ *
+ * @param progname NUL-terminated C string containing name of program
+ * @return program exit status
+ */
+int
+nfsref_remove_help(const char *progname)
+{
+ fprintf(stderr, " \n");
+
+ fprintf(stderr, "Usage: %s [ -t type ] remove <junction path>\n\n",
+ progname);
+
+ fprintf(stderr, "Remove the junction at <junction path>. For FedFS "
+ "junctions, FSL and FSN\n");
+ fprintf(stderr, "records are removed from the NSDB.\n");
+
+ return EXIT_SUCCESS;
+}
+
+/**
+ * Remove an NFS locations-style junction
+ *
+ * @param junct_path NUL-terminated C string containing pathname of junction
+ * @return program exit status
+ */
+static int
+nfsref_remove_nfs_basic(const char *junct_path)
+{
+ int status = EXIT_FAILURE;
+ FedFsStatus retval;
+
+ xlog(D_GENERAL, "%s: Removing FedFS junction from %s",
+ __func__, junct_path);
+
+ retval = nfs_delete_junction(junct_path);
+ switch (retval) {
+ case FEDFS_OK:
+ printf("Removed nfs-basic junction from %s\n", junct_path);
+ status = EXIT_SUCCESS;
+ break;
+ case FEDFS_ERR_NOTJUNCT:
+ xlog(L_ERROR, "%s is not an nfs-basic junction", junct_path);
+ break;
+ default:
+ xlog(L_ERROR, "Failed to delete %s: %s",
+ junct_path, nsdb_display_fedfsstatus(retval));
+ }
+
+ return status;
+}
+
+/**
+ * Remove any NFS junction information
+ *
+ * @param junct_path NUL-terminated C string containing pathname of junction
+ * @return program exit status
+ */
+static int
+nfsref_remove_unspecified(const char *junct_path)
+{
+ FedFsStatus retval;
+
+ xlog(D_GENERAL, "%s: Removing junction from %s",
+ __func__, junct_path);
+
+ retval = nfs_delete_junction(junct_path);
+ if (retval != FEDFS_OK) {
+ if (retval != FEDFS_ERR_NOTJUNCT)
+ goto out_err;
+ }
+
+ printf("Removed junction from %s\n", junct_path);
+ return EXIT_SUCCESS;
+
+out_err:
+ switch (retval) {
+ case FEDFS_ERR_NOTJUNCT:
+ xlog(L_ERROR, "No junction information found in %s", junct_path);
+ break;
+ default:
+ xlog(L_ERROR, "Failed to delete %s: %s",
+ junct_path, nsdb_display_fedfsstatus(retval));
+ }
+ return EXIT_FAILURE;
+}
+
+/**
+ * Remove an NFS junction
+ *
+ * @param type type of junction to add
+ * @param junct_path NUL-terminated C string containing pathname of junction
+ * @return program exit status
+ */
+int
+nfsref_remove(enum nfsref_type type, const char *junct_path)
+{
+ switch (type) {
+ case NFSREF_TYPE_UNSPECIFIED:
+ return nfsref_remove_unspecified(junct_path);
+ case NFSREF_TYPE_NFS_BASIC:
+ return nfsref_remove_nfs_basic(junct_path);
+ default:
+ xlog(L_ERROR, "Unrecognized junction type");
+ }
+ return EXIT_FAILURE;
+}
diff --git a/utils/nfsstat/Makefile.am b/utils/nfsstat/Makefile.am
new file mode 100644
index 0000000..d1555a7
--- /dev/null
+++ b/utils/nfsstat/Makefile.am
@@ -0,0 +1,12 @@
+## Process this file with automake to produce Makefile.in
+
+man8_MANS = nfsstat.man
+EXTRA_DIST = $(man8_MANS)
+
+sbin_PROGRAMS = nfsstat
+nfsstat_SOURCES = nfsstat.c
+nfsstat_LDADD = ../../support/export/libexport.a \
+ ../../support/nfs/libnfs.la \
+ ../../support/misc/libmisc.a
+
+MAINTAINERCLEANFILES = Makefile.in
diff --git a/utils/nfsstat/Makefile.in b/utils/nfsstat/Makefile.in
new file mode 100644
index 0000000..f964d10
--- /dev/null
+++ b/utils/nfsstat/Makefile.in
@@ -0,0 +1,811 @@
+# 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@
+sbin_PROGRAMS = nfsstat$(EXEEXT)
+subdir = utils/nfsstat
+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 =
+am__installdirs = "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man8dir)"
+PROGRAMS = $(sbin_PROGRAMS)
+am_nfsstat_OBJECTS = nfsstat.$(OBJEXT)
+nfsstat_OBJECTS = $(am_nfsstat_OBJECTS)
+nfsstat_DEPENDENCIES = ../../support/export/libexport.a \
+ ../../support/nfs/libnfs.la ../../support/misc/libmisc.a
+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 =
+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)/nfsstat.Po
+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 = $(nfsstat_SOURCES)
+DIST_SOURCES = $(nfsstat_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+man8dir = $(mandir)/man8
+NROFF = nroff
+MANS = $(man8_MANS)
+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@
+man8_MANS = nfsstat.man
+EXTRA_DIST = $(man8_MANS)
+nfsstat_SOURCES = nfsstat.c
+nfsstat_LDADD = ../../support/export/libexport.a \
+ ../../support/nfs/libnfs.la \
+ ../../support/misc/libmisc.a
+
+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 utils/nfsstat/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu utils/nfsstat/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):
+install-sbinPROGRAMS: $(sbin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(sbindir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-sbinPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(sbindir)" && rm -f $$files
+
+clean-sbinPROGRAMS:
+ @list='$(sbin_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+nfsstat$(EXEEXT): $(nfsstat_OBJECTS) $(nfsstat_DEPENDENCIES) $(EXTRA_nfsstat_DEPENDENCIES)
+ @rm -f nfsstat$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(nfsstat_OBJECTS) $(nfsstat_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nfsstat.Po@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 $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-man8: $(man8_MANS)
+ @$(NORMAL_INSTALL)
+ @list1='$(man8_MANS)'; \
+ list2=''; \
+ test -n "$(man8dir)" \
+ && test -n "`echo $$list1$$list2`" \
+ || exit 0; \
+ echo " $(MKDIR_P) '$(DESTDIR)$(man8dir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(man8dir)" || exit 1; \
+ { for i in $$list1; do echo "$$i"; done; \
+ if test -n "$$list2"; then \
+ for i in $$list2; do echo "$$i"; done \
+ | sed -n '/\.8[a-z]*$$/p'; \
+ fi; \
+ } | while read p; do \
+ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; echo "$$p"; \
+ done | \
+ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \
+ sed 'N;N;s,\n, ,g' | { \
+ list=; while read file base inst; do \
+ if test "$$base" = "$$inst"; then list="$$list $$file"; else \
+ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man8dir)/$$inst'"; \
+ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man8dir)/$$inst" || exit $$?; \
+ fi; \
+ done; \
+ for i in $$list; do echo "$$i"; done | $(am__base_list) | \
+ while read files; do \
+ test -z "$$files" || { \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man8dir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(man8dir)" || exit $$?; }; \
+ done; }
+
+uninstall-man8:
+ @$(NORMAL_UNINSTALL)
+ @list='$(man8_MANS)'; test -n "$(man8dir)" || exit 0; \
+ files=`{ for i in $$list; do echo "$$i"; done; \
+ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
+ dir='$(DESTDIR)$(man8dir)'; $(am__uninstall_files_from_dir)
+
+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 $(PROGRAMS) $(MANS)
+installdirs:
+ for dir in "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man8dir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+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-sbinPROGRAMS \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/nfsstat.Po
+ -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-man
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-sbinPROGRAMS
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man: install-man8
+
+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)/nfsstat.Po
+ -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: uninstall-man uninstall-sbinPROGRAMS
+
+uninstall-man: uninstall-man8
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-sbinPROGRAMS 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-man8 install-pdf \
+ install-pdf-am install-ps install-ps-am install-sbinPROGRAMS \
+ 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 \
+ uninstall-man uninstall-man8 uninstall-sbinPROGRAMS
+
+.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/utils/nfsstat/nfsstat.c b/utils/nfsstat/nfsstat.c
new file mode 100644
index 0000000..ca84532
--- /dev/null
+++ b/utils/nfsstat/nfsstat.c
@@ -0,0 +1,1183 @@
+/*
+ * nfsstat.c Output NFS statistics
+ *
+ * Copyright (C) 1995-2005 Olaf Kirch <okir@suse.de>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define NFSSRVSTAT "/proc/net/rpc/nfsd"
+#define NFSCLTSTAT "/proc/net/rpc/nfs"
+
+#define MOUNTSFILE "/proc/mounts"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <time.h>
+
+#define MAXNRVALS 32
+
+enum {
+ SRVPROC2_SZ = 18,
+ CLTPROC2_SZ = 18,
+ SRVPROC3_SZ = 22,
+ CLTPROC3_SZ = 22,
+ SRVPROC4_SZ = 2,
+ CLTPROC4_SZ = 59,
+ SRVPROC4OPS_SZ = 71,
+};
+
+static unsigned int srvproc2info[SRVPROC2_SZ+2],
+ srvproc2info_old[SRVPROC2_SZ+2]; /* NFSv2 call counts ([0] == 18) */
+static unsigned int cltproc2info[CLTPROC2_SZ+2],
+ cltproc2info_old[CLTPROC2_SZ+2]; /* NFSv2 call counts ([0] == 18) */
+static unsigned int srvproc3info[SRVPROC3_SZ+2],
+ srvproc3info_old[SRVPROC3_SZ+2]; /* NFSv3 call counts ([0] == 22) */
+static unsigned int cltproc3info[CLTPROC3_SZ+2],
+ cltproc3info_old[CLTPROC3_SZ+2]; /* NFSv3 call counts ([0] == 22) */
+static unsigned int srvproc4info[SRVPROC4_SZ+2],
+ srvproc4info_old[SRVPROC4_SZ+2]; /* NFSv4 call counts ([0] == 2) */
+static unsigned int cltproc4info[CLTPROC4_SZ+2],
+ cltproc4info_old[CLTPROC4_SZ+2]; /* NFSv4 call counts ([0] == 49) */
+static unsigned int srvproc4opsinfo[SRVPROC4OPS_SZ+2],
+ srvproc4opsinfo_old[SRVPROC4OPS_SZ+2]; /* NFSv4 call counts ([0] == 59) */
+static unsigned int srvnetinfo[5], srvnetinfo_old[5]; /* 0 # of received packets
+ * 1 UDP packets
+ * 2 TCP packets
+ * 3 TCP connections
+ */
+static unsigned int cltnetinfo[5], cltnetinfo_old[5]; /* 0 # of received packets
+ * 1 UDP packets
+ * 2 TCP packets
+ * 3 TCP connections
+ */
+
+static unsigned int srvrpcinfo[6], srvrpcinfo_old[6]; /* 0 total # of RPC calls
+ * 1 total # of bad calls
+ * 2 bad format
+ * 3 authentication failed
+ * 4 unknown client
+ */
+static unsigned int cltrpcinfo[4], cltrpcinfo_old[4]; /* 0 total # of RPC calls
+ * 1 retransmitted calls
+ * 2 cred refreshs
+ */
+
+static unsigned int srvrcinfo[9], srvrcinfo_old[9]; /* 0 repcache hits
+ * 1 repcache hits
+ * 2 uncached reqs
+ * (for pre-2.4 kernels:)
+ * 3 FH lookups
+ * 4 'anon' FHs
+ * 5 noncached non-directories
+ * 6 noncached directories
+ * 7 stale
+ */
+
+static unsigned int srvfhinfo[7], srvfhinfo_old[7]; /* (for kernels >= 2.4.0)
+ * 0 stale
+ * 1 FH lookups
+ * 2 'anon' FHs
+ * 3 noncached directories
+ * 4 noncached non-directories
+ * leave hole to relocate stale for order
+ * compatability.
+ */
+
+static unsigned int srvioinfo[3], srvioinfo_old[3]; /* 0 bytes read
+ * 1 bytes written
+ */
+
+static unsigned int srvrainfo[13], srvrainfo_old[13]; /* 0 ra cache size
+ * 1..11 depth of ra cache hit
+ * 12 ra cache misses
+ */
+
+static const char * nfsv2name[SRVPROC2_SZ] = {
+ "null", "getattr", "setattr", "root", "lookup", "readlink",
+ "read", "wrcache", "write", "create", "remove", "rename",
+ "link", "symlink", "mkdir", "rmdir", "readdir", "fsstat"
+};
+
+static const char * nfsv3name[SRVPROC3_SZ] = {
+ "null", "getattr", "setattr", "lookup", "access", "readlink",
+ "read", "write", "create", "mkdir", "symlink", "mknod",
+ "remove", "rmdir", "rename", "link", "readdir", "readdirplus",
+ "fsstat", "fsinfo", "pathconf", "commit"
+};
+
+static const char * nfssrvproc4name[SRVPROC4_SZ] = {
+ "null",
+ "compound",
+};
+
+static const char * nfscltproc4name[CLTPROC4_SZ] = {
+ "null", "read", "write", "commit", "open", "open_conf",
+ "open_noat", "open_dgrd", "close", "setattr", "fsinfo", "renew",
+ "setclntid", "confirm", "lock",
+ "lockt", "locku", "access", "getattr", "lookup", "lookup_root",
+ "remove", "rename", "link", "symlink", "create", "pathconf",
+ "statfs", "readlink", "readdir", "server_caps", "delegreturn", "getacl",
+ "setacl", "fs_locations",
+ "rel_lkowner", "secinfo", "fsid_present",
+ /* nfsv4.1 client ops */
+ "exchange_id",
+ "create_session",
+ "destroy_session",
+ "sequence",
+ "get_lease_time",
+ "reclaim_comp",
+ "layoutget",
+ "getdevinfo",
+ "layoutcommit",
+ "layoutreturn",
+ "secinfo_no",
+ "test_stateid",
+ "free_stateid",
+ "getdevicelist",
+ "bind_conn_to_ses",
+ "destroy_clientid",
+ /* nfsv4.2 client ops */
+ "seek",
+ "allocate",
+ "deallocate",
+ "layoutstats",
+ "clone",
+};
+
+static const char * nfssrvproc4opname[SRVPROC4OPS_SZ] = {
+ "op0-unused", "op1-unused", "op2-future", "access", "close", "commit",
+ "create", "delegpurge", "delegreturn", "getattr", "getfh", "link",
+ "lock", "lockt", "locku", "lookup", "lookup_root", "nverify",
+ "open", "openattr", "open_conf", "open_dgrd", "putfh", "putpubfh",
+ "putrootfh", "read", "readdir", "readlink", "remove", "rename",
+ "renew", "restorefh", "savefh", "secinfo", "setattr", "setcltid",
+ "setcltidconf", "verify", "write", "rellockowner",
+ /* nfsv4.1 server ops */
+ "bc_ctl",
+ "bind_conn",
+ "exchange_id",
+ "create_ses",
+ "destroy_ses",
+ "free_stateid",
+ "getdirdeleg",
+ "getdevinfo",
+ "getdevlist",
+ "layoutcommit",
+ "layoutget",
+ "layoutreturn",
+ "secinfononam",
+ "sequence",
+ "set_ssv",
+ "test_stateid",
+ "want_deleg",
+ "destroy_clid",
+ "reclaim_comp",
+ /* nfsv4.2 server ops */
+ "allocate",
+ "copy",
+ "copy_notify",
+ "deallocate",
+ "ioadvise",
+ "layouterror",
+ "layoutstats",
+ "offloadcancel",
+ "offloadstatus",
+ "readplus",
+ "seek",
+ "write_same",
+};
+
+#define LABEL_srvnet "Server packet stats:\n"
+#define LABEL_srvrpc "Server rpc stats:\n"
+#define LABEL_srvrc "Server reply cache:\n"
+#define LABEL_srvfh "Server file handle cache:\n"
+#define LABEL_srvio "Server io stats:\n"
+#define LABEL_srvra "Server read ahead cache:\n"
+#define LABEL_srvproc2 "Server nfs v2:\n"
+#define LABEL_srvproc3 "Server nfs v3:\n"
+#define LABEL_srvproc4 "Server nfs v4:\n"
+#define LABEL_srvproc4ops "Server nfs v4 operations:\n"
+#define LABEL_cltnet "Client packet stats:\n"
+#define LABEL_cltrpc "Client rpc stats:\n"
+#define LABEL_cltproc2 "Client nfs v2:\n"
+#define LABEL_cltproc3 "Client nfs v3:\n"
+#define LABEL_cltproc4 "Client nfs v4:\n"
+
+typedef struct statinfo {
+ char *tag;
+ char *label;
+ int nrvals;
+ unsigned int * valptr;
+} statinfo;
+
+/*
+ * We now build the arrays of statinfos using macros, which will make it easier
+ * to add new variables for --sleep. e.g., SRV(net) expands into the struct
+ * statinfo: { "net", "Server packet stats:\n", 5, srvnetinfo }
+ */
+#define ARRAYSIZE(x) sizeof(x)/sizeof(*x)
+#define STATINFO(k, t, s...) { #t, LABEL_##k##t, ARRAYSIZE(k##t##info##s), k##t##info##s }
+#define SRV(t, s...) STATINFO(srv, t, s)
+#define CLT(t, s...) STATINFO(clt, t, s)
+#define DECLARE_SRV(n, s...) static statinfo n##s[] = { \
+ SRV(net,s), \
+ SRV(rpc,s), \
+ SRV(rc,s), \
+ SRV(fh,s), \
+ SRV(io,s), \
+ SRV(ra,s), \
+ SRV(proc2,s), \
+ SRV(proc3,s),\
+ SRV(proc4,s), \
+ SRV(proc4ops,s),\
+ { NULL, NULL, 0, NULL }\
+ }
+#define DECLARE_CLT(n, s...) static statinfo n##s[] = { \
+ CLT(net,s), \
+ CLT(rpc,s), \
+ CLT(proc2,s),\
+ CLT(proc3,s), \
+ CLT(proc4,s),\
+ { NULL, NULL, 0, NULL }\
+ }
+DECLARE_SRV(srvinfo);
+DECLARE_SRV(srvinfo, _old);
+DECLARE_CLT(cltinfo);
+DECLARE_CLT(cltinfo, _old);
+
+static void print_all_stats(int, int, int);
+static void print_server_stats(int);
+static void print_client_stats(int);
+static void print_stats_list(int, int, int);
+static void print_numbers(const char *, unsigned int *,
+ unsigned int);
+static void print_callstats(const char *, const char **,
+ unsigned int *, unsigned int);
+static void print_callstats_list(const char *, const char **,
+ unsigned int *, unsigned int);
+static int parse_raw_statfile(const char *, struct statinfo *);
+static int parse_pretty_statfile(const char *, struct statinfo *);
+
+static statinfo *get_stat_info(const char *, struct statinfo *);
+
+static int mounts(const char *);
+
+static void get_stats(const char *, struct statinfo *, int *, int,
+ int);
+static int has_stats(const unsigned int *, int);
+static int has_rpcstats(const unsigned int *, int);
+static void diff_stats(struct statinfo *, struct statinfo *, int);
+static void unpause(int);
+static void update_old_counters(struct statinfo *, struct statinfo *);
+
+static time_t starttime;
+
+#define PRNT_CALLS 0x0001
+#define PRNT_RPC 0x0002
+#define PRNT_NET 0x0004
+#define PRNT_FH 0x0008
+#define PRNT_RC 0x0010
+#define PRNT_IO 0x0020
+#define PRNT_RA 0x0040
+#define PRNT_AUTO 0x1000
+#define PRNT_V2 0x2000
+#define PRNT_V3 0x4000
+#define PRNT_V4 0x8000
+#define PRNT_ALL 0x0fff
+
+int versions[] = {
+ PRNT_V2,
+ PRNT_V3,
+ PRNT_V4
+};
+
+static void usage(char *name)
+{
+ printf("Usage: %s [OPTION]...\n\
+\n\
+ -m, --mounts Show statistics on mounted NFS filesystems\n\
+ -c, --client Show NFS client statistics\n\
+ -s, --server Show NFS server statistics\n\
+ -2 Show NFS version 2 statistics\n\
+ -3 Show NFS version 3 statistics\n\
+ -4 Show NFS version 4 statistics\n\
+ -o [facility] Show statistics on particular facilities.\n\
+ nfs NFS protocol information\n\
+ rpc General RPC information\n\
+ net Network layer statistics\n\
+ fh Usage information on the server's file handle cache\n\
+ io Usage information on the server's io statistics\n\
+ ra Usage information on the server's read ahead cache\n\
+ rc Usage information on the server's request reply cache\n\
+ all Select all of the above\n\
+ -v, --verbose, --all Same as '-o all'\n\
+ -r, --rpc Show RPC statistics\n\
+ -n, --nfs Show NFS statistics\n\
+ -Z[#], --sleep[=#] Collects stats until interrupted.\n\
+ Cumulative stats are then printed\n\
+ If # is provided, stats will be output every\n\
+ # seconds.\n\
+ -S, --since file Shows difference between current stats and those in 'file'\n\
+ -l, --list Prints stats in list format\n\
+ --version Show program version\n\
+ --help What you just did\n\
+\n", name);
+ exit(0);
+}
+
+static struct option longopts[] =
+{
+ { "acl", 0, 0, 'a' },
+ { "all", 0, 0, 'v' },
+ { "auto", 0, 0, '\3' },
+ { "client", 0, 0, 'c' },
+ { "mounts", 0, 0, 'm' },
+ { "nfs", 0, 0, 'n' },
+ { "rpc", 0, 0, 'r' },
+ { "server", 0, 0, 's' },
+ { "verbose", 0, 0, 'v' },
+ { "zero", 0, 0, 'z' },
+ { "help", 0, 0, '\1' },
+ { "version", 0, 0, '\2' },
+ { "sleep", 2, 0, 'Z' },
+ { "since", 1, 0, 'S' },
+ { "list", 0, 0, 'l' },
+ { NULL, 0, 0, 0 }
+};
+int opt_sleep;
+
+int
+main(int argc, char **argv)
+{
+ int opt_all = 0,
+ opt_srv = 0,
+ opt_clt = 0,
+ opt_prt = 0,
+ sleep_time = 0,
+ opt_list =0,
+ opt_since = 0;
+ int c;
+ char *progname,
+ *serverfile = NFSSRVSTAT,
+ *clientfile = NFSCLTSTAT;
+
+ struct statinfo *serverinfo = srvinfo,
+ *serverinfo_tmp = srvinfo_old,
+ *clientinfo = cltinfo,
+ *clientinfo_tmp = cltinfo_old;
+
+ struct sigaction act = {
+ .sa_handler = unpause,
+ .sa_flags = SA_RESETHAND,
+ };
+
+ if ((progname = strrchr(argv[0], '/')))
+ progname++;
+ else
+ progname = argv[0];
+
+ while ((c = getopt_long(argc, argv, "234acmno:Z::S:vrslz\1\2", longopts, NULL)) != EOF) {
+ switch (c) {
+ case 'a':
+ fprintf(stderr, "nfsstat: nfs acls are not yet supported.\n");
+ return 1;
+ case 'c':
+ opt_clt = 1;
+ break;
+ case 'n':
+ opt_prt |= PRNT_CALLS;
+ break;
+ case 'o':
+ if (!strcmp(optarg, "nfs"))
+ opt_prt |= PRNT_CALLS;
+ else if (!strcmp(optarg, "rpc"))
+ opt_prt |= PRNT_RPC;
+ else if (!strcmp(optarg, "net"))
+ opt_prt |= PRNT_NET;
+ else if (!strcmp(optarg, "rc"))
+ opt_prt |= PRNT_RC;
+ else if (!strcmp(optarg, "fh"))
+ opt_prt |= PRNT_FH;
+ else if (!strcmp(optarg, "io"))
+ opt_prt |= PRNT_IO;
+ else if (!strcmp(optarg, "ra"))
+ opt_prt |= PRNT_RA;
+ else if (!strcmp(optarg, "all"))
+ opt_prt |= PRNT_CALLS | PRNT_RPC | PRNT_NET | PRNT_RC | PRNT_FH | PRNT_IO | PRNT_RA;
+ else {
+ fprintf(stderr, "nfsstat: unknown category: "
+ "%s\n", optarg);
+ return 2;
+ }
+ break;
+ case 'Z':
+ opt_sleep = 1;
+ if (optarg) {
+ sleep_time = atoi(optarg);
+ }
+ break;
+ case 'S':
+ opt_since = 1;
+ serverfile = optarg;
+ clientfile = optarg;
+ break;
+ case '2':
+ case '3':
+ case '4':
+ opt_prt |= versions[c - '2'];
+ break;
+ case 'v':
+ opt_all = 1;
+ break;
+ case '\3':
+ opt_prt |= PRNT_AUTO;
+ break;
+ case 'r':
+ opt_prt |= PRNT_RPC;
+ break;
+ case 's':
+ opt_srv = 1;
+ break;
+ case 'l':
+ opt_list = 1;
+ break;
+ case 'z':
+ fprintf(stderr, "nfsstat: zeroing of nfs statistics "
+ "not yet supported\n");
+ return 2;
+ case 'm':
+ return ! mounts(MOUNTSFILE);
+ case '\1':
+ usage(progname);
+ return 0;
+ case '\2':
+ fprintf(stdout, "nfsstat: " VERSION "\n");
+ return 0;
+ default:
+ printf("Try `%s --help' for more information.\n", progname);
+ return 1;
+ }
+ }
+
+ if (opt_all) {
+ opt_srv = opt_clt = 1;
+ opt_prt |= PRNT_ALL;
+ }
+ if (!(opt_srv + opt_clt))
+ opt_srv = opt_clt = 1;
+ if (!(opt_prt & 0xfff)) {
+ opt_prt |= PRNT_CALLS + PRNT_RPC;
+ }
+ if (!(opt_prt & 0xe000)) {
+ opt_prt |= PRNT_AUTO;
+ }
+ if ((opt_prt & (PRNT_FH|PRNT_RC|PRNT_IO|PRNT_RA)) && !opt_srv) {
+ fprintf(stderr,
+ "You requested fh/io/ra/rc "
+ "statistics while using the -c option.\n"
+ "This information is available only for the NFS "
+ "server.\n");
+ }
+
+ if (opt_since || opt_sleep) {
+ serverinfo = srvinfo_old;
+ serverinfo_tmp = srvinfo;
+ clientinfo = cltinfo_old;
+ clientinfo_tmp = cltinfo;
+ }
+
+ if (opt_srv)
+ get_stats(serverfile, serverinfo, &opt_srv, opt_clt, 1);
+ if (opt_clt)
+ get_stats(clientfile, clientinfo, &opt_clt, opt_srv, 0);
+
+ if (opt_sleep && !sleep_time) {
+ starttime = time(NULL);
+ printf("Collecting statistics; press CTRL-C to view results from interval (i.e., from pause to CTRL-C).\n");
+ if (sigaction(SIGINT, &act, NULL) != 0) {
+ fprintf(stderr, "Error: couldn't register for signal and pause.\n");
+ return 1;
+ }
+ pause();
+ }
+
+ if (opt_since || (opt_sleep && !sleep_time)) {
+ if (opt_srv) {
+ get_stats(NFSSRVSTAT, serverinfo_tmp, &opt_srv, opt_clt, 1);
+ diff_stats(serverinfo_tmp, serverinfo, 1);
+ }
+ if (opt_clt) {
+ get_stats(NFSCLTSTAT, clientinfo_tmp, &opt_clt, opt_srv, 0);
+ diff_stats(clientinfo_tmp, clientinfo, 0);
+ }
+ }
+ if(sleep_time) {
+ while(1) {
+ if (opt_srv) {
+ get_stats(NFSSRVSTAT, serverinfo_tmp , &opt_srv, opt_clt, 1);
+ diff_stats(serverinfo_tmp, serverinfo, 1);
+ }
+ if (opt_clt) {
+ get_stats(NFSCLTSTAT, clientinfo_tmp, &opt_clt, opt_srv, 0);
+ diff_stats(clientinfo_tmp, clientinfo, 0);
+ }
+ if (opt_list) {
+ print_stats_list(opt_srv, opt_clt, opt_prt);
+ } else {
+ print_all_stats(opt_srv, opt_clt, opt_prt);
+ }
+ fflush(stdout);
+
+ if (opt_srv)
+ update_old_counters(serverinfo_tmp, serverinfo);
+ if (opt_clt)
+ update_old_counters(clientinfo_tmp, clientinfo);
+
+ sleep(sleep_time);
+ }
+ } else {
+ if (opt_list) {
+ print_stats_list(opt_srv, opt_clt, opt_prt);
+ } else {
+ print_all_stats(opt_srv, opt_clt, opt_prt);
+ }
+ }
+
+ return 0;
+}
+
+static void
+print_all_stats (int opt_srv, int opt_clt, int opt_prt)
+{
+ if (opt_srv)
+ print_server_stats(opt_prt);
+
+ if (opt_clt)
+ print_client_stats(opt_prt);
+}
+
+static void
+print_server_stats(int opt_prt)
+{
+ if (opt_prt & PRNT_NET) {
+ if (opt_sleep && !has_rpcstats(srvnetinfo, 4)) {
+ } else {
+ print_numbers( LABEL_srvnet
+ "packets udp tcp tcpconn\n",
+ srvnetinfo, 4);
+ printf("\n");
+ }
+ }
+ if (opt_prt & PRNT_RPC) {
+ if (opt_sleep && !has_rpcstats(srvrpcinfo, 5)) {
+ ;
+ } else {
+ print_numbers(LABEL_srvrpc
+ "calls badcalls badfmt badauth badclnt\n",
+ srvrpcinfo, 5);
+ printf("\n");
+ }
+ }
+ if (opt_prt & PRNT_RC) {
+ if (opt_sleep && !has_rpcstats(srvrcinfo, 3)) {
+ ;
+ } else {
+ print_numbers(LABEL_srvrc
+ "hits misses nocache\n",
+ srvrcinfo, 3);
+ printf("\n");
+ }
+ }
+ if (opt_prt & PRNT_IO) {
+ if (opt_sleep && !has_rpcstats(srvioinfo, 3)) {
+ ;
+ } else {
+ print_numbers(LABEL_srvio
+ "read write\n",
+ srvioinfo, 2);
+ printf("\n");
+ }
+ }
+ if (opt_prt & PRNT_RA) {
+ if (opt_sleep && !has_rpcstats(srvrainfo, 3)) {
+ ;
+ } else {
+ print_numbers(LABEL_srvra
+ "size 0-10% 10-20% 20-30% 30-40% 40-50% 50-60% 60-70% 70-80% 80-90% 90-100% notfound\n",
+ srvrainfo, 12);
+ printf("\n");
+ }
+ }
+
+ /*
+ * 2.2 puts all fh-related info after the 'rc' header
+ * 2.4 puts all fh-related info after the 'fh' header, but relocates
+ * 'stale' to the start and swaps dir and nondir :-(
+ * We preseve the 2.2 order
+ */
+ if (opt_prt & PRNT_FH) {
+ if (get_stat_info("fh", srvinfo)) { /* >= 2.4 */
+ int t = srvfhinfo[3];
+ srvfhinfo[3]=srvfhinfo[4];
+ srvfhinfo[4]=t;
+
+ srvfhinfo[5]=srvfhinfo[0]; /* relocate 'stale' */
+
+ print_numbers(
+ LABEL_srvfh
+ "lookup anon ncachedir ncachenondir stale\n",
+ srvfhinfo + 1, 5);
+ } else /* < 2.4 */
+ print_numbers(
+ LABEL_srvfh
+ "lookup anon ncachedir ncachedir stale\n",
+ srvrcinfo + 3, 5);
+ printf("\n");
+ }
+ if (opt_prt & PRNT_CALLS) {
+ int has_v2_stats = has_stats(srvproc2info, SRVPROC2_SZ+2);
+ int has_v3_stats = has_stats(srvproc3info, SRVPROC3_SZ+2);
+ int has_v4_stats = has_stats(srvproc4info, SRVPROC4_SZ+2);
+
+ if ((opt_prt & PRNT_V2) ||
+ ((opt_prt & PRNT_AUTO) && has_v2_stats)) {
+ if (!opt_sleep || has_v2_stats) {
+ print_callstats(LABEL_srvproc2,
+ nfsv2name, srvproc2info + 1,
+ sizeof(nfsv2name)/sizeof(char *));
+ }
+ }
+ if ((opt_prt & PRNT_V3) ||
+ ((opt_prt & PRNT_AUTO) && has_v3_stats)) {
+ if (!opt_sleep || has_v3_stats) {
+ print_callstats(LABEL_srvproc3,
+ nfsv3name, srvproc3info + 1,
+ sizeof(nfsv3name)/sizeof(char *));
+ }
+ }
+ if ((opt_prt & PRNT_V4) ||
+ ((opt_prt & PRNT_AUTO) && has_v4_stats)) {
+ if (!opt_sleep || has_v4_stats) {
+ print_callstats( LABEL_srvproc4,
+ nfssrvproc4name, srvproc4info + 1,
+ sizeof(nfssrvproc4name)/sizeof(char *));
+ print_callstats(LABEL_srvproc4ops,
+ nfssrvproc4opname, srvproc4opsinfo + 1,
+ sizeof(nfssrvproc4opname)/sizeof(char *));
+ }
+ }
+ }
+}
+static void
+print_client_stats(int opt_prt)
+{
+ if (opt_prt & PRNT_NET) {
+ if (opt_sleep && !has_rpcstats(cltnetinfo, 4)) {
+ ;
+ } else {
+ print_numbers(LABEL_cltnet
+ "packets udp tcp tcpconn\n",
+ cltnetinfo, 4);
+ printf("\n");
+ }
+ }
+ if (opt_prt & PRNT_RPC) {
+ if (opt_sleep && !has_rpcstats(cltrpcinfo, 3)) {
+ ;
+ } else {
+ print_numbers(LABEL_cltrpc
+ "calls retrans authrefrsh\n",
+ cltrpcinfo, 3);
+ printf("\n");
+ }
+ }
+ if (opt_prt & PRNT_CALLS) {
+ int has_v2_stats = has_stats(cltproc2info, CLTPROC2_SZ+2);
+ int has_v3_stats = has_stats(cltproc3info, CLTPROC3_SZ+2);
+ int has_v4_stats = has_stats(cltproc4info, CLTPROC4_SZ+2);
+ if ((opt_prt & PRNT_V2) ||
+ ((opt_prt & PRNT_AUTO) && has_v2_stats)) {
+ if (!opt_sleep || has_v2_stats) {
+ print_callstats(LABEL_cltproc2,
+ nfsv2name, cltproc2info + 1,
+ sizeof(nfsv2name)/sizeof(char *));
+ }
+ }
+ if ((opt_prt & PRNT_V3) ||
+ ((opt_prt & PRNT_AUTO) && has_v3_stats)) {
+ if (!opt_sleep || has_v3_stats) {
+ print_callstats(LABEL_cltproc3,
+ nfsv3name, cltproc3info + 1,
+ sizeof(nfsv3name)/sizeof(char *));
+ }
+ }
+ if ((opt_prt & PRNT_V4) ||
+ ((opt_prt & PRNT_AUTO) && has_v4_stats)) {
+ if (!opt_sleep || has_v4_stats) {
+ print_callstats(LABEL_cltproc4,
+ nfscltproc4name, cltproc4info + 1,
+ sizeof(nfscltproc4name)/sizeof(char *));
+ }
+ }
+ }
+}
+
+static void
+print_clnt_list(int opt_prt)
+{
+ if (opt_prt & PRNT_CALLS) {
+ int has_v2_stats = has_stats(cltproc2info, CLTPROC2_SZ+2);
+ int has_v3_stats = has_stats(cltproc3info, CLTPROC3_SZ+2);
+ int has_v4_stats = has_stats(cltproc4info, CLTPROC4_SZ+2);
+ if ((opt_prt & PRNT_V2) ||
+ ((opt_prt & PRNT_AUTO) && has_v2_stats)) {
+ if (!opt_sleep || has_v2_stats) {
+ print_callstats_list("nfs v2 client",
+ nfsv2name, cltproc2info + 1,
+ sizeof(nfsv2name)/sizeof(char *));
+ }
+ }
+ if ((opt_prt & PRNT_V3) ||
+ ((opt_prt & PRNT_AUTO) && has_v3_stats)) {
+ if (!opt_sleep || has_v3_stats) {
+ print_callstats_list("nfs v3 client",
+ nfsv3name, cltproc3info + 1,
+ sizeof(nfsv3name)/sizeof(char *));
+ }
+ }
+ if ((opt_prt & PRNT_V4) ||
+ ((opt_prt & PRNT_AUTO) && has_v4_stats)) {
+ if (!opt_sleep || has_v4_stats) {
+ print_callstats_list("nfs v4 client",
+ nfscltproc4name, cltproc4info + 1,
+ sizeof(nfscltproc4name)/sizeof(char *));
+ }
+ }
+ }
+}
+static void
+print_serv_list(int opt_prt)
+{
+ if (opt_prt & PRNT_CALLS) {
+ int has_v2_stats = has_stats(srvproc2info, SRVPROC2_SZ+2);
+ int has_v3_stats = has_stats(srvproc3info, SRVPROC3_SZ+2);
+ int has_v4_stats = has_stats(srvproc4info, SRVPROC4_SZ+2);
+ if ((opt_prt & PRNT_V2) ||
+ ((opt_prt & PRNT_AUTO) && has_v2_stats)) {
+ if (!opt_sleep || has_v2_stats) {
+ print_callstats_list("nfs v2 server",
+ nfsv2name, srvproc2info + 1,
+ sizeof(nfsv2name)/sizeof(char *));
+ }
+ }
+ if ((opt_prt & PRNT_V3) ||
+ ((opt_prt & PRNT_AUTO) && has_v3_stats)) {
+ if (!opt_sleep || has_v3_stats) {
+ print_callstats_list("nfs v3 server",
+ nfsv3name, srvproc3info + 1,
+ sizeof(nfsv3name)/sizeof(char *));
+ }
+ }
+ if ((opt_prt & PRNT_V4) ||
+ ((opt_prt & PRNT_AUTO) && has_v4_stats)) {
+ if (!opt_sleep || has_v4_stats) {
+ print_callstats_list("nfs v4 server",
+ nfssrvproc4name, srvproc4info + 1,
+ sizeof(nfssrvproc4name)/sizeof(char *));
+ print_callstats_list("nfs v4 servop",
+ nfssrvproc4opname, srvproc4opsinfo + 1,
+ sizeof(nfssrvproc4opname)/sizeof(char *));
+ }
+ }
+ }
+}
+static void
+print_stats_list(int opt_srv, int opt_clt, int opt_prt)
+{
+ if (opt_srv)
+ print_serv_list(opt_prt);
+
+ if (opt_clt)
+ print_clnt_list(opt_prt);
+}
+
+static statinfo *
+get_stat_info(const char *sp, struct statinfo *statp)
+{
+ struct statinfo *ip;
+
+ for (ip = statp; ip->tag; ip++) {
+ if (!strcmp(sp, ip->tag))
+ return ip;
+ }
+
+ return NULL;
+}
+
+static void
+print_numbers(const char *hdr, unsigned int *info, unsigned int nr)
+{
+ unsigned int i;
+
+ fputs(hdr, stdout);
+ for (i = 0; i < nr; i++)
+ printf("%s%-8u", i? " " : "", info[i]);
+ printf("\n");
+}
+
+static void
+print_callstats(const char *hdr, const char **names,
+ unsigned int *info, unsigned int nr)
+{
+ unsigned long long total;
+ unsigned long long pct;
+ unsigned int i, j;
+
+ fputs(hdr, stdout);
+ for (i = 0, total = 0; i < nr; i++)
+ total += info[i];
+ if (!total)
+ total = 1;
+ for (i = 0; i < nr; i += 5) {
+ for (j = 0; j < 5 && i + j < nr; j++)
+ printf("%-17s", names[i+j]);
+ printf("\n");
+ for (j = 0; j < 5 && i + j < nr; j++) {
+ pct = ((unsigned long long) info[i+j]*100)/total;
+ printf("%-8u%3llu%% ", info[i+j], pct);
+ }
+ printf("\n");
+ }
+ printf("\n");
+}
+
+static void
+print_callstats_list(const char *hdr, const char **names,
+ unsigned int *callinfo, unsigned int nr)
+{
+ unsigned long long calltotal;
+ unsigned int i;
+
+ for (i = 0, calltotal = 0; i < nr; i++) {
+ calltotal += callinfo[i];
+ }
+ if (!calltotal)
+ return;
+ printf("%13s %13s %8llu \n", hdr, "total:", calltotal);
+ printf("------------- ------------- --------\n");
+ for (i = 0; i < nr; i++) {
+ if (callinfo[i])
+ printf("%13s %12s: %8u \n", hdr, names[i], callinfo[i]);
+ }
+ printf("\n");
+
+}
+
+
+/* returns 0 on success, 1 otherwise */
+static int
+parse_raw_statfile(const char *name, struct statinfo *statp)
+{
+ char buffer[4096], *next;
+ FILE *fp;
+
+ /* Being unable to read e.g. the nfsd stats file shouldn't
+ * be a fatal error -- it usually means the module isn't loaded.
+ */
+ if ((fp = fopen(name, "r")) == NULL) {
+ // fprintf(stderr, "Warning: %s: %m\n", name);
+ return 1;
+ }
+
+ while (fgets(buffer, sizeof(buffer), fp) != NULL) {
+ struct statinfo *ip;
+ char *sp, *line = buffer;
+ unsigned int i, cnt;
+ unsigned int total = 0;
+
+ if ((next = strchr(line, '\n')) != NULL)
+ *next++ = '\0';
+ if (!(sp = strtok(line, " \t")))
+ continue;
+
+ ip = get_stat_info(sp, statp);
+ if (!ip)
+ continue;
+
+ cnt = ip->nrvals;
+
+ for (i = 0; i < cnt; i++) {
+ if (!(sp = strtok(NULL, " \t")))
+ break;
+ ip->valptr[i] = (unsigned int) strtoul(sp, NULL, 0);
+ total += ip->valptr[i];
+ }
+ ip->valptr[cnt - 1] = total;
+ }
+
+ fclose(fp);
+ return 0;
+}
+
+/* returns 0 on success, 1 otherwise */
+static int
+parse_pretty_statfile(const char *filename, struct statinfo *info)
+{
+ int numvals, curindex, numconsumed, n, err = 1;
+ unsigned int sum;
+ char buf[4096], *bufp, *fmt, is_proc;
+ FILE *fp = NULL;
+ struct statinfo *ip;
+
+ if ((fp = fopen(filename, "r")) == NULL)
+ //err(2, "Unable to open statfile '%s'.\n", filename);
+ goto out;
+
+ while (fgets(buf, sizeof(buf), fp) != NULL) {
+ for (ip = info; ip->tag; ip++) {
+ if (strcmp(buf, ip->label))
+ continue;
+
+ sum = 0;
+ numvals = ip->nrvals - 1;
+ is_proc = strncmp("proc", ip->tag, 4) ? 0 : 1;
+ if (is_proc) {
+ fmt = " %u %*u%% %n";
+ curindex = 1;
+ ip->valptr[0] = 0;
+ } else {
+ fmt = " %u %n";
+ curindex = 0;
+ }
+more_stats:
+ /* get (and skip) header */
+ if (fgets(buf, sizeof(buf), fp) == NULL) {
+ fprintf(stderr, "Failed to locate header after "
+ "label for '%s' in %s.\n",
+ ip->tag, filename);
+ goto out;
+ }
+ /* no header -- done with this "tag" */
+ if (*buf == '\n') {
+ ip->valptr[numvals] = sum;
+ break;
+ }
+ /* get stats */
+ if (fgets(buf, sizeof(buf), fp) == NULL) {
+ fprintf(stderr, "Failed to locate stats after "
+ "header for '%s' in %s.\n",
+ ip->tag, filename);
+ goto out;
+ }
+ bufp = buf;
+ for (; curindex < numvals; curindex++) {
+#pragma GCC diagnostic ignored "-Wformat-nonliteral"
+ n = sscanf(bufp, fmt, &ip->valptr[curindex],
+ &numconsumed);
+#pragma GCC diagnostic warning "-Wformat-nonliteral"
+ if (n != 1)
+ break;
+ if (is_proc) {
+ ip->valptr[0]++;
+ sum++;
+ }
+ sum += ip->valptr[curindex];
+ bufp += numconsumed;
+ }
+ goto more_stats;
+ }
+ }
+ err = 0;
+out:
+ if (fp)
+ fclose(fp);
+ return err;
+}
+
+static int
+mounts(const char *name)
+{
+ char buffer[4096], *next;
+ FILE *fp;
+
+ /* Being unable to read e.g. the nfsd stats file shouldn't
+ * be a fatal error -- it usually means the module isn't loaded.
+ */
+ if ((fp = fopen(name, "r")) == NULL) {
+ fprintf(stderr, "Warning: %s: %s\n", name, strerror(errno));
+ return 0;
+ }
+
+ while (fgets(buffer, sizeof(buffer), fp) != NULL) {
+ char *line = buffer;
+ char *device, *mount, *type, *flags;
+
+ if ((next = strchr(line, '\n')) != NULL)
+ *next = '\0';
+
+ if (!(device = strtok(line, " \t")))
+ continue;
+
+ if (!(mount = strtok(NULL, " \t")))
+ continue;
+
+ if (!(type = strtok(NULL, " \t")))
+ continue;
+
+ if (strcmp(type, "nfs") && strcmp(type,"nfs4")) {
+ continue;
+ }
+
+ if (!(flags = strtok(NULL, " \t")))
+ continue;
+
+ printf("%s from %s\n", mount, device);
+ printf(" Flags:\t%s\n", flags);
+ printf("\n");
+
+ continue;
+ }
+
+ fclose(fp);
+ return 1;
+}
+
+static void
+get_stats(const char *file, struct statinfo *info, int *opt, int other_opt,
+ int is_srv)
+{
+ FILE *fp;
+ char buf[10];
+ int err = 1;
+ char *label = is_srv ? "Server" : "Client";
+
+ /* try to guess what type of stat file we're dealing with */
+ if ((fp = fopen(file, "r")) == NULL)
+ goto out;
+ if (fgets(buf, 10, fp) == NULL)
+ goto out;
+ if (!strncmp(buf, "net ", 4)) {
+ /* looks like raw client stats */
+ if (is_srv) {
+ fprintf(stderr, "Warning: no server info present in "
+ "raw client stats file.\n");
+ *opt = 0;
+ } else
+ err = parse_raw_statfile(file, info);
+ } else if (!strncmp(buf, "rc ", 3)) {
+ /* looks like raw server stats */
+ if (!is_srv) {
+ fprintf(stderr, "Warning: no client info present in "
+ "raw server stats file.\n");
+ *opt = 0;
+ } else
+ err = parse_raw_statfile(file, info);
+ } else
+ /* looks like pretty client and server stats */
+ err = parse_pretty_statfile(file, info);
+out:
+ if (fp)
+ fclose(fp);
+ if (err) {
+ if (!other_opt) {
+ fprintf(stderr, "Error: No %s Stats (%s: %s). \n",
+ label, file, strerror(errno));
+ exit(2);
+ }
+ *opt = 0;
+ }
+}
+
+/*
+ * This is for proc2/3/4-type stats, where, in the /proc files, the first entry's value
+ * denotes the number of subsequent entries. statinfo value arrays contain an additional
+ * field at the end which contains the sum of all previous elements in the array -- so,
+ * there are stats if the sum's greater than the entry-count.
+ */
+static int
+has_stats(const unsigned int *info, int nr)
+{
+ return (info[0] && info[nr-1] > info[0]);
+}
+static int
+has_rpcstats(const unsigned int *info, int size)
+{
+ int i, cnt;
+
+ for (i=0, cnt=0; i < size; i++)
+ cnt += info[i];
+ return cnt;
+}
+
+/*
+ * take the difference of each individual stat value in 'new' and 'old'
+ * and store the results back into 'new'
+ */
+static void
+diff_stats(struct statinfo *new, struct statinfo *old, int is_srv)
+{
+ int i, j, nodiff_first_index, should_diff;
+
+ /*
+ * Different stat types have different formats in the /proc
+ * files: for the proc2/3/4-type stats, the first entry has
+ * the total number of subsequent entries; one does not want
+ * to diff that first entry. The other stat types aren't like
+ * this. So, we diff a given entry if it's not of one of the
+ * procX types ("i" < 2 for clt, < 4 for srv), or if it's not
+ * the first entry ("j" > 0).
+ */
+ nodiff_first_index = 2 + (2 * is_srv);
+
+ for (i = 0; old[i].tag; i++) {
+ for (j = 0; j < new[i].nrvals; j++) {
+ should_diff = (i < nodiff_first_index || j > 0);
+ if (should_diff)
+ new[i].valptr[j] -= old[i].valptr[j];
+ }
+
+ /*
+ * Make sure that the "totals" entry (last value in
+ * each stat array) for the procX-type stats has the
+ * "numentries" entry's (first value in procX-type
+ * stat arrays) constant value added-back after the
+ * diff -- i.e., it should always be included in the
+ * total.
+ */
+ if (!strncmp("proc", new[i].tag, 4) && old[i].valptr[0])
+ new[i].valptr[new[i].nrvals - 1] += new[i].valptr[0];
+ }
+}
+
+static void
+unpause(int sig)
+{
+ double time_diff;
+ int minutes, seconds;
+ time_t endtime;
+
+ endtime = time(NULL);
+ time_diff = difftime(endtime, starttime);
+ minutes = time_diff / 60;
+ seconds = (int)time_diff % 60;
+ printf("Signal %d received; displaying (only) statistics gathered over the last %d minutes, %d seconds:\n\n", sig, minutes, seconds);
+}
+
+static void
+update_old_counters(struct statinfo *new, struct statinfo *old)
+{
+ int z, i;
+ for (z = 0; old[z].tag; z++)
+ for (i = 0; i <= old[z].nrvals; i++)
+ old[z].valptr[i] += new[z].valptr[i];
+
+}
diff --git a/utils/nfsstat/nfsstat.man b/utils/nfsstat/nfsstat.man
new file mode 100644
index 0000000..2e52935
--- /dev/null
+++ b/utils/nfsstat/nfsstat.man
@@ -0,0 +1,173 @@
+.\"
+.\" nfsstat(8)
+.\"
+.\" Copyright (C) 1996-2005 Olaf Kirch <okir@suse.de>
+.TH nfsstat 8 "7 Aug 2007"
+.SH NAME
+nfsstat \- list NFS statistics
+.SH SYNOPSIS
+.B nfsstat
+[\fIOPTION\fR]...
+.SH DESCRIPTION
+The
+.B nfsstat
+displays statistics kept about NFS client and server activity.
+.SH OPTIONS
+.TP
+.B \-s, \-\-server
+Print only server-side statistics. The default is to print both server and
+client statistics.
+.TP
+.B \-c, \-\-client
+Print only client-side statistics.
+.TP
+.B \-n, \-\-nfs
+Print only NFS statistics. The default is to print both NFS and RPC
+information.
+.TP
+.B \-2
+Print only NFS v2 statistics. The default is to only print information
+about the versions of \fBNFS\fR that have non-zero counts.
+.TP
+.B \-3
+Print only NFS v3 statistics. The default is to only print information
+about the versions of \fBNFS\fR that have non-zero counts.
+.TP
+.B \-4
+Print only NFS v4 statistics. The default is to only print information
+about the versions of \fBNFS\fR that have non-zero counts.
+.TP
+.B \-m, \-\-mounts
+Print information about each of the mounted \fBNFS\fR file systems.
+
+If this option is used, all other options are ignored.
+.TP
+.B \-r, \-\-rpc
+Print only RPC statistics.
+.TP
+.BI \-o " facility
+Display statistics for the specified facility, which must be one of:
+.RS
+.TP
+.B nfs
+NFS protocol information, split up by RPC call.
+.TP
+.B rpc
+General RPC information.
+.TP
+.B net
+Network layer statistics, such as the number of received packets, number
+of TCP connections, etc.
+.TP
+.B fh
+Usage information on the server's file handle cache, including the
+total number of lookups, and the number of hits and misses.
+.TP
+.B rc
+Usage information on the server's request reply cache, including the
+total number of lookups, and the number of hits and misses.
+.TP
+.B io
+Usage information on the server's io statistics; bytes read and
+written.
+.TP
+.B ra
+Usage information on the server's read ahead cache, including the
+ra cache size, the depth of ra cache hits, and ra cache misses.
+.TP
+.B all
+Display all of the above facilities.
+.RE
+.TP
+.B \-v, \-\-verbose
+This is equivalent to \fB\-o all\fR.
+.TP
+.B \-l, \-\-list
+Print information in list form.
+.TP
+.BI "\-S, \-\-since " file
+Instead of printing current statistics,
+.B nfsstat
+imports statistics from
+.I file
+and displays the difference between those and the current statistics.
+Valid input
+.IR file "s may be in the form of "
+.B /proc/net/rpc/nfs
+(raw client stats),
+.B /proc/net/rpc/nfsd
+(raw server stats), or saved output from
+.B nfsstat
+itself (client and/or server stats). Any statistics missing from a saved
+.B nfsstat
+output
+.I file
+are treated as zeroes.
+.TP
+.B \-Z[interval], \-\-sleep=[interval]
+Instead of printing current statistics and immediately exiting,
+.B nfsstat
+takes a snapshot of the current statistics and pauses until it receives
+.B SIGINT
+(typically from
+.BR Ctrl-C ),
+at which point it takes another snapshot and displays the difference
+between the two.
+If \fIinterval\fR is specified,
+.B nfsstat
+will print the number of \fBNFS\fR calls made since the previous report.
+Stats will be printed repeatedly every \fIinterval\fR seconds.
+.\" --------------------- EXAMPLES -------------------------------
+.SH EXAMPLES
+.TP
+.B nfsstat \-o all \-234
+Show all information about all versions of \fBNFS\fR.
+.TP
+.B nfsstat \-\-verbose \-234
+Same as above.
+.TP
+.B nfsstat \-o all
+Show all information about active versions of \fBNFS\fR.
+.TP
+.B nfsstat \-\-nfs \-\-server \-3
+Show statistics for \fBNFS\fR version 3 server.
+.TP
+.B nfsstat \-m
+Show information about mounted \fBNFS\fR filesystems.
+.\" --------------------- DISPLAY --------------------------------
+.SH DISPLAY
+The \fBFlags\fR output from the \fB\-m\fR option is the same as the
+flags give to the \fBmount\fR command.
+.\" --------------------- FILES ----------------------------------
+.SH FILES
+.TP
+.B /proc/net/rpc/nfsd
+.BR procfs -based
+interface to kernel NFS server statistics.
+.TP
+.B /proc/net/rpc/nfs
+.BR procfs -based
+interface to kernel NFS client statistics.
+.TP
+.B /proc/mounts
+.BR procfs -based
+interface to the mounted filesystems.
+.\" -------------------- SEE ALSO --------------------------------
+.SH SEE ALSO
+.BR rpc.nfsd (8).
+.BR nfs (5).
+.\" ---------------------- BUGS ----------------------------------
+.SH BUGS
+The default output has been changed. To get the old default output you must run \fBnfsstat \-\-auto \-2\fR.
+.P
+The function of the \fB\-v\fR and \fB\-a\fR options have changed. The \fB\-a\fR option
+is now reserved for future use. The \fB\-v\fR does what the \fB\-a\fR option used to do,
+and the new \fB\-[234]\fR options replace the \fB\-v\fR option.
+.P
+The \fBDisplay\fR section should be more complete.
+.P
+Further bugs can be found or reported at
+.BR http://nfs.sf.net/ .
+.\" -------------------- AUTHOR ----------------------------------
+.SH AUTHOR
+Olaf Kirch, <okir@suse.de>
diff --git a/utils/showmount/Makefile.am b/utils/showmount/Makefile.am
new file mode 100644
index 0000000..d0a16b2
--- /dev/null
+++ b/utils/showmount/Makefile.am
@@ -0,0 +1,15 @@
+## Process this file with automake to produce Makefile.in
+
+man8_MANS = showmount.man
+EXTRA_DIST = $(man8_MANS)
+
+sbin_PROGRAMS = showmount
+showmount_SOURCES = showmount.c
+showmount_LDADD = ../../support/export/libexport.a \
+ ../../support/nfs/libnfs.la \
+ ../../support/misc/libmisc.a \
+ $(LIBTIRPC)
+showmount_CPPFLAGS = $(AM_CPPFLAGS) $(CPPFLAGS) \
+ -I$(top_builddir)/support/export
+
+MAINTAINERCLEANFILES = Makefile.in
diff --git a/utils/showmount/Makefile.in b/utils/showmount/Makefile.in
new file mode 100644
index 0000000..7f842f7
--- /dev/null
+++ b/utils/showmount/Makefile.in
@@ -0,0 +1,831 @@
+# 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@
+sbin_PROGRAMS = showmount$(EXEEXT)
+subdir = utils/showmount
+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 =
+am__installdirs = "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man8dir)"
+PROGRAMS = $(sbin_PROGRAMS)
+am_showmount_OBJECTS = showmount-showmount.$(OBJEXT)
+showmount_OBJECTS = $(am_showmount_OBJECTS)
+am__DEPENDENCIES_1 =
+showmount_DEPENDENCIES = ../../support/export/libexport.a \
+ ../../support/nfs/libnfs.la ../../support/misc/libmisc.a \
+ $(am__DEPENDENCIES_1)
+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 =
+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)/showmount-showmount.Po
+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 = $(showmount_SOURCES)
+DIST_SOURCES = $(showmount_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+man8dir = $(mandir)/man8
+NROFF = nroff
+MANS = $(man8_MANS)
+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@
+man8_MANS = showmount.man
+EXTRA_DIST = $(man8_MANS)
+showmount_SOURCES = showmount.c
+showmount_LDADD = ../../support/export/libexport.a \
+ ../../support/nfs/libnfs.la \
+ ../../support/misc/libmisc.a \
+ $(LIBTIRPC)
+
+showmount_CPPFLAGS = $(AM_CPPFLAGS) $(CPPFLAGS) \
+ -I$(top_builddir)/support/export
+
+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 utils/showmount/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu utils/showmount/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):
+install-sbinPROGRAMS: $(sbin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(sbindir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-sbinPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(sbindir)" && rm -f $$files
+
+clean-sbinPROGRAMS:
+ @list='$(sbin_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+showmount$(EXEEXT): $(showmount_OBJECTS) $(showmount_DEPENDENCIES) $(EXTRA_showmount_DEPENDENCIES)
+ @rm -f showmount$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(showmount_OBJECTS) $(showmount_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/showmount-showmount.Po@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 $@ $<
+
+showmount-showmount.o: showmount.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(showmount_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT showmount-showmount.o -MD -MP -MF $(DEPDIR)/showmount-showmount.Tpo -c -o showmount-showmount.o `test -f 'showmount.c' || echo '$(srcdir)/'`showmount.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/showmount-showmount.Tpo $(DEPDIR)/showmount-showmount.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='showmount.c' object='showmount-showmount.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(showmount_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o showmount-showmount.o `test -f 'showmount.c' || echo '$(srcdir)/'`showmount.c
+
+showmount-showmount.obj: showmount.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(showmount_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT showmount-showmount.obj -MD -MP -MF $(DEPDIR)/showmount-showmount.Tpo -c -o showmount-showmount.obj `if test -f 'showmount.c'; then $(CYGPATH_W) 'showmount.c'; else $(CYGPATH_W) '$(srcdir)/showmount.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/showmount-showmount.Tpo $(DEPDIR)/showmount-showmount.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='showmount.c' object='showmount-showmount.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(showmount_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o showmount-showmount.obj `if test -f 'showmount.c'; then $(CYGPATH_W) 'showmount.c'; else $(CYGPATH_W) '$(srcdir)/showmount.c'; fi`
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-man8: $(man8_MANS)
+ @$(NORMAL_INSTALL)
+ @list1='$(man8_MANS)'; \
+ list2=''; \
+ test -n "$(man8dir)" \
+ && test -n "`echo $$list1$$list2`" \
+ || exit 0; \
+ echo " $(MKDIR_P) '$(DESTDIR)$(man8dir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(man8dir)" || exit 1; \
+ { for i in $$list1; do echo "$$i"; done; \
+ if test -n "$$list2"; then \
+ for i in $$list2; do echo "$$i"; done \
+ | sed -n '/\.8[a-z]*$$/p'; \
+ fi; \
+ } | while read p; do \
+ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; echo "$$p"; \
+ done | \
+ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \
+ sed 'N;N;s,\n, ,g' | { \
+ list=; while read file base inst; do \
+ if test "$$base" = "$$inst"; then list="$$list $$file"; else \
+ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man8dir)/$$inst'"; \
+ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man8dir)/$$inst" || exit $$?; \
+ fi; \
+ done; \
+ for i in $$list; do echo "$$i"; done | $(am__base_list) | \
+ while read files; do \
+ test -z "$$files" || { \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man8dir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(man8dir)" || exit $$?; }; \
+ done; }
+
+uninstall-man8:
+ @$(NORMAL_UNINSTALL)
+ @list='$(man8_MANS)'; test -n "$(man8dir)" || exit 0; \
+ files=`{ for i in $$list; do echo "$$i"; done; \
+ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
+ dir='$(DESTDIR)$(man8dir)'; $(am__uninstall_files_from_dir)
+
+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 $(PROGRAMS) $(MANS)
+installdirs:
+ for dir in "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man8dir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+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-sbinPROGRAMS \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/showmount-showmount.Po
+ -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-man
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-sbinPROGRAMS
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man: install-man8
+
+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)/showmount-showmount.Po
+ -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: uninstall-man uninstall-sbinPROGRAMS
+
+uninstall-man: uninstall-man8
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-sbinPROGRAMS 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-man8 install-pdf \
+ install-pdf-am install-ps install-ps-am install-sbinPROGRAMS \
+ 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 \
+ uninstall-man uninstall-man8 uninstall-sbinPROGRAMS
+
+.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/utils/showmount/showmount.c b/utils/showmount/showmount.c
new file mode 100644
index 0000000..394f528
--- /dev/null
+++ b/utils/showmount/showmount.c
@@ -0,0 +1,323 @@
+/*
+ * showmount.c -- show mount information for an NFS server
+ * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <rpc/rpc.h>
+#include <rpc/pmap_prot.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/time.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <memory.h>
+#include <stdlib.h>
+#include <fcntl.h>
+
+#include <netdb.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <getopt.h>
+#include <mount.h>
+#include <unistd.h>
+
+#include "nfsrpc.h"
+
+#define TIMEOUT_UDP 3
+#define TOTAL_TIMEOUT 20
+
+static char * version = "showmount for " VERSION;
+static char * program_name;
+static int headers = 1;
+static int hflag = 0;
+static int aflag = 0;
+static int dflag = 0;
+static int eflag = 0;
+
+static struct option longopts[] =
+{
+ { "all", 0, 0, 'a' },
+ { "directories", 0, 0, 'd' },
+ { "exports", 0, 0, 'e' },
+ { "no-headers", 0, &headers, 0 },
+ { "version", 0, 0, 'v' },
+ { "help", 0, 0, 'h' },
+ { NULL, 0, 0, 0 }
+};
+
+#define MAXHOSTLEN 256
+
+static int dump_cmp(const void *pv, const void *qv)
+{
+ const char **p = (const char **)pv;
+ const char **q = (const char **)qv;
+ return strcmp(*p, *q);
+}
+
+static void usage(FILE *fp, int n)
+{
+ fprintf(fp, "Usage: %s [-adehv]\n", program_name);
+ fprintf(fp, " [--all] [--directories] [--exports]\n");
+ fprintf(fp, " [--no-headers] [--help] [--version] [host]\n");
+ exit(n);
+}
+
+static const char *mount_pgm_tbl[] = {
+ "showmount",
+ "mount",
+ "mountd",
+ NULL,
+};
+
+static const rpcvers_t mount_vers_tbl[] = {
+ MOUNTVERS_NFSV3,
+ MOUNTVERS_POSIX,
+ MOUNTVERS,
+};
+static const unsigned int max_vers_tblsz =
+ (sizeof(mount_vers_tbl)/sizeof(mount_vers_tbl[0]));
+
+/*
+ * Generate an RPC client handle connected to the mountd service
+ * at @hostname, or die trying.
+ *
+ * Supports both AF_INET and AF_INET6 server addresses.
+ */
+static CLIENT *nfs_get_mount_client(const char *hostname, rpcvers_t vers)
+{
+ rpcprog_t program = nfs_getrpcbyname(MOUNTPROG, mount_pgm_tbl);
+ CLIENT *client;
+
+ client = clnt_create(hostname, program, vers, "tcp");
+ if (client)
+ return client;
+ client = clnt_create(hostname, program, vers, "udp");
+ if (client)
+ return client;
+
+ clnt_pcreateerror("clnt_create");
+ exit(1);
+}
+
+int main(int argc, char **argv)
+{
+ char hostname_buf[MAXHOSTLEN];
+ char *hostname;
+ enum clnt_stat clnt_stat;
+ struct timeval total_timeout;
+ int c;
+ CLIENT *mclient;
+ groups grouplist;
+ exports exportlist, exl;
+ mountlist dumplist;
+ mountlist list;
+ int i;
+ int n;
+ int maxlen;
+ int unsigned vers=0;
+ char **dumpv;
+
+ program_name = argv[0];
+ while ((c = getopt_long(argc, argv, "adehv", longopts, NULL)) != EOF) {
+ switch (c) {
+ case 'a':
+ aflag = 1;
+ break;
+ case 'd':
+ dflag = 1;
+ break;
+ case 'e':
+ eflag = 1;
+ break;
+ case 'h':
+ usage(stdout, 0);
+ break;
+ case 'v':
+ printf("%s\n", version);
+ exit(0);
+ case 0:
+ break;
+ case '?':
+ default:
+ usage(stderr, 1);
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ switch (aflag + dflag + eflag) {
+ case 0:
+ hflag = 1;
+ break;
+ case 1:
+ break;
+ default:
+ fprintf(stderr, "%s: only one of -a, -d or -e is allowed\n",
+ program_name);
+ exit(1);
+ break;
+ }
+
+ switch (argc) {
+ case 0:
+ if (gethostname(hostname_buf, MAXHOSTLEN) < 0) {
+ perror("getting hostname");
+ exit(1);
+ }
+ hostname = hostname_buf;
+ break;
+ case 1:
+ hostname = argv[0];
+ break;
+ default:
+ fprintf(stderr, "%s: only one hostname is allowed\n",
+ program_name);
+ exit(1);
+ break;
+ }
+
+ mclient = nfs_get_mount_client(hostname, mount_vers_tbl[vers]);
+ mclient->cl_auth = nfs_authsys_create();
+ if (mclient->cl_auth == NULL) {
+ fprintf(stderr, "%s: unable to create RPC auth handle.\n",
+ program_name);
+ clnt_destroy(mclient);
+ exit(1);
+ }
+ total_timeout.tv_sec = TOTAL_TIMEOUT;
+ total_timeout.tv_usec = 0;
+
+again:
+ if (eflag) {
+ memset(&exportlist, '\0', sizeof(exportlist));
+
+ clnt_stat = clnt_call(mclient, MOUNTPROC_EXPORT,
+ (xdrproc_t) xdr_void, NULL,
+ (xdrproc_t) xdr_exports, (caddr_t) &exportlist,
+ total_timeout);
+ if (clnt_stat == RPC_PROGVERSMISMATCH) {
+ if (++vers < max_vers_tblsz) {
+ (void)CLNT_CONTROL(mclient, CLSET_VERS,
+ (void *)&mount_vers_tbl[vers]);
+ goto again;
+ }
+ }
+ if (clnt_stat != RPC_SUCCESS) {
+ clnt_perror(mclient, "rpc mount export");
+ clnt_destroy(mclient);
+ exit(1);
+ }
+ if (headers)
+ printf("Export list for %s:\n", hostname);
+ maxlen = 0;
+ for (exl = exportlist; exl; exl = exl->ex_next) {
+ if ((n = strlen(exl->ex_dir)) > maxlen)
+ maxlen = n;
+ }
+ while (exportlist) {
+ printf("%-*s ", maxlen, exportlist->ex_dir);
+ grouplist = exportlist->ex_groups;
+ if (grouplist)
+ while (grouplist) {
+ printf("%s%s", grouplist->gr_name,
+ grouplist->gr_next ? "," : "");
+ grouplist = grouplist->gr_next;
+ }
+ else
+ printf("(everyone)");
+ printf("\n");
+ exportlist = exportlist->ex_next;
+ }
+ clnt_destroy(mclient);
+ exit(0);
+ }
+
+ memset(&dumplist, '\0', sizeof(dumplist));
+ clnt_stat = clnt_call(mclient, MOUNTPROC_DUMP,
+ (xdrproc_t) xdr_void, NULL,
+ (xdrproc_t) xdr_mountlist, (caddr_t) &dumplist,
+ total_timeout);
+ if (clnt_stat == RPC_PROGVERSMISMATCH) {
+ if (++vers < max_vers_tblsz) {
+ (void)CLNT_CONTROL(mclient, CLSET_VERS,
+ (void *)&mount_vers_tbl[vers]);
+ goto again;
+ }
+ }
+ if (clnt_stat != RPC_SUCCESS) {
+ clnt_perror(mclient, "rpc mount dump");
+ clnt_destroy(mclient);
+ exit(1);
+ }
+ clnt_destroy(mclient);
+
+ n = 0;
+ for (list = dumplist; list; list = list->ml_next)
+ n++;
+ dumpv = (char **) calloc(n, sizeof (char *));
+ if (n && !dumpv) {
+ fprintf(stderr, "%s: out of memory\n", program_name);
+ exit(1);
+ }
+ i = 0;
+
+ if (hflag) {
+ if (headers)
+ printf("Hosts on %s:\n", hostname);
+ while (dumplist) {
+ dumpv[i++] = dumplist->ml_hostname;
+ dumplist = dumplist->ml_next;
+ }
+ }
+ else if (aflag) {
+ if (headers)
+ printf("All mount points on %s:\n", hostname);
+ while (dumplist) {
+ char *t;
+
+ t=malloc(strlen(dumplist->ml_hostname)+strlen(dumplist->ml_directory)+2);
+ if (!t)
+ {
+ fprintf(stderr, "%s: out of memory\n", program_name);
+ exit(1);
+ }
+ sprintf(t, "%s:%s", dumplist->ml_hostname, dumplist->ml_directory);
+ dumpv[i++] = t;
+ dumplist = dumplist->ml_next;
+ }
+ }
+ else if (dflag) {
+ if (headers)
+ printf("Directories on %s:\n", hostname);
+ while (dumplist) {
+ dumpv[i++] = dumplist->ml_directory;
+ dumplist = dumplist->ml_next;
+ }
+ }
+
+ qsort(dumpv, n, sizeof (char *), dump_cmp);
+
+ for (i = 0; i < n; i++) {
+ if (i == 0 || strcmp(dumpv[i], dumpv[i - 1]) != 0)
+ printf("%s\n", dumpv[i]);
+ }
+ exit(0);
+}
+
diff --git a/utils/showmount/showmount.man b/utils/showmount/showmount.man
new file mode 100644
index 0000000..35818e1
--- /dev/null
+++ b/utils/showmount/showmount.man
@@ -0,0 +1,65 @@
+.\" Copyright 1993 Rick Sladkey <jrs@world.std.com>
+.\" May be distributed under the GNU General Public License
+.TH SHOWMOUNT 8 "6 October 1993"
+.SH NAME
+showmount \- show mount information for an NFS server
+.SH SYNOPSIS
+.B showmount
+.B "[\ \-adehv\ ]"
+.B "[\ \-\-all\ ]"
+.B "[\ \-\-directories\ ]"
+.B "[\ \-\-exports\ ]"
+.B "[\ \-\-help\ ]"
+.B "[\ \-\-version\ ]"
+.B "[\ host\ ]"
+.SH DESCRIPTION
+.B showmount
+queries the mount daemon on a remote host for information about
+the state of the NFS server on that machine. With no options
+.B showmount
+lists the set of clients who are mounting from that host.
+The output from
+.B showmount
+is designed to
+appear as though it were processed through ``sort \-u''.
+.SH OPTIONS
+.TP
+.BR \-a " or " \-\-all
+List both the client hostname or IP address and mounted directory in
+host:dir format. This info should not be considered reliable. See the notes
+on rmtab in
+.BR rpc.mountd (8).
+.TP
+.BR \-d " or " \-\-directories
+List only the directories mounted by some client.
+.TP
+.BR \-e " or " \-\-exports
+Show the NFS server's export list.
+.TP
+.BR \-h " or " \-\-help
+Provide a short help summary.
+.TP
+.BR \-v " or " \-\-version
+Report the current version number of the program.
+.TP
+.B \-\-no\-headers
+Suppress the descriptive headings from the output.
+.SH "SEE ALSO"
+.BR rpc.mountd (8),
+.BR rpc.nfsd (8)
+.SH BUGS
+The completeness and accuracy of the information that
+.B showmount
+displays varies according to the NFS server's implementation.
+.P
+Because
+.B showmount
+sorts and uniqs the output, it is impossible to determine from
+the output whether a client is mounting the same directory more than once.
+.P
+.B showmount
+works by contacting the server's MNT service directly. NFSv4-only servers have
+no need to advertise their exported root filehandles via this method, and may
+not expose their MNT service to clients.
+.SH AUTHOR
+Rick Sladkey <jrs@world.std.com>
diff --git a/utils/statd/.gitignore b/utils/statd/.gitignore
new file mode 100644
index 0000000..99b0cce
--- /dev/null
+++ b/utils/statd/.gitignore
@@ -0,0 +1 @@
+sm-notify
diff --git a/utils/statd/COPYING b/utils/statd/COPYING
new file mode 100644
index 0000000..60549be
--- /dev/null
+++ b/utils/statd/COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/utils/statd/Makefile.am b/utils/statd/Makefile.am
new file mode 100644
index 0000000..6facc15
--- /dev/null
+++ b/utils/statd/Makefile.am
@@ -0,0 +1,93 @@
+## Process this file with automake to produce Makefile.in
+
+man8_MANS = statd.man sm-notify.man
+
+RPCPREFIX = rpc.
+KPREFIX = @kprefix@
+sbin_PROGRAMS = statd sm-notify
+dist_sbin_SCRIPTS = start-statd
+statd_SOURCES = callback.c notlist.c misc.c monitor.c hostname.c \
+ simu.c stat.c statd.c svc_run.c rmtcall.c \
+ notlist.h statd.h system.h
+sm_notify_SOURCES = sm-notify.c
+
+BUILT_SOURCES = $(GENFILES)
+statd_LDADD = ../../support/nsm/libnsm.a \
+ ../../support/nfs/libnfs.la \
+ ../../support/misc/libmisc.a \
+ $(LIBWRAP) $(LIBNSL) $(LIBCAP) $(LIBTIRPC)
+sm_notify_LDADD = ../../support/nsm/libnsm.a \
+ ../../support/nfs/libnfs.la \
+ ../../support/misc/libmisc.a \
+ $(LIBNSL) $(LIBCAP) $(LIBTIRPC)
+
+EXTRA_DIST = sim_sm_inter.x $(man8_MANS) simulate.c
+
+if CONFIG_RPCGEN
+RPCGEN = $(top_builddir)/tools/rpcgen/rpcgen
+$(RPCGEN):
+ make -C ../../tools/rpcgen all
+else
+RPCGEN = @RPCGEN_PATH@
+endif
+
+$(GENFILES_CLNT): %_clnt.c: %.x $(RPCGEN)
+ test -f $@ && rm -rf $@ || true
+ $(RPCGEN) -l -o $@ $<
+
+$(GENFILES_SVC): %_svc.c: %.x $(RPCGEN)
+ test -f $@ && rm -rf $@ || true
+ $(RPCGEN) -m -o $@ $<
+
+$(GENFILES_XDR): %_xdr.c: %.x $(RPCGEN)
+ test -f $@ && rm -rf $@ || true
+ $(RPCGEN) -c -o $@ $<
+
+$(GENFILES_H): %.h: %.x $(RPCGEN)
+ test -f $@ && rm -rf $@ || true
+ $(RPCGEN) -h -o $@ $<
+
+MAINTAINERCLEANFILES = Makefile.in
+
+CLEANFILES = $(GENFILES)
+
+#######################################################################
+# The following allows the current practice of having
+# daemons renamed during the install to include RPCPREFIX
+# and the KPREFIX
+# This could all be done much easier with program_transform_name
+# ( program_transform_name = s/^/$(RPCPREFIX)$(KPREFIX)/ )
+# but that also renames the man pages, which the current
+# practice does not do.
+install-exec-hook:
+ (cd $(DESTDIR)$(sbindir) && \
+ for p in $(sbin_PROGRAMS); do \
+ [ $$p = sm-notify ] || mv -f $$p$(EXEEXT) $(RPCPREFIX)$(KPREFIX)$$p$(EXEEXT) ;\
+ done)
+uninstall-hook:
+ (cd $(DESTDIR)$(sbindir) && \
+ for p in $(sbin_PROGRAMS); do \
+ [ $$p = sm-notify ] || rm -f $(RPCPREFIX)$(KPREFIX)$$p$(EXEEXT) ;\
+ done)
+
+
+# XXX This makes some assumptions about what automake does.
+# XXX But there is no install-man-hook or install-man-local.
+install-man: install-man8 install-man-links
+uninstall-man: uninstall-man8 uninstall-man-links
+
+install-man-links:
+ (cd $(DESTDIR)$(man8dir) && \
+ for m in $(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS); do \
+ inst=`echo $$m | sed -e 's/man$$/8/'`; \
+ rm -f $(RPCPREFIX)$$inst ; \
+ $(LN_S) $$inst $(RPCPREFIX)$$inst ; \
+ done)
+
+uninstall-man-links:
+ (cd $(DESTDIR)$(man8dir) && \
+ for m in $(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS); do \
+ inst=`echo $$m | sed -e 's/man$$/8/'`; \
+ rm -f $(RPCPREFIX)$$inst ; \
+ done)
+
diff --git a/utils/statd/Makefile.in b/utils/statd/Makefile.in
new file mode 100644
index 0000000..8754504
--- /dev/null
+++ b/utils/statd/Makefile.in
@@ -0,0 +1,984 @@
+# 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@
+sbin_PROGRAMS = statd$(EXEEXT) sm-notify$(EXEEXT)
+subdir = utils/statd
+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 $(dist_sbin_SCRIPTS) \
+ $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/support/include/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(sbindir)" \
+ "$(DESTDIR)$(man8dir)"
+PROGRAMS = $(sbin_PROGRAMS)
+am_sm_notify_OBJECTS = sm-notify.$(OBJEXT)
+sm_notify_OBJECTS = $(am_sm_notify_OBJECTS)
+am__DEPENDENCIES_1 =
+sm_notify_DEPENDENCIES = ../../support/nsm/libnsm.a \
+ ../../support/nfs/libnfs.la ../../support/misc/libmisc.a \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+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 =
+am_statd_OBJECTS = callback.$(OBJEXT) notlist.$(OBJEXT) misc.$(OBJEXT) \
+ monitor.$(OBJEXT) hostname.$(OBJEXT) simu.$(OBJEXT) \
+ stat.$(OBJEXT) statd.$(OBJEXT) svc_run.$(OBJEXT) \
+ rmtcall.$(OBJEXT)
+statd_OBJECTS = $(am_statd_OBJECTS)
+statd_DEPENDENCIES = ../../support/nsm/libnsm.a \
+ ../../support/nfs/libnfs.la ../../support/misc/libmisc.a \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+SCRIPTS = $(dist_sbin_SCRIPTS)
+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)/callback.Po ./$(DEPDIR)/hostname.Po \
+ ./$(DEPDIR)/misc.Po ./$(DEPDIR)/monitor.Po \
+ ./$(DEPDIR)/notlist.Po ./$(DEPDIR)/rmtcall.Po \
+ ./$(DEPDIR)/simu.Po ./$(DEPDIR)/sm-notify.Po \
+ ./$(DEPDIR)/stat.Po ./$(DEPDIR)/statd.Po \
+ ./$(DEPDIR)/svc_run.Po
+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 = $(sm_notify_SOURCES) $(statd_SOURCES)
+DIST_SOURCES = $(sm_notify_SOURCES) $(statd_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+man8dir = $(mandir)/man8
+NROFF = nroff
+MANS = $(man8_MANS)
+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 COPYING \
+ TODO
+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@
+man8_MANS = statd.man sm-notify.man
+RPCPREFIX = rpc.
+KPREFIX = @kprefix@
+dist_sbin_SCRIPTS = start-statd
+statd_SOURCES = callback.c notlist.c misc.c monitor.c hostname.c \
+ simu.c stat.c statd.c svc_run.c rmtcall.c \
+ notlist.h statd.h system.h
+
+sm_notify_SOURCES = sm-notify.c
+BUILT_SOURCES = $(GENFILES)
+statd_LDADD = ../../support/nsm/libnsm.a \
+ ../../support/nfs/libnfs.la \
+ ../../support/misc/libmisc.a \
+ $(LIBWRAP) $(LIBNSL) $(LIBCAP) $(LIBTIRPC)
+
+sm_notify_LDADD = ../../support/nsm/libnsm.a \
+ ../../support/nfs/libnfs.la \
+ ../../support/misc/libmisc.a \
+ $(LIBNSL) $(LIBCAP) $(LIBTIRPC)
+
+EXTRA_DIST = sim_sm_inter.x $(man8_MANS) simulate.c
+@CONFIG_RPCGEN_FALSE@RPCGEN = @RPCGEN_PATH@
+@CONFIG_RPCGEN_TRUE@RPCGEN = $(top_builddir)/tools/rpcgen/rpcgen
+MAINTAINERCLEANFILES = Makefile.in
+CLEANFILES = $(GENFILES)
+all: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) 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 utils/statd/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu utils/statd/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):
+install-sbinPROGRAMS: $(sbin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(sbindir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-sbinPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(sbindir)" && rm -f $$files
+
+clean-sbinPROGRAMS:
+ @list='$(sbin_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+sm-notify$(EXEEXT): $(sm_notify_OBJECTS) $(sm_notify_DEPENDENCIES) $(EXTRA_sm_notify_DEPENDENCIES)
+ @rm -f sm-notify$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(sm_notify_OBJECTS) $(sm_notify_LDADD) $(LIBS)
+
+statd$(EXEEXT): $(statd_OBJECTS) $(statd_DEPENDENCIES) $(EXTRA_statd_DEPENDENCIES)
+ @rm -f statd$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(statd_OBJECTS) $(statd_LDADD) $(LIBS)
+install-dist_sbinSCRIPTS: $(dist_sbin_SCRIPTS)
+ @$(NORMAL_INSTALL)
+ @list='$(dist_sbin_SCRIPTS)'; test -n "$(sbindir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(sbindir)" || exit 1; \
+ fi; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n' \
+ -e 'h;s|.*|.|' \
+ -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) { files[d] = files[d] " " $$1; \
+ if (++n[d] == $(am__install_max)) { \
+ print "f", d, files[d]; n[d] = 0; files[d] = "" } } \
+ else { print "f", d "/" $$4, $$1 } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(sbindir)$$dir'"; \
+ $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-dist_sbinSCRIPTS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(dist_sbin_SCRIPTS)'; test -n "$(sbindir)" || exit 0; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 's,.*/,,;$(transform)'`; \
+ dir='$(DESTDIR)$(sbindir)'; $(am__uninstall_files_from_dir)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/callback.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hostname.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/misc.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/monitor.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/notlist.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rmtcall.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/simu.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sm-notify.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stat.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/statd.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/svc_run.Po@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 $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-man8: $(man8_MANS)
+ @$(NORMAL_INSTALL)
+ @list1='$(man8_MANS)'; \
+ list2=''; \
+ test -n "$(man8dir)" \
+ && test -n "`echo $$list1$$list2`" \
+ || exit 0; \
+ echo " $(MKDIR_P) '$(DESTDIR)$(man8dir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(man8dir)" || exit 1; \
+ { for i in $$list1; do echo "$$i"; done; \
+ if test -n "$$list2"; then \
+ for i in $$list2; do echo "$$i"; done \
+ | sed -n '/\.8[a-z]*$$/p'; \
+ fi; \
+ } | while read p; do \
+ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; echo "$$p"; \
+ done | \
+ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \
+ sed 'N;N;s,\n, ,g' | { \
+ list=; while read file base inst; do \
+ if test "$$base" = "$$inst"; then list="$$list $$file"; else \
+ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man8dir)/$$inst'"; \
+ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man8dir)/$$inst" || exit $$?; \
+ fi; \
+ done; \
+ for i in $$list; do echo "$$i"; done | $(am__base_list) | \
+ while read files; do \
+ test -z "$$files" || { \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man8dir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(man8dir)" || exit $$?; }; \
+ done; }
+
+uninstall-man8:
+ @$(NORMAL_UNINSTALL)
+ @list='$(man8_MANS)'; test -n "$(man8dir)" || exit 0; \
+ files=`{ for i in $$list; do echo "$$i"; done; \
+ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
+ dir='$(DESTDIR)$(man8dir)'; $(am__uninstall_files_from_dir)
+
+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: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) check-am
+all-am: Makefile $(PROGRAMS) $(SCRIPTS) $(MANS)
+installdirs:
+ for dir in "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man8dir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) install-am
+install-exec: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) 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:
+ -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+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 "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES)
+ -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES)
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-sbinPROGRAMS \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/callback.Po
+ -rm -f ./$(DEPDIR)/hostname.Po
+ -rm -f ./$(DEPDIR)/misc.Po
+ -rm -f ./$(DEPDIR)/monitor.Po
+ -rm -f ./$(DEPDIR)/notlist.Po
+ -rm -f ./$(DEPDIR)/rmtcall.Po
+ -rm -f ./$(DEPDIR)/simu.Po
+ -rm -f ./$(DEPDIR)/sm-notify.Po
+ -rm -f ./$(DEPDIR)/stat.Po
+ -rm -f ./$(DEPDIR)/statd.Po
+ -rm -f ./$(DEPDIR)/svc_run.Po
+ -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-man
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-dist_sbinSCRIPTS install-sbinPROGRAMS
+ @$(NORMAL_INSTALL)
+ $(MAKE) $(AM_MAKEFLAGS) install-exec-hook
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+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)/callback.Po
+ -rm -f ./$(DEPDIR)/hostname.Po
+ -rm -f ./$(DEPDIR)/misc.Po
+ -rm -f ./$(DEPDIR)/monitor.Po
+ -rm -f ./$(DEPDIR)/notlist.Po
+ -rm -f ./$(DEPDIR)/rmtcall.Po
+ -rm -f ./$(DEPDIR)/simu.Po
+ -rm -f ./$(DEPDIR)/sm-notify.Po
+ -rm -f ./$(DEPDIR)/stat.Po
+ -rm -f ./$(DEPDIR)/statd.Po
+ -rm -f ./$(DEPDIR)/svc_run.Po
+ -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: uninstall-dist_sbinSCRIPTS uninstall-man \
+ uninstall-sbinPROGRAMS
+ @$(NORMAL_INSTALL)
+ $(MAKE) $(AM_MAKEFLAGS) uninstall-hook
+.MAKE: all check install install-am install-exec install-exec-am \
+ install-strip uninstall-am
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-sbinPROGRAMS 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-dist_sbinSCRIPTS install-dvi \
+ install-dvi-am install-exec install-exec-am install-exec-hook \
+ install-html install-html-am install-info install-info-am \
+ install-man install-man8 install-pdf install-pdf-am install-ps \
+ install-ps-am install-sbinPROGRAMS 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 uninstall-dist_sbinSCRIPTS \
+ uninstall-hook uninstall-man uninstall-man8 \
+ uninstall-sbinPROGRAMS
+
+.PRECIOUS: Makefile
+
+@CONFIG_RPCGEN_TRUE@$(RPCGEN):
+@CONFIG_RPCGEN_TRUE@ make -C ../../tools/rpcgen all
+
+$(GENFILES_CLNT): %_clnt.c: %.x $(RPCGEN)
+ test -f $@ && rm -rf $@ || true
+ $(RPCGEN) -l -o $@ $<
+
+$(GENFILES_SVC): %_svc.c: %.x $(RPCGEN)
+ test -f $@ && rm -rf $@ || true
+ $(RPCGEN) -m -o $@ $<
+
+$(GENFILES_XDR): %_xdr.c: %.x $(RPCGEN)
+ test -f $@ && rm -rf $@ || true
+ $(RPCGEN) -c -o $@ $<
+
+$(GENFILES_H): %.h: %.x $(RPCGEN)
+ test -f $@ && rm -rf $@ || true
+ $(RPCGEN) -h -o $@ $<
+
+#######################################################################
+# The following allows the current practice of having
+# daemons renamed during the install to include RPCPREFIX
+# and the KPREFIX
+# This could all be done much easier with program_transform_name
+# ( program_transform_name = s/^/$(RPCPREFIX)$(KPREFIX)/ )
+# but that also renames the man pages, which the current
+# practice does not do.
+install-exec-hook:
+ (cd $(DESTDIR)$(sbindir) && \
+ for p in $(sbin_PROGRAMS); do \
+ [ $$p = sm-notify ] || mv -f $$p$(EXEEXT) $(RPCPREFIX)$(KPREFIX)$$p$(EXEEXT) ;\
+ done)
+uninstall-hook:
+ (cd $(DESTDIR)$(sbindir) && \
+ for p in $(sbin_PROGRAMS); do \
+ [ $$p = sm-notify ] || rm -f $(RPCPREFIX)$(KPREFIX)$$p$(EXEEXT) ;\
+ done)
+
+# XXX This makes some assumptions about what automake does.
+# XXX But there is no install-man-hook or install-man-local.
+install-man: install-man8 install-man-links
+uninstall-man: uninstall-man8 uninstall-man-links
+
+install-man-links:
+ (cd $(DESTDIR)$(man8dir) && \
+ for m in $(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS); do \
+ inst=`echo $$m | sed -e 's/man$$/8/'`; \
+ rm -f $(RPCPREFIX)$$inst ; \
+ $(LN_S) $$inst $(RPCPREFIX)$$inst ; \
+ done)
+
+uninstall-man-links:
+ (cd $(DESTDIR)$(man8dir) && \
+ for m in $(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS); do \
+ inst=`echo $$m | sed -e 's/man$$/8/'`; \
+ rm -f $(RPCPREFIX)$$inst ; \
+ done)
+
+# 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/utils/statd/TODO b/utils/statd/TODO
new file mode 100644
index 0000000..0ee050a
--- /dev/null
+++ b/utils/statd/TODO
@@ -0,0 +1,13 @@
+Some things still left to do (not a comprehensive list):
+
+* Go through Olaf's extensive changes (especially the list and callback
+ handling, which is the meat of the server) and understand everything
+ that he's done.
+
+* Continue checking for security holes.
+
+* Handle multiple SM_MON requests that are identical save for the "priv"
+ information. How should I do this? No spec's...(it's not really
+ supposed to happen). [Did Olaf already address this?]
+
+* BETTER CODE COMMENTS!
diff --git a/utils/statd/callback.c b/utils/statd/callback.c
new file mode 100644
index 0000000..bb7c590
--- /dev/null
+++ b/utils/statd/callback.c
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 1995, 1997-1999 Jeffrey A. Uphoff
+ * Modified by Olaf Kirch, Oct. 1996.
+ * Modified by Lon Hohberger, Oct. 2000.
+ *
+ * NSM for Linux.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <netdb.h>
+
+#include "rpcmisc.h"
+#include "statd.h"
+#include "notlist.h"
+#include "ha-callout.h"
+
+/* Callback notify list. */
+/* notify_list *cbnl = NULL; ... never used */
+
+
+/*
+ * Services SM_NOTIFY requests.
+ *
+ * When NLM uses an SM_MON request to tell statd to monitor a remote,
+ * the request contains a "mon_name" argument. This is usually the
+ * "caller_name" argument of an NLMPROC_LOCK request. On Linux, the
+ * NLM can send statd the remote's IP address instead of its
+ * caller_name. The NSM protocol does not allow both the remote's
+ * caller_name and it's IP address to be sent in the same SM_MON
+ * request.
+ *
+ * The remote's caller_name is useful because it makes it simple
+ * to identify rebooting remotes by matching the "mon_name" argument
+ * they sent via an SM_NOTIFY request.
+ *
+ * The caller_name string may not be a fully qualified domain name,
+ * or even registered in the DNS database, however. Having the
+ * remote's IP address is useful because then there is no ambiguity
+ * about where to send an SM_NOTIFY after the local system reboots.
+ *
+ * Without the actual caller_name, however, statd must use an
+ * heuristic to match an incoming SM_NOTIFY request to one of the
+ * hosts it is currently monitoring. The incoming mon_name in an
+ * SM_NOTIFY address is converted to a list of IP addresses using
+ * DNS. Each mon_name on statd's monitor list is also converted to
+ * an address list, and the two lists are checked to see if there is
+ * a matching address.
+ *
+ * There are some risks to this strategy:
+ *
+ * 1. The external DNS database is not reliable. It can change
+ * over time, or the forward and reverse mappings could be
+ * inconsistent.
+ *
+ * 2. If statd's monitor list becomes substantial, finding a match
+ * can generate a not inconsequential amount of DNS traffic.
+ *
+ * 3. statd is a single-threaded service. When DNS becomes slow or
+ * unresponsive, statd also becomes slow or unresponsive.
+ *
+ * 4. If the remote does not have a DNS entry at all (or if the
+ * remote can resolve itself, but the local host can't resolve
+ * the remote's hostname), the remote cannot be monitored, and
+ * therefore NLM locking cannot be provided for that host.
+ *
+ * 5. Local DNS resolution can produce different results for the
+ * mon_name than the results the remote might see for the same
+ * query, especially if the remote did not send a caller_name
+ * or mon_name that is a fully qualified domain name.
+ *
+ * Note that a caller_name is passed from NFS client to server,
+ * but the client never knows what mon_name the server might use
+ * to notify it of a reboot. On Linux, the client extracts the
+ * server's name from the devname it was passed by the mount
+ * command. This is often not a fully-qualified domain name.
+ */
+void *
+sm_notify_1_svc(struct stat_chge *argp, struct svc_req *rqstp)
+{
+ notify_list *lp, *call;
+ static char *result = NULL;
+ struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt);
+ char ip_addr[INET6_ADDRSTRLEN];
+
+ xlog(D_CALL, "Received SM_NOTIFY from %s, state: %d",
+ argp->mon_name, argp->state);
+
+ if (!statd_present_address(sap, ip_addr, sizeof(ip_addr))) {
+ xlog_warn("Unrecognized sender address");
+ return ((void *) &result);
+ }
+
+ ha_callout("sm-notify", argp->mon_name, ip_addr, argp->state);
+
+ /* quick check - don't bother if we're not monitoring anyone */
+ if (rtnl == NULL) {
+ xlog_warn("SM_NOTIFY from %s while not monitoring any hosts",
+ argp->mon_name);
+ return ((void *) &result);
+ }
+
+ /* okir change: statd doesn't remove the remote host from its
+ * internal monitor list when receiving an SM_NOTIFY call from
+ * it. Lockd will want to continue monitoring the remote host
+ * until it issues an SM_UNMON call.
+ */
+ for (lp = rtnl ; lp ; lp = lp->next)
+ if (NL_STATE(lp) != argp->state &&
+ (statd_matchhostname(argp->mon_name, lp->dns_name) ||
+ statd_matchhostname(ip_addr, lp->dns_name))) {
+ NL_STATE(lp) = argp->state;
+ call = nlist_clone(lp);
+ nlist_insert(&notify, call);
+ }
+
+
+ return ((void *) &result);
+}
diff --git a/utils/statd/hostname.c b/utils/statd/hostname.c
new file mode 100644
index 0000000..16e21fc
--- /dev/null
+++ b/utils/statd/hostname.c
@@ -0,0 +1,319 @@
+/*
+ * 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/>.
+ */
+
+/*
+ * NSM for Linux.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <strings.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+
+#include "nfslib.h"
+#include "sockaddr.h"
+#include "statd.h"
+#include "xlog.h"
+
+/**
+ * statd_present_address - convert sockaddr to presentation address
+ * @sap: pointer to socket address to convert
+ * @buf: pointer to buffer to fill in
+ * @buflen: length of buffer
+ *
+ * Convert the passed-in sockaddr-style address to presentation format.
+ * The presentation format address is placed in @buf and is
+ * '\0'-terminated.
+ *
+ * Returns true if successful; otherwise false.
+ *
+ * getnameinfo(3) is preferred, since it can parse IPv6 scope IDs.
+ * An alternate version of statd_present_address() is available to
+ * handle older glibcs that do not have getnameinfo(3).
+ */
+#ifdef HAVE_GETNAMEINFO
+_Bool
+statd_present_address(const struct sockaddr *sap, char *buf, const size_t buflen)
+{
+ socklen_t salen;
+ int error;
+
+ salen = nfs_sockaddr_length(sap);
+ if (salen == 0) {
+ xlog(D_GENERAL, "%s: unsupported address family",
+ __func__);
+ return false;
+ }
+
+ error = getnameinfo(sap, salen, buf, (socklen_t)buflen,
+ NULL, 0, NI_NUMERICHOST);
+ if (error != 0) {
+ xlog(D_GENERAL, "%s: getnameinfo(3): %s",
+ __func__, gai_strerror(error));
+ return false;
+ }
+ return true;
+}
+#else /* !HAVE_GETNAMEINFO */
+_Bool
+statd_present_address(const struct sockaddr *sap, char *buf, const size_t buflen)
+{
+ const struct sockaddr_in *sin = (const struct sockaddr_in *)sap;
+
+ if (sin->sin_family != AF_INET) {
+ xlog(D_GENERAL, "%s: unsupported address family", __func__);
+ return false;
+ }
+
+ /* ensure '\0' termination */
+ memset(buf, 0, buflen);
+
+ if (inet_ntop(AF_INET, (char *)&sin->sin_addr,
+ buf, (socklen_t)buflen) == NULL) {
+ xlog(D_GENERAL, "%s: inet_ntop(3): %m", __func__);
+ return false;
+ }
+ return true;
+}
+#endif /* !HAVE_GETNAMEINFO */
+
+/*
+ * Look up the hostname; report exceptional errors. Caller must
+ * call freeaddrinfo(3) if a valid addrinfo is returned.
+ */
+__attribute__((__malloc__))
+static struct addrinfo *
+get_addrinfo(const char *hostname, const struct addrinfo *hint)
+{
+ struct addrinfo *ai = NULL;
+ int error;
+
+ error = getaddrinfo(hostname, NULL, hint, &ai);
+ switch (error) {
+ case 0:
+ return ai;
+ case EAI_NONAME:
+ break;
+ default:
+ xlog(D_GENERAL, "%s: failed to resolve host %s: %s",
+ __func__, hostname, gai_strerror(error));
+ }
+
+ return NULL;
+}
+
+#ifdef HAVE_GETNAMEINFO
+static _Bool
+get_nameinfo(const struct sockaddr *sap, const socklen_t salen,
+ /*@out@*/ char *buf, const socklen_t buflen)
+{
+ int error;
+
+ error = getnameinfo(sap, salen, buf, buflen, NULL, 0, NI_NAMEREQD);
+ if (error != 0) {
+ xlog(D_GENERAL, "%s: failed to resolve address: %s",
+ __func__, gai_strerror(error));
+ return false;
+ }
+
+ return true;
+}
+#else /* !HAVE_GETNAMEINFO */
+static _Bool
+get_nameinfo(const struct sockaddr *sap,
+ __attribute__ ((unused)) const socklen_t salen,
+ /*@out@*/ char *buf, socklen_t buflen)
+{
+ struct sockaddr_in *sin = (struct sockaddr_in *)(char *)sap;
+ struct hostent *hp;
+
+ if (sin->sin_family != AF_INET) {
+ xlog(D_GENERAL, "%s: unknown address family: %d",
+ sin->sin_family);
+ return false;
+ }
+
+ hp = gethostbyaddr((const char *)&(sin->sin_addr.s_addr),
+ sizeof(struct in_addr), AF_INET);
+ if (hp == NULL) {
+ xlog(D_GENERAL, "%s: failed to resolve address: %m", __func__);
+ return false;
+ }
+
+ strncpy(buf, hp->h_name, (size_t)buflen);
+ return true;
+}
+#endif /* !HAVE_GETNAMEINFO */
+
+/**
+ * statd_canonical_name - choose file name for monitor record files
+ * @hostname: C string containing hostname or presentation address
+ *
+ * Returns a '\0'-terminated ASCII string containing a fully qualified
+ * canonical hostname, or NULL if @hostname does not have a reverse
+ * mapping. Caller must free the result with free(3).
+ *
+ * Incoming hostnames are looked up to determine the canonical hostname,
+ * and incoming presentation addresses are converted to canonical
+ * hostnames.
+ */
+__attribute__((__malloc__))
+char *
+statd_canonical_name(const char *hostname)
+{
+ struct addrinfo hint = {
+#ifdef IPV6_SUPPORTED
+ .ai_family = AF_UNSPEC,
+#else /* !IPV6_SUPPORTED */
+ .ai_family = AF_INET,
+#endif /* !IPV6_SUPPORTED */
+ .ai_flags = AI_NUMERICHOST,
+ .ai_protocol = (int)IPPROTO_UDP,
+ };
+ char buf[NI_MAXHOST];
+ struct addrinfo *ai;
+
+ ai = get_addrinfo(hostname, &hint);
+ if (ai != NULL) {
+ /* @hostname was a presentation address */
+ _Bool result;
+ result = get_nameinfo(ai->ai_addr, ai->ai_addrlen,
+ buf, (socklen_t)sizeof(buf));
+ nfs_freeaddrinfo(ai);
+ if (!result || buf[0] == '\0')
+ /* OK to use presentation address,
+ * if no reverse map exists */
+ return strdup(hostname);
+ return strdup(buf);
+ }
+
+ /* @hostname was a hostname */
+ hint.ai_flags = AI_CANONNAME;
+ ai = get_addrinfo(hostname, &hint);
+ if (ai == NULL)
+ return NULL;
+ strcpy(buf, ai->ai_canonname);
+ nfs_freeaddrinfo(ai);
+
+ return strdup(buf);
+}
+
+/*
+ * Take care to perform an explicit reverse lookup on presentation
+ * addresses. Otherwise we don't get a real canonical name or a
+ * complete list of addresses.
+ *
+ * Returns an addrinfo list that has ai_canonname filled in, or
+ * NULL if some error occurs. Caller must free the returned
+ * list with freeaddrinfo(3).
+ */
+__attribute__((__malloc__))
+static struct addrinfo *
+statd_canonical_list(const char *hostname)
+{
+ struct addrinfo hint = {
+#ifdef IPV6_SUPPORTED
+ .ai_family = AF_UNSPEC,
+#else /* !IPV6_SUPPORTED */
+ .ai_family = AF_INET,
+#endif /* !IPV6_SUPPORTED */
+ .ai_flags = AI_NUMERICHOST,
+ .ai_protocol = (int)IPPROTO_UDP,
+ };
+ char buf[NI_MAXHOST];
+ struct addrinfo *ai;
+
+ ai = get_addrinfo(hostname, &hint);
+ if (ai != NULL) {
+ /* @hostname was a presentation address */
+ _Bool result;
+ result = get_nameinfo(ai->ai_addr, ai->ai_addrlen,
+ buf, (socklen_t)sizeof(buf));
+ nfs_freeaddrinfo(ai);
+ if (result)
+ goto out;
+ }
+ /* @hostname was a hostname or had no reverse mapping */
+ strcpy(buf, hostname);
+
+out:
+ hint.ai_flags = AI_CANONNAME;
+ return get_addrinfo(buf, &hint);
+}
+
+/**
+ * statd_matchhostname - check if two hostnames are equivalent
+ * @hostname1: C string containing hostname
+ * @hostname2: C string containing hostname
+ *
+ * Returns true if the hostnames are the same, the hostnames resolve
+ * to the same canonical name, or the hostnames resolve to at least
+ * one address that is the same. False is returned if the hostnames
+ * do not match in any of these ways, if either hostname contains
+ * wildcard characters, if either hostname is a netgroup name, or
+ * if an error occurs.
+ */
+_Bool
+statd_matchhostname(const char *hostname1, const char *hostname2)
+{
+ struct addrinfo *ai1, *ai2, *results1 = NULL, *results2 = NULL;
+ _Bool result = false;
+
+ if (strcasecmp(hostname1, hostname2) == 0) {
+ result = true;
+ goto out;
+ }
+
+ results1 = statd_canonical_list(hostname1);
+ if (results1 == NULL)
+ goto out;
+ results2 = statd_canonical_list(hostname2);
+ if (results2 == NULL)
+ goto out;
+
+ if (strcasecmp(results1->ai_canonname, results2->ai_canonname) == 0) {
+ result = true;
+ goto out;
+ }
+
+ for (ai1 = results1; ai1 != NULL; ai1 = ai1->ai_next)
+ for (ai2 = results2; ai2 != NULL; ai2 = ai2->ai_next)
+ if (nfs_compare_sockaddr(ai1->ai_addr, ai2->ai_addr)) {
+ result = true;
+ break;
+ }
+
+out:
+ nfs_freeaddrinfo(results2);
+ nfs_freeaddrinfo(results1);
+
+ xlog(D_CALL, "%s: hostnames %s and %s %s", __func__,
+ hostname1, hostname2,
+ (result ? "matched" : "did not match"));
+ return result;
+}
diff --git a/utils/statd/misc.c b/utils/statd/misc.c
new file mode 100644
index 0000000..f2a086f
--- /dev/null
+++ b/utils/statd/misc.c
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 1995-1999 Jeffrey A. Uphoff
+ * Modified by Olaf Kirch, 1996.
+ * Modified by H.J. Lu, 1998.
+ *
+ * NSM for Linux.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <limits.h>
+#include <string.h>
+#include <unistd.h>
+#include "statd.h"
+#include "notlist.h"
+
+/*
+ * Error-checking malloc() wrapper.
+ */
+void *
+xmalloc (size_t size)
+{
+ void *ptr;
+
+ if (size == 0)
+ return ((void *)NULL);
+
+ if (!(ptr = malloc (size)))
+ xlog_err ("malloc failed");
+
+ return (ptr);
+}
+
+
+/*
+ * Error-checking strdup() wrapper.
+ */
+char *
+xstrdup (const char *string)
+{
+ char *result;
+
+ /* Will only fail if underlying malloc() fails (ENOMEM). */
+ if (!(result = strdup (string)))
+ xlog_err ("strdup failed");
+
+ return (result);
+}
diff --git a/utils/statd/monitor.c b/utils/statd/monitor.c
new file mode 100644
index 0000000..c76589c
--- /dev/null
+++ b/utils/statd/monitor.c
@@ -0,0 +1,399 @@
+/*
+ * Copyright (C) 1995-1999 Jeffrey A. Uphoff
+ * Major rewrite by Olaf Kirch, Dec. 1996.
+ * Modified by H.J. Lu, 1998.
+ * Tighter access control, Olaf Kirch June 1999.
+ *
+ * NSM for Linux.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <fcntl.h>
+#include <limits.h>
+#include <netdb.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <arpa/inet.h>
+#include <dirent.h>
+
+#include "sockaddr.h"
+#include "rpcmisc.h"
+#include "nsm.h"
+#include "statd.h"
+#include "notlist.h"
+#include "ha-callout.h"
+
+notify_list * rtnl = NULL; /* Run-time notify list. */
+
+/*
+ * Reject requests from non-loopback addresses in order
+ * to prevent attack described in CERT CA-99.05.
+ *
+ * Although the kernel contacts the statd service via only IPv4
+ * transports, the statd service can receive other requests, such
+ * as SM_NOTIFY, from remote peers via IPv6.
+ */
+static _Bool
+caller_is_localhost(struct svc_req *rqstp)
+{
+ struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt);
+ char buf[INET6_ADDRSTRLEN];
+
+ if (!nfs_is_v4_loopback(sap))
+ goto out_nonlocal;
+ return true;
+
+out_nonlocal:
+ if (!statd_present_address(sap, buf, sizeof(buf)))
+ buf[0] = '\0';
+ xlog_warn("SM_MON/SM_UNMON call from non-local host %s", buf);
+ return false;
+}
+
+/*
+ * Services SM_MON requests.
+ */
+struct sm_stat_res *
+sm_mon_1_svc(struct mon *argp, struct svc_req *rqstp)
+{
+ static sm_stat_res result;
+ char *mon_name = argp->mon_id.mon_name,
+ *my_name = argp->mon_id.my_id.my_name;
+ struct my_id *id = &argp->mon_id.my_id;
+ char *cp;
+ notify_list *clnt = NULL;
+ struct sockaddr_in my_addr = {
+ .sin_family = AF_INET,
+ .sin_addr.s_addr = htonl(INADDR_LOOPBACK),
+ };
+ char *dnsname = NULL;
+ int existing = 0;
+
+ xlog(D_CALL, "Received SM_MON for %s from %s", mon_name, my_name);
+
+ /* Assume that we'll fail. */
+ result.res_stat = STAT_FAIL;
+ result.state = -1; /* State is undefined for STAT_FAIL. */
+
+ /* 1. Reject any remote callers.
+ * Ignore the my_name specified by the caller, and
+ * use "127.0.0.1" instead.
+ */
+ if (!caller_is_localhost(rqstp))
+ goto failure;
+
+ /* 2. Reject any registrations for non-lockd services.
+ *
+ * This is specific to the linux kernel lockd, which
+ * makes the callback procedure part of the lockd interface.
+ * It is also prone to break when lockd changes its callback
+ * procedure number -- which, in fact, has now happened once.
+ * There must be a better way.... XXX FIXME
+ */
+ if (id->my_prog != 100021 ||
+ (id->my_proc != 16 && id->my_proc != 24))
+ {
+ xlog_warn("Attempt to register callback to %d/%d",
+ id->my_prog, id->my_proc);
+ goto failure;
+ }
+
+ /*
+ * Check hostnames. If I can't look them up, I won't monitor. This
+ * might not be legal, but it adds a little bit of safety and sanity.
+ */
+
+ /* must check for /'s in hostname! See CERT's CA-96.09 for details. */
+ if (strchr(mon_name, '/') || mon_name[0] == '.') {
+ xlog(L_ERROR, "SM_MON request for hostname containing '/' "
+ "or starting '.': %s", mon_name);
+ xlog(L_ERROR, "POSSIBLE SPOOF/ATTACK ATTEMPT!");
+ goto failure;
+ }
+
+ /* my_name must not have white space */
+ for (cp=my_name ; *cp ; cp++)
+ if (*cp == ' ' || *cp == '\t' || *cp == '\r' || *cp == '\n')
+ *cp = '_';
+
+ /*
+ * Hostnames checked OK.
+ * Now choose a hostname to use for matching. We cannot
+ * really trust much in the incoming NOTIFY, so to make
+ * sure that multi-homed hosts work nicely, we get an
+ * FQDN now, and use that for matching.
+ */
+ dnsname = statd_canonical_name(mon_name);
+ if (dnsname == NULL) {
+ xlog(L_WARNING, "No canonical hostname found for %s", mon_name);
+ goto failure;
+ }
+
+ /* Now check to see if this is a duplicate, and warn if so.
+ * I will also return STAT_FAIL. (I *think* this is how I should
+ * handle it.)
+ *
+ * Olaf requests that I allow duplicate SM_MON requests for
+ * hosts due to the way he is coding lockd. No problem,
+ * I'll just do a quickie success return and things should
+ * be happy.
+ */
+ clnt = rtnl;
+
+ while ((clnt = nlist_gethost(clnt, mon_name, 0))) {
+ if (statd_matchhostname(NL_MY_NAME(clnt), my_name) &&
+ NL_MY_PROC(clnt) == id->my_proc &&
+ NL_MY_PROG(clnt) == id->my_prog &&
+ NL_MY_VERS(clnt) == id->my_vers) {
+ if (memcmp(NL_PRIV(clnt), argp->priv, SM_PRIV_SIZE)) {
+ xlog(D_GENERAL,
+ "Received SM_MON request with new "
+ "cookie for %s from procedure on %s",
+ mon_name, my_name);
+
+ existing = 1;
+ break;
+ } else {
+ /* Hey! We already know you guys! */
+ xlog(D_GENERAL,
+ "Duplicate SM_MON request for %s "
+ "from procedure on %s",
+ mon_name, my_name);
+
+ /* But we'll let you pass anyway. */
+ free(dnsname);
+ goto success;
+ }
+ }
+ clnt = NL_NEXT(clnt);
+ }
+
+ /*
+ * We're committed...ignoring errors. Let's hope that a malloc()
+ * doesn't fail. (I should probably fix this assumption.)
+ */
+ if (!existing && !(clnt = nlist_new(my_name, mon_name, 0))) {
+ free(dnsname);
+ xlog_warn("out of memory");
+ goto failure;
+ }
+
+ NL_MY_PROG(clnt) = id->my_prog;
+ NL_MY_VERS(clnt) = id->my_vers;
+ NL_MY_PROC(clnt) = id->my_proc;
+ memcpy(NL_PRIV(clnt), argp->priv, SM_PRIV_SIZE);
+ clnt->dns_name = dnsname;
+
+ /*
+ * Now, Create file on stable storage for host, first deleting any
+ * existing records on file.
+ */
+ nsm_delete_monitored_host(dnsname, mon_name, my_name, 0);
+
+ if (!nsm_insert_monitored_host(dnsname,
+ (struct sockaddr *)(char *)&my_addr, argp)) {
+ nlist_free(existing ? &rtnl : NULL, clnt);
+ goto failure;
+ }
+
+ /* PRC: do the HA callout: */
+ ha_callout("add-client", mon_name, my_name, -1);
+ if (!existing)
+ nlist_insert(&rtnl, clnt);
+ xlog(D_GENERAL, "MONITORING %s for %s", mon_name, my_name);
+ success:
+ result.res_stat = STAT_SUCC;
+ /* SUN's sm_inter.x says this should be "state number of local site".
+ * X/Open says '"state" will be contain the state of the remote NSM.'
+ * href=http://www.opengroup.org/onlinepubs/9629799/SM_MON.htm
+ * Linux lockd currently (2.6.21 and prior) ignores whatever is
+ * returned, and given the above contraction, it probably always will..
+ * So we just return what we always returned. If possible, we
+ * have already told lockd about our state number via a sysctl.
+ * If lockd wants the remote state, it will need to
+ * use SM_STAT (and prayer).
+ */
+ result.state = MY_STATE;
+ return (&result);
+
+failure:
+ xlog_warn("STAT_FAIL to %s for SM_MON of %s", my_name, mon_name);
+ free(clnt);
+ return (&result);
+}
+
+static unsigned int
+load_one_host(const char *hostname,
+ __attribute__ ((unused)) const struct sockaddr *sap,
+ const struct mon *m,
+ __attribute__ ((unused)) const time_t timestamp)
+{
+ notify_list *clnt;
+
+ clnt = nlist_new(m->mon_id.my_id.my_name,
+ m->mon_id.mon_name, 0);
+ if (clnt == NULL)
+ return 0;
+
+ clnt->dns_name = strdup(hostname);
+ if (clnt->dns_name == NULL) {
+ nlist_free(NULL, clnt);
+ free(clnt);
+ return 0;
+ }
+
+ xlog(D_GENERAL, "Adding record for %s to the monitor list...",
+ hostname);
+
+ NL_MY_PROG(clnt) = m->mon_id.my_id.my_prog;
+ NL_MY_VERS(clnt) = m->mon_id.my_id.my_vers;
+ NL_MY_PROC(clnt) = m->mon_id.my_id.my_proc;
+ memcpy(NL_PRIV(clnt), m->priv, SM_PRIV_SIZE);
+
+ nlist_insert(&rtnl, clnt);
+ return 1;
+}
+
+void load_state(void)
+{
+ unsigned int count;
+
+ count = nsm_load_monitor_list(load_one_host);
+ if (count)
+ xlog(D_GENERAL, "Loaded %u previously monitored hosts", count);
+}
+
+/*
+ * Services SM_UNMON requests.
+ *
+ * There is no statement in the X/Open spec's about returning an error
+ * for requests to unmonitor a host that we're *not* monitoring. I just
+ * return the state of the NSM when I get such foolish requests for lack
+ * of any better ideas. (I also log the "offense.")
+ */
+struct sm_stat *
+sm_unmon_1_svc(struct mon_id *argp, struct svc_req *rqstp)
+{
+ static sm_stat result;
+ notify_list *clnt;
+ char *mon_name = argp->mon_name,
+ *my_name = argp->my_id.my_name;
+ struct my_id *id = &argp->my_id;
+ char *cp;
+
+ xlog(D_CALL, "Received SM_UNMON for %s from %s", mon_name, my_name);
+
+ result.state = MY_STATE;
+
+ if (!caller_is_localhost(rqstp))
+ goto failure;
+
+ /* my_name must not have white space */
+ for (cp=my_name ; *cp ; cp++)
+ if (*cp == ' ' || *cp == '\t' || *cp == '\r' || *cp == '\n')
+ *cp = '_';
+
+
+ /* Check if we're monitoring anyone. */
+ if (rtnl == NULL) {
+ xlog_warn("Received SM_UNMON request from %s for %s while not "
+ "monitoring any hosts", my_name, argp->mon_name);
+ return (&result);
+ }
+ clnt = rtnl;
+
+ /*
+ * OK, we are. Now look for appropriate entry in run-time list.
+ * There should only be *one* match on this, since I block "duplicate"
+ * SM_MON calls. (Actually, duplicate calls are allowed, but only one
+ * entry winds up in the list the way I'm currently handling them.)
+ */
+ while ((clnt = nlist_gethost(clnt, mon_name, 0))) {
+ if (statd_matchhostname(NL_MY_NAME(clnt), my_name) &&
+ NL_MY_PROC(clnt) == id->my_proc &&
+ NL_MY_PROG(clnt) == id->my_prog &&
+ NL_MY_VERS(clnt) == id->my_vers) {
+ /* Match! */
+ xlog(D_GENERAL, "UNMONITORING %s for %s",
+ mon_name, my_name);
+
+ /* PRC: do the HA callout: */
+ ha_callout("del-client", mon_name, my_name, -1);
+
+ nsm_delete_monitored_host(clnt->dns_name,
+ mon_name, my_name, 1);
+ nlist_free(&rtnl, clnt);
+
+ return (&result);
+ } else
+ clnt = NL_NEXT(clnt);
+ }
+
+ failure:
+ xlog_warn("Received erroneous SM_UNMON request from %s for %s",
+ my_name, mon_name);
+ return (&result);
+}
+
+
+struct sm_stat *
+sm_unmon_all_1_svc(struct my_id *argp, struct svc_req *rqstp)
+{
+ short int count = 0;
+ static sm_stat result;
+ notify_list *clnt;
+ char *my_name = argp->my_name;
+
+ xlog(D_CALL, "Received SM_UNMON_ALL for %s", my_name);
+
+ if (!caller_is_localhost(rqstp))
+ goto failure;
+
+ result.state = MY_STATE;
+
+ if (rtnl == NULL) {
+ xlog_warn("Received SM_UNMON_ALL request from %s "
+ "while not monitoring any hosts", my_name);
+ return (&result);
+ }
+ clnt = rtnl;
+
+ while ((clnt = nlist_gethost(clnt, my_name, 1))) {
+ if (NL_MY_PROC(clnt) == argp->my_proc &&
+ NL_MY_PROG(clnt) == argp->my_prog &&
+ NL_MY_VERS(clnt) == argp->my_vers) {
+ /* Watch stack! */
+ char mon_name[SM_MAXSTRLEN + 1];
+ notify_list *temp;
+
+ xlog(D_GENERAL,
+ "UNMONITORING (SM_UNMON_ALL) %s for %s",
+ NL_MON_NAME(clnt), NL_MY_NAME(clnt));
+ strncpy(mon_name, NL_MON_NAME(clnt),
+ sizeof (mon_name) - 1);
+ mon_name[sizeof (mon_name) - 1] = '\0';
+ temp = NL_NEXT(clnt);
+ /* PRC: do the HA callout: */
+ ha_callout("del-client", mon_name, my_name, -1);
+ nsm_delete_monitored_host(clnt->dns_name,
+ mon_name, my_name, 1);
+ nlist_free(&rtnl, clnt);
+ ++count;
+ clnt = temp;
+ } else
+ clnt = NL_NEXT(clnt);
+ }
+
+ if (!count) {
+ xlog(D_GENERAL, "SM_UNMON_ALL request from %s with no "
+ "SM_MON requests from it", my_name);
+ }
+
+ failure:
+ return (&result);
+}
diff --git a/utils/statd/notlist.c b/utils/statd/notlist.c
new file mode 100644
index 0000000..45879a4
--- /dev/null
+++ b/utils/statd/notlist.c
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 1995, 1997-1999 Jeffrey A. Uphoff
+ * Modified by Olaf Kirch, 1996.
+ * Modified by H.J. Lu, 1998.
+ * Modified by Lon Hohberger, Oct. 2000.
+ * - Fixed memory leaks, run-off-end problems, etc.
+ *
+ * NSM for Linux.
+ */
+
+/*
+ * Simple list management for notify list
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include "statd.h"
+#include "notlist.h"
+
+
+#ifdef DEBUG
+/*
+ * LH - The linked list code had some bugs. Used this to help debug
+ * new code.
+ */
+static void
+plist(notify_list *head, int en)
+{
+ /* case where we ran off the end */
+ if (!head) return;
+
+ printf("Entry %d: %s\n",en, NL_MON_NAME(head));
+ plist(head->next, ++en);
+}
+
+static void
+nlist_print(notify_list **head)
+{
+ printf("--- Begin notify list dump ---\n");
+ plist(*head,1);
+ printf("--- End notify list dump ---\n");
+}
+#endif /* DEBUG */
+
+/*
+ * Allocate memory and set up a new notify list entry.
+ */
+notify_list *
+nlist_new(char *my_name, char *mon_name, int state)
+{
+ notify_list *new;
+
+ new = (notify_list *) xmalloc(sizeof(notify_list));
+ memset(new, 0, sizeof(*new));
+
+ NL_TIMES(new) = MAX_TRIES;
+ NL_STATE(new) = state;
+ NL_MY_NAME(new) = xstrdup(my_name);
+ NL_MON_NAME(new) = xstrdup(mon_name);
+
+ return new;
+}
+
+/*
+ * Insert *entry into a notify list at the point specified by
+ * **head. This can be in the middle. However, we do not handle
+ * list _append_ in this function; rather, the only place we should
+ * have to worry about this case is in nlist_insert_timer below.
+ * - entry must not be NULL.
+ */
+void
+nlist_insert(notify_list **head, notify_list *entry)
+{
+ if (*head) {
+ /*
+ * Cases where we're prepending a non-empty list
+ * or inserting possibly in the middle somewhere (eg,
+ * nlist_insert_timer...)
+ */
+ entry->next = (*head); /* Forward pointer */
+ entry->prev = (*head)->prev; /* Back pointer */
+ (*head)->prev = entry; /* head's new back pointer */
+ }
+
+ /* Common to all cases, including new list creation */
+ *head = entry; /* New head */
+
+#ifdef DEBUG
+ nlist_print(head);
+#endif
+}
+
+/*
+ * (re)insert *entry into notify_list **head. This requires that
+ * NL_WHEN(entry) has been set (usually, this is time() + 5 seconds).
+ * - entry must not be NULL
+ *
+ * LH - This used to cause (a) a memory leak and (b) dropped notify-list
+ * entries. The pointer ran off the end of the list, and changed the
+ * head-end to point to the new, one-entry list. All other entries became garbage.
+ *
+ * FIXME: Optimize this function. (I'll work on it - LH)
+ */
+void
+nlist_insert_timer(notify_list **head, notify_list *entry)
+{
+ notify_list *spot = *head, /* Insertion location */
+ /* ...Start at head */
+ *back = NULL; /* Back pointer */
+
+
+ /* Find first entry with higher timeout value or end of list */
+ while (spot && NL_WHEN(spot) <= NL_WHEN(entry)) {
+ /*
+ * Keep the back pointer in case we
+ * run off the end... (see below)
+ */
+ back = spot;
+ spot = spot->next;
+ }
+
+ if (spot == (*head)) {
+ /*
+ * case where we're prepending an empty or non-empty
+ * list or inserting in the middle somewhere. Pass
+ * the real head of the list, since we'll be changing
+ * during the insert...
+ */
+ nlist_insert(head, entry);
+ } else {
+ /* all other cases - don't move the real head pointer */
+ nlist_insert(&spot, entry);
+
+ /*
+ * If spot == entry, then spot was NULL when we called
+ * nlist_insert. This happened because we had run off
+ * the end of the list. Append entry to original list.
+ */
+ if (spot == entry) {
+ back->next = entry;
+ entry->prev = back;
+ }
+ }
+}
+
+/*
+ * Remove *entry from the list pointed to by **head.
+ * Do not destroy *entry. This is normally done before
+ * a re-insertion with a timer, but can be done anywhere.
+ * - entry must not be NULL.
+ */
+void
+nlist_remove(notify_list **head, notify_list *entry)
+{
+ notify_list *prev = entry->prev,
+ *next = entry->next;
+
+ if (next) {
+ next->prev = prev;
+ }
+
+ if (prev) {
+ /* Case(s) where entry isn't at the front */
+ prev->next = next;
+ } else {
+ /* cases where entry is at the front */
+ *head = next;
+ }
+
+ entry->next = entry->prev = NULL;
+#ifdef DEBUG
+ nlist_print(head);
+#endif
+}
+
+/*
+ * Clone an entry in the notify list -
+ * - entry must not be NULL
+ */
+notify_list *
+nlist_clone(notify_list *entry)
+{
+ notify_list *new;
+
+ new = nlist_new(NL_MY_NAME(entry), NL_MON_NAME(entry), NL_STATE(entry));
+ NL_MY_PROG(new) = NL_MY_PROG(entry);
+ NL_MY_VERS(new) = NL_MY_VERS(entry);
+ NL_MY_PROC(new) = NL_MY_PROC(entry);
+ memcpy(NL_PRIV(new), NL_PRIV(entry), SM_PRIV_SIZE);
+
+ return new;
+}
+
+/*
+ * Destroy an entry in a notify list and free the memory.
+ * If *head is NULL, just free the entry. This would be
+ * done only when we know entry isn't in any list.
+ * - entry must not be NULL.
+ */
+void
+nlist_free(notify_list **head, notify_list *entry)
+{
+ if (head && (*head))
+ nlist_remove(head, entry);
+ if (NL_MY_NAME(entry))
+ free(NL_MY_NAME(entry));
+ if (NL_MON_NAME(entry))
+ free(NL_MON_NAME(entry));
+ free(entry->dns_name);
+}
+
+/*
+ * Destroy an entire notify list
+ */
+void
+nlist_kill(notify_list **head)
+{
+ notify_list *next;
+
+ while (*head) {
+ next = (*head)->next;
+ nlist_free(head, *head);
+ free(*head);
+ *head = next;
+ }
+}
+
+/*
+ * Walk a list looking for a matching name in the NL_MON_NAME field.
+ */
+notify_list *
+nlist_gethost(notify_list *list, char *host, int myname)
+{
+ notify_list *lp;
+
+ for (lp = list; lp; lp = lp->next) {
+ if (statd_matchhostname(host,
+ myname? NL_MY_NAME(lp) : NL_MON_NAME(lp)))
+ return lp;
+ }
+
+ return (notify_list *) NULL;
+}
diff --git a/utils/statd/notlist.h b/utils/statd/notlist.h
new file mode 100644
index 0000000..6ed0da8
--- /dev/null
+++ b/utils/statd/notlist.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 1995, 1997-1999, 2002 Jeffrey A. Uphoff
+ * Major rewrite by Olaf Kirch, Dec. 1996.
+ *
+ * NSM for Linux.
+ */
+
+#include <netinet/in.h>
+
+/*
+ * Primary information structure.
+ */
+struct notify_list {
+ mon mon; /* Big honkin' NSM structure. */
+ in_port_t port; /* port number for callback */
+ short int times; /* Counter used for various things. */
+ int state; /* For storing notified state for callbacks. */
+ char *dns_name; /* used for matching incoming
+ * NOTIFY requests */
+ struct notify_list *next; /* Linked list forward pointer. */
+ struct notify_list *prev; /* Linked list backward pointer. */
+ uint32_t xid; /* XID of MS_NOTIFY RPC call */
+ time_t when; /* notify: timeout for re-xmit */
+};
+
+typedef struct notify_list notify_list;
+
+/*
+ * Global Variables
+ */
+extern notify_list * rtnl; /* Run-time notify list */
+extern notify_list * notify; /* Pending RPC calls */
+
+/*
+ * List-handling functions
+ */
+extern notify_list * nlist_new(char *, char *, int);
+extern void nlist_insert(notify_list **, notify_list *);
+extern void nlist_remove(notify_list **, notify_list *);
+extern void nlist_insert_timer(notify_list **, notify_list *);
+extern notify_list * nlist_clone(notify_list *);
+extern void nlist_free(notify_list **, notify_list *);
+extern void nlist_kill(notify_list **);
+extern notify_list * nlist_gethost(notify_list *, char *, int);
+
+/*
+ * List-handling macros.
+ * THESE INHERIT INFORMATION FROM PREVIOUSLY-DEFINED MACROS.
+ * (So don't change their order unless you study them first!)
+ */
+#define NL_NEXT(L) ((L)->next)
+#define NL_FIRST NL_NEXT
+#define NL_PREV(L) ((L)->prev)
+#define NL_DATA(L) ((L)->mon)
+#define NL_STATE(L) ((L)->state)
+#define NL_TIMES(L) ((L)->times)
+#define NL_MON_ID(L) (NL_DATA((L)).mon_id)
+#define NL_PRIV(L) (NL_DATA((L)).priv)
+#define NL_MON_NAME(L) (NL_MON_ID((L)).mon_name)
+#define NL_MY_ID(L) (NL_MON_ID((L)).my_id)
+#define NL_MY_NAME(L) (NL_MY_ID((L)).my_name)
+#define NL_MY_PROC(L) (NL_MY_ID((L)).my_proc)
+#define NL_MY_PROG(L) (NL_MY_ID((L)).my_prog)
+#define NL_MY_VERS(L) (NL_MY_ID((L)).my_vers)
+#define NL_WHEN(L) ((L)->when)
diff --git a/utils/statd/rmtcall.c b/utils/statd/rmtcall.c
new file mode 100644
index 0000000..5b26148
--- /dev/null
+++ b/utils/statd/rmtcall.c
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) 1996, 1999 Olaf Kirch
+ * Modified by Jeffrey A. Uphoff, 1997-1999.
+ * Modified by H.J. Lu, 1998.
+ * Modified by Lon Hohberger, Oct. 2000
+ * - Bugfix handling client responses.
+ * - Paranoia on NOTIFY_CALLBACK case
+ *
+ * NSM for Linux.
+ */
+
+/*
+ * After reboot, notify all hosts on our notify list. In order not to
+ * hang statd with delivery to dead hosts, we perform all RPC calls in
+ * parallel.
+ *
+ * It would have been nice to use the portmapper's rmtcall feature,
+ * but that's not possible for security reasons (the portmapper would
+ * have to forward the call with root privs for most statd's, which
+ * it won't if it's worth its money).
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#include <net/if.h>
+#include <arpa/inet.h>
+#include <rpc/rpc.h>
+#include <rpc/pmap_prot.h>
+#include <rpc/pmap_rmt.h>
+#include <time.h>
+#include <netdb.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "sm_inter.h"
+#include "statd.h"
+#include "notlist.h"
+#include "ha-callout.h"
+
+#include "nsm.h"
+#include "nfsrpc.h"
+
+#if SIZEOF_SOCKLEN_T - 0 == 0
+#define socklen_t int
+#endif
+
+static int sockfd = -1; /* notify socket */
+
+/* How many times to try looking for an unused privileged port */
+#define MAX_BRP_RETRIES 100
+
+/*
+ * Initialize socket used to notify lockd of peer reboots.
+ *
+ * Returns the file descriptor of the new socket if successful;
+ * otherwise returns -1 and logs an error.
+ *
+ * Lockd rejects such requests if the source port is not privileged.
+ * statd_get_socket() must be invoked while statd still holds root
+ * privileges in order for the socket to acquire a privileged source
+ * port.
+ */
+int
+statd_get_socket(void)
+{
+ struct sockaddr_in sin;
+ struct servent *se;
+ static int prevsocks[MAX_BRP_RETRIES];
+ unsigned int retries;
+
+ if (sockfd >= 0)
+ return sockfd;
+
+ retries = 0;
+ do {
+ if ((sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
+ xlog(L_ERROR, "%s: Can't create socket: %m", __func__);
+ break;
+ }
+
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ if (bindresvport(sockfd, &sin) < 0) {
+ xlog(D_GENERAL, "%s: can't bind to reserved port",
+ __func__);
+ break;
+ }
+ se = getservbyport(sin.sin_port, "udp");
+ if (se == NULL)
+ break;
+
+ if (retries == MAX_BRP_RETRIES) {
+ xlog(D_GENERAL, "%s: No unused privileged ports",
+ __func__);
+ break;
+ }
+
+ /* rather not use that port, try again */
+ prevsocks[retries++] = sockfd;
+ } while (1);
+
+ while (retries)
+ close(prevsocks[--retries]);
+
+ if (sockfd < 0)
+ return -1;
+
+ return sockfd;
+}
+
+static notify_list *
+recv_rply(u_long *portp)
+{
+ char msgbuf[NSM_MAXMSGSIZE];
+ ssize_t msglen;
+ notify_list *lp = NULL;
+ XDR xdr;
+ struct sockaddr_in sin;
+ socklen_t alen = (socklen_t)sizeof(sin);
+ uint32_t xid;
+
+ memset(msgbuf, 0, sizeof(msgbuf));
+ msglen = recvfrom(sockfd, msgbuf, sizeof(msgbuf), 0,
+ (struct sockaddr *)(char *)&sin, &alen);
+ if (msglen == (ssize_t)-1) {
+ xlog_warn("%s: recvfrom failed: %m", __func__);
+ return NULL;
+ }
+
+ memset(&xdr, 0, sizeof(xdr));
+ xdrmem_create(&xdr, msgbuf, (unsigned int)msglen, XDR_DECODE);
+ xid = nsm_parse_reply(&xdr);
+ if (xid == 0)
+ goto done;
+ if (sin.sin_addr.s_addr != htonl(INADDR_LOOPBACK)) {
+ struct in_addr addr = sin.sin_addr;
+ char buf[INET_ADDRSTRLEN];
+
+ xlog_warn("%s: Unrecognized reply from %s", __func__,
+ inet_ntop(AF_INET, &addr, buf,
+ (socklen_t)sizeof(buf)));
+ goto done;
+ }
+
+ for (lp = notify; lp != NULL; lp = lp->next) {
+ /* LH - this was a bug... it should have been checking
+ * the xid from the response message from the client,
+ * not the static, internal xid */
+ if (lp->xid != xid)
+ continue;
+ if (lp->port == 0)
+ *portp = nsm_recv_getport(&xdr);
+ break;
+ }
+
+done:
+ xdr_destroy(&xdr);
+ return lp;
+}
+
+/*
+ * Notify operation for a single list entry
+ */
+static int
+process_entry(notify_list *lp)
+{
+ struct sockaddr_in sin;
+
+ if (NL_TIMES(lp) == 0) {
+ xlog(D_GENERAL, "%s: Cannot notify localhost, giving up",
+ __func__);
+ return 0;
+ }
+
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_port = lp->port;
+ /* LH - moved address into switch */
+
+ /* __FORCE__ loopback for callbacks to lockd ... */
+ /* Just in case we somehow ignored it thus far */
+ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ if (sin.sin_port == 0)
+ lp->xid = nsm_xmit_getport(sockfd, &sin,
+ (rpcprog_t)NL_MY_PROG(lp),
+ (rpcvers_t)NL_MY_VERS(lp));
+ else {
+ struct mon m;
+
+ memcpy(m.priv, NL_PRIV(lp), SM_PRIV_SIZE);
+
+ m.mon_id.mon_name = NL_MON_NAME(lp);
+ m.mon_id.my_id.my_name = NULL;
+ m.mon_id.my_id.my_prog = NL_MY_PROG(lp);
+ m.mon_id.my_id.my_vers = NL_MY_VERS(lp);
+ m.mon_id.my_id.my_proc = NL_MY_PROC(lp);
+
+ lp->xid = nsm_xmit_nlmcall(sockfd,
+ (struct sockaddr *)(char *)&sin,
+ (socklen_t)sizeof(sin), &m, NL_STATE(lp));
+ }
+ if (lp->xid == 0) {
+ xlog_warn("%s: failed to notify port %d",
+ __func__, ntohs(lp->port));
+ }
+ NL_TIMES(lp) -= 1;
+
+ return 1;
+}
+
+/*
+ * Process a datagram received on the notify socket
+ */
+int
+process_reply(FD_SET_TYPE *rfds)
+{
+ notify_list *lp;
+ u_long port;
+
+ if (sockfd == -1 || !FD_ISSET(sockfd, rfds))
+ return 0;
+
+ /* Should not be processed again. */
+ FD_CLR (sockfd, rfds);
+
+ if (!(lp = recv_rply(&port)))
+ return 1;
+
+ if (lp->port == 0) {
+ if (port != 0) {
+ lp->port = htons((unsigned short) port);
+ process_entry(lp);
+ NL_WHEN(lp) = time(NULL) + NOTIFY_TIMEOUT;
+ nlist_remove(&notify, lp);
+ nlist_insert_timer(&notify, lp);
+ return 1;
+ }
+ xlog_warn("%s: service %d not registered on localhost",
+ __func__, NL_MY_PROG(lp));
+ } else {
+ xlog(D_GENERAL, "%s: Callback to %s (for %s) succeeded",
+ __func__, NL_MY_NAME(lp), NL_MON_NAME(lp));
+ }
+ nlist_free(&notify, lp);
+ return 1;
+}
+
+/*
+ * Process a notify list, either for notifying remote hosts after reboot
+ * or for calling back (local) statd clients when the remote has notified
+ * us of a crash.
+ */
+int
+process_notify_list(void)
+{
+ notify_list *entry;
+ time_t now;
+
+ while ((entry = notify) != NULL && NL_WHEN(entry) < time(&now)) {
+ if (process_entry(entry)) {
+ NL_WHEN(entry) = time(NULL) + NOTIFY_TIMEOUT;
+ nlist_remove(&notify, entry);
+ nlist_insert_timer(&notify, entry);
+ } else {
+ xlog(L_ERROR,
+ "%s: Can't callback %s (%d,%d), giving up",
+ __func__,
+ NL_MY_NAME(entry),
+ NL_MY_PROG(entry),
+ NL_MY_VERS(entry));
+ nlist_free(&notify, entry);
+ }
+ }
+
+ return 1;
+}
diff --git a/utils/statd/sim_sm_inter.x b/utils/statd/sim_sm_inter.x
new file mode 100644
index 0000000..4346199
--- /dev/null
+++ b/utils/statd/sim_sm_inter.x
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 1995, 1997-1999 Jeffrey A. Uphoff
+ * Modified by Olaf Kirch, 1996.
+ * Modified by H.J. Lu, 1998.
+ *
+ * NSM for Linux.
+ */
+
+#ifdef RPC_CLNT
+%#include <string.h>
+#endif
+
+program SIM_SM_PROG {
+ version SIM_SM_VERS {
+ void SIM_SM_MON(struct status) = 1;
+ } = 1;
+} = 200048;
+
+const SM_MAXSTRLEN = 1024;
+const SM_PRIV_SIZE = 16;
+
+/*
+ * structure of the status message sent back by the status monitor
+ * when monitor site status changes
+ */
+%#ifndef SM_INTER_X
+struct status {
+ string mon_name<SM_MAXSTRLEN>;
+ int state;
+ opaque priv[SM_PRIV_SIZE]; /* stored private information */
+};
+%#endif /* SM_INTER_X */
diff --git a/utils/statd/simu.c b/utils/statd/simu.c
new file mode 100644
index 0000000..f1d0bf8
--- /dev/null
+++ b/utils/statd/simu.c
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 1995, 1997-1999 Jeffrey A. Uphoff
+ *
+ * NSM for Linux.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <netdb.h>
+#include <arpa/inet.h>
+
+#include "sockaddr.h"
+#include "rpcmisc.h"
+#include "statd.h"
+#include "notlist.h"
+
+extern void my_svc_exit (void);
+
+
+/*
+ * Services SM_SIMU_CRASH requests.
+ *
+ * Although the kernel contacts the statd service via only IPv4
+ * transports, the statd service can receive other requests, such
+ * as SM_NOTIFY, from remote peers via IPv6.
+ */
+void *
+sm_simu_crash_1_svc (__attribute__ ((unused)) void *argp, struct svc_req *rqstp)
+{
+ struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt);
+ char buf[INET6_ADDRSTRLEN];
+ static char *result = NULL;
+
+ xlog(D_CALL, "Received SM_SIMU_CRASH");
+
+ if (!nfs_is_v4_loopback(sap))
+ goto out_nonlocal;
+
+ if ((int)nfs_get_port(sap) >= IPPORT_RESERVED) {
+ xlog_warn("SM_SIMU_CRASH call from unprivileged port");
+ goto failure;
+ }
+
+ my_svc_exit ();
+
+ if (rtnl)
+ nlist_kill (&rtnl);
+
+ failure:
+ return ((void *)&result);
+
+ out_nonlocal:
+ if (!statd_present_address(sap, buf, sizeof(buf)))
+ buf[0] = '\0';
+ xlog_warn("SM_SIMU_CRASH call from non-local host %s", buf);
+ goto failure;
+}
diff --git a/utils/statd/simulate.c b/utils/statd/simulate.c
new file mode 100644
index 0000000..4ed1468
--- /dev/null
+++ b/utils/statd/simulate.c
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 1995-1997, 1999 Jeffrey A. Uphoff
+ *
+ * NSM for Linux.
+ */
+
+#include "config.h"
+#ifndef SIMULATIONS
+# error How the hell did we get here?
+#endif
+
+/* If we're running the simulator, we're debugging. Pretty simple. */
+#ifndef DEBUG
+# define DEBUG
+#endif
+
+#include <signal.h>
+#include <string.h>
+#include <rpc/rpc.h>
+#include <rpc/pmap_clnt.h>
+#include <rpcmisc.h>
+#include "statd.h"
+#include "sim_sm_inter.h"
+
+static void daemon_simulator (void);
+static void sim_killer (int sig);
+static void simulate_crash (char *);
+static void simulate_mon (char *, char *, char *, char *, char *);
+static void simulate_stat (char *, char *);
+static void simulate_unmon (char *, char *, char *, char *);
+static void simulate_unmon_all (char *, char *, char *);
+
+static int sim_port = 0;
+
+extern void sim_sm_prog_1 (struct svc_req *, register SVCXPRT);
+extern void svc_exit (void);
+
+void
+simulator (int argc, char **argv)
+{
+ xlog_stderr (1);
+ xlog_syslog (0);
+ xlog_open ("statd simulator");
+
+ if (argc == 2)
+ if (!strcasecmp (*argv, "crash"))
+ simulate_crash (*(&argv[1]));
+
+ if (argc == 3) {
+ if (!strcasecmp (*argv, "stat"))
+ simulate_stat (*(&argv[1]), *(&argv[2]));
+ }
+ if (argc == 4) {
+ if (!strcasecmp (*argv, "unmon_all"))
+ simulate_unmon_all (*(&argv[1]), *(&argv[2]), *(&argv[3]));
+ }
+ if (argc == 5) {
+ if (!strcasecmp (*argv, "unmon"))
+ simulate_unmon (*(&argv[1]), *(&argv[2]), *(&argv[3]), *(&argv[4]));
+ }
+ if (argc == 6) {
+ if (!strcasecmp (*argv, "mon"))
+ simulate_mon (*(&argv[1]), *(&argv[2]), *(&argv[3]), *(&argv[4]),
+ *(&argv[5]));
+ }
+ xlog_err ("WTF? Give me something I can use!");
+}
+
+static void
+simulate_mon (char *calling, char *monitoring, char *as, char *proggy,
+ char *fool)
+{
+ CLIENT *client;
+ sm_stat_res *result;
+ mon mon;
+
+ xlog (D_GENERAL, "Calling %s (as %s) to monitor %s", calling, as,
+ monitoring);
+
+ if ((client = clnt_create (calling, SM_PROG, SM_VERS, "udp")) == NULL)
+ xlog_err ("%s", clnt_spcreateerror ("clnt_create"));
+
+ memcpy (mon.priv, fool, SM_PRIV_SIZE);
+ mon.mon_id.my_id.my_name = xstrdup (as);
+ sim_port = atoi (proggy) * SIM_SM_PROG;
+ mon.mon_id.my_id.my_prog = sim_port; /* Pseudo-dummy */
+ mon.mon_id.my_id.my_vers = SIM_SM_VERS;
+ mon.mon_id.my_id.my_proc = SIM_SM_MON;
+ mon.mon_id.mon_name = monitoring;
+
+ if (!(result = sm_mon_1 (&mon, client)))
+ xlog_err ("%s", clnt_sperror (client, "sm_mon_1"));
+
+ free (mon.mon_id.my_id.my_name);
+
+ if (result->res_stat != STAT_SUCC) {
+ xlog_err ("SM_MON request failed, state: %d", result->state);
+ } else {
+ xlog (D_GENERAL, "SM_MON result successful, state: %d\n", result->state);
+ xlog (D_GENERAL, "Waiting for callback");
+ daemon_simulator ();
+ exit (0);
+ }
+}
+
+static void
+simulate_unmon (char *calling, char *unmonitoring, char *as, char *proggy)
+{
+ CLIENT *client;
+ sm_stat *result;
+ mon_id mon_id;
+
+ xlog (D_GENERAL, "Calling %s (as %s) to unmonitor %s", calling, as,
+ unmonitoring);
+
+ if ((client = clnt_create (calling, SM_PROG, SM_VERS, "udp")) == NULL)
+ xlog_err ("%s", clnt_spcreateerror ("clnt_create"));
+
+ mon_id.my_id.my_name = xstrdup (as);
+ mon_id.my_id.my_prog = atoi (proggy) * SIM_SM_PROG;
+ mon_id.my_id.my_vers = SIM_SM_VERS;
+ mon_id.my_id.my_proc = SIM_SM_MON;
+ mon_id.mon_name = unmonitoring;
+
+ if (!(result = sm_unmon_1 (&mon_id, client)))
+ xlog_err ("%s", clnt_sperror (client, "sm_unmon_1"));
+
+ free (mon_id.my_id.my_name);
+ xlog (D_GENERAL, "SM_UNMON request returned state: %d\n", result->state);
+ exit (0);
+}
+
+static void
+simulate_unmon_all (char *calling, char *as, char *proggy)
+{
+ CLIENT *client;
+ sm_stat *result;
+ my_id my_id;
+
+ xlog (D_GENERAL, "Calling %s (as %s) to unmonitor all hosts", calling, as);
+
+ if ((client = clnt_create (calling, SM_PROG, SM_VERS, "udp")) == NULL)
+ xlog_err ("%s", clnt_spcreateerror ("clnt_create"));
+
+ my_id.my_name = xstrdup (as);
+ my_id.my_prog = atoi (proggy) * SIM_SM_PROG;
+ my_id.my_vers = SIM_SM_VERS;
+ my_id.my_proc = SIM_SM_MON;
+
+ if (!(result = sm_unmon_all_1 (&my_id, client)))
+ xlog_err ("%s", clnt_sperror (client, "sm_unmon_all_1"));
+
+ free (my_id.my_name);
+ xlog (D_GENERAL, "SM_UNMON_ALL request returned state: %d\n", result->state);
+ exit (0);
+}
+
+static void
+simulate_crash (char *host)
+{
+ CLIENT *client;
+
+ if ((client = clnt_create (host, SM_PROG, SM_VERS, "udp")) == NULL)
+ xlog_err ("%s", clnt_spcreateerror ("clnt_create"));
+
+ if (!sm_simu_crash_1 (NULL, client))
+ xlog_err ("%s", clnt_sperror (client, "sm_simu_crash_1"));
+
+ exit (0);
+}
+
+static void
+simulate_stat (char *calling, char *monitoring)
+{
+ CLIENT *client;
+ sm_name checking;
+ sm_stat_res *result;
+
+ if ((client = clnt_create (calling, SM_PROG, SM_VERS, "udp")) == NULL)
+ xlog_err ("%s", clnt_spcreateerror ("clnt_create"));
+
+ checking.mon_name = monitoring;
+
+ if (!(result = sm_stat_1 (&checking, client)))
+ xlog_err ("%s", clnt_sperror (client, "sm_stat_1"));
+
+ if (result->res_stat == STAT_SUCC)
+ xlog (D_GENERAL, "STAT_SUCC from %s for %s, state: %d", calling,
+ monitoring, result->state);
+ else
+ xlog (D_GENERAL, "STAT_FAIL from %s for %s, state: %d", calling,
+ monitoring, result->state);
+
+ exit (0);
+}
+
+static void
+sim_killer (int sig)
+{
+ pmap_unset (sim_port, SIM_SM_VERS);
+ xlog_err ("Simulator caught signal %d, un-registering and exiting", sig);
+}
+
+static void
+daemon_simulator (void)
+{
+ signal (SIGHUP, sim_killer);
+ signal (SIGINT, sim_killer);
+ signal (SIGTERM, sim_killer);
+ pmap_unset (sim_port, SIM_SM_VERS);
+ /* this registers both UDP and TCP services */
+ rpc_init("statd", sim_port, SIM_SM_VERS, sim_sm_prog_1, 0);
+ svc_run ();
+ pmap_unset (sim_port, SIM_SM_VERS);
+}
+
+void *
+sim_sm_mon_1_svc (struct status *argp, struct svc_req *rqstp)
+{
+ static char *result;
+
+ xlog (D_GENERAL, "Recieved state %d for mon_name %s (opaque \"%s\")",
+ argp->state, argp->mon_name, argp->priv);
+ svc_exit ();
+ return ((void *)&result);
+}
diff --git a/utils/statd/sm-notify.c b/utils/statd/sm-notify.c
new file mode 100644
index 0000000..ed82b8f
--- /dev/null
+++ b/utils/statd/sm-notify.c
@@ -0,0 +1,928 @@
+/*
+ * Send NSM notify calls to all hosts listed in /var/lib/sm
+ *
+ * Copyright (C) 2004-2006 Olaf Kirch <okir@suse.de>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <err.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <poll.h>
+#include <sys/param.h>
+#include <sys/syslog.h>
+#include <arpa/inet.h>
+#include <dirent.h>
+#include <time.h>
+#include <stdio.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdarg.h>
+#include <netdb.h>
+#include <errno.h>
+#include <grp.h>
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <resolv.h>
+
+#include "conffile.h"
+#include "sockaddr.h"
+#include "xlog.h"
+#include "nsm.h"
+#include "nfslib.h"
+#include "nfsrpc.h"
+
+/* glibc before 2.3.4 */
+#ifndef AI_NUMERICSERV
+#define AI_NUMERICSERV 0
+#endif
+
+#define NSM_TIMEOUT 2
+#define NSM_MAX_TIMEOUT 120 /* don't make this too big */
+
+#define NLM_END_GRACE_FILE "/proc/fs/lockd/nlm_end_grace"
+
+int lift_grace = 1;
+int force = 0;
+
+struct nsm_host {
+ struct nsm_host * next;
+ char * name;
+ const char * mon_name;
+ const char * my_name;
+ char * notify_arg;
+ struct addrinfo *ai;
+ time_t last_used;
+ time_t send_next;
+ unsigned int timeout;
+ unsigned int retries;
+ uint32_t xid;
+};
+
+static char nsm_hostname[SM_MAXSTRLEN + 1];
+static int nsm_state;
+static int nsm_family = AF_INET;
+static int opt_debug = 0;
+static _Bool opt_update_state = true;
+static unsigned int opt_max_retry = 15 * 60;
+static char * opt_srcaddr = NULL;
+static char * opt_srcport = NULL;
+
+static void notify(const int sock);
+static int notify_host(int, struct nsm_host *);
+static void recv_reply(int);
+static void insert_host(struct nsm_host *);
+static struct nsm_host *find_host(uint32_t);
+static int record_pid(void);
+
+static struct nsm_host * hosts = NULL;
+
+__attribute__((__malloc__))
+static struct addrinfo *
+smn_lookup(const char *name)
+{
+ struct addrinfo *ai = NULL;
+ struct addrinfo hint = {
+ .ai_family = (nsm_family == AF_INET ? AF_INET: AF_UNSPEC),
+ .ai_protocol = (int)IPPROTO_UDP,
+ };
+ int error;
+
+ res_init();
+ error = getaddrinfo(name, NULL, &hint, &ai);
+ if (error != 0) {
+ xlog(D_GENERAL, "getaddrinfo(3): %s", gai_strerror(error));
+ return NULL;
+ }
+
+ return ai;
+}
+
+#ifdef HAVE_GETNAMEINFO
+static char *
+smn_get_hostname(const struct sockaddr *sap, const socklen_t salen,
+ const char *name)
+{
+ char buf[NI_MAXHOST];
+ int error;
+
+ error = getnameinfo(sap, salen, buf, sizeof(buf), NULL, 0, NI_NAMEREQD);
+ if (error != 0) {
+ xlog(L_ERROR, "my_name '%s' is unusable: %s",
+ name, gai_strerror(error));
+ return NULL;
+ }
+ return strdup(buf);
+}
+#else /* !HAVE_GETNAMEINFO */
+static char *
+smn_get_hostname(const struct sockaddr *sap,
+ __attribute__ ((unused)) const socklen_t salen,
+ const char *name)
+{
+ const struct sockaddr_in *sin = (const struct sockaddr_in *)(char *)sap;
+ const struct in_addr *addr = &sin->sin_addr;
+ struct hostent *hp;
+
+ if (sap->sa_family != AF_INET) {
+ xlog(L_ERROR, "my_name '%s' is unusable: Bad address family",
+ name);
+ return NULL;
+ }
+
+ hp = gethostbyaddr(addr, (socklen_t)sizeof(addr), AF_INET);
+ if (hp == NULL) {
+ xlog(L_ERROR, "my_name '%s' is unusable: %s",
+ name, hstrerror(h_errno));
+ return NULL;
+ }
+ return strdup(hp->h_name);
+}
+#endif /* !HAVE_GETNAMEINFO */
+
+/*
+ * Presentation addresses are converted to their canonical hostnames.
+ * If the IP address does not map to a hostname, it is an error:
+ * we never send a presentation address as the argument of SM_NOTIFY.
+ *
+ * If "name" is not a presentation address, it is left alone. This
+ * allows the administrator some flexibility if DNS isn't configured
+ * exactly how sm-notify prefers it.
+ *
+ * Returns NUL-terminated C string containing the result, or NULL
+ * if the canonical name doesn't exist or cannot be determined.
+ * The caller must free the result with free(3).
+ */
+__attribute__((__malloc__))
+static char *
+smn_verify_my_name(const char *name)
+{
+ struct addrinfo *ai = NULL;
+ struct addrinfo hint = {
+#ifdef IPV6_SUPPORTED
+ .ai_family = AF_UNSPEC,
+#else /* !IPV6_SUPPORTED */
+ .ai_family = AF_INET,
+#endif /* !IPV6_SUPPORTED */
+ .ai_flags = AI_NUMERICHOST,
+ };
+ char *retval;
+ int error;
+
+ error = getaddrinfo(name, NULL, &hint, &ai);
+ switch (error) {
+ case 0:
+ /* @name was a presentation address */
+ retval = smn_get_hostname(ai->ai_addr, ai->ai_addrlen, name);
+ nfs_freeaddrinfo(ai);
+ if (retval == NULL)
+ return NULL;
+ break;
+ case EAI_NONAME:
+ /* @name was not a presentation address */
+ retval = strdup(name);
+ break;
+ default:
+ xlog(L_ERROR, "my_name '%s' is unusable: %s",
+ name, gai_strerror(error));
+ return NULL;
+ }
+
+ xlog(D_GENERAL, "Canonical name for my_name '%s': %s",
+ name, retval);
+ return retval;
+}
+
+__attribute__((__malloc__))
+static struct nsm_host *
+smn_alloc_host(const char *hostname, const char *mon_name,
+ const char *my_name, const time_t timestamp)
+{
+ struct nsm_host *host;
+
+ host = calloc(1, sizeof(*host));
+ if (host == NULL)
+ goto out_nomem;
+
+ /*
+ * mon_name and my_name are preserved so sm-notify can
+ * find the right monitor record to remove when it is
+ * done processing this host.
+ */
+ host->name = strdup(hostname);
+ host->mon_name = (const char *)strdup(mon_name);
+ host->my_name = (const char *)strdup(my_name);
+ host->notify_arg = strdup(opt_srcaddr != NULL ?
+ nsm_hostname : my_name);
+ if (host->name == NULL ||
+ host->mon_name == NULL ||
+ host->my_name == NULL ||
+ host->notify_arg == NULL) {
+ free(host->notify_arg);
+ free((void *)host->my_name);
+ free((void *)host->mon_name);
+ free(host->name);
+ free(host);
+ goto out_nomem;
+ }
+
+ host->last_used = timestamp;
+ host->timeout = NSM_TIMEOUT;
+ host->retries = 100; /* force address retry */
+
+ return host;
+
+out_nomem:
+ xlog_warn("Unable to allocate memory");
+ return NULL;
+}
+
+static void smn_forget_host(struct nsm_host *host)
+{
+ xlog(D_CALL, "Removing %s (%s, %s) from notify list",
+ host->name, host->mon_name, host->my_name);
+
+ nsm_delete_notified_host(host->name, host->mon_name, host->my_name);
+
+ free(host->notify_arg);
+ free((void *)host->my_name);
+ free((void *)host->mon_name);
+ free(host->name);
+ nfs_freeaddrinfo(host->ai);
+
+ free(host);
+}
+
+static unsigned int
+smn_get_host(const char *hostname,
+ __attribute__ ((unused)) const struct sockaddr *sap,
+ const struct mon *m, const time_t timestamp)
+{
+ struct nsm_host *host;
+
+ host = smn_alloc_host(hostname,
+ m->mon_id.mon_name, m->mon_id.my_id.my_name, timestamp);
+ if (host == NULL)
+ return 0;
+
+ insert_host(host);
+ return 1;
+}
+
+#ifdef IPV6_SUPPORTED
+static int smn_socket(void)
+{
+ int sock;
+
+ /*
+ * Use an AF_INET socket if IPv6 is disabled on the
+ * local system.
+ */
+ sock = socket(AF_INET6, SOCK_DGRAM, 0);
+ if (sock == -1) {
+ if (errno != EAFNOSUPPORT) {
+ xlog(L_ERROR, "Failed to create RPC socket: %m");
+ return -1;
+ }
+ sock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock < 0) {
+ xlog(L_ERROR, "Failed to create RPC socket: %m");
+ return -1;
+ }
+ } else
+ nsm_family = AF_INET6;
+
+ if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) {
+ xlog(L_ERROR, "fcntl(3) on RPC socket failed: %m");
+ goto out_close;
+ }
+
+ /*
+ * TI-RPC over IPv6 (udp6/tcp6) does not handle IPv4. However,
+ * since sm-notify open-codes all of its RPC support, it can
+ * use a single socket and let the local network stack provide
+ * the correct mapping between address families automatically.
+ * This is the same thing that is done in the kernel.
+ */
+ if (nsm_family == AF_INET6) {
+ const int zero = 0;
+ socklen_t zerolen = (socklen_t)sizeof(zero);
+
+ if (setsockopt(sock, SOL_IPV6, IPV6_V6ONLY,
+ (char *)&zero, zerolen) == -1) {
+ xlog(L_ERROR, "setsockopt(3) on RPC socket failed: %m");
+ goto out_close;
+ }
+ }
+
+ return sock;
+
+out_close:
+ (void)close(sock);
+ return -1;
+}
+#else /* !IPV6_SUPPORTED */
+static int smn_socket(void)
+{
+ int sock;
+
+ sock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock == -1) {
+ xlog(L_ERROR, "Failed to create RPC socket: %m");
+ return -1;
+ }
+
+ if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) {
+ xlog(L_ERROR, "fcntl(3) on RPC socket failed: %m");
+ (void)close(sock);
+ return -1;
+ }
+
+ return sock;
+}
+#endif /* !IPV6_SUPPORTED */
+
+/*
+ * If admin specified a source address or srcport, then convert those
+ * to a sockaddr and return it. Otherwise, return an ANYADDR address.
+ */
+__attribute__((__malloc__))
+static struct addrinfo *
+smn_bind_address(const char *srcaddr, const char *srcport)
+{
+ struct addrinfo *ai = NULL;
+ struct addrinfo hint = {
+ .ai_flags = AI_NUMERICSERV | AI_V4MAPPED,
+ .ai_family = nsm_family,
+ .ai_protocol = (int)IPPROTO_UDP,
+ };
+ int error;
+
+ if (srcaddr == NULL)
+ hint.ai_flags |= AI_PASSIVE;
+
+ /* Do not allow "node" and "service" parameters both to be NULL */
+ if (srcport == NULL)
+ error = getaddrinfo(srcaddr, "", &hint, &ai);
+ else
+ error = getaddrinfo(srcaddr, srcport, &hint, &ai);
+ if (error != 0) {
+ xlog(L_ERROR,
+ "Invalid bind address or port for RPC socket: %s",
+ gai_strerror(error));
+ return NULL;
+ }
+
+ return ai;
+}
+
+#ifdef HAVE_LIBTIRPC
+static int
+smn_bindresvport(int sock, struct sockaddr *sap)
+{
+ return bindresvport_sa(sock, sap);
+}
+
+#else /* !HAVE_LIBTIRPC */
+static int
+smn_bindresvport(int sock, struct sockaddr *sap)
+{
+ if (sap->sa_family != AF_INET) {
+ errno = EAFNOSUPPORT;
+ return -1;
+ }
+
+ return bindresvport(sock, (struct sockaddr_in *)(char *)sap);
+}
+#endif /* !HAVE_LIBTIRPC */
+
+/*
+ * Prepare a socket for sending RPC requests
+ *
+ * Returns a bound datagram socket file descriptor, or -1 if
+ * an error occurs.
+ */
+static int
+smn_create_socket(const char *srcaddr, const char *srcport)
+{
+ int sock, retry_cnt = 0;
+ struct addrinfo *ai;
+
+retry:
+ sock = smn_socket();
+ if (sock == -1)
+ return -1;
+
+ ai = smn_bind_address(srcaddr, srcport);
+ if (ai == NULL) {
+ (void)close(sock);
+ return -1;
+ }
+
+ /* Use source port if provided on the command line,
+ * otherwise use bindresvport */
+ if (srcport) {
+ if (bind(sock, ai->ai_addr, ai->ai_addrlen) == -1) {
+ xlog(L_ERROR, "Failed to bind RPC socket: %m");
+ nfs_freeaddrinfo(ai);
+ (void)close(sock);
+ return -1;
+ }
+ } else {
+ struct servent *se;
+
+ if (smn_bindresvport(sock, ai->ai_addr) == -1) {
+ xlog(L_ERROR,
+ "bindresvport on RPC socket failed: %m");
+ nfs_freeaddrinfo(ai);
+ (void)close(sock);
+ return -1;
+ }
+
+ /* try to avoid known ports */
+ se = getservbyport((int)nfs_get_port(ai->ai_addr), "udp");
+ if (se != NULL && retry_cnt < 100) {
+ retry_cnt++;
+ nfs_freeaddrinfo(ai);
+ (void)close(sock);
+ goto retry;
+ }
+ }
+
+ nfs_freeaddrinfo(ai);
+ return sock;
+}
+
+/* Inform the kernel that it's OK to lift lockd's grace period */
+static void
+nsm_lift_grace_period(void)
+{
+ int fd;
+
+ fd = open(NLM_END_GRACE_FILE, O_WRONLY);
+ if (fd < 0) {
+ /* Don't warn if file isn't present */
+ if (errno != ENOENT)
+ xlog(L_WARNING, "Unable to open %s: %m",
+ NLM_END_GRACE_FILE);
+ return;
+ }
+
+ if (write(fd, "Y", 1) < 0)
+ xlog(L_WARNING, "Unable to write to %s: %m", NLM_END_GRACE_FILE);
+
+ close(fd);
+ return;
+}
+inline static void
+read_smnotify_conf(char **argv)
+{
+ char *s;
+
+ conf_init_file(NFS_CONFFILE);
+ xlog_set_debug("sm-notify");
+ opt_max_retry = conf_get_num("sm-notify", "retry-time", opt_max_retry / 60) * 60;
+ opt_srcport = conf_get_str("sm-notify", "outgoing-port");
+ opt_srcaddr = conf_get_str("sm-notify", "outgoing-addr");
+ lift_grace = conf_get_bool("sm-notify", "lift-grace", lift_grace);
+
+ s = conf_get_str("statd", "state-directory-path");
+ if (s && !nsm_setup_pathnames(argv[0], s))
+ exit(1);
+ opt_update_state = conf_get_bool("sm-notify", "update-state", opt_update_state);
+ force = conf_get_bool("sm-notify", "force", force);
+}
+
+int
+main(int argc, char **argv)
+{
+ int c, sock;
+ char * progname;
+
+ progname = strrchr(argv[0], '/');
+ if (progname != NULL)
+ progname++;
+ else
+ progname = argv[0];
+
+ /* Read in config setting */
+ read_smnotify_conf(argv);
+
+ while ((c = getopt(argc, argv, "dm:np:v:P:f")) != -1) {
+ switch (c) {
+ case 'f':
+ force = 1;
+ break;
+ case 'd':
+ opt_debug++;
+ break;
+ case 'm':
+ opt_max_retry = atoi(optarg) * 60;
+ break;
+ case 'n':
+ opt_update_state = false;
+ break;
+ case 'p':
+ opt_srcport = optarg;
+ break;
+ case 'v':
+ opt_srcaddr = optarg;
+ break;
+ case 'P':
+ if (!nsm_setup_pathnames(argv[0], optarg))
+ exit(1);
+ break;
+
+ default:
+ goto usage;
+ }
+ }
+
+ if (optind < argc) {
+usage: fprintf(stderr,
+ "Usage: %s -notify [-dfq] [-m max-retry-minutes] [-p srcport]\n"
+ " [-P /path/to/state/directory] [-v my_host_name]\n",
+ progname);
+ exit(1);
+ }
+
+ if (opt_debug) {
+ xlog_syslog(0);
+ xlog_stderr(1);
+ xlog_config(D_ALL, 1);
+ } else {
+ xlog_syslog(1);
+ xlog_stderr(0);
+ }
+
+ xlog_open(progname);
+ xlog(L_NOTICE, "Version " VERSION " starting");
+
+ if (nsm_is_default_parentdir()) {
+ if (record_pid() == 0 && force == 0 && opt_update_state) {
+ /* already run, don't try again */
+ xlog(L_NOTICE, "Already notifying clients; Exiting!");
+ exit(0);
+ }
+ }
+
+ if (opt_srcaddr != NULL) {
+ char *name;
+
+ name = smn_verify_my_name(opt_srcaddr);
+ if (name == NULL)
+ exit(1);
+
+ strncpy(nsm_hostname, name, sizeof(nsm_hostname)-1);
+ free(name);
+ }
+
+ (void)nsm_retire_monitored_hosts();
+ if (nsm_load_notify_list(smn_get_host) == 0) {
+ xlog(D_GENERAL, "No hosts to notify; exiting");
+ if (lift_grace)
+ nsm_lift_grace_period();
+ return 0;
+ }
+
+ nsm_state = nsm_get_state(opt_update_state);
+ if (nsm_state == 0)
+ exit(1);
+ nsm_update_kernel_state(nsm_state);
+
+ if (!opt_debug) {
+ xlog(L_NOTICE, "Backgrounding to notify hosts...\n");
+
+ if (daemon(0, 0) < 0) {
+ xlog(L_ERROR, "unable to background: %m");
+ exit(1);
+ }
+
+ close(0);
+ close(1);
+ close(2);
+ }
+
+ sock = smn_create_socket(opt_srcaddr, opt_srcport);
+ if (sock == -1)
+ exit(1);
+
+ if (!nsm_drop_privileges(-1))
+ exit(1);
+
+ notify(sock);
+
+ if (hosts) {
+ struct nsm_host *hp;
+
+ while ((hp = hosts) != 0) {
+ hosts = hp->next;
+ xlog(L_NOTICE, "Unable to notify %s, giving up",
+ hp->name);
+ }
+ exit(1);
+ }
+
+ exit(0);
+}
+
+/*
+ * Notify hosts
+ */
+static void
+notify(const int sock)
+{
+ time_t failtime = 0;
+
+ if (opt_max_retry)
+ failtime = time(NULL) + opt_max_retry;
+
+ while (hosts) {
+ struct pollfd pfd;
+ time_t now = time(NULL);
+ unsigned int sent = 0;
+ struct nsm_host *hp;
+ long wait;
+
+ if (failtime && now >= failtime)
+ break;
+
+ while (hosts && ((wait = hosts->send_next - now) <= 0)) {
+ /* Never send more than 10 packets at once */
+ if (sent++ >= 10)
+ break;
+
+ /* Remove queue head */
+ hp = hosts;
+ hosts = hp->next;
+
+ if (notify_host(sock, hp))
+ continue;
+
+ /* Set the timeout for this call, using an
+ exponential timeout strategy */
+ wait = hp->timeout;
+ if ((hp->timeout <<= 1) > NSM_MAX_TIMEOUT)
+ hp->timeout = NSM_MAX_TIMEOUT;
+ hp->send_next = now + wait;
+ hp->retries++;
+
+ insert_host(hp);
+ }
+ if (hosts == NULL)
+ return;
+
+ xlog(D_GENERAL, "Host %s due in %ld seconds",
+ hosts->name, wait);
+
+ pfd.fd = sock;
+ pfd.events = POLLIN;
+
+ wait *= 1000;
+ if (wait < 100)
+ wait = 100;
+ if (poll(&pfd, 1, wait) != 1)
+ continue;
+
+ recv_reply(sock);
+ }
+}
+
+/*
+ * Send notification to a single host
+ */
+static int
+notify_host(int sock, struct nsm_host *host)
+{
+ struct sockaddr *sap;
+ socklen_t salen;
+
+ if (host->ai == NULL) {
+ host->ai = smn_lookup(host->name);
+ if (host->ai == NULL) {
+ xlog_warn("DNS resolution of %s failed; "
+ "retrying later", host->name);
+ return 0;
+ }
+ }
+
+ /* If we retransmitted 4 times, reset the port to force
+ * a new portmap lookup (in case statd was restarted).
+ * We also rotate through multiple IP addresses at this
+ * point.
+ */
+ if (host->retries >= 4) {
+ /* don't rotate if there is only one addrinfo */
+ if (host->ai->ai_next != NULL) {
+ struct addrinfo *first = host->ai;
+ struct addrinfo **next = &host->ai;
+
+ /* remove the first entry from the list */
+ host->ai = first->ai_next;
+ first->ai_next = NULL;
+ /* find the end of the list */
+ next = &first->ai_next;
+ while ( *next )
+ next = & (*next)->ai_next;
+ /* put first entry at end */
+ *next = first;
+ }
+
+ nfs_set_port(host->ai->ai_addr, 0);
+ host->retries = 0;
+ }
+
+ sap = host->ai->ai_addr;
+ salen = host->ai->ai_addrlen;
+
+ if (nfs_get_port(sap) == 0)
+ host->xid = nsm_xmit_rpcbind(sock, sap, SM_PROG, SM_VERS);
+ else
+ host->xid = nsm_xmit_notify(sock, sap, salen,
+ SM_PROG, host->notify_arg, nsm_state);
+
+ return 0;
+}
+
+static void
+smn_defer(struct nsm_host *host)
+{
+ host->xid = 0;
+ host->send_next = time(NULL) + NSM_MAX_TIMEOUT;
+ host->timeout = NSM_MAX_TIMEOUT;
+ insert_host(host);
+}
+
+static void
+smn_schedule(struct nsm_host *host)
+{
+ host->retries = 0;
+ host->xid = 0;
+ host->send_next = time(NULL);
+ host->timeout = NSM_TIMEOUT;
+ insert_host(host);
+}
+
+/*
+ * Extract the returned port number and set up the SM_NOTIFY call.
+ */
+static void
+recv_rpcbind_reply(struct sockaddr *sap, struct nsm_host *host, XDR *xdr)
+{
+ uint16_t port = nsm_recv_rpcbind(sap->sa_family, xdr);
+
+ if (port == 0) {
+ /* No binding for statd... */
+ xlog(D_GENERAL, "No statd on host %s", host->name);
+ smn_defer(host);
+ } else {
+ xlog(D_GENERAL, "Processing rpcbind reply for %s (port %u)",
+ host->name, port);
+ nfs_set_port(sap, port);
+ smn_schedule(host);
+ }
+}
+
+/*
+ * Successful NOTIFY call. Server returns void.
+ *
+ * Try sending another SM_NOTIFY with an unqualified "my_name"
+ * argument. Reuse the port number. If "my_name" is already
+ * unqualified, we're done.
+ */
+static void
+recv_notify_reply(struct nsm_host *host)
+{
+ char *dot = strchr(host->notify_arg, '.');
+
+ if (dot != NULL) {
+ *dot = '\0';
+ smn_schedule(host);
+ } else {
+ xlog(D_GENERAL, "Host %s notified successfully", host->name);
+ smn_forget_host(host);
+ }
+}
+
+/*
+ * Receive reply from remote host
+ */
+static void
+recv_reply(int sock)
+{
+ struct nsm_host *hp;
+ struct sockaddr *sap;
+ char msgbuf[NSM_MAXMSGSIZE];
+ uint32_t xid;
+ ssize_t msglen;
+ XDR xdr;
+
+ memset(msgbuf, 0 , sizeof(msgbuf));
+ msglen = recv(sock, msgbuf, sizeof(msgbuf), 0);
+ if (msglen < 0)
+ return;
+
+ xlog(D_GENERAL, "Received packet...");
+
+ memset(&xdr, 0, sizeof(xdr));
+ xdrmem_create(&xdr, msgbuf, (unsigned int)msglen, XDR_DECODE);
+ xid = nsm_parse_reply(&xdr);
+ if (xid == 0)
+ goto out;
+
+ /* Before we look at the data, find the host struct for
+ this reply */
+ if ((hp = find_host(xid)) == NULL)
+ goto out;
+
+ sap = hp->ai->ai_addr;
+ if (nfs_get_port(sap) == 0)
+ recv_rpcbind_reply(sap, hp, &xdr);
+ else
+ recv_notify_reply(hp);
+
+out:
+ xdr_destroy(&xdr);
+}
+
+/*
+ * Insert host into notification list, sorted by next send time
+ */
+static void
+insert_host(struct nsm_host *host)
+{
+ struct nsm_host **where, *p;
+
+ where = &hosts;
+ while ((p = *where) != 0) {
+ /* Sort in ascending order of timeout */
+ if (host->send_next < p->send_next)
+ break;
+ /* If we have the same timeout, put the
+ * most recently used host first.
+ * This makes sure that "recent" hosts
+ * get notified first.
+ */
+ if (host->send_next == p->send_next
+ && host->last_used > p->last_used)
+ break;
+ where = &p->next;
+ }
+
+ host->next = *where;
+ *where = host;
+ xlog(D_GENERAL, "Added host %s to notify list", host->name);
+}
+
+/*
+ * Find host given the XID
+ */
+static struct nsm_host *
+find_host(uint32_t xid)
+{
+ struct nsm_host **where, *p;
+
+ where = &hosts;
+ while ((p = *where) != 0) {
+ if (p->xid == xid) {
+ *where = p->next;
+ return p;
+ }
+ where = &p->next;
+ }
+ return NULL;
+}
+
+/*
+ * Record pid in /run/sm-notify.pid
+ * This file should remain until a reboot, even if the
+ * program exits.
+ * If file already exists, fail.
+ */
+static int record_pid(void)
+{
+ char pid[20];
+ ssize_t len;
+ int fd;
+
+ (void)snprintf(pid, sizeof(pid), "%d\n", (int)getpid());
+ fd = open("/run/sm-notify.pid", O_CREAT|O_EXCL|O_WRONLY, 0600);
+ if (fd < 0)
+ return 0;
+
+ len = write(fd, pid, strlen(pid));
+ if ((len < 0) || ((size_t)len != strlen(pid))) {
+ xlog_warn("Writing to pid file failed: errno %d (%m)",
+ errno);
+ }
+
+ (void)close(fd);
+ return 1;
+}
diff --git a/utils/statd/sm-notify.man b/utils/statd/sm-notify.man
new file mode 100644
index 0000000..addf5d3
--- /dev/null
+++ b/utils/statd/sm-notify.man
@@ -0,0 +1,366 @@
+.\"@(#)sm-notify.8"
+.\"
+.\" Copyright (C) 2004 Olaf Kirch <okir@suse.de>
+.\"
+.\" Rewritten by Chuck Lever <chuck.lever@oracle.com>, 2009.
+.\" Copyright 2009 Oracle. All rights reserved.
+.\"
+.TH SM-NOTIFY 8 "1 November 2009
+.SH NAME
+sm-notify \- send reboot notifications to NFS peers
+.SH SYNOPSIS
+.BI "/usr/sbin/sm-notify [-dfn] [-m " minutes "] [-v " name "] [-p " notify-port "] [-P " path "]
+.SH DESCRIPTION
+File locks are not part of persistent file system state.
+Lock state is thus lost when a host reboots.
+.PP
+Network file systems must also detect when lock state is lost
+because a remote host has rebooted.
+After an NFS client reboots, an NFS server must release all file locks
+held by applications that were running on that client.
+After a server reboots, a client must remind the
+server of file locks held by applications running on that client.
+.PP
+For NFS version 2 and version 3, the
+.I Network Status Monitor
+protocol (or NSM for short)
+is used to notify NFS peers of reboots.
+On Linux, two separate user-space components constitute the NSM service:
+.TP
+.B sm-notify
+A helper program that notifies NFS peers after the local system reboots
+.TP
+.B rpc.statd
+A daemon that listens for reboot notifications from other hosts, and
+manages the list of hosts to be notified when the local system reboots
+.PP
+The local NFS lock manager alerts its local
+.B rpc.statd
+of each remote peer that should be monitored.
+When the local system reboots, the
+.B sm-notify
+command notifies the NSM service on monitored peers of the reboot.
+When a remote reboots, that peer notifies the local
+.BR rpc.statd ,
+which in turn passes the reboot notification
+back to the local NFS lock manager.
+.SH NSM OPERATION IN DETAIL
+The first file locking interaction between an NFS client and server causes
+the NFS lock managers on both peers to contact their local NSM service to
+store information about the opposite peer.
+On Linux, the local lock manager contacts
+.BR rpc.statd .
+.PP
+.B rpc.statd
+records information about each monitored NFS peer on persistent storage.
+This information describes how to contact a remote peer
+in case the local system reboots,
+how to recognize which monitored peer is reporting a reboot,
+and how to notify the local lock manager when a monitored peer
+indicates it has rebooted.
+.PP
+An NFS client sends a hostname, known as the client's
+.IR caller_name ,
+in each file lock request.
+An NFS server can use this hostname to send asynchronous GRANT
+calls to a client, or to notify the client it has rebooted.
+.PP
+The Linux NFS server can provide the client's
+.I caller_name
+or the client's network address to
+.BR rpc.statd .
+For the purposes of the NSM protocol,
+this name or address is known as the monitored peer's
+.IR mon_name .
+In addition, the local lock manager tells
+.B rpc.statd
+what it thinks its own hostname is.
+For the purposes of the NSM protocol,
+this hostname is known as
+.IR my_name .
+.PP
+There is no equivalent interaction between an NFS server and a client
+to inform the client of the server's
+.IR caller_name .
+Therefore NFS clients do not actually know what
+.I mon_name
+an NFS server might use in an SM_NOTIFY request.
+The Linux NFS client records the server's hostname used on the mount command
+to identify rebooting NFS servers.
+.SS Reboot notification
+When the local system reboots, the
+.B sm-notify
+command reads the list of monitored peers from persistent storage and
+sends an SM_NOTIFY request to the NSM service on each listed remote peer.
+It uses the
+.I mon_name
+string as the destination.
+To identify which host has rebooted, the
+.B sm-notify
+command normally sends
+.I my_name
+string recorded when that remote was monitored.
+The remote
+.B rpc.statd
+matches incoming SM_NOTIFY requests using this string,
+or the caller's network address,
+to one or more peers on its own monitor list.
+.PP
+If
+.B rpc.statd
+does not find a peer on its monitor list that matches
+an incoming SM_NOTIFY request,
+the notification is not forwarded to the local lock manager.
+In addition, each peer has its own
+.IR "NSM state number" ,
+a 32-bit integer that is bumped after each reboot by the
+.B sm-notify
+command.
+.B rpc.statd
+uses this number to distinguish between actual reboots
+and replayed notifications.
+.PP
+Part of NFS lock recovery is rediscovering
+which peers need to be monitored again.
+The
+.B sm-notify
+command clears the monitor list on persistent storage after each reboot.
+.SH OPTIONS
+.TP
+.B -d
+Keeps
+.B sm-notify
+attached to its controlling terminal and running in the foreground
+so that notification progress may be monitored directly.
+.TP
+.B -f
+Send notifications even if
+.B sm-notify
+has already run since the last system reboot.
+.TP
+.BI -m " retry-time
+Specifies the length of time, in minutes, to continue retrying
+notifications to unresponsive hosts.
+If this option is not specified,
+.B sm-notify
+attempts to send notifications for 15 minutes.
+Specifying a value of 0 causes
+.B sm-notify
+to continue sending notifications to unresponsive peers
+until it is manually killed.
+.IP
+Notifications are retried if sending fails,
+the remote does not respond,
+the remote's NSM service is not registered,
+or if there is a DNS failure
+which prevents the remote's
+.I mon_name
+from being resolved to an address.
+.IP
+Hosts are not removed from the notification list until a valid
+reply has been received.
+However, the SM_NOTIFY procedure has a void result.
+There is no way for
+.B sm-notify
+to tell if the remote recognized the sender and has started
+appropriate lock recovery.
+.TP
+.B -n
+Prevents
+.B sm-notify
+from updating the local system's NSM state number.
+.TP
+.BI -p " port
+Specifies the source port number
+.B sm-notify
+should use when sending reboot notifications.
+If this option is not specified, a randomly chosen ephemeral port is used.
+.IP
+This option can be used to traverse a firewall between client and server.
+.TP
+.BI "\-P, " "" \-\-state\-directory\-path " pathname
+Specifies the pathname of the parent directory
+where NSM state information resides.
+If this option is not specified,
+.B sm-notify
+uses
+.I /var/lib/nfs
+by default.
+.IP
+After starting,
+.B sm-notify
+attempts to set its effective UID and GID to the owner
+and group of the subdirectory
+.B sm
+of this directory. After changing the effective ids,
+.B sm-notify
+only needs to access files in
+.B sm
+and
+.B sm.bak
+within the state-directory-path.
+.TP
+.BI -v " ipaddr " | " hostname
+Specifies the network address from which to send reboot notifications,
+and the
+.I mon_name
+argument to use when sending SM_NOTIFY requests.
+If this option is not specified,
+.B sm-notify
+uses a wildcard address as the transport bind address,
+and uses the
+.I my_name
+recorded when the remote was monitored as the
+.I mon_name
+argument when sending SM_NOTIFY requests.
+.IP
+The
+.I ipaddr
+form can be expressed as either an IPv4 or an IPv6 presentation address.
+If the
+.I ipaddr
+form is used, the
+.B sm-notify
+command converts this address to a hostname for use as the
+.I mon_name
+argument when sending SM_NOTIFY requests.
+.IP
+This option can be useful in multi-homed configurations where
+the remote requires notification from a specific network address.
+.SH CONFIGURATION FILE
+Many of the options that can be set on the command line can also be
+controlled through values set in the
+.B [sm-notify]
+or, in one case, the
+.B [statd]
+section of the
+.I /etc/nfs.conf
+configuration file.
+
+Values recognized in the
+.B [sm-notify]
+section include:
+.BR retry-time ,
+.BR outgoing-port ", and"
+.BR outgoing-addr .
+These have the same effect as the command line options
+.BR m ,
+.BR p ", and"
+.B v
+respectively.
+
+An additional value recognized in the
+.B [sm-notify]
+section is
+.BR lift-grace .
+By default,
+.B sm-notify
+will lift lockd's grace period early if it has no hosts to notify.
+Some high availability configurations will run one
+.B sm-notify
+per floating IP address. In these configurations, lifting the
+grace period early may prevent clients from reclaiming locks.
+.RB "Setting " lift-grace " to " n
+will prevent
+.B sm-notify
+from ending the grace period early.
+.B lift-grace
+has no corresponding command line option.
+
+The value recognized in the
+.B [statd]
+section is
+.BR state-directory-path .
+
+.SH SECURITY
+The
+.B sm-notify
+command must be started as root to acquire privileges needed
+to access the state information database.
+It drops root privileges
+as soon as it starts up to reduce the risk of a privilege escalation attack.
+.PP
+During normal operation,
+the effective user ID it chooses is the owner of the state directory.
+This allows it to continue to access files in that directory after it
+has dropped its root privileges.
+To control which user ID
+.B rpc.statd
+chooses, simply use
+.BR chown (1)
+to set the owner of
+the state directory.
+.SH ADDITIONAL NOTES
+Lock recovery after a reboot is critical to maintaining data integrity
+and preventing unnecessary application hangs.
+.PP
+To help
+.B rpc.statd
+match SM_NOTIFY requests to NLM requests, a number of best practices
+should be observed, including:
+.IP
+The UTS nodename of your systems should match the DNS names that NFS
+peers use to contact them
+.IP
+The UTS nodenames of your systems should always be fully qualified domain names
+.IP
+The forward and reverse DNS mapping of the UTS nodenames should be
+consistent
+.IP
+The hostname the client uses to mount the server should match the server's
+.I mon_name
+in SM_NOTIFY requests it sends
+.PP
+Unmounting an NFS file system does not necessarily stop
+either the NFS client or server from monitoring each other.
+Both may continue monitoring each other for a time in case subsequent
+NFS traffic between the two results in fresh mounts and additional
+file locking.
+.PP
+On Linux, if the
+.B lockd
+kernel module is unloaded during normal operation,
+all remote NFS peers are unmonitored.
+This can happen on an NFS client, for example,
+if an automounter removes all NFS mount
+points due to inactivity.
+.SS IPv6 and TI-RPC support
+TI-RPC is a pre-requisite for supporting NFS on IPv6.
+If TI-RPC support is built into the
+.B sm-notify
+command ,it will choose an appropriate IPv4 or IPv6 transport
+based on the network address returned by DNS for each remote peer.
+It should be fully compatible with remote systems
+that do not support TI-RPC or IPv6.
+.PP
+Currently, the
+.B sm-notify
+command supports sending notification only via datagram transport protocols.
+.SH FILES
+.TP 2.5i
+.I /var/lib/nfs/sm
+directory containing monitor list
+.TP 2.5i
+.I /var/lib/nfs/sm.bak
+directory containing notify list
+.TP 2.5i
+.I /var/lib/nfs/state
+NSM state number for this host
+.TP 2.5i
+.I /proc/sys/fs/nfs/nsm_local_state
+kernel's copy of the NSM state number
+.SH SEE ALSO
+.BR rpc.statd (8),
+.BR nfs (5),
+.BR uname (2),
+.BR hostname (7)
+.PP
+RFC 1094 - "NFS: Network File System Protocol Specification"
+.br
+RFC 1813 - "NFS Version 3 Protocol Specification"
+.br
+OpenGroup Protocols for Interworking: XNFS, Version 3W - Chapter 11
+.SH AUTHORS
+Olaf Kirch <okir@suse.de>
+.br
+Chuck Lever <chuck.lever@oracle.com>
diff --git a/utils/statd/start-statd b/utils/statd/start-statd
new file mode 100755
index 0000000..b11a7d9
--- /dev/null
+++ b/utils/statd/start-statd
@@ -0,0 +1,33 @@
+#!/bin/sh
+# nfsmount calls this script when mounting a filesystem with locking
+# enabled, but when statd does not seem to be running (based on
+# /run/rpc.statd.pid).
+# It should run statd with whatever flags are apropriate for this
+# site.
+PATH="/sbin:/usr/sbin:/bin:/usr/bin"
+
+# Use flock to serialize the running of this script
+exec 9> /run/rpc.statd.lock
+flock -e 9
+
+if [ -s /run/rpc.statd.pid ] &&
+ [ "1$(cat /run/rpc.statd.pid)" -gt 1 ] &&
+ kill -0 "$(cat /run/rpc.statd.pid)" > /dev/null 2>&1
+then
+ # statd already running - must have been slow to respond.
+ exit 0
+fi
+# First try systemd if it's installed.
+if [ -d /run/systemd/system ]; then
+ # Quit only if the call worked.
+ if systemctl start rpc-statd.service; then
+ # Ensure systemd knows not to stop rpc.statd or its dependencies
+ # on 'systemctl isolate ..'
+ systemctl add-wants --runtime remote-fs.target rpc-statd.service
+ exit 0
+ fi
+fi
+
+cd /
+# Fall back to launching it ourselves.
+exec rpc.statd --no-notify
diff --git a/utils/statd/stat.c b/utils/statd/stat.c
new file mode 100644
index 0000000..8d8b65e
--- /dev/null
+++ b/utils/statd/stat.c
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 1995, 1997, 1999 Jeffrey A. Uphoff
+ * Modified by Olaf Kirch, 1996.
+ *
+ * NSM for Linux.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <netdb.h>
+#include "statd.h"
+
+/*
+ * Services SM_STAT requests.
+ *
+ * According the the X/Open spec's on this procedure: "Implementations
+ * should not rely on this procedure being operative. In many current
+ * implementations of the NSM it will always return a 'STAT_FAIL'
+ * status." My implementation is operative; it returns 'STAT_SUCC'
+ * whenever it can resolve the hostname that it's being asked to
+ * monitor, and returns 'STAT_FAIL' otherwise.
+ *
+ * sm_inter.x says the 'state' returned should be
+ * "state number of site sm_name". It is not clear how to get this.
+ * X/Open says:
+ * STAT_SUCC
+ * The NSM will monitor the given host. "sm_stat_res.state" contains
+ * the state of the NSM.
+ * Which implies that 'state' is the state number of the *local* NSM.
+ * href=http://www.opengroup.org/onlinepubs/9629799/SM_STAT.htm
+ *
+ * We return the *local* state as
+ * 1/ We have easy access to it.
+ * 2/ It might be useful to a remote client who needs it and has no
+ * other way to get it.
+ * 3/ That's what we always did in the past.
+ */
+struct sm_stat_res *
+sm_stat_1_svc(struct sm_name *argp,
+ __attribute__ ((unused)) struct svc_req *rqstp)
+{
+ static sm_stat_res result;
+ char *name;
+
+ xlog(D_CALL, "Received SM_STAT from %s", argp->mon_name);
+
+ name = statd_canonical_name(argp->mon_name);
+ if (name == NULL) {
+ result.res_stat = STAT_FAIL;
+ xlog (D_GENERAL, "STAT_FAIL for %s", argp->mon_name);
+ } else {
+ result.res_stat = STAT_SUCC;
+ xlog (D_GENERAL, "STAT_SUCC for %s", argp->mon_name);
+ free(name);
+ }
+ result.state = MY_STATE;
+ return(&result);
+}
diff --git a/utils/statd/statd.c b/utils/statd/statd.c
new file mode 100644
index 0000000..a469a67
--- /dev/null
+++ b/utils/statd/statd.c
@@ -0,0 +1,549 @@
+/*
+ * Copyright (C) 1995, 1997-1999 Jeffrey A. Uphoff
+ * Modified by Olaf Kirch, Oct. 1996.
+ * Modified by H.J. Lu, 1998.
+ * Modified by L. Hohberger of Mission Critical Linux, 2000.
+ *
+ * NSM for Linux.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/stat.h>
+#include <limits.h>
+#include <signal.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <getopt.h>
+#include <rpc/rpc.h>
+#include <rpc/pmap_clnt.h>
+#include <rpcmisc.h>
+#include <sys/resource.h>
+#include <sys/wait.h>
+#include <grp.h>
+
+#include "conffile.h"
+#include "statd.h"
+#include "nfslib.h"
+#include "nfsrpc.h"
+#include "nsm.h"
+
+/* Socket operations */
+#include <sys/types.h>
+#include <sys/socket.h>
+
+int run_mode = 0; /* foreground logging mode */
+
+/* LH - I had these local to main, but it seemed silly to have
+ * two copies of each - one in main(), one static in log.c...
+ * It also eliminates the 256-char static in log.c */
+static char *name_p = NULL;
+
+/* PRC: a high-availability callout program can be specified with -H
+ * When this is done, the program will receive callouts whenever clients
+ * are added or deleted to the notify list */
+char *ha_callout_prog = NULL;
+
+static struct option longopts[] =
+{
+ { "foreground", 0, 0, 'F' },
+ { "no-syslog", 0, 0, 'd' },
+ { "help", 0, 0, 'h' },
+ { "version", 0, 0, 'v' },
+ { "outgoing-port", 1, 0, 'o' },
+ { "port", 1, 0, 'p' },
+ { "name", 1, 0, 'n' },
+ { "state-directory-path", 1, 0, 'P' },
+ { "notify-mode", 0, 0, 'N' },
+ { "ha-callout", 1, 0, 'H' },
+ { "no-notify", 0, 0, 'L' },
+ { "nlm-port", 1, 0, 'T'},
+ { "nlm-udp-port", 1, 0, 'U'},
+ { NULL, 0, 0, 0 }
+};
+
+extern void sm_prog_1 (struct svc_req *, register SVCXPRT *);
+stat_chge SM_stat_chge;
+
+#ifdef SIMULATIONS
+extern void simulator (int, char **);
+#endif
+
+
+#ifdef HAVE_TCP_WRAPPER
+#include "tcpwrapper.h"
+
+static void
+sm_prog_1_wrapper (struct svc_req *rqstp, register SVCXPRT *transp)
+{
+ /* remote host authorization check */
+ if (!check_default("statd", nfs_getrpccaller(transp), SM_PROG)) {
+ svcerr_auth (transp, AUTH_FAILED);
+ return;
+ }
+
+ sm_prog_1 (rqstp, transp);
+}
+
+#define sm_prog_1 sm_prog_1_wrapper
+#endif
+
+static void
+statd_unregister(void) {
+ nfs_svc_unregister(SM_PROG, SM_VERS);
+}
+
+/*
+ * Signal handler.
+ */
+static void
+killer (int sig)
+{
+ statd_unregister ();
+ xlog(D_GENERAL, "Caught signal %d, un-registering and exiting", sig);
+ exit(0);
+}
+
+static void
+sigusr (int sig)
+{
+ extern void my_svc_exit (void);
+ xlog(D_GENERAL, "Caught signal %d, re-notifying (state %d)", sig,
+ MY_STATE);
+ my_svc_exit();
+}
+
+/*
+ * Startup information.
+ */
+static void log_modes(void)
+{
+ char buf[128]; /* watch stack size... */
+
+ /* No flags = no message */
+ if (!run_mode) return;
+
+ memset(buf,0,128);
+ sprintf(buf,"Flags: ");
+ if (run_mode & MODE_NODAEMON)
+ strcat(buf,"No-Daemon ");
+ if (run_mode & MODE_LOG_STDERR)
+ strcat(buf,"Log-STDERR ");
+#ifdef HAVE_LIBTIRPC
+ strcat(buf, "TI-RPC ");
+#endif
+
+ xlog_warn("%s", buf);
+}
+
+/*
+ * Since we do more than standard statd stuff, we might need to
+ * help the occasional admin.
+ */
+static void
+usage(void)
+{
+ fprintf(stderr,"usage: %s [options]\n", name_p);
+ fprintf(stderr," -h, -?, --help Print this help screen.\n");
+ fprintf(stderr," -F, --foreground Foreground (no-daemon mode)\n");
+ fprintf(stderr," -d, --no-syslog Verbose logging to stderr. Foreground mode only.\n");
+ fprintf(stderr," -p, --port Port to listen on\n");
+ fprintf(stderr," -o, --outgoing-port Port for outgoing connections\n");
+ fprintf(stderr," -V, -v, --version Display version information and exit.\n");
+ fprintf(stderr," -n, --name Specify a local hostname.\n");
+ fprintf(stderr," -P State directory path.\n");
+ fprintf(stderr," -N Run in notify only mode.\n");
+ fprintf(stderr," -L, --no-notify Do not perform any notification.\n");
+ fprintf(stderr," -H Specify a high-availability callout program.\n");
+}
+
+static const char *pidfile = "/run/rpc.statd.pid";
+
+int pidfd = -1;
+static void create_pidfile(void)
+{
+ FILE *fp;
+
+ unlink(pidfile);
+ fp = fopen(pidfile, "w");
+ if (!fp)
+ xlog_err("Opening %s failed: %m\n", pidfile);
+ fprintf(fp, "%d\n", getpid());
+ pidfd = dup(fileno(fp));
+ if (fclose(fp) < 0) {
+ xlog_warn("Flushing pid file failed: errno %d (%m)\n",
+ errno);
+ }
+}
+
+static void truncate_pidfile(void)
+{
+ if (pidfd >= 0) {
+ if (ftruncate(pidfd, 0) < 0) {
+ xlog_warn("truncating pid file failed: errno %d (%m)\n",
+ errno);
+ }
+ }
+}
+
+static void run_sm_notify(int outport)
+{
+ char op[20];
+ char *av[6];
+ int ac = 0;
+
+ av[ac++] = "/usr/sbin/sm-notify";
+ if (run_mode & MODE_NODAEMON)
+ av[ac++] = "-d";
+ if (outport) {
+ sprintf(op, "-p%d", outport);
+ av[ac++] = op;
+ }
+ if (run_mode & STATIC_HOSTNAME) {
+ av[ac++] = "-v";
+ av[ac++] = MY_NAME;
+ }
+ av[ac] = NULL;
+ execv(av[0], av);
+ fprintf(stderr, "%s: failed to run %s\n", name_p, av[0]);
+ exit(2);
+
+}
+
+static void set_nlm_port(char *type, int port)
+{
+ char nbuf[20];
+ char pathbuf[40];
+ int fd;
+ if (!port)
+ return;
+ snprintf(nbuf, sizeof(nbuf), "%d", port);
+ snprintf(pathbuf, sizeof(pathbuf), "/proc/sys/fs/nfs/nlm_%sport", type);
+ fd = open(pathbuf, O_WRONLY);
+ if (fd < 0 && errno == ENOENT) {
+ /* probably module not loaded */
+ if (system("modprobe lockd"))
+ {/* ignore return value */};
+ fd = open(pathbuf, O_WRONLY);
+ }
+ if (fd >= 0) {
+ if (write(fd, nbuf, strlen(nbuf)) != (ssize_t)strlen(nbuf))
+ fprintf(stderr, "%s: fail to set NLM %s port: %s\n",
+ name_p, type, strerror(errno));
+ close(fd);
+ } else
+ fprintf(stderr, "%s: failed to open %s: %s\n",
+ name_p, pathbuf, strerror(errno));
+}
+int port = 0, out_port = 0;
+int nlm_udp = 0, nlm_tcp = 0;
+
+inline static void
+read_statd_conf(char **argv)
+{
+ char *s;
+
+ conf_init_file(NFS_CONFFILE);
+ xlog_set_debug("statd");
+
+ out_port = conf_get_num("statd", "outgoing-port", out_port);
+ port = conf_get_num("statd", "port", port);
+
+ MY_NAME = conf_get_str("statd", "name");
+ if (MY_NAME)
+ run_mode |= STATIC_HOSTNAME;
+
+ s = conf_get_str("statd", "state-directory-path");
+ if (s && !nsm_setup_pathnames(argv[0], s))
+ exit(1);
+
+ s = conf_get_str("statd", "ha-callout");
+ if (s)
+ ha_callout_prog = s;
+
+ nlm_tcp = conf_get_num("lockd", "port", nlm_tcp);
+ /* udp defaults to the same as tcp ! */
+ nlm_udp = conf_get_num("lockd", "udp-port", nlm_tcp);
+
+ if (conf_get_bool("statd", "no-notify", false))
+ run_mode |= MODE_NO_NOTIFY;
+}
+
+/*
+ * Entry routine/main loop.
+ */
+int main (int argc, char **argv)
+{
+ extern char *optarg;
+ int pid;
+ int arg;
+ struct rlimit rlim;
+ int notify_sockfd;
+ char *env;
+
+ /* Default: daemon mode, no other options */
+ run_mode = 0;
+
+ env = getenv("RPC_STATD_NO_NOTIFY");
+ if (env && atoi(env) > 0)
+ run_mode |= MODE_NO_NOTIFY;
+
+ /* Log to stderr if there's an error during startup */
+ xlog_stderr(1);
+ xlog_syslog(0);
+
+ /* Set the basename */
+ if ((name_p = strrchr(argv[0],'/')) != NULL) {
+ name_p ++;
+ } else {
+ name_p = argv[0];
+ }
+
+ /* Set hostname */
+ MY_NAME = NULL;
+
+ /* Read in config setting */
+ read_statd_conf(argv);
+
+ /* Process command line switches */
+ while ((arg = getopt_long(argc, argv, "h?vVFNH:dn:p:o:P:LT:U:", longopts, NULL)) != EOF) {
+ switch (arg) {
+ case 'V': /* Version */
+ case 'v':
+ printf("%s version " VERSION "\n",name_p);
+ exit(0);
+ case 'F': /* Foreground/nodaemon mode */
+ run_mode |= MODE_NODAEMON;
+ break;
+ case 'N':
+ run_mode |= MODE_NOTIFY_ONLY;
+ break;
+ case 'L': /* Listen only */
+ run_mode |= MODE_NO_NOTIFY;
+ break;
+ case 'd': /* No daemon only - log to stderr */
+ run_mode |= MODE_LOG_STDERR;
+ break;
+ case 'o':
+ out_port = atoi(optarg);
+ if (out_port < 1 || out_port > 65535) {
+ fprintf(stderr, "%s: bad port number: %s\n",
+ argv[0], optarg);
+ usage();
+ exit(1);
+ }
+ break;
+ case 'p':
+ port = atoi(optarg);
+ if (port < 1 || port > 65535) {
+ fprintf(stderr, "%s: bad port number: %s\n",
+ argv[0], optarg);
+ usage();
+ exit(1);
+ }
+ break;
+ case 'T': /* NLM TCP and UDP port */
+ nlm_tcp = atoi(optarg);
+ if (nlm_tcp < 1 || nlm_tcp > 65535) {
+ fprintf(stderr, "%s: bad nlm port number: %s\n",
+ argv[0], optarg);
+ usage();
+ exit(1);
+ }
+ if (nlm_udp == 0)
+ nlm_udp = nlm_tcp;
+ break;
+ case 'U': /* NLM UDP port */
+ nlm_udp = atoi(optarg);
+ if (nlm_udp < 1 || nlm_udp > 65535) {
+ fprintf(stderr, "%s: bad nlm UDP port number: %s\n",
+ argv[0], optarg);
+ usage();
+ exit(1);
+ }
+ break;
+ case 'n': /* Specify local hostname */
+ run_mode |= STATIC_HOSTNAME;
+ MY_NAME = xstrdup(optarg);
+ break;
+ case 'P':
+ if (!nsm_setup_pathnames(argv[0], optarg))
+ exit(1);
+ break;
+ case 'H': /* PRC: specify the ha-callout program */
+ if ((ha_callout_prog = xstrdup(optarg)) == NULL)
+ exit(1);
+ break;
+ case '?': /* heeeeeelllllllpppp? heh */
+ case 'h':
+ usage();
+ exit (0);
+ default: /* oh dear ... heh */
+ usage();
+ exit(-1);
+ }
+ }
+
+ /* Refuse to start if another statd is running */
+ if (nfs_probe_statd()) {
+ fprintf(stderr, "Statd service already running!\n");
+ exit(1);
+ }
+
+ if (port == out_port && port != 0) {
+ fprintf(stderr, "Listening and outgoing ports cannot be the same!\n");
+ exit(-1);
+ }
+
+ if (run_mode & MODE_NOTIFY_ONLY) {
+ fprintf(stderr, "%s: -N deprecated, consider using /usr/sbin/sm-notify directly\n",
+ name_p);
+ run_sm_notify(out_port);
+ }
+
+ if (!(run_mode & MODE_NODAEMON)) {
+ run_mode &= ~MODE_LOG_STDERR; /* Never log to console in
+ daemon mode. */
+ }
+
+ if (getrlimit (RLIMIT_NOFILE, &rlim) != 0)
+ fprintf(stderr, "%s: getrlimit (RLIMIT_NOFILE) failed: %s\n",
+ argv [0], strerror(errno));
+ else {
+ /* glibc sunrpc code dies if getdtablesize > FD_SETSIZE */
+ if (rlim.rlim_cur > FD_SETSIZE) {
+ rlim.rlim_cur = FD_SETSIZE;
+
+ if (setrlimit (RLIMIT_NOFILE, &rlim) != 0) {
+ fprintf(stderr, "%s: setrlimit (RLIMIT_NOFILE) failed: %s\n",
+ argv [0], strerror(errno));
+ }
+ }
+ }
+
+ set_nlm_port("tcp", nlm_tcp);
+ set_nlm_port("udp", nlm_udp);
+
+#ifdef SIMULATIONS
+ if (argc > 1)
+ /* LH - I _really_ need to update simulator... */
+ simulator (--argc, ++argv); /* simulator() does exit() */
+#endif
+
+ daemon_init((run_mode & MODE_NODAEMON));
+
+ if (run_mode & MODE_LOG_STDERR) {
+ xlog_syslog(0);
+ xlog_stderr(1);
+ xlog_config(D_ALL, 1);
+ } else {
+ xlog_syslog(1);
+ xlog_stderr(0);
+ }
+
+ xlog_open(name_p);
+ xlog(L_NOTICE, "Version " VERSION " starting");
+
+ log_modes();
+
+ signal (SIGHUP, killer);
+ signal (SIGINT, killer);
+ signal (SIGTERM, killer);
+ /* PRC: trap SIGUSR1 to re-read notify list from disk */
+ signal(SIGUSR1, sigusr);
+ /* WARNING: the following works on Linux and SysV, but not BSD! */
+ signal(SIGCHLD, SIG_IGN);
+ /*
+ * Ignore SIGPIPE to avoid statd dying when peers close their
+ * TCP connection while we're trying to reply to them.
+ */
+ signal(SIGPIPE, SIG_IGN);
+
+ create_pidfile();
+ atexit(truncate_pidfile);
+
+ if (! (run_mode & MODE_NO_NOTIFY))
+ switch (pid = fork()) {
+ case 0:
+ run_sm_notify(out_port);
+ break;
+ case -1:
+ break;
+ default:
+ waitpid(pid, NULL, 0);
+ }
+
+ /* Make sure we have a privilege port for calling into the kernel */
+ if ((notify_sockfd = statd_get_socket()) < 0)
+ exit(1);
+
+ /* If sm-notify didn't take all the state files, load
+ * state information into our notify-list so we can
+ * pass on any SM_NOTIFY that arrives
+ */
+ load_state();
+
+ MY_STATE = nsm_get_state(0);
+ if (MY_STATE == 0)
+ exit(1);
+ xlog(D_GENERAL, "Local NSM state number: %d", MY_STATE);
+ nsm_update_kernel_state(MY_STATE);
+
+ /*
+ * ORDER
+ * Clear old listeners while still root, to override any
+ * permission checking done by rpcbind.
+ */
+ statd_unregister();
+
+ /*
+ * ORDER
+ */
+ if (!nsm_drop_privileges(pidfd))
+ exit(1);
+
+ /*
+ * ORDER
+ * Create RPC listeners after dropping privileges. This permits
+ * statd to unregister its own listeners when it exits.
+ */
+ if (nfs_svc_create("statd", SM_PROG, SM_VERS, sm_prog_1, port) == 0) {
+ xlog(L_ERROR, "failed to create RPC listeners, exiting");
+ exit(1);
+ }
+ atexit(statd_unregister);
+
+ /* If we got this far, we have successfully started */
+ daemon_ready();
+
+ for (;;) {
+ /*
+ * Handle incoming requests: SM_NOTIFY socket requests, as
+ * well as callbacks from lockd.
+ */
+ my_svc_run(notify_sockfd); /* I rolled my own, Olaf made it better... */
+
+ /* Only get here when simulating a crash so we should probably
+ * start sm-notify running again. As we have already dropped
+ * privileges, this might not work, but I don't think
+ * responding to SM_SIMU_CRASH is an important use cases to
+ * get perfect.
+ */
+ if (! (run_mode & MODE_NO_NOTIFY))
+ switch (pid = fork()) {
+ case 0:
+ run_sm_notify(out_port);
+ break;
+ case -1:
+ break;
+ default:
+ waitpid(pid, NULL, 0);
+ }
+
+ }
+ return 0;
+}
diff --git a/utils/statd/statd.h b/utils/statd/statd.h
new file mode 100644
index 0000000..bb1fecb
--- /dev/null
+++ b/utils/statd/statd.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 1995-1997, 1999 Jeffrey A. Uphoff
+ * Modified by Olaf Kirch, Dec. 1996.
+ *
+ * NSM for Linux.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "sm_inter.h"
+#include "system.h"
+#include "xlog.h"
+
+/*
+ * Status definitions.
+ */
+#define STAT_FAIL stat_fail
+#define STAT_SUCC stat_succ
+
+/*
+ * Function prototypes.
+ */
+extern _Bool statd_matchhostname(const char *hostname1, const char *hostname2);
+extern _Bool statd_present_address(const struct sockaddr *sap, char *buf,
+ const size_t buflen);
+__attribute__((__malloc__))
+extern char * statd_canonical_name(const char *hostname);
+
+extern void my_svc_run(int);
+extern void notify_hosts(void);
+extern void shuffle_dirs(void);
+extern int statd_get_socket(void);
+extern int process_notify_list(void);
+extern int process_reply(FD_SET_TYPE *);
+extern char * xstrdup(const char *);
+extern void * xmalloc(size_t);
+extern void load_state(void);
+
+/*
+ * Host status structure and macros.
+ */
+extern stat_chge SM_stat_chge;
+#define MY_NAME SM_stat_chge.mon_name
+#define MY_STATE SM_stat_chge.state
+
+/*
+ * Some timeout values. (Timeout values are in whole seconds.)
+ */
+#define CALLBACK_TIMEOUT 3 /* For client call-backs. */
+#define NOTIFY_TIMEOUT 5 /* For status-change notifications. */
+#define SELECT_TIMEOUT 10 /* Max select() timeout when work to do. */
+#define MAX_TRIES 5 /* Max number of tries for any host. */
+
+/*
+ * Modes of operation - Lon
+ */
+extern int run_mode;
+#define MODE_NODAEMON 1 /* No-daemon/foreground mode. */
+#define MODE_LOG_STDERR 2 /* in foreground mode, log to stderr */
+#define MODE_NOTIFY_ONLY 4 /* Send SM_NOTIFY to everyone monitored on
+ a single interface/alias */
+/* LH - notify_only mode would be for notifying hosts on an IP alias
+ * that just came back up, for ex, when failing over a HA service to
+ * another host.... */
+#define STATIC_HOSTNAME 8 /* Always use the hostname set by -n */
+#define MODE_NO_NOTIFY 16 /* Don't notify peers of a reboot */
diff --git a/utils/statd/statd.man b/utils/statd/statd.man
new file mode 100644
index 0000000..7441ffd
--- /dev/null
+++ b/utils/statd/statd.man
@@ -0,0 +1,474 @@
+.\"@(#)rpc.statd.8"
+.\"
+.\" Copyright (C) 1999 Olaf Kirch <okir@monad.swb.de>
+.\" Modified by Jeffrey A. Uphoff, 1999, 2002, 2005.
+.\" Modified by Lon Hohberger, 2000.
+.\" Modified by Paul Clements, 2004.
+.\"
+.\" Rewritten by Chuck Lever <chuck.lever@oracle.com>, 2009.
+.\" Copyright 2009 Oracle. All rights reserved.
+.\"
+.TH RPC.STATD 8 "1 November 2009"
+.SH NAME
+rpc.statd \- NSM service daemon
+.SH SYNOPSIS
+.BI "rpc.statd [-dh?FLNvV] [-H " prog "] [-n " my-name "] [-o " outgoing-port ]
+.ti +10
+.BI "[-p " listener-port "] [-P " path ]
+.ti +10
+.BI "[--nlm-port " port "] [--nlm-udp-port " port ]
+.SH DESCRIPTION
+File locks are not part of persistent file system state.
+Lock state is thus lost when a host reboots.
+.PP
+Network file systems must also detect when lock state is lost
+because a remote host has rebooted.
+After an NFS client reboots, an NFS server must release all file locks
+held by applications that were running on that client.
+After a server reboots, a client must remind the
+server of file locks held by applications running on that client.
+.PP
+For NFS version 2 [RFC1094] and NFS version 3 [RFC1813], the
+.I Network Status Monitor
+protocol (or NSM for short)
+is used to notify NFS peers of reboots.
+On Linux, two separate user-space components constitute the NSM service:
+.TP
+.B rpc.statd
+A daemon that listens for reboot notifications from other hosts, and
+manages the list of hosts to be notified when the local system reboots
+.TP
+.B sm-notify
+A helper program that notifies NFS peers after the local system reboots
+.PP
+The local NFS lock manager alerts its local
+.B rpc.statd
+of each remote peer that should be monitored.
+When the local system reboots, the
+.B sm-notify
+command notifies the NSM service on monitored peers of the reboot.
+When a remote reboots, that peer notifies the local
+.BR rpc.statd ,
+which in turn passes the reboot notification
+back to the local NFS lock manager.
+.SH NSM OPERATION IN DETAIL
+The first file locking interaction between an NFS client and server causes
+the NFS lock managers on both peers to contact their local NSM service to
+store information about the opposite peer.
+On Linux, the local lock manager contacts
+.BR rpc.statd .
+.PP
+.B rpc.statd
+records information about each monitored NFS peer on persistent storage.
+This information describes how to contact a remote peer
+in case the local system reboots,
+how to recognize which monitored peer is reporting a reboot,
+and how to notify the local lock manager when a monitored peer
+indicates it has rebooted.
+.PP
+An NFS client sends a hostname, known as the client's
+.IR caller_name ,
+in each file lock request.
+An NFS server can use this hostname to send asynchronous GRANT
+calls to a client, or to notify the client it has rebooted.
+.PP
+The Linux NFS server can provide the client's
+.I caller_name
+or the client's network address to
+.BR rpc.statd .
+For the purposes of the NSM protocol,
+this name or address is known as the monitored peer's
+.IR mon_name .
+In addition, the local lock manager tells
+.B rpc.statd
+what it thinks its own hostname is.
+For the purposes of the NSM protocol,
+this hostname is known as
+.IR my_name .
+.PP
+There is no equivalent interaction between an NFS server and a client
+to inform the client of the server's
+.IR caller_name .
+Therefore NFS clients do not actually know what
+.I mon_name
+an NFS server might use in an SM_NOTIFY request.
+The Linux NFS client uses the server hostname from the mount command
+to identify rebooting NFS servers.
+.SS Reboot notification
+When the local system reboots, the
+.B sm-notify
+command reads the list of monitored peers from persistent storage and
+sends an SM_NOTIFY request to the NSM service on each listed remote peer.
+It uses the
+.I mon_name
+string as the destination.
+To identify which host has rebooted, the
+.B sm-notify
+command sends the
+.I my_name
+string recorded when that remote was monitored.
+The remote
+.B rpc.statd
+matches incoming SM_NOTIFY requests using this string,
+or the caller's network address,
+to one or more peers on its own monitor list.
+.PP
+If
+.B rpc.statd
+does not find a peer on its monitor list that matches
+an incoming SM_NOTIFY request,
+the notification is not forwarded to the local lock manager.
+In addition, each peer has its own
+.IR "NSM state number" ,
+a 32-bit integer that is bumped after each reboot by the
+.B sm-notify
+command.
+.B rpc.statd
+uses this number to distinguish between actual reboots
+and replayed notifications.
+.PP
+Part of NFS lock recovery is rediscovering
+which peers need to be monitored again.
+The
+.B sm-notify
+command clears the monitor list on persistent storage after each reboot.
+.SH OPTIONS
+.TP
+.BR -d , " --no-syslog
+Causes
+.B rpc.statd
+to write log messages on
+.I stderr
+instead of to the system log,
+if the
+.B -F
+option was also specified.
+.TP
+.BR -F , " --foreground
+Keeps
+.B rpc.statd
+attached to its controlling terminal so that NSM
+operation can be monitored directly or run under a debugger.
+If this option is not specified,
+.B rpc.statd
+backgrounds itself soon after it starts.
+.TP
+.BR -h , " -?" , " --help
+Causes
+.B rpc.statd
+to display usage information on
+.I stderr
+and then exit.
+.TP
+.BI "\-H," "" " \-\-ha-callout " prog
+Specifies a high availability callout program.
+If this option is not specified, no callouts are performed.
+See the
+.B High-availability callouts
+section below for details.
+.TP
+.BR -L , " --no-notify
+Prevents
+.B rpc.statd
+from running the
+.B sm-notify
+command when it starts up,
+preserving the existing NSM state number and monitor list.
+.IP
+Note: the
+.B sm-notify
+command contains a check to ensure it runs only once after each system reboot.
+This prevents spurious reboot notification if
+.B rpc.statd
+restarts without the
+.B -L
+option.
+.TP
+.BI "\-n, " "" "\-\-name " ipaddr " | " hostname
+This string is only used by the
+.B sm-notify
+command as the source address from which to send reboot notification requests.
+.IP
+The
+.I ipaddr
+form can be expressed as either an IPv4 or an IPv6 presentation address.
+If this option is not specified,
+.B rpc.statd
+uses a wildcard address as the transport bind address.
+See
+.BR sm-notify (8)
+for details.
+.TP
+.BR -N
+Causes
+.B rpc.statd
+to run the
+.B sm-notify
+command, and then exit.
+Since the
+.B sm-notify
+command can also be run directly, this option is deprecated.
+.TP
+.BI "\-o," "" " \-\-outgoing\-port " port
+Specifies the source port number the
+.B sm-notify
+command should use when sending reboot notifications.
+See
+.BR sm-notify (8)
+for details.
+.TP
+.BI "\-p," "" " \-\-port " port
+Specifies the port number used for RPC listener sockets.
+If this option is not specified,
+.B rpc.statd
+will try to consult
+.IR /etc/services ,
+if gets port succeed, set the same port for all listener socket,
+otherwise chooses a random ephemeral port for each listener socket.
+.IP
+This option can be used to fix the port value of its listeners when
+SM_NOTIFY requests must traverse a firewall between clients and
+servers.
+.TP
+.BI "\-T," "" " \-\-nlm\-port " port
+Specifies the port number that
+.I lockd
+should listen on for
+.B NLM
+requests. This sets both the TCP and UDP ports unless the UDP port is
+set separately.
+.TP
+.BI "\-U," "" " \-\-nlm\-udp\-port " port
+Specifies the UDP port number that
+.I lockd
+should listen on for
+.B NLM
+requests.
+.TP
+.BI "\-P, " "" \-\-state\-directory\-path " pathname"
+Specifies the pathname of the parent directory
+where NSM state information resides.
+If this option is not specified,
+.B rpc.statd
+uses
+.I /var/lib/nfs
+by default.
+.IP
+After starting,
+.B rpc.statd
+attempts to set its effective UID and GID to the owner
+and group of the subdirectory
+.B sm
+of this directory. After changing the effective ids,
+.B rpc.statd
+only needs to access files in
+.B sm
+and
+.B sm.bak
+within the state-directory-path.
+.TP
+.BR -v ", " -V ", " --version
+Causes
+.B rpc.statd
+to display version information on
+.I stderr
+and then exit.
+.SH CONFIGURATION FILE
+Many of the options that can be set on the command line can also be
+controlled through values set in the
+.B [statd]
+or, in some cases, the
+.B [lockd]
+sections of the
+.I /etc/nfs.conf
+configuration file.
+Values recognized in the
+.B [statd]
+section include
+.BR port ,
+.BR outgoing-port ,
+.BR name ,
+.BR state-directory-path ", and"
+.B ha-callout
+which each have the same effect as the option with the same name.
+
+The values recognized in the
+.B [lockd]
+section include
+.B port
+and
+.B udp-port
+which have the same effect as the
+.B --nlm-port
+and
+.B --nlm-udp-port
+options, respectively.
+
+.SH SECURITY
+The
+.B rpc.statd
+daemon must be started as root to acquire privileges needed
+to create sockets with privileged source ports, and to access the
+state information database.
+Because
+.B rpc.statd
+maintains a long-running network service, however, it drops root privileges
+as soon as it starts up to reduce the risk of a privilege escalation attack.
+.PP
+During normal operation,
+the effective user ID it chooses is the owner of the state directory.
+This allows it to continue to access files in that directory after it
+has dropped its root privileges.
+To control which user ID
+.B rpc.statd
+chooses, simply use
+.BR chown (1)
+to set the owner of
+the state directory.
+.PP
+You can also protect your
+.B rpc.statd
+listeners using the
+.B tcp_wrapper
+library or
+.BR iptables (8).
+To use the
+.B tcp_wrapper
+library, add the hostnames of peers that should be allowed access to
+.IR /etc/hosts.allow .
+Use the daemon name
+.B statd
+even if the
+.B rpc.statd
+binary has a different filename.
+.P
+For further information see the
+.BR tcpd (8)
+and
+.BR hosts_access (5)
+man pages.
+.SH ADDITIONAL NOTES
+Lock recovery after a reboot is critical to maintaining data integrity
+and preventing unnecessary application hangs.
+To help
+.B rpc.statd
+match SM_NOTIFY requests to NLM requests, a number of best practices
+should be observed, including:
+.IP
+The UTS nodename of your systems should match the DNS names that NFS
+peers use to contact them
+.IP
+The UTS nodenames of your systems should always be fully qualified domain names
+.IP
+The forward and reverse DNS mapping of the UTS nodenames should be
+consistent
+.IP
+The hostname the client uses to mount the server should match the server's
+.I mon_name
+in SM_NOTIFY requests it sends
+.PP
+Unmounting an NFS file system does not necessarily stop
+either the NFS client or server from monitoring each other.
+Both may continue monitoring each other for a time in case subsequent
+NFS traffic between the two results in fresh mounts and additional
+file locking.
+.PP
+On Linux, if the
+.B lockd
+kernel module is unloaded during normal operation,
+all remote NFS peers are unmonitored.
+This can happen on an NFS client, for example,
+if an automounter removes all NFS mount
+points due to inactivity.
+.SS High-availability callouts
+.B rpc.statd
+can exec a special callout program during processing of
+successful SM_MON, SM_UNMON, and SM_UNMON_ALL requests,
+or when it receives SM_NOTIFY.
+Such a program may be used in High Availability NFS (HA-NFS)
+environments to track lock state that may need to be migrated after
+a system reboot.
+.PP
+The name of the callout program is specified with the
+.B -H
+option.
+The program is run with 3 arguments:
+The first is either
+.B add-client
+.B del-client
+or
+.B sm-notify
+depending on the reason for the callout.
+The second is the
+.I mon_name
+of the monitored peer.
+The third is the
+.I caller_name
+of the requesting lock manager for
+.B add-client
+or
+.B del-client
+, otherwise it is
+.I IP_address
+of the caller sending SM_NOTIFY.
+The forth is the
+.I state_value
+in the SM_NOTIFY request.
+
+.SS IPv6 and TI-RPC support
+TI-RPC is a pre-requisite for supporting NFS on IPv6.
+If TI-RPC support is built into
+.BR rpc.statd ,
+it attempts to start listeners on network transports marked 'visible' in
+.IR /etc/netconfig .
+As long as at least one network transport listener starts successfully,
+.B rpc.statd
+will operate.
+.SH ENVIRONMENT
+.TP
+.B RPC_STATD_NO_NOTIFY=
+If set to a positive integer, has the same effect as
+.IR \-\-no\-notify .
+.SH FILES
+.TP 2.5i
+.I /var/lib/nfs/sm
+directory containing monitor list
+.TP 2.5i
+.I /var/lib/nfs/sm.bak
+directory containing notify list
+.TP 2.5i
+.I /var/lib/nfs/state
+NSM state number for this host
+.TP 2.5i
+.I /run/run.statd.pid
+pid file
+.TP 2.5i
+.I /etc/netconfig
+network transport capability database
+.SH SEE ALSO
+.BR sm-notify (8),
+.BR nfs (5),
+.BR rpc.nfsd (8),
+.BR rpcbind (8),
+.BR tcpd (8),
+.BR hosts_access (5),
+.BR iptables (8),
+.BR netconfig (5)
+.sp
+RFC 1094 - "NFS: Network File System Protocol Specification"
+.br
+RFC 1813 - "NFS Version 3 Protocol Specification"
+.br
+OpenGroup Protocols for Interworking: XNFS, Version 3W - Chapter 11
+.SH AUTHORS
+Jeff Uphoff <juphoff@users.sourceforge.net>
+.br
+Olaf Kirch <okir@monad.swb.de>
+.br
+H.J. Lu <hjl@gnu.org>
+.br
+Lon Hohberger <hohberger@missioncriticallinux.com>
+.br
+Paul Clements <paul.clements@steeleye.com>
+.br
+Chuck Lever <chuck.lever@oracle.com>
diff --git a/utils/statd/svc_run.c b/utils/statd/svc_run.c
new file mode 100644
index 0000000..e343c76
--- /dev/null
+++ b/utils/statd/svc_run.c
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 1984 Sun Microsystems, Inc.
+ * Modified by Jeffrey A. Uphoff, 1995, 1997-1999.
+ * Modified by Olaf Kirch, 1996.
+ *
+ * NSM for Linux.
+ */
+
+/*
+ * Copyright (c) 2009, Sun Microsystems, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * - 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.
+ * - Neither the name of Sun Microsystems, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER 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.
+ */
+
+/*
+ * This has been modified for my own evil purposes to prevent deadlocks
+ * when two hosts start NSM's simultaneously and try to notify each
+ * other (which mainly occurs during testing), or to stop and smell the
+ * roses when I have callbacks due.
+ * --Jeff Uphoff.
+ */
+
+/*
+ * This is the RPC server side idle loop.
+ * Wait for input, call server program.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <time.h>
+#include <inttypes.h>
+#include "statd.h"
+#include "notlist.h"
+
+void my_svc_exit(void);
+static int svc_stop = 0;
+
+/*
+ * This is the global notify list onto which all SM_NOTIFY and CALLBACK
+ * requests are put.
+ */
+notify_list * notify = NULL;
+
+/*
+ * Jump-off function.
+ */
+void
+my_svc_exit(void)
+{
+ svc_stop = 1;
+}
+
+
+/*
+ * The heart of the server. A crib from libc for the most part...
+ */
+void
+my_svc_run(int sockfd)
+{
+ FD_SET_TYPE readfds;
+ int selret;
+ time_t now;
+
+ svc_stop = 0;
+
+ for (;;) {
+ if (svc_stop)
+ return;
+
+ /* Ah, there are some notifications to be processed */
+ while (notify && NL_WHEN(notify) <= time(&now)) {
+ process_notify_list();
+ }
+
+ readfds = SVC_FDSET;
+ /* Set notify sockfd for waiting for reply */
+ FD_SET(sockfd, &readfds);
+ if (notify) {
+ struct timeval tv;
+
+ tv.tv_sec = NL_WHEN(notify) - now;
+ tv.tv_usec = 0;
+ xlog(D_GENERAL, "Waiting for reply... (timeo %jd)",
+ (intmax_t)tv.tv_sec);
+ selret = select(FD_SETSIZE, &readfds,
+ (void *) 0, (void *) 0, &tv);
+ } else {
+ xlog(D_GENERAL, "Waiting for client connections");
+ selret = select(FD_SETSIZE, &readfds,
+ (void *) 0, (void *) 0, (struct timeval *) 0);
+ }
+
+ switch (selret) {
+ case -1:
+ if (errno == EINTR || errno == ECONNREFUSED
+ || errno == ENETUNREACH || errno == EHOSTUNREACH)
+ continue;
+ xlog(L_ERROR, "my_svc_run() - select: %m");
+ return;
+
+ case 0:
+ /* A notify/callback timed out. */
+ continue;
+
+ default:
+ selret -= process_reply(&readfds);
+ if (selret) {
+ FD_CLR(sockfd, &readfds);
+ svc_getreqset(&readfds);
+ }
+ }
+ }
+}
diff --git a/utils/statd/system.h b/utils/statd/system.h
new file mode 100644
index 0000000..a1739c4
--- /dev/null
+++ b/utils/statd/system.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 1996 Olaf Kirch
+ * Modified by Jeffrey A. Uphoff, 1997, 1999.
+ *
+ * NSM for Linux.
+ */
+
+/*
+ * System-dependent declarations
+ */
+
+#ifdef FD_SETSIZE
+# define FD_SET_TYPE fd_set
+# define SVC_FDSET svc_fdset
+#else
+# define FD_SET_TYPE int
+# define SVC_FDSET svc_fds
+#endif