diff options
Diffstat (limited to '')
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(¬ify, 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(¬ify, lp); + nlist_insert_timer(¬ify, 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(¬ify, lp); + return 1; +} + +/* + * Process a notify list, either for notifying remote hosts after reboot + * or for calling back (local) statd clients when the remote has notified + * us of a crash. + */ +int +process_notify_list(void) +{ + notify_list *entry; + time_t now; + + while ((entry = notify) != NULL && NL_WHEN(entry) < time(&now)) { + if (process_entry(entry)) { + NL_WHEN(entry) = time(NULL) + NOTIFY_TIMEOUT; + nlist_remove(¬ify, entry); + nlist_insert_timer(¬ify, 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(¬ify, entry); + } + } + + return 1; +} diff --git a/utils/statd/sim_sm_inter.x b/utils/statd/sim_sm_inter.x new file mode 100644 index 0000000..4346199 --- /dev/null +++ b/utils/statd/sim_sm_inter.x @@ -0,0 +1,32 @@ +/* + * Copyright (C) 1995, 1997-1999 Jeffrey A. Uphoff + * Modified by Olaf Kirch, 1996. + * Modified by H.J. Lu, 1998. + * + * NSM for Linux. + */ + +#ifdef RPC_CLNT +%#include <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 |