diff options
Diffstat (limited to 'support/nsm')
-rw-r--r-- | support/nsm/Makefile.am | 46 | ||||
-rw-r--r-- | support/nsm/Makefile.in | 744 | ||||
-rw-r--r-- | support/nsm/file.c | 1092 | ||||
-rw-r--r-- | support/nsm/rpc.c | 536 | ||||
-rw-r--r-- | support/nsm/sm_inter.x | 131 |
5 files changed, 2549 insertions, 0 deletions
diff --git a/support/nsm/Makefile.am b/support/nsm/Makefile.am new file mode 100644 index 0000000..8f5874e --- /dev/null +++ b/support/nsm/Makefile.am @@ -0,0 +1,46 @@ +## Process this file with automake to produce Makefile.in + +GENFILES_CLNT = sm_inter_clnt.c +GENFILES_SVC = sm_inter_svc.c +GENFILES_XDR = sm_inter_xdr.c +GENFILES_H = sm_inter.h + +GENFILES = $(GENFILES_CLNT) $(GENFILES_SVC) $(GENFILES_XDR) $(GENFILES_H) + +EXTRA_DIST = sm_inter.x + +noinst_LIBRARIES = libnsm.a +libnsm_a_SOURCES = $(GENFILES) file.c rpc.c + +BUILT_SOURCES = $(GENFILES) + +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 -i 0 -o $@ $< + +$(GENFILES_H): %.h: %.x $(RPCGEN) + test -f $@ && rm -rf $@ || true + $(RPCGEN) -h -o $@ $< + echo "void sm_prog_1(struct svc_req *, SVCXPRT *);" >> $@ + rm -f $(top_builddir)/support/include/sm_inter.h + $(LN_S) ../nsm/sm_inter.h $(top_builddir)/support/include/sm_inter.h + +MAINTAINERCLEANFILES = Makefile.in + +CLEANFILES = $(GENFILES) $(top_builddir)/support/include/sm_inter.h diff --git a/support/nsm/Makefile.in b/support/nsm/Makefile.in new file mode 100644 index 0000000..54eaea9 --- /dev/null +++ b/support/nsm/Makefile.in @@ -0,0 +1,744 @@ +# Makefile.in generated by automake 1.16.5 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2021 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = support/nsm +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/aclocal/ax_gcc_func_attribute.m4 \ + $(top_srcdir)/aclocal/bsdsignals.m4 \ + $(top_srcdir)/aclocal/getrandom.m4 \ + $(top_srcdir)/aclocal/ipv6.m4 \ + $(top_srcdir)/aclocal/kerberos5.m4 \ + $(top_srcdir)/aclocal/keyutils.m4 \ + $(top_srcdir)/aclocal/libblkid.m4 \ + $(top_srcdir)/aclocal/libcap.m4 \ + $(top_srcdir)/aclocal/libevent.m4 \ + $(top_srcdir)/aclocal/libpthread.m4 \ + $(top_srcdir)/aclocal/libsqlite3.m4 \ + $(top_srcdir)/aclocal/libtirpc.m4 \ + $(top_srcdir)/aclocal/libxml2.m4 \ + $(top_srcdir)/aclocal/nfs-utils.m4 \ + $(top_srcdir)/aclocal/rpcsec_vers.m4 \ + $(top_srcdir)/aclocal/tcp-wrappers.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/support/include/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +LIBRARIES = $(noinst_LIBRARIES) +ARFLAGS = cru +AM_V_AR = $(am__v_AR_@AM_V@) +am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@) +am__v_AR_0 = @echo " AR " $@; +am__v_AR_1 = +libnsm_a_AR = $(AR) $(ARFLAGS) +libnsm_a_LIBADD = +am__objects_1 = sm_inter_clnt.$(OBJEXT) +am__objects_2 = sm_inter_svc.$(OBJEXT) +am__objects_3 = sm_inter_xdr.$(OBJEXT) +am__objects_4 = +am__objects_5 = $(am__objects_1) $(am__objects_2) $(am__objects_3) \ + $(am__objects_4) +am_libnsm_a_OBJECTS = $(am__objects_5) file.$(OBJEXT) rpc.$(OBJEXT) +libnsm_a_OBJECTS = $(am_libnsm_a_OBJECTS) +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/support/include +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/file.Po ./$(DEPDIR)/rpc.Po \ + ./$(DEPDIR)/sm_inter_clnt.Po ./$(DEPDIR)/sm_inter_svc.Po \ + ./$(DEPDIR)/sm_inter_xdr.Po +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +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 = +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 = $(libnsm_a_SOURCES) +DIST_SOURCES = $(libnsm_a_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ +ALLOCA = @ALLOCA@ +AMTAR = @AMTAR@ +AM_CFLAGS = @AM_CFLAGS@ +AM_CPPFLAGS = @AM_CPPFLAGS@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CC_FOR_BUILD = @CC_FOR_BUILD@ +CFLAGS = @CFLAGS@ +CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@ +CSCOPE = @CSCOPE@ +CTAGS = @CTAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CXXFLAGS_FOR_BUILD = @CXXFLAGS_FOR_BUILD@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +ETAGS = @ETAGS@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +FILECMD = @FILECMD@ +GREP = @GREP@ +GSSAPI_CFLAGS = @GSSAPI_CFLAGS@ +GSSAPI_LIBS = @GSSAPI_LIBS@ +GSSD = @GSSD@ +GSSGLUE_CFLAGS = @GSSGLUE_CFLAGS@ +GSSGLUE_LIBS = @GSSGLUE_LIBS@ +GSSKRB_CFLAGS = @GSSKRB_CFLAGS@ +GSSKRB_LIBS = @GSSKRB_LIBS@ +HAVE_GETRANDOM = @HAVE_GETRANDOM@ +HAVE_LIBWRAP = @HAVE_LIBWRAP@ +HAVE_TCP_WRAPPER = @HAVE_TCP_WRAPPER@ +IDMAPD = @IDMAPD@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +K5VERS = @K5VERS@ +KRBCFLAGS = @KRBCFLAGS@ +KRBDIR = @KRBDIR@ +KRBLDFLAGS = @KRBLDFLAGS@ +KRBLIBS = @KRBLIBS@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@ +LIBBLKID = @LIBBLKID@ +LIBBSD = @LIBBSD@ +LIBCAP = @LIBCAP@ +LIBCRYPT = @LIBCRYPT@ +LIBEVENT = @LIBEVENT@ +LIBKEYUTILS = @LIBKEYUTILS@ +LIBMOUNT = @LIBMOUNT@ +LIBMOUNT_CFLAGS = @LIBMOUNT_CFLAGS@ +LIBMOUNT_LIBS = @LIBMOUNT_LIBS@ +LIBNSL = @LIBNSL@ +LIBOBJS = @LIBOBJS@ +LIBPTHREAD = @LIBPTHREAD@ +LIBS = @LIBS@ +LIBSOCKET = @LIBSOCKET@ +LIBSQLITE = @LIBSQLITE@ +LIBTIRPC = @LIBTIRPC@ +LIBTOOL = @LIBTOOL@ +LIBWRAP = @LIBWRAP@ +LIBXML2 = @LIBXML2@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_PLUGINS = @PATH_PLUGINS@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +RANLIB = @RANLIB@ +RELEASE = @RELEASE@ +RPCGEN_PATH = @RPCGEN_PATH@ +RPCSECGSS_CFLAGS = @RPCSECGSS_CFLAGS@ +RPCSECGSS_LIBS = @RPCSECGSS_LIBS@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +SVCGSSD = @SVCGSSD@ +TIRPC_CFLAGS = @TIRPC_CFLAGS@ +TIRPC_LIBS = @TIRPC_LIBS@ +VERSION = @VERSION@ +XML2_CFLAGS = @XML2_CFLAGS@ +XML2_LIBS = @XML2_LIBS@ +_rpc_pipefsmount = @_rpc_pipefsmount@ +_statedir = @_statedir@ +_sysconfdir = @_sysconfdir@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +enable_gss = @enable_gss@ +enable_ipv6 = @enable_ipv6@ +enable_mountconfig = @enable_mountconfig@ +enable_nfsv4 = @enable_nfsv4@ +enable_nfsv41 = @enable_nfsv41@ +enable_svcgss = @enable_svcgss@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +kprefix = @kprefix@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +mountfile = @mountfile@ +nfsconfig = @nfsconfig@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +rpc_pipefsmount = @rpc_pipefsmount@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +startstatd = @startstatd@ +statdpath = @statdpath@ +statduser = @statduser@ +statedir = @statedir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +unitdir = @unitdir@ +GENFILES_CLNT = sm_inter_clnt.c +GENFILES_SVC = sm_inter_svc.c +GENFILES_XDR = sm_inter_xdr.c +GENFILES_H = sm_inter.h +GENFILES = $(GENFILES_CLNT) $(GENFILES_SVC) $(GENFILES_XDR) $(GENFILES_H) +EXTRA_DIST = sm_inter.x +noinst_LIBRARIES = libnsm.a +libnsm_a_SOURCES = $(GENFILES) file.c rpc.c +BUILT_SOURCES = $(GENFILES) +@CONFIG_RPCGEN_FALSE@RPCGEN = @RPCGEN_PATH@ +@CONFIG_RPCGEN_TRUE@RPCGEN = $(top_builddir)/tools/rpcgen/rpcgen +MAINTAINERCLEANFILES = Makefile.in +CLEANFILES = $(GENFILES) $(top_builddir)/support/include/sm_inter.h +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 support/nsm/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu support/nsm/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) + +libnsm.a: $(libnsm_a_OBJECTS) $(libnsm_a_DEPENDENCIES) $(EXTRA_libnsm_a_DEPENDENCIES) + $(AM_V_at)-rm -f libnsm.a + $(AM_V_AR)$(libnsm_a_AR) libnsm.a $(libnsm_a_OBJECTS) $(libnsm_a_LIBADD) + $(AM_V_at)$(RANLIB) libnsm.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rpc.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sm_inter_clnt.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sm_inter_svc.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sm_inter_xdr.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 + +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 $(LIBRARIES) +installdirs: +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-noinstLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/file.Po + -rm -f ./$(DEPDIR)/rpc.Po + -rm -f ./$(DEPDIR)/sm_inter_clnt.Po + -rm -f ./$(DEPDIR)/sm_inter_svc.Po + -rm -f ./$(DEPDIR)/sm_inter_xdr.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-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f ./$(DEPDIR)/file.Po + -rm -f ./$(DEPDIR)/rpc.Po + -rm -f ./$(DEPDIR)/sm_inter_clnt.Po + -rm -f ./$(DEPDIR)/sm_inter_svc.Po + -rm -f ./$(DEPDIR)/sm_inter_xdr.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: + +.MAKE: all check install install-am install-exec install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ + clean-generic clean-libtool clean-noinstLIBRARIES \ + cscopelist-am ctags ctags-am distclean distclean-compile \ + distclean-generic distclean-libtool distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am uninstall uninstall-am + +.PRECIOUS: Makefile + +@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 -i 0 -o $@ $< + +$(GENFILES_H): %.h: %.x $(RPCGEN) + test -f $@ && rm -rf $@ || true + $(RPCGEN) -h -o $@ $< + echo "void sm_prog_1(struct svc_req *, SVCXPRT *);" >> $@ + rm -f $(top_builddir)/support/include/sm_inter.h + $(LN_S) ../nsm/sm_inter.h $(top_builddir)/support/include/sm_inter.h + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/support/nsm/file.c b/support/nsm/file.c new file mode 100644 index 0000000..f5b4480 --- /dev/null +++ b/support/nsm/file.c @@ -0,0 +1,1092 @@ +/* + * 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. + * + * Callback information and NSM state is stored in files, usually + * under /var/lib/nfs. A database of information contained in local + * files stores NLM callback data and what remote peers to notify of + * reboots. + * + * For each monitored remote peer, a text file is created under the + * directory specified by NSM_MONITOR_DIR. The name of the file + * is a valid DNS hostname. The hostname string must be a valid + * ASCII DNS name, and must not contain slash characters, white space, + * or '\0' (ie. anything that might have some special meaning in a + * path name). + * + * The contents of each file include seven blank-separated fields of + * text, finished with '\n'. The first field contains the network + * address of the NLM service to call back. The current implementation + * supports using only IPv4 addresses, so the only contents of this + * field are a network order IPv4 address expressed in 8 hexadecimal + * characters. + * + * The next four fields are text strings of hexadecimal characters, + * representing: + * + * 2. A 4 byte RPC program number of the NLM service to call back + * 3. A 4 byte RPC version number of the NLM service to call back + * 4. A 4 byte RPC procedure number of the NLM service to call back + * 5. A 16 byte opaque cookie that the NLM service uses to identify + * the monitored host + * + * The sixth field is the monitored host's mon_name, passed to statd + * via an SM_MON request. + * + * The seventh field is the my_name for this peer, which is the + * hostname of the local NLM (currently on Linux, the result of + * `uname -n`). This can be used as the source address/hostname + * when sending SM_NOTIFY requests. + * + * The NSM protocol does not limit the contents of these strings + * in any way except that they must fit into 1024 bytes. Our + * implementation requires that these strings not contain + * white space or '\0'. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <sys/types.h> +#ifdef HAVE_SYS_CAPABILITY_H +#include <sys/capability.h> +#endif +#include <sys/prctl.h> +#include <sys/stat.h> + +#include <ctype.h> +#include <string.h> +#include <stdint.h> +#ifndef S_SPLINT_S +#include <unistd.h> +#endif +#include <libgen.h> +#include <stdio.h> +#include <errno.h> +#include <fcntl.h> +#include <dirent.h> +#include <grp.h> + +#include "xlog.h" +#include "nsm.h" +#include "misc.h" + +#define RPCARGSLEN (4 * (8 + 1)) +#define LINELEN (RPCARGSLEN + SM_PRIV_SIZE * 2 + 1) + +#define NSM_KERNEL_STATE_FILE "/proc/sys/fs/nfs/nsm_local_state" + +static char nsm_base_dirname[PATH_MAX] = NSM_DEFAULT_STATEDIR; + +#define NSM_MONITOR_DIR "sm" +#define NSM_NOTIFY_DIR "sm.bak" +#define NSM_STATE_FILE "state" + + +static _Bool +error_check(const int len, const size_t buflen) +{ + return (len < 0) || ((size_t)len >= buflen); +} + +static _Bool +exact_error_check(const ssize_t len, const size_t buflen) +{ + return (len < 0) || ((size_t)len != buflen); +} + +/* + * Returns a dynamically allocated, '\0'-terminated buffer + * containing an appropriate pathname, or NULL if an error + * occurs. Caller must free the returned result with free(3). + */ +__attribute__((__malloc__)) +static char * +nsm_make_record_pathname(const char *directory, const char *hostname) +{ + const char *c; + size_t size; + char *path; + int len; + + /* + * Block hostnames that contain characters that have + * meaning to the file system (like '/'), or that can + * be confusing on visual inspection (like ' '). + */ + for (c = hostname; *c != '\0'; c++) + if (*c == '/' || isspace((int)*c) != 0) { + xlog(D_GENERAL, "Hostname contains invalid characters"); + return NULL; + } + + size = strlen(nsm_base_dirname) + strlen(directory) + strlen(hostname) + 3; + if (size > PATH_MAX) { + xlog(D_GENERAL, "Hostname results in pathname that is too long"); + return NULL; + } + + path = malloc(size); + if (path == NULL) { + xlog(D_GENERAL, "Failed to allocate memory for pathname"); + return NULL; + } + + len = snprintf(path, size, "%s/%s/%s", + nsm_base_dirname, directory, hostname); + if (error_check(len, size)) { + xlog(D_GENERAL, "Pathname did not fit in specified buffer"); + free(path); + return NULL; + } + + return path; +} + +/* + * Returns a dynamically allocated, '\0'-terminated buffer + * containing an appropriate pathname, or NULL if an error + * occurs. Caller must free the returned result with free(3). + */ +__attribute__((__malloc__)) +static char * +nsm_make_pathname(const char *directory) +{ + return generic_make_pathname(nsm_base_dirname, directory); +} + +/* + * Returns a dynamically allocated, '\0'-terminated buffer + * containing an appropriate pathname, or NULL if an error + * occurs. Caller must free the returned result with free(3). + */ +__attribute__((__malloc__)) +static char * +nsm_make_temp_pathname(const char *pathname) +{ + size_t size; + char *path; + int len; + + size = strlen(pathname) + sizeof(".new") + 2; + if (size > PATH_MAX) + return NULL; + + path = malloc(size); + if (path == NULL) + return NULL; + + len = snprintf(path, size, "%s.new", pathname); + if (error_check(len, size)) { + free(path); + return NULL; + } + + return path; +} + +/* + * Use "mktemp, write, rename" to update the contents of a file atomically. + * + * Returns true if completely successful, or false if some error occurred. + */ +static _Bool +nsm_atomic_write(const char *path, const void *buf, const size_t buflen) +{ + _Bool result = false; + ssize_t len; + char *temp; + int fd; + + temp = nsm_make_temp_pathname(path); + if (temp == NULL) { + xlog(L_ERROR, "Failed to create new path for %s", path); + goto out; + } + + fd = open(temp, O_CREAT | O_TRUNC | O_SYNC | O_WRONLY, 0644); + if (fd == -1) { + xlog(L_ERROR, "Failed to create %s: %m", temp); + goto out; + } + + len = write(fd, buf, buflen); + if (exact_error_check(len, buflen)) { + xlog(L_ERROR, "Failed to write %s: %m", temp); + (void)close(fd); + (void)unlink(temp); + goto out; + } + + if (close(fd) == -1) { + xlog(L_ERROR, "Failed to close %s: %m", temp); + (void)unlink(temp); + goto out; + } + + if (rename(temp, path) == -1) { + xlog(L_ERROR, "Failed to rename %s -> %s: %m", + temp, path); + (void)unlink(temp); + goto out; + } + + /* Ostensibly, a sync(2) is not needed here because + * open(O_CREAT), write(O_SYNC), and rename(2) are + * already synchronous with persistent storage, for + * any file system we care about. */ + + result = true; + +out: + free(temp); + return result; +} + +/** + * nsm_setup_pathnames - set up pathname + * @progname: C string containing name of program, for error messages + * @parentdir: C string containing pathname to on-disk state, or NULL + * + * This runs before logging is set up, so error messages are directed + * to stderr. + * + * Returns true and sets up our pathnames, if @parentdir was valid + * and usable; otherwise false is returned. + */ +_Bool +nsm_setup_pathnames(const char *progname, const char *parentdir) +{ + return generic_setup_basedir(progname, parentdir, nsm_base_dirname, + PATH_MAX); +} + +/** + * nsm_is_default_parentdir - check if parent directory is default + * + * Returns true if the active statd parent directory, set by + * nsm_change_pathname(), is the same as the built-in default + * parent directory; otherwise false is returned. + */ +_Bool +nsm_is_default_parentdir(void) +{ + return strcmp(nsm_base_dirname, NSM_DEFAULT_STATEDIR) == 0; +} + +/* + * Clear all capabilities but CAP_NET_BIND_SERVICE. This permits + * callers to acquire privileged source ports, but all other root + * capabilities are disallowed. + * + * Returns true if successful, or false if some error occurred. + */ +#ifdef HAVE_SYS_CAPABILITY_H +static _Bool +nsm_clear_capabilities(void) +{ + cap_t caps; + + caps = cap_from_text("cap_net_bind_service=ep"); + if (caps == NULL) { + xlog(L_ERROR, "Failed to allocate capability: %m"); + return false; + } + + if (cap_set_proc(caps) == -1) { + xlog(L_ERROR, "Failed to set capability flags: %m"); + (void)cap_free(caps); + return false; + } + + (void)cap_free(caps); + return true; +} + +#define CAP_BOUND_PROCFILE "/proc/sys/kernel/cap-bound" +static _Bool +prune_bounding_set(void) +{ +#ifdef PR_CAPBSET_DROP + int ret; + unsigned long i; + struct stat st; + + /* + * Prior to kernel 2.6.25, the capabilities bounding set was a global + * value. Check to see if /proc/sys/kernel/cap-bound exists and don't + * bother to clear the bounding set if it does. + */ + ret = stat(CAP_BOUND_PROCFILE, &st); + if (!ret) { + xlog(L_WARNING, "%s exists. Not attempting to clear " + "capabilities bounding set.", + CAP_BOUND_PROCFILE); + return true; + } else if (errno != ENOENT) { + /* Warn, but attempt to clear the bounding set anyway. */ + xlog(L_WARNING, "Unable to stat %s: %m", CAP_BOUND_PROCFILE); + } + + /* 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 false; + } + } +#endif /* PR_CAPBSET_DROP */ + return true; +} +#else /* !HAVE_SYS_CAPABILITY_H */ +static _Bool +nsm_clear_capabilities(void) +{ + return true; +} + +static _Bool +prune_bounding_set(void) +{ + return true; +} +#endif /* HAVE_SYS_CAPABILITY_H */ + +/** + * nsm_drop_privileges - drop root privileges + * @pidfd: file descriptor of a pid file + * + * Returns true if successful, or false if some error occurred. + * + * Set our effective UID and GID to that of our on-disk database. + */ +_Bool +nsm_drop_privileges(const int pidfd) +{ + struct stat st; + + (void)umask(S_IRWXO); + + if (chdir(nsm_base_dirname) == -1) { + xlog(L_ERROR, "Failed to change working directory to %s: %m", + nsm_base_dirname); + return false; + } + + if (lstat(NSM_MONITOR_DIR, &st) == -1) { + xlog(L_ERROR, "Failed to stat %s/%s: %m", nsm_base_dirname, NSM_MONITOR_DIR); + return false; + } + + if (!prune_bounding_set()) + return false; + + if (st.st_uid == 0) { + xlog_warn("Running as root. " + "chown %s to choose different user", nsm_base_dirname); + return true; + } + + /* + * If the pidfile happens to reside on NFS, dropping privileges + * will probably cause us to lose access, even though we are + * holding it open. Chown it to prevent this. + */ + if (pidfd >= 0) + if (fchown(pidfd, st.st_uid, st.st_gid) == -1) + xlog_warn("Failed to change owner of pidfile: %m"); + + /* + * Don't clear capabilities when dropping root. + */ + if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == -1) { + xlog(L_ERROR, "prctl(PR_SET_KEEPCAPS) failed: %m"); + return false; + } + + if (setgroups(0, NULL) == -1) { + xlog(L_ERROR, "Failed to drop supplementary groups: %m"); + return false; + } + + /* + * ORDER + * + * setgid(2) first, as setuid(2) may remove privileges needed + * to set the group id. + */ + if (setgid(st.st_gid) == -1 || setuid(st.st_uid) == -1) { + xlog(L_ERROR, "Failed to drop privileges: %m"); + return false; + } + + xlog(D_CALL, "Effective UID, GID: %u, %u", st.st_uid, st.st_gid); + + return nsm_clear_capabilities(); +} + +/** + * nsm_get_state - retrieve on-disk NSM state number + * + * Returns an odd NSM state number read from disk, or an initial + * state number. Zero is returned if some error occurs. + */ +int +nsm_get_state(_Bool update) +{ + int fd, state = 0; + ssize_t result; + char *path = NULL; + + path = nsm_make_pathname(NSM_STATE_FILE); + if (path == NULL) { + xlog(L_ERROR, "Failed to allocate path for " NSM_STATE_FILE); + goto out; + } + + fd = open(path, O_RDONLY); + if (fd == -1) { + if (errno != ENOENT) { + xlog(L_ERROR, "Failed to open %s: %m", path); + goto out; + } + + xlog(L_NOTICE, "Initializing NSM state"); + state = 1; + update = true; + goto update; + } + + result = read(fd, &state, sizeof(state)); + if (exact_error_check(result, sizeof(state))) { + xlog_warn("Failed to read %s: %m", path); + + xlog(L_NOTICE, "Initializing NSM state"); + state = 1; + update = true; + goto update; + } + + if ((state & 1) == 0) + state++; + +update: + if(fd >= 0) + (void)close(fd); + + if (update) { + state += 2; + if (!nsm_atomic_write(path, &state, sizeof(state))) + state = 0; + } + +out: + free(path); + return state; +} + +/** + * nsm_update_kernel_state - attempt to post new NSM state to kernel + * @state: NSM state number + * + */ +void +nsm_update_kernel_state(const int state) +{ + ssize_t result; + char buf[20]; + int fd, len; + + fd = open(NSM_KERNEL_STATE_FILE, O_WRONLY); + if (fd == -1) { + xlog(D_GENERAL, "Failed to open " NSM_KERNEL_STATE_FILE ": %m"); + return; + } + + len = snprintf(buf, sizeof(buf), "%d", state); + if (error_check(len, sizeof(buf))) { + xlog_warn("Failed to form NSM state number string"); + close(fd); + return; + } + + result = write(fd, buf, strlen(buf)); + if (exact_error_check(result, strlen(buf))) + xlog_warn("Failed to write NSM state number: %m"); + + if (close(fd) == -1) + xlog(L_ERROR, "Failed to close NSM state file " + NSM_KERNEL_STATE_FILE ": %m"); +} + +/** + * nsm_retire_monitored_hosts - back up all hosts from "sm/" to "sm.bak/" + * + * Returns the count of host records that were moved. + * + * Note that if any error occurs during this process, some monitor + * records may be left in the "sm" directory. + */ +unsigned int +nsm_retire_monitored_hosts(void) +{ + unsigned int count = 0; + struct dirent *de; + char *path; + DIR *dir; + + path = nsm_make_pathname(NSM_MONITOR_DIR); + if (path == NULL) { + xlog(L_ERROR, "Failed to allocate path for " NSM_MONITOR_DIR); + return count; + } + + dir = opendir(path); + free(path); + if (dir == NULL) { + xlog_warn("Failed to open " NSM_MONITOR_DIR ": %m"); + return count; + } + + while ((de = readdir(dir)) != NULL) { + char *src, *dst; + struct stat stb; + + if (de->d_name[0] == '.') + continue; + + src = nsm_make_record_pathname(NSM_MONITOR_DIR, de->d_name); + if (src == NULL) { + xlog_warn("Bad monitor file name, skipping"); + continue; + } + + /* NB: not all file systems fill in d_type correctly */ + if (lstat(src, &stb) == -1) { + xlog_warn("Bad monitor file %s, skipping: %m", + de->d_name); + free(src); + continue; + } + if (!S_ISREG(stb.st_mode)) { + xlog(D_GENERAL, "Skipping non-regular file %s", + de->d_name); + free(src); + continue; + } + + dst = nsm_make_record_pathname(NSM_NOTIFY_DIR, de->d_name); + if (dst == NULL) { + free(src); + xlog_warn("Bad notify file name, skipping"); + continue; + } + + if (rename(src, dst) == -1) + xlog_warn("Failed to rename %s -> %s: %m", + src, dst); + else { + xlog(D_GENERAL, "Retired record for mon_name %s", + de->d_name); + count++; + } + + free(dst); + free(src); + } + + (void)closedir(dir); + return count; +} + +/* + * nsm_priv_to_hex - convert a NSM private cookie to a hex string. + * + * @priv: buffer holding the binary NSM private cookie + * @buf: output buffer for NULL terminated hex string + * @buflen: size of output buffer + * + * Returns the length of the resulting string or 0 on error + */ +size_t +nsm_priv_to_hex(const char *priv, char *buf, const size_t buflen) +{ + int i, len; + size_t remaining = buflen; + + for (i = 0; i < SM_PRIV_SIZE; i++) { + len = snprintf(buf, remaining, "%02x", + (unsigned int)(0xff & priv[i])); + if (error_check(len, remaining)) + return 0; + buf += len; + remaining -= (size_t)len; + } + + return buflen - remaining; +} + +/* + * Returns the length in bytes of the created record. + */ +__attribute__((__noinline__)) +static size_t +nsm_create_monitor_record(char *buf, const size_t buflen, + const struct sockaddr *sap, const struct mon *m) +{ + const struct sockaddr_in *sin = (const struct sockaddr_in *)sap; + size_t hexlen, remaining = buflen; + int len; + + len = snprintf(buf, remaining, "%08x %08x %08x %08x ", + (unsigned int)sin->sin_addr.s_addr, + (unsigned int)m->mon_id.my_id.my_prog, + (unsigned int)m->mon_id.my_id.my_vers, + (unsigned int)m->mon_id.my_id.my_proc); + if (error_check(len, remaining)) + return 0; + buf += len; + remaining -= (size_t)len; + + hexlen = nsm_priv_to_hex(m->priv, buf, remaining); + if (hexlen == 0) + return 0; + buf += hexlen; + remaining -= hexlen; + + len = snprintf(buf, remaining, " %s %s\n", + m->mon_id.mon_name, m->mon_id.my_id.my_name); + if (error_check(len, remaining)) + return 0; + remaining -= (size_t)len; + + return buflen - remaining; +} + +static _Bool +nsm_append_monitored_host(const char *path, const char *line) +{ + _Bool result = false; + char *buf = NULL; + struct stat stb; + size_t buflen; + ssize_t len; + int fd; + + if (stat(path, &stb) == -1) { + xlog(L_ERROR, "Failed to insert: " + "could not stat original file %s: %m", path); + goto out; + } + buflen = (size_t)stb.st_size + strlen(line); + + buf = malloc(buflen + 1); + if (buf == NULL) { + xlog(L_ERROR, "Failed to insert: no memory"); + goto out; + } + memset(buf, 0, buflen + 1); + + fd = open(path, O_RDONLY); + if (fd == -1) { + xlog(L_ERROR, "Failed to insert: " + "could not open original file %s: %m", path); + goto out; + } + + len = read(fd, buf, (size_t)stb.st_size); + if (exact_error_check(len, (size_t)stb.st_size)) { + xlog(L_ERROR, "Failed to insert: " + "could not read original file %s: %m", path); + (void)close(fd); + goto out; + } + (void)close(fd); + + strcat(buf, line); + + if (nsm_atomic_write(path, buf, buflen)) + result = true; + +out: + free(buf); + return result; +} + +/** + * nsm_insert_monitored_host - write callback data for one host to disk + * @hostname: C string containing a hostname + * @sap: sockaddr containing NLM callback address + * @mon: SM_MON arguments to save + * + * Returns true if successful, otherwise false if some error occurs. + */ +_Bool +nsm_insert_monitored_host(const char *hostname, const struct sockaddr *sap, + const struct mon *m) +{ + static char buf[LINELEN + 1 + SM_MAXSTRLEN + 2]; + char *path; + _Bool result = false; + ssize_t len; + size_t size; + int fd; + + path = nsm_make_record_pathname(NSM_MONITOR_DIR, hostname); + if (path == NULL) { + xlog(L_ERROR, "Failed to insert: bad monitor hostname '%s'", + hostname); + return false; + } + + size = nsm_create_monitor_record(buf, sizeof(buf), sap, m); + if (size == 0) { + xlog(L_ERROR, "Failed to insert: record too long"); + goto out; + } + + /* + * If exclusive create fails, we're adding a new line to an + * existing file. + */ + fd = open(path, O_WRONLY | O_CREAT | O_EXCL | O_SYNC, S_IRUSR | S_IWUSR); + if (fd == -1) { + if (errno != EEXIST) { + xlog(L_ERROR, "Failed to insert: creating %s: %m", path); + goto out; + } + + result = nsm_append_monitored_host(path, buf); + goto out; + } + result = true; + + len = write(fd, buf, size); + if (exact_error_check(len, size)) { + xlog_warn("Failed to insert: writing %s: %m", path); + (void)unlink(path); + result = false; + } + + if (close(fd) == -1) { + xlog(L_ERROR, "Failed to insert: closing %s: %m", path); + (void)unlink(path); + result = false; + } + +out: + free(path); + return result; +} + +__attribute__((__noinline__)) +static _Bool +nsm_parse_line(char *line, struct sockaddr_in *sin, struct mon *m) +{ + unsigned int i, tmp; + int count; + char *c; + + c = strchr(line, '\n'); + if (c != NULL) + *c = '\0'; + + count = sscanf(line, "%8x %8x %8x %8x ", + (unsigned int *)&sin->sin_addr.s_addr, + (unsigned int *)&m->mon_id.my_id.my_prog, + (unsigned int *)&m->mon_id.my_id.my_vers, + (unsigned int *)&m->mon_id.my_id.my_proc); + if (count != 4) + return false; + + c = line + RPCARGSLEN; + for (i = 0; i < SM_PRIV_SIZE; i++) { + if (sscanf(c, "%2x", &tmp) != 1) + return false; + m->priv[i] = (char)tmp; + c += 2; + } + + c++; + m->mon_id.mon_name = c; + while (*c != '\0' && *c != ' ') + c++; + if (*c != '\0') + *c++ = '\0'; + while (*c == ' ') + c++; + m->mon_id.my_id.my_name = c; + + return true; +} + +/* + * Stuff a 'struct mon' with callback data, and call @func. + * + * Returns the count of in-core records created. + */ +static unsigned int +nsm_read_line(const char *hostname, const time_t timestamp, char *line, + nsm_populate_t func) +{ + struct sockaddr_in sin = { + .sin_family = AF_INET, + }; + struct mon m; + + if (!nsm_parse_line(line, &sin, &m)) + return 0; + + return func(hostname, (struct sockaddr *)(char *)&sin, &m, timestamp); +} + +/* + * Given a filename, reads data from a file under "directory" + * and invokes @func so caller can populate their in-core + * database with this data. + */ +static unsigned int +nsm_load_host(const char *directory, const char *filename, nsm_populate_t func) +{ + char buf[LINELEN + 1 + SM_MAXSTRLEN + 2]; + unsigned int result = 0; + struct stat stb; + char *path; + FILE *f; + + path = nsm_make_record_pathname(directory, filename); + if (path == NULL) + goto out_err; + + if (lstat(path, &stb) == -1) { + xlog(L_ERROR, "Failed to stat %s: %m", path); + goto out_freepath; + } + if (!S_ISREG(stb.st_mode)) { + xlog(D_GENERAL, "Skipping non-regular file %s", + path); + goto out_freepath; + } + + f = fopen(path, "r"); + if (f == NULL) { + xlog(L_ERROR, "Failed to open %s: %m", path); + goto out_freepath; + } + + while (fgets(buf, (int)sizeof(buf), f) != NULL) { + buf[sizeof(buf) - 1] = '\0'; + result += nsm_read_line(filename, stb.st_mtime, buf, func); + } + if (result == 0) + xlog(L_ERROR, "Failed to read monitor data from %s", path); + + (void)fclose(f); + +out_freepath: + free(path); +out_err: + return result; +} + +static unsigned int +nsm_load_dir(const char *directory, nsm_populate_t func) +{ + unsigned int count = 0; + struct dirent *de; + char *path; + DIR *dir; + + path = nsm_make_pathname(directory); + if (path == NULL) { + xlog(L_ERROR, "Failed to allocate path for directory %s", + directory); + return 0; + } + + dir = opendir(path); + free(path); + if (dir == NULL) { + xlog(L_ERROR, "Failed to open directory %s: %m", + directory); + return 0; + } + + while ((de = readdir(dir)) != NULL) { + if (de->d_name[0] == '.') + continue; + + count += nsm_load_host(directory, de->d_name, func); + } + + (void)closedir(dir); + return count; +} + +/** + * nsm_load_monitor_list - load list of hosts to monitor + * @func: callback function to create entry for one host + * + * Returns the count of hosts that were found in the directory. + */ +unsigned int +nsm_load_monitor_list(nsm_populate_t func) +{ + return nsm_load_dir(NSM_MONITOR_DIR, func); +} + +/** + * nsm_load_notify_list - load list of hosts to notify + * @func: callback function to create entry for one host + * + * Returns the count of hosts that were found in the directory. + */ +unsigned int +nsm_load_notify_list(nsm_populate_t func) +{ + return nsm_load_dir(NSM_NOTIFY_DIR, func); +} + +static void +nsm_delete_host(const char *directory, const char *hostname, + const char *mon_name, const char *my_name, const int chatty) +{ + char line[LINELEN + 1 + SM_MAXSTRLEN + 2]; + char *outbuf = NULL; + struct stat stb; + char *path, *next; + size_t remaining; + FILE *f; + + path = nsm_make_record_pathname(directory, hostname); + if (path == NULL) { + xlog(L_ERROR, "Bad filename, not deleting"); + return; + } + + if (stat(path, &stb) == -1) { + if (chatty) + xlog(L_ERROR, "Failed to delete: " + "could not stat original file %s: %m", path); + goto out; + } + remaining = (size_t)stb.st_size + 1; + + outbuf = malloc(remaining); + if (outbuf == NULL) { + xlog(L_ERROR, "Failed to delete: no memory"); + goto out; + } + + f = fopen(path, "r"); + if (f == NULL) { + xlog(L_ERROR, "Failed to delete: " + "could not open original file %s: %m", path); + goto out; + } + + /* + * Walk the records in the file, and copy the non-matching + * ones to our output buffer. + */ + next = outbuf; + while (fgets(line, (int)sizeof(line), f) != NULL) { + struct sockaddr_in sin; + struct mon m; + size_t len; + + if (!nsm_parse_line(line, &sin, &m)) { + xlog(L_ERROR, "Failed to delete: " + "could not parse original file %s", path); + (void)fclose(f); + goto out; + } + + if (strcmp(mon_name, m.mon_id.mon_name) == 0 && + strcmp(my_name, m.mon_id.my_id.my_name) == 0) + continue; + + /* nsm_parse_line destroys the contents of line[], so + * reconstruct the copy in our output buffer. */ + len = nsm_create_monitor_record(next, remaining, + (struct sockaddr *)(char *)&sin, &m); + if (len == 0) { + xlog(L_ERROR, "Failed to delete: " + "could not construct output record"); + (void)fclose(f); + goto out; + } + next += len; + remaining -= len; + } + + (void)fclose(f); + + /* + * If nothing was copied when we're done, then unlink the file. + * Otherwise, atomically update the contents of the file. + */ + if (next != outbuf) { + if (!nsm_atomic_write(path, outbuf, strlen(outbuf))) + xlog(L_ERROR, "Failed to delete: " + "could not write new file %s: %m", path); + } else { + if (unlink(path) == -1) + xlog(L_ERROR, "Failed to delete: " + "could not unlink file %s: %m", path); + } + +out: + free(outbuf); + free(path); +} + +/** + * nsm_delete_monitored_host - delete on-disk record for monitored host + * @hostname: '\0'-terminated C string containing hostname of record to delete + * @mon_name: '\0'-terminated C string containing monname of record to delete + * @my_name: '\0'-terminated C string containing myname of record to delete + * @chatty: should an error be logged if the monitor file doesn't exist? + * + */ +void +nsm_delete_monitored_host(const char *hostname, const char *mon_name, + const char *my_name, const int chatty) +{ + nsm_delete_host(NSM_MONITOR_DIR, hostname, mon_name, my_name, chatty); +} + +/** + * nsm_delete_notified_host - delete on-disk host record after notification + * @hostname: '\0'-terminated C string containing hostname of record to delete + * @mon_name: '\0'-terminated C string containing monname of record to delete + * @my_name: '\0'-terminated C string containing myname of record to delete + * + */ +void +nsm_delete_notified_host(const char *hostname, const char *mon_name, + const char *my_name) +{ + nsm_delete_host(NSM_NOTIFY_DIR, hostname, mon_name, my_name, 1); +} diff --git a/support/nsm/rpc.c b/support/nsm/rpc.c new file mode 100644 index 0000000..08b4746 --- /dev/null +++ b/support/nsm/rpc.c @@ -0,0 +1,536 @@ +/* + * 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. + * + * Instead of using ONC or TI RPC library calls, statd constructs + * RPC calls directly in socket buffers. This allows a single + * socket to be concurrently shared among several different RPC + * programs and versions using a simple RPC request dispatcher. + * + * This file contains the details of RPC header and call + * construction and reply parsing, and a method for creating a + * socket for use with these functions. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/time.h> + +#include <stdint.h> +#include <time.h> +#include <stdbool.h> +#include <stdint.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> + +#include <netinet/in.h> +#include <net/if.h> +#include <arpa/inet.h> +#include <netdb.h> + +#include <rpc/rpc.h> +#include <rpc/pmap_prot.h> +#include <rpc/pmap_rmt.h> + +#ifdef HAVE_LIBTIRPC +#include <netconfig.h> +#include <rpc/rpcb_prot.h> +#endif /* HAVE_LIBTIRPC */ + +#include "xlog.h" +#include "nfsrpc.h" +#include "nsm.h" +#include "sm_inter.h" + +/* + * Returns a fresh XID appropriate for RPC over UDP -- never zero. + */ +static uint32_t +nsm_next_xid(void) +{ + static uint32_t nsm_xid = 0; + struct timeval now; + + if (nsm_xid == 0) { + (void)gettimeofday(&now, NULL); + nsm_xid = (uint32_t)getpid() ^ + (uint32_t)now.tv_sec ^ (uint32_t)now.tv_usec; + } + + return nsm_xid++; +} + +/* + * Select a fresh XID and construct an RPC header in @mesg. + * Always use AUTH_NULL credentials and verifiers. + * + * Returns the new XID. + */ +static uint32_t +nsm_init_rpc_header(const rpcprog_t program, const rpcvers_t version, + const rpcproc_t procedure, struct rpc_msg *mesg) +{ + struct call_body *cb = &mesg->rm_call; + uint32_t xid = nsm_next_xid(); + + memset(mesg, 0, sizeof(*mesg)); + + mesg->rm_xid = (unsigned long)xid; + mesg->rm_direction = CALL; + + cb->cb_rpcvers = RPC_MSG_VERSION; + cb->cb_prog = program; + cb->cb_vers = version; + cb->cb_proc = procedure; + + cb->cb_cred.oa_flavor = AUTH_NULL; + cb->cb_cred.oa_base = (caddr_t) NULL; + cb->cb_cred.oa_length = 0; + cb->cb_verf.oa_flavor = AUTH_NULL; + cb->cb_verf.oa_base = (caddr_t) NULL; + cb->cb_verf.oa_length = 0; + + return xid; +} + +/* + * Initialize the network send buffer and XDR memory for encoding. + */ +static void +nsm_init_xdrmem(char *msgbuf, const unsigned int msgbuflen, + XDR *xdrp) +{ + memset(msgbuf, 0, (size_t)msgbuflen); + memset(xdrp, 0, sizeof(*xdrp)); + xdrmem_create(xdrp, msgbuf, msgbuflen, XDR_ENCODE); +} + +/* + * Send a completed RPC call on a socket. + * + * Returns true if all the bytes were sent successfully; otherwise + * false if any error occurred. + */ +static _Bool +nsm_rpc_sendto(const int sock, const struct sockaddr *sap, + const socklen_t salen, XDR *xdrs, void *buf) +{ + const size_t buflen = (size_t)xdr_getpos(xdrs); + ssize_t err; + + err = sendto(sock, buf, buflen, 0, sap, salen); + if ((err < 0) || ((size_t)err != buflen)) { + xlog(L_ERROR, "%s: sendto failed: %m", __func__); + return false; + } + return true; +} + +/** + * nsm_xmit_getport - post a PMAP_GETPORT call on a socket descriptor + * @sock: datagram socket descriptor + * @sin: pointer to AF_INET socket address of server + * @program: RPC program number to query + * @version: RPC version number to query + * + * Send a PMAP_GETPORT call to the portmap daemon at @sin using + * socket descriptor @sock. This request queries the RPC program + * [program, version, IPPROTO_UDP]. + * + * NB: PMAP_GETPORT works only for IPv4 hosts. This implementation + * works only over UDP, and queries only UDP registrations. + * + * Returns the XID of the call, or zero if an error occurred. + */ +uint32_t +nsm_xmit_getport(const int sock, const struct sockaddr_in *sin, + const unsigned long program, + const unsigned long version) +{ + char msgbuf[NSM_MAXMSGSIZE]; + struct sockaddr_in addr; + struct rpc_msg mesg; + _Bool sent = false; + struct pmap parms = { + .pm_prog = program, + .pm_vers = version, + .pm_prot = (unsigned long)IPPROTO_UDP, + }; + uint32_t xid; + XDR xdr; + + xlog(D_CALL, "Sending PMAP_GETPORT for %lu, %lu, udp", program, version); + + nsm_init_xdrmem(msgbuf, NSM_MAXMSGSIZE, &xdr); + xid = nsm_init_rpc_header(PMAPPROG, PMAPVERS, + (rpcproc_t)PMAPPROC_GETPORT, &mesg); + + addr = *sin; + addr.sin_port = htons(PMAPPORT); + + if (xdr_callmsg(&xdr, &mesg) == TRUE && + xdr_pmap(&xdr, &parms) == TRUE) + sent = nsm_rpc_sendto(sock, (struct sockaddr *)(char *)&addr, + (socklen_t)sizeof(addr), &xdr, msgbuf); + else + xlog(L_ERROR, "%s: can't encode PMAP_GETPORT call", __func__); + + xdr_destroy(&xdr); + + if (sent == false) + return 0; + return xid; +} + +/** + * nsm_xmit_getaddr - post an RPCB_GETADDR call on a socket descriptor + * @sock: datagram socket descriptor + * @sin: pointer to AF_INET6 socket address of server + * @program: RPC program number to query + * @version: RPC version number to query + * + * Send an RPCB_GETADDR call to the rpcbind daemon at @sap using + * socket descriptor @sock. This request queries the RPC program + * [program, version, "udp6"]. + * + * NB: RPCB_GETADDR works for both IPv4 and IPv6 hosts. This + * implementation works only over UDP and AF_INET6, and queries + * only "udp6" registrations. + * + * Returns the XID of the call, or zero if an error occurred. + */ +#ifdef HAVE_LIBTIRPC +uint32_t +nsm_xmit_getaddr(const int sock, const struct sockaddr_in6 *sin6, + const rpcprog_t program, const rpcvers_t version) +{ + char msgbuf[NSM_MAXMSGSIZE]; + struct sockaddr_in6 addr; + struct rpc_msg mesg; + _Bool sent = false; + struct rpcb parms = { + .r_prog = program, + .r_vers = version, + .r_netid = "udp6", + .r_owner = "", + }; + uint32_t xid; + XDR xdr; + + xlog(D_CALL, "Sending RPCB_GETADDR for %u, %u, udp6", program, version); + + nsm_init_xdrmem(msgbuf, NSM_MAXMSGSIZE, &xdr); + xid = nsm_init_rpc_header(RPCBPROG, RPCBVERS, + (rpcproc_t)RPCBPROC_GETADDR, &mesg); + + addr = *sin6; + addr.sin6_port = htons(PMAPPORT); + parms.r_addr = nfs_sockaddr2universal((struct sockaddr *)(char *)&addr); + if (parms.r_addr == NULL) { + xlog(L_ERROR, "%s: can't encode socket address", __func__); + return 0; + } + + if (xdr_callmsg(&xdr, &mesg) == TRUE && + xdr_rpcb(&xdr, &parms) == TRUE) + sent = nsm_rpc_sendto(sock, (struct sockaddr *)(char *)&addr, + (socklen_t)sizeof(addr), &xdr, msgbuf); + else + xlog(L_ERROR, "%s: can't encode RPCB_GETADDR call", __func__); + + xdr_destroy(&xdr); + free(parms.r_addr); + + if (sent == false) + return 0; + return xid; +} +#else /* !HAVE_LIBTIRPC */ +uint32_t +nsm_xmit_getaddr(const int sock __attribute__((unused)), + const struct sockaddr_in6 *sin6 __attribute__((unused)), + const rpcprog_t program __attribute__((unused)), + const rpcvers_t version __attribute__((unused))) +{ + return 0; +} +#endif /* !HAVE_LIBTIRPC */ + +/** + * nsm_xmit_rpcbind - post an rpcbind request + * @sock: datagram socket descriptor + * @sap: pointer to socket address of server + * @program: RPC program number to query + * @version: RPC version number to query + * + * Send an rpcbind query to the rpcbind daemon at @sap using + * socket descriptor @sock. + * + * NB: This implementation works only over UDP, but can query IPv4 or IPv6 + * hosts. It queries only UDP registrations. + * + * Returns the XID of the call, or zero if an error occurred. + */ +uint32_t +nsm_xmit_rpcbind(const int sock, const struct sockaddr *sap, + const rpcprog_t program, const rpcvers_t version) +{ + switch (sap->sa_family) { + case AF_INET: + return nsm_xmit_getport(sock, (const struct sockaddr_in *)sap, + program, version); + case AF_INET6: + return nsm_xmit_getaddr(sock, (const struct sockaddr_in6 *)sap, + program, version); + } + return 0; +} + +/** + * nsm_xmit_notify - post an NSMPROC_NOTIFY call on a socket descriptor + * @sock: datagram socket descriptor + * @sap: pointer to socket address of peer to notify (port already filled in) + * @salen: length of socket address + * @program: RPC program number to use + * @mon_name: mon_name of local peer (ie the rebooting system) + * @state: state of local peer + * + * Send an NSMPROC_NOTIFY call to the peer at @sap using socket descriptor @sock. + * This request notifies the peer that we have rebooted. + * + * NB: This implementation works only over UDP, but supports both AF_INET + * and AF_INET6. + * + * Returns the XID of the call, or zero if an error occurred. + */ +uint32_t +nsm_xmit_notify(const int sock, const struct sockaddr *sap, + const socklen_t salen, const rpcprog_t program, + const char *mon_name, const int state) +{ + char msgbuf[NSM_MAXMSGSIZE]; + struct stat_chge state_change; + struct rpc_msg mesg; + _Bool sent = false; + uint32_t xid; + XDR xdr; + + state_change.mon_name = strdup(mon_name); + if (state_change.mon_name == NULL) { + xlog(L_ERROR, "%s: no memory", __func__); + return 0; + } + state_change.state = state; + + xlog(D_CALL, "Sending SM_NOTIFY for %s", mon_name); + + nsm_init_xdrmem(msgbuf, NSM_MAXMSGSIZE, &xdr); + xid = nsm_init_rpc_header(program, SM_VERS, SM_NOTIFY, &mesg); + + if (xdr_callmsg(&xdr, &mesg) == TRUE && + xdr_stat_chge(&xdr, &state_change) == TRUE) + sent = nsm_rpc_sendto(sock, sap, salen, &xdr, msgbuf); + else + xlog(L_ERROR, "%s: can't encode NSMPROC_NOTIFY call", + __func__); + + xdr_destroy(&xdr); + free(state_change.mon_name); + + if (sent == false) + return 0; + return xid; +} + +/** + * nsm_xmit_nlmcall - post an unnamed call to local NLM on a socket descriptor + * @sock: datagram socket descriptor + * @sap: address/port of NLM service to contact + * @salen: size of @sap + * @m: callback data defining RPC call to make + * @state: state of rebooting host + * + * Send an unnamed call (previously requested via NSMPROC_MON) to the + * specified local UDP-based RPC service using socket descriptor @sock. + * + * NB: This implementation works only over UDP, but supports both AF_INET + * and AF_INET6. + * + * Returns the XID of the call, or zero if an error occurred. + */ +uint32_t +nsm_xmit_nlmcall(const int sock, const struct sockaddr *sap, + const socklen_t salen, const struct mon *m, + const int state) +{ + const struct my_id *id = &m->mon_id.my_id; + char msgbuf[NSM_MAXMSGSIZE]; + struct status new_status; + struct rpc_msg mesg; + _Bool sent = false; + uint32_t xid; + XDR xdr; + + xlog(D_CALL, "Sending NLM downcall for %s", m->mon_id.mon_name); + + nsm_init_xdrmem(msgbuf, NSM_MAXMSGSIZE, &xdr); + xid = nsm_init_rpc_header((rpcprog_t)id->my_prog, + (rpcvers_t)id->my_vers, + (rpcproc_t)id->my_proc, &mesg); + + new_status.mon_name = m->mon_id.mon_name; + new_status.state = state; + memcpy(&new_status.priv, &m->priv, sizeof(new_status.priv)); + + if (xdr_callmsg(&xdr, &mesg) == TRUE && + xdr_status(&xdr, &new_status) == TRUE) + sent = nsm_rpc_sendto(sock, sap, salen, &xdr, msgbuf); + else + xlog(L_ERROR, "%s: can't encode NLM downcall", __func__); + + xdr_destroy(&xdr); + + if (sent == false) + return 0; + return xid; +} + +/** + * nsm_parse_reply - parse and validate the header in an RPC reply + * @xdrs: pointer to XDR + * + * Returns the XID of the reply, or zero if an error occurred. + */ +uint32_t +nsm_parse_reply(XDR *xdrs) +{ + struct rpc_msg mesg = { + .rm_reply.rp_acpt.ar_results.proc = (xdrproc_t)xdr_void, + }; + uint32_t xid; + + if (xdr_replymsg(xdrs, &mesg) == FALSE) { + xlog(L_ERROR, "%s: can't decode RPC reply", __func__); + return 0; + } + xid = (uint32_t)mesg.rm_xid; + + if (mesg.rm_reply.rp_stat != MSG_ACCEPTED) { + xlog(L_ERROR, "%s: [0x%x] RPC status %d", + __func__, xid, mesg.rm_reply.rp_stat); + return 0; + } + + if (mesg.rm_reply.rp_acpt.ar_stat != SUCCESS) { + xlog(L_ERROR, "%s: [0x%x] RPC accept status %d", + __func__, xid, mesg.rm_reply.rp_acpt.ar_stat); + return 0; + } + + return xid; +} + +/** + * nsm_recv_getport - parse PMAP_GETPORT reply + * @xdrs: pointer to XDR + * + * Returns the port number from the RPC reply, or zero + * if an error occurred. + */ +unsigned long +nsm_recv_getport(XDR *xdrs) +{ + unsigned long port = 0; + + if (xdr_u_long(xdrs, &port) == FALSE) + xlog(L_ERROR, "%s: can't decode pmap reply", + __func__); + if (port > UINT16_MAX) { + xlog(L_ERROR, "%s: bad port number", + __func__); + port = 0; + } + + xlog(D_CALL, "Received PMAP_GETPORT result: %lu", port); + return port; +} + +/** + * nsm_recv_getaddr - parse RPCB_GETADDR reply + * @xdrs: pointer to XDR + * + * Returns the port number from the RPC reply, or zero + * if an error occurred. + */ +uint16_t +nsm_recv_getaddr(XDR *xdrs) +{ + char *uaddr = NULL; + int port; + + if (xdr_wrapstring(xdrs, &uaddr) == FALSE) + xlog(L_ERROR, "%s: can't decode rpcb reply", + __func__); + + if ((uaddr == NULL) || (uaddr[0] == '\0')) { + xlog(D_CALL, "Received RPCB_GETADDR result: " + "program not registered"); + return 0; + } + + port = nfs_universal2port(uaddr); + + xdr_free((xdrproc_t)xdr_wrapstring, (char *)&uaddr); + + if (port < 0 || port > UINT16_MAX) { + xlog(L_ERROR, "%s: bad port number", + __func__); + return 0; + } + + xlog(D_CALL, "Received RPCB_GETADDR result: %d", port); + return (uint16_t)port; +} + +/** + * nsm_recv_rpcbind - parse rpcbind reply + * @af: address family of reply + * @xdrs: pointer to XDR + * + * Returns the port number from the RPC reply, or zero + * if an error occurred. + */ +uint16_t +nsm_recv_rpcbind(const sa_family_t family, XDR *xdrs) +{ + switch (family) { + case AF_INET: + return (uint16_t)nsm_recv_getport(xdrs); + case AF_INET6: + return nsm_recv_getaddr(xdrs); + } + return 0; +} diff --git a/support/nsm/sm_inter.x b/support/nsm/sm_inter.x new file mode 100644 index 0000000..d8e0ad7 --- /dev/null +++ b/support/nsm/sm_inter.x @@ -0,0 +1,131 @@ +/* + * Copyright (C) 1986 Sun Microsystems, Inc. + * Modified by Jeffrey A. Uphoff, 1995, 1997-1999. + * Modified by Olaf Kirch, 1996. + * Modified by H.J. Lu, 1998. + * + * NSM for Linux. + */ + +/* + * 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. + */ + +/* + * Status monitor protocol specification + */ + +#ifdef RPC_CLNT +%#include <string.h> +#endif + +program SM_PROG { + version SM_VERS { + /* res_stat = stat_succ if status monitor agrees to monitor */ + /* res_stat = stat_fail if status monitor cannot monitor */ + /* if res_stat == stat_succ, state = state number of site sm_name */ + struct sm_stat_res SM_STAT(struct sm_name) = 1; + + /* res_stat = stat_succ if status monitor agrees to monitor */ + /* res_stat = stat_fail if status monitor cannot monitor */ + /* stat consists of state number of local site */ + struct sm_stat_res SM_MON(struct mon) = 2; + + /* stat consists of state number of local site */ + struct sm_stat SM_UNMON(struct mon_id) = 3; + + /* stat consists of state number of local site */ + struct sm_stat SM_UNMON_ALL(struct my_id) = 4; + + void SM_SIMU_CRASH(void) = 5; + + void SM_NOTIFY(struct stat_chge) = 6; + + } = 1; +} = 100024; + +const SM_MAXSTRLEN = 1024; +const SM_PRIV_SIZE = 16; + +struct sm_name { + string mon_name<SM_MAXSTRLEN>; +}; + +struct my_id { + string my_name<SM_MAXSTRLEN>; /* name of the site iniates the monitoring request*/ + int my_prog; /* rpc program # of the requesting process */ + int my_vers; /* rpc version # of the requesting process */ + int my_proc; /* rpc procedure # of the requesting process */ +}; + +struct mon_id { + string mon_name<SM_MAXSTRLEN>; /* name of the site to be monitored */ + struct my_id my_id; +}; + + +struct mon { + struct mon_id mon_id; + opaque priv[SM_PRIV_SIZE]; /* private information to store at monitor for requesting process */ +}; + +struct stat_chge { + string mon_name<SM_MAXSTRLEN>; /* name of the site that had the state change */ + int state; +}; + +/* + * state # of status monitor monitonically increases each time + * status of the site changes: + * an even number (>= 0) indicates the site is down and + * an odd number (> 0) indicates the site is up; + */ +struct sm_stat { + int state; /* state # of status monitor */ +}; + +enum res { + stat_succ = 0, /* status monitor agrees to monitor */ + stat_fail = 1 /* status monitor cannot monitor */ +}; + +struct sm_stat_res { + res res_stat; + int state; +}; + +/* + * structure of the status message sent back by the status monitor + * when monitor site status changes + */ +struct status { + string mon_name<SM_MAXSTRLEN>; + int state; + opaque priv[SM_PRIV_SIZE]; /* stored private information */ +}; + +%#define SM_INTER_X |