From: Ben Hutchings Date: Wed, 14 Jul 2010 02:16:17 +0000 (+0100) Subject: Merge branch 'upstream' X-Git-Tag: debian/1%1.2.2-1~1 X-Git-Url: https://git.decadent.org.uk/gitweb/?p=nfs-utils.git;a=commitdiff_plain;h=6295bb04c73403a219bc96cc6770fc96bec19ba6;hp=d3dcf7a556c2782f02ac275c0c1b708c324b5541 Merge branch 'upstream' Conflicts: Makefile.in aclocal.m4 aclocal/ltversion.m4 compile config.guess config.sub configure linux-nfs/Makefile.in ltmain.sh support/Makefile.in support/export/Makefile.in support/include/Makefile.in support/include/config.h.in support/include/nfs/Makefile.in support/include/rpcsvc/Makefile.in support/include/sys/Makefile.in support/include/sys/fs/Makefile.in support/misc/Makefile.in support/nfs/Makefile.in tools/Makefile.in tools/locktest/Makefile.in tools/nlmtest/Makefile.in tools/rpcdebug/Makefile.in tools/rpcgen/Makefile.in utils/Makefile.in utils/exportfs/Makefile.in utils/gssd/Makefile.in utils/idmapd/Makefile.in utils/mount/Makefile.in utils/mountd/Makefile.in utils/nfsd/Makefile.in utils/nfsstat/Makefile.in utils/showmount/Makefile.in utils/statd/Makefile.in --- diff --git a/.gitignore b/.gitignore index 632609e..4bff9e3 100644 --- a/.gitignore +++ b/.gitignore @@ -55,10 +55,15 @@ support/export/mount.h support/export/mount_clnt.c support/export/mount_xdr.c support/include/mount.h -utils/statd/sm_inter.h -utils/statd/sm_inter_clnt.c -utils/statd/sm_inter_svc.c -utils/statd/sm_inter_xdr.c +support/nsm/sm_inter.h +support/nsm/sm_inter_clnt.c +support/nsm/sm_inter_svc.c +support/nsm/sm_inter_xdr.c +support/include/sm_inter.h +tests/nsm_client/nlm_sm_inter.h +tests/nsm_client/nlm_sm_inter_clnt.c +tests/nsm_client/nlm_sm_inter_svc.c +tests/nsm_client/nlm_sm_inter_xdr.c # cscope database files cscope.* # generic editor backup et al diff --git a/Makefile.am b/Makefile.am index b3a6e91..ae7cd16 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2,7 +2,7 @@ AUTOMAKE_OPTIONS = foreign -SUBDIRS = tools support utils linux-nfs +SUBDIRS = tools support utils linux-nfs tests MAINTAINERCLEANFILES = Makefile.in diff --git a/Makefile.in b/Makefile.in index 95eb3c7..5d2f746 100644 --- a/Makefile.in +++ b/Makefile.in @@ -1,4 +1,4 @@ -# Makefile.in generated by automake 1.11 from Makefile.am. +# Makefile.in generated by automake 1.11.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, @@ -43,6 +43,7 @@ am__aclocal_m4_deps = $(top_srcdir)/aclocal/bsdsignals.m4 \ $(top_srcdir)/aclocal/ipv6.m4 \ $(top_srcdir)/aclocal/kerberos5.m4 \ $(top_srcdir)/aclocal/libblkid.m4 \ + $(top_srcdir)/aclocal/libcap.m4 \ $(top_srcdir)/aclocal/libevent.m4 \ $(top_srcdir)/aclocal/libnfsidmap.m4 \ $(top_srcdir)/aclocal/librpcsecgss.m4 \ @@ -169,6 +170,7 @@ LDFLAGS = @LDFLAGS@ LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@ LIBBLKID = @LIBBLKID@ LIBBSD = @LIBBSD@ +LIBCAP = @LIBCAP@ LIBCRYPT = @LIBCRYPT@ LIBNSL = @LIBNSL@ LIBOBJS = @LIBOBJS@ @@ -235,6 +237,7 @@ enable_ipv6 = @enable_ipv6@ enable_mountconfig = @enable_mountconfig@ enable_nfsv3 = @enable_nfsv3@ enable_nfsv4 = @enable_nfsv4@ +enable_nfsv41 = @enable_nfsv41@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ @@ -271,7 +274,7 @@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AUTOMAKE_OPTIONS = foreign -SUBDIRS = tools support utils linux-nfs +SUBDIRS = tools support utils linux-nfs tests MAINTAINERCLEANFILES = Makefile.in EXTRA_DIST = \ autogen.sh \ @@ -368,7 +371,7 @@ distclean-libtool: # (which will cause the Makefiles to be regenerated when you run `make'); # (2) otherwise, pass the desired values on the `make' command line. $(RECURSIVE_TARGETS): - @failcom='exit 1'; \ + @fail= failcom='exit 1'; \ for f in x $$MAKEFLAGS; do \ case $$f in \ *=* | --[!k]*);; \ @@ -393,7 +396,7 @@ $(RECURSIVE_TARGETS): fi; test -z "$$fail" $(RECURSIVE_CLEAN_TARGETS): - @failcom='exit 1'; \ + @fail= failcom='exit 1'; \ for f in x $$MAKEFLAGS; do \ case $$f in \ *=* | --[!k]*);; \ @@ -557,7 +560,8 @@ distdir: $(DISTFILES) fi; \ done -test -n "$(am__skip_mode_fix)" \ - || find "$(distdir)" -type d ! -perm -777 -exec chmod a+rwx {} \; -o \ + || find "$(distdir)" -type d ! -perm -755 \ + -exec chmod u+rwx,go+rx {} \; -o \ ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \ ! -type d ! -perm -400 -exec chmod a+r {} \; -o \ ! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \ @@ -601,17 +605,17 @@ dist dist-all: distdir distcheck: dist case '$(DIST_ARCHIVES)' in \ *.tar.gz*) \ - GZIP=$(GZIP_ENV) gunzip -c $(distdir).tar.gz | $(am__untar) ;;\ + GZIP=$(GZIP_ENV) gzip -dc $(distdir).tar.gz | $(am__untar) ;;\ *.tar.bz2*) \ - bunzip2 -c $(distdir).tar.bz2 | $(am__untar) ;;\ + bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\ *.tar.lzma*) \ - unlzma -c $(distdir).tar.lzma | $(am__untar) ;;\ + lzma -dc $(distdir).tar.lzma | $(am__untar) ;;\ *.tar.xz*) \ xz -dc $(distdir).tar.xz | $(am__untar) ;;\ *.tar.Z*) \ uncompress -c $(distdir).tar.Z | $(am__untar) ;;\ *.shar.gz*) \ - GZIP=$(GZIP_ENV) gunzip -c $(distdir).shar.gz | unshar ;;\ + GZIP=$(GZIP_ENV) gzip -dc $(distdir).shar.gz | unshar ;;\ *.zip*) \ unzip $(distdir).zip ;;\ esac diff --git a/aclocal.m4 b/aclocal.m4 index 8c8ee96..45e277c 100644 --- a/aclocal.m4 +++ b/aclocal.m4 @@ -1,4 +1,4 @@ -# generated automatically by aclocal 1.11 -*- Autoconf -*- +# generated automatically by aclocal 1.11.1 -*- Autoconf -*- # Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, # 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc. @@ -7862,15 +7862,15 @@ m4_define([lt_dict_filter], # Generated from ltversion.in. -# serial 3012 ltversion.m4 +# serial 3017 ltversion.m4 # This file is part of GNU Libtool -m4_define([LT_PACKAGE_VERSION], [2.2.6]) -m4_define([LT_PACKAGE_REVISION], [1.3012]) +m4_define([LT_PACKAGE_VERSION], [2.2.6b]) +m4_define([LT_PACKAGE_REVISION], [1.3017]) AC_DEFUN([LTVERSION_VERSION], -[macro_version='2.2.6' -macro_revision='1.3012' +[macro_version='2.2.6b' +macro_revision='1.3017' _LT_DECL(, macro_version, 0, [Which release of libtool.m4 was used?]) _LT_DECL(, macro_revision, 0) ]) @@ -8139,7 +8139,7 @@ AC_DEFUN([AM_AUTOMAKE_VERSION], [am__api_version='1.11' dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to dnl require some minimum version. Point them to the right macro. -m4_if([$1], [1.11], [], +m4_if([$1], [1.11.1], [], [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl ]) @@ -8155,7 +8155,7 @@ m4_define([_AM_AUTOCONF_VERSION], []) # Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced. # This function is AC_REQUIREd by AM_INIT_AUTOMAKE. AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], -[AM_AUTOMAKE_VERSION([1.11])dnl +[AM_AUTOMAKE_VERSION([1.11.1])dnl m4_ifndef([AC_AUTOCONF_VERSION], [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl _AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))]) @@ -9133,6 +9133,7 @@ m4_include([aclocal/bsdsignals.m4]) m4_include([aclocal/ipv6.m4]) m4_include([aclocal/kerberos5.m4]) m4_include([aclocal/libblkid.m4]) +m4_include([aclocal/libcap.m4]) m4_include([aclocal/libevent.m4]) m4_include([aclocal/libnfsidmap.m4]) m4_include([aclocal/librpcsecgss.m4]) diff --git a/aclocal/ipv6.m4 b/aclocal/ipv6.m4 index 2490f3d..5ee8fb6 100644 --- a/aclocal/ipv6.m4 +++ b/aclocal/ipv6.m4 @@ -15,8 +15,8 @@ AC_DEFUN([AC_IPV6], [ fi dnl IPv6-enabled networking functions required for IPv6 - AC_CHECK_FUNCS([getnameinfo bindresvport_sa], , - [AC_MSG_ERROR([Missing functions needed for IPv6.])]) + AC_CHECK_FUNCS([getifaddrs getnameinfo bindresvport_sa], , + [AC_MSG_ERROR([Missing library functions needed for IPv6.])]) dnl Need to detect presence of IPv6 networking at run time via dnl getaddrinfo(3); old versions of glibc do not support ADDRCONFIG diff --git a/aclocal/libcap.m4 b/aclocal/libcap.m4 new file mode 100644 index 0000000..eabe507 --- /dev/null +++ b/aclocal/libcap.m4 @@ -0,0 +1,15 @@ +dnl Checks for libcap.so +dnl +AC_DEFUN([AC_LIBCAP], [ + + dnl look for prctl + AC_CHECK_FUNC([prctl], , ) + + dnl look for the library; do not add to LIBS if found + AC_CHECK_LIB([cap], [cap_get_proc], [LIBCAP=-lcap], ,) + AC_SUBST(LIBCAP) + + AC_CHECK_HEADERS([sys/capability.h], , + [AC_MSG_ERROR([libcap headers not found.])]) + +])dnl diff --git a/aclocal/ltversion.m4 b/aclocal/ltversion.m4 index b8e154f..f3c5309 100644 --- a/aclocal/ltversion.m4 +++ b/aclocal/ltversion.m4 @@ -9,15 +9,15 @@ # Generated from ltversion.in. -# serial 3012 ltversion.m4 +# serial 3017 ltversion.m4 # This file is part of GNU Libtool -m4_define([LT_PACKAGE_VERSION], [2.2.6]) -m4_define([LT_PACKAGE_REVISION], [1.3012]) +m4_define([LT_PACKAGE_VERSION], [2.2.6b]) +m4_define([LT_PACKAGE_REVISION], [1.3017]) AC_DEFUN([LTVERSION_VERSION], -[macro_version='2.2.6' -macro_revision='1.3012' +[macro_version='2.2.6b' +macro_revision='1.3017' _LT_DECL(, macro_version, 0, [Which release of libtool.m4 was used?]) _LT_DECL(, macro_revision, 0) ]) diff --git a/compile b/compile index ec64c62..c0096a7 100755 --- a/compile +++ b/compile @@ -1,7 +1,7 @@ #! /bin/sh # Wrapper for compilers which do not understand `-c -o'. -scriptversion=2009-04-28.21; # UTC +scriptversion=2009-10-06.20; # UTC # Copyright (C) 1999, 2000, 2003, 2004, 2005, 2009 Free Software # Foundation, Inc. @@ -124,9 +124,9 @@ trap "rmdir '$lockdir'; exit 1" 1 2 15 ret=$? if test -f "$cofile"; then - mv "$cofile" "$ofile" + test "$cofile" = "$ofile" || mv "$cofile" "$ofile" elif test -f "${cofile}bj"; then - mv "${cofile}bj" "$ofile" + test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile" fi rmdir "$lockdir" diff --git a/config.guess b/config.guess index da83314..dc84c68 100755 --- a/config.guess +++ b/config.guess @@ -1,10 +1,10 @@ #! /bin/sh # Attempt to guess a canonical system name. # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, -# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 +# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 # Free Software Foundation, Inc. -timestamp='2009-04-27' +timestamp='2009-11-20' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by @@ -27,16 +27,16 @@ timestamp='2009-04-27' # the same distribution terms that you use for the rest of that program. -# Originally written by Per Bothner . -# Please send patches to . Submit a context -# diff and a properly formatted ChangeLog entry. +# Originally written by Per Bothner. Please send patches (context +# diff format) to and include a ChangeLog +# entry. # # This script attempts to guess a canonical system name similar to # config.sub. If it succeeds, it prints the system name on stdout, and # exits with 0. Otherwise, it exits with 1. # -# The plan is that this can be called by configure scripts if you -# don't specify an explicit build system type. +# You can get the latest version of this script from: +# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD me=`echo "$0" | sed -e 's,.*/,,'` @@ -170,7 +170,7 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in arm*|i386|m68k|ns32k|sh3*|sparc|vax) eval $set_cc_for_build if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ - | grep __ELF__ >/dev/null + | grep -q __ELF__ then # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). # Return netbsd for either. FIX? @@ -333,6 +333,9 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; + i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) + echo i386-pc-auroraux${UNAME_RELEASE} + exit ;; i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) eval $set_cc_for_build SUN_ARCH="i386" @@ -656,7 +659,7 @@ EOF # => hppa64-hp-hpux11.23 if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | - grep __LP64__ >/dev/null + grep -q __LP64__ then HP_ARCH="hppa2.0w" else @@ -807,12 +810,12 @@ EOF i*:PW*:*) echo ${UNAME_MACHINE}-pc-pw32 exit ;; - *:Interix*:[3456]*) + *:Interix*:*) case ${UNAME_MACHINE} in x86) echo i586-pc-interix${UNAME_RELEASE} exit ;; - EM64T | authenticamd | genuineintel) + authenticamd | genuineintel | EM64T) echo x86_64-unknown-interix${UNAME_RELEASE} exit ;; IA64) @@ -822,6 +825,9 @@ EOF [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) echo i${UNAME_MACHINE}-pc-mks exit ;; + 8664:Windows_NT:*) + echo x86_64-pc-mks + exit ;; i*:Windows_NT*:* | Pentium*:Windows_NT*:*) # How do we know it's Interix rather than the generic POSIX subsystem? # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we @@ -851,6 +857,20 @@ EOF i*86:Minix:*:*) echo ${UNAME_MACHINE}-pc-minix exit ;; + alpha:Linux:*:*) + case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in + EV5) UNAME_MACHINE=alphaev5 ;; + EV56) UNAME_MACHINE=alphaev56 ;; + PCA56) UNAME_MACHINE=alphapca56 ;; + PCA57) UNAME_MACHINE=alphapca56 ;; + EV6) UNAME_MACHINE=alphaev6 ;; + EV67) UNAME_MACHINE=alphaev67 ;; + EV68*) UNAME_MACHINE=alphaev68 ;; + esac + objdump --private-headers /bin/sh | grep -q ld.so.1 + if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi + echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} + exit ;; arm*:Linux:*:*) eval $set_cc_for_build if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ @@ -873,6 +893,17 @@ EOF frv:Linux:*:*) echo frv-unknown-linux-gnu exit ;; + i*86:Linux:*:*) + LIBC=gnu + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #ifdef __dietlibc__ + LIBC=dietlibc + #endif +EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC'` + echo "${UNAME_MACHINE}-pc-linux-${LIBC}" + exit ;; ia64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; @@ -882,78 +913,34 @@ EOF m68*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; - mips:Linux:*:*) - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c - #undef CPU - #undef mips - #undef mipsel - #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) - CPU=mipsel - #else - #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) - CPU=mips - #else - CPU= - #endif - #endif -EOF - eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n ' - /^CPU/{ - s: ::g - p - }'`" - test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; } - ;; - mips64:Linux:*:*) + mips:Linux:*:* | mips64:Linux:*:*) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #undef CPU - #undef mips64 - #undef mips64el + #undef ${UNAME_MACHINE} + #undef ${UNAME_MACHINE}el #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) - CPU=mips64el + CPU=${UNAME_MACHINE}el #else #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) - CPU=mips64 + CPU=${UNAME_MACHINE} #else CPU= #endif #endif EOF - eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n ' - /^CPU/{ - s: ::g - p - }'`" + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'` test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; } ;; or32:Linux:*:*) echo or32-unknown-linux-gnu exit ;; - ppc:Linux:*:*) - echo powerpc-unknown-linux-gnu - exit ;; - ppc64:Linux:*:*) - echo powerpc64-unknown-linux-gnu - exit ;; - alpha:Linux:*:*) - case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in - EV5) UNAME_MACHINE=alphaev5 ;; - EV56) UNAME_MACHINE=alphaev56 ;; - PCA56) UNAME_MACHINE=alphapca56 ;; - PCA57) UNAME_MACHINE=alphapca56 ;; - EV6) UNAME_MACHINE=alphaev6 ;; - EV67) UNAME_MACHINE=alphaev67 ;; - EV68*) UNAME_MACHINE=alphaev68 ;; - esac - objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null - if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi - echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} - exit ;; padre:Linux:*:*) echo sparc-unknown-linux-gnu exit ;; + parisc64:Linux:*:* | hppa64:Linux:*:*) + echo hppa64-unknown-linux-gnu + exit ;; parisc:Linux:*:* | hppa:Linux:*:*) # Look for CPU level case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in @@ -962,8 +949,11 @@ EOF *) echo hppa-unknown-linux-gnu ;; esac exit ;; - parisc64:Linux:*:* | hppa64:Linux:*:*) - echo hppa64-unknown-linux-gnu + ppc64:Linux:*:*) + echo powerpc64-unknown-linux-gnu + exit ;; + ppc:Linux:*:*) + echo powerpc-unknown-linux-gnu exit ;; s390:Linux:*:* | s390x:Linux:*:*) echo ${UNAME_MACHINE}-ibm-linux @@ -986,66 +976,6 @@ EOF xtensa*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; - i*86:Linux:*:*) - # The BFD linker knows what the default object file format is, so - # first see if it will tell us. cd to the root directory to prevent - # problems with other programs or directories called `ld' in the path. - # Set LC_ALL=C to ensure ld outputs messages in English. - ld_supported_targets=`cd /; LC_ALL=C ld --help 2>&1 \ - | sed -ne '/supported targets:/!d - s/[ ][ ]*/ /g - s/.*supported targets: *// - s/ .*// - p'` - case "$ld_supported_targets" in - elf32-i386) - TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu" - ;; - a.out-i386-linux) - echo "${UNAME_MACHINE}-pc-linux-gnuaout" - exit ;; - "") - # Either a pre-BFD a.out linker (linux-gnuoldld) or - # one that does not give us useful --help. - echo "${UNAME_MACHINE}-pc-linux-gnuoldld" - exit ;; - esac - # Determine whether the default compiler is a.out or elf - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c - #include - #ifdef __ELF__ - # ifdef __GLIBC__ - # if __GLIBC__ >= 2 - LIBC=gnu - # else - LIBC=gnulibc1 - # endif - # else - LIBC=gnulibc1 - # endif - #else - #if defined(__INTEL_COMPILER) || defined(__PGI) || defined(__SUNPRO_C) || defined(__SUNPRO_CC) - LIBC=gnu - #else - LIBC=gnuaout - #endif - #endif - #ifdef __dietlibc__ - LIBC=dietlibc - #endif -EOF - eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n ' - /^LIBC/{ - s: ::g - p - }'`" - test x"${LIBC}" != x && { - echo "${UNAME_MACHINE}-pc-linux-${LIBC}" - exit - } - test x"${TENTATIVE}" != x && { echo "${TENTATIVE}"; exit; } - ;; i*86:DYNIX/ptx:4*:*) # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. # earlier versions are messed up and put the nodename in both @@ -1074,7 +1004,7 @@ EOF i*86:syllable:*:*) echo ${UNAME_MACHINE}-pc-syllable exit ;; - i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*) + i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) echo i386-unknown-lynxos${UNAME_RELEASE} exit ;; i*86:*DOS:*:*) @@ -1182,7 +1112,7 @@ EOF rs6000:LynxOS:2.*:*) echo rs6000-unknown-lynxos${UNAME_RELEASE} exit ;; - PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*) + PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) echo powerpc-unknown-lynxos${UNAME_RELEASE} exit ;; SM[BE]S:UNIX_SV:*:*) @@ -1275,6 +1205,16 @@ EOF *:Darwin:*:*) UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown case $UNAME_PROCESSOR in + i386) + eval $set_cc_for_build + if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then + if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + UNAME_PROCESSOR="x86_64" + fi + fi ;; unknown) UNAME_PROCESSOR=powerpc ;; esac echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} diff --git a/config.sub b/config.sub index a39437d..2a55a50 100755 --- a/config.sub +++ b/config.sub @@ -1,10 +1,10 @@ #! /bin/sh # Configuration validation subroutine script. # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, -# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 +# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 # Free Software Foundation, Inc. -timestamp='2009-04-17' +timestamp='2009-11-20' # This file is (in principle) common to ALL GNU software. # The presence of a machine in this file suggests that SOME GNU software @@ -32,13 +32,16 @@ timestamp='2009-04-17' # Please send patches to . Submit a context -# diff and a properly formatted ChangeLog entry. +# diff and a properly formatted GNU ChangeLog entry. # # Configuration subroutine to validate and canonicalize a configuration type. # Supply the specified configuration type as an argument. # If it is invalid, we print an error message on stderr and exit with code 1. # Otherwise, we print the canonical config type on stdout and succeed. +# You can get the latest version of this script from: +# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD + # This file is supposed to be the same for all GNU packages # and recognize all the CPU types, system types and aliases # that are meaningful with *any* GNU software. @@ -149,10 +152,13 @@ case $os in -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ - -apple | -axis | -knuth | -cray) + -apple | -axis | -knuth | -cray | -microblaze) os= basic_machine=$1 ;; + -bluegene*) + os=-cnk + ;; -sim | -cisco | -oki | -wec | -winbond) os= basic_machine=$1 @@ -281,6 +287,7 @@ case $basic_machine in | pdp10 | pdp11 | pj | pjl \ | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \ | pyramid \ + | rx \ | score \ | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ | sh64 | sh64le \ @@ -288,13 +295,14 @@ case $basic_machine in | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ | spu | strongarm \ | tahoe | thumb | tic4x | tic80 | tron \ + | ubicom32 \ | v850 | v850e \ | we32k \ | x86 | xc16x | xscale | xscalee[bl] | xstormy16 | xtensa \ | z8k | z80) basic_machine=$basic_machine-unknown ;; - m6811 | m68hc11 | m6812 | m68hc12) + m6811 | m68hc11 | m6812 | m68hc12 | picochip) # Motorola 68HC11/12. basic_machine=$basic_machine-unknown os=-none @@ -337,7 +345,7 @@ case $basic_machine in | lm32-* \ | m32c-* | m32r-* | m32rle-* \ | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ - | m88110-* | m88k-* | maxq-* | mcore-* | metag-* \ + | m88110-* | m88k-* | maxq-* | mcore-* | metag-* | microblaze-* \ | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ | mips16-* \ | mips64-* | mips64el-* \ @@ -365,7 +373,7 @@ case $basic_machine in | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \ | pyramid-* \ - | romp-* | rs6000-* \ + | romp-* | rs6000-* | rx-* \ | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ @@ -374,6 +382,7 @@ case $basic_machine in | tahoe-* | thumb-* \ | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* | tile-* \ | tron-* \ + | ubicom32-* \ | v850-* | v850e-* | vax-* \ | we32k-* \ | x86-* | x86_64-* | xc16x-* | xps100-* | xscale-* | xscalee[bl]-* \ @@ -467,6 +476,10 @@ case $basic_machine in basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'` os=-linux ;; + bluegene*) + basic_machine=powerpc-ibm + os=-cnk + ;; c90) basic_machine=c90-cray os=-unicos @@ -719,6 +732,9 @@ case $basic_machine in basic_machine=ns32k-utek os=-sysv ;; + microblaze) + basic_machine=microblaze-xilinx + ;; mingw32) basic_machine=i386-pc os=-mingw32 @@ -1240,6 +1256,9 @@ case $os in # First match some system type aliases # that might get confused with valid system types. # -solaris* is a basic system type, with this one exception. + -auroraux) + os=-auroraux + ;; -solaris1 | -solaris1.*) os=`echo $os | sed -e 's|solaris1|sunos4|'` ;; @@ -1260,9 +1279,9 @@ case $os in # Each alternative MUST END IN A *, to match a version number. # -sysv* is not here because it comes later, after sysvr4. -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ - | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\ - | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \ - | -kopensolaris* \ + | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\ + | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \ + | -sym* | -kopensolaris* \ | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ | -aos* | -aros* \ | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ @@ -1283,7 +1302,7 @@ case $os in | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ - | -skyos* | -haiku* | -rdos* | -toppers* | -drops*) + | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es*) # Remember, each alternative MUST END IN *, to match a version number. ;; -qnx*) @@ -1613,7 +1632,7 @@ case $basic_machine in -sunos*) vendor=sun ;; - -aix*) + -cnk*|-aix*) vendor=ibm ;; -beos*) diff --git a/configure b/configure index 926d735..0080be3 100755 --- a/configure +++ b/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.63 for linux nfs-utils 1.2.1. +# Generated by GNU Autoconf 2.63 for linux nfs-utils 1.2.2. # # Report bugs to . # @@ -745,8 +745,8 @@ SHELL=${CONFIG_SHELL-/bin/sh} # Identity of this package. PACKAGE_NAME='linux nfs-utils' PACKAGE_TARNAME='nfs-utils' -PACKAGE_VERSION='1.2.1' -PACKAGE_STRING='linux nfs-utils 1.2.1' +PACKAGE_VERSION='1.2.2' +PACKAGE_STRING='linux nfs-utils 1.2.2' PACKAGE_BUGREPORT='linux-nfs@vger.kernel.org' ac_default_prefix=/usr @@ -841,6 +841,7 @@ CXX HAVE_TCP_WRAPPER HAVE_LIBWRAP LIBWRAP +LIBCAP EGREP GREP CPP @@ -879,6 +880,9 @@ CONFIG_GSS_TRUE enable_gss SVCGSSD GSSD +CONFIG_NFSV41_FALSE +CONFIG_NFSV41_TRUE +enable_nfsv41 CONFIG_NFSV4_FALSE CONFIG_NFSV4_TRUE enable_nfsv4 @@ -969,6 +973,7 @@ with_statduser with_start_statd enable_nfsv3 enable_nfsv4 +enable_nfsv41 enable_gss enable_kprefix with_rpcgen @@ -1560,7 +1565,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures linux nfs-utils 1.2.1 to adapt to many kinds of systems. +\`configure' configures linux nfs-utils 1.2.2 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1630,7 +1635,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of linux nfs-utils 1.2.1:";; + short | recursive ) echo "Configuration of linux nfs-utils 1.2.2:";; esac cat <<\_ACEOF @@ -1642,6 +1647,7 @@ Optional Features: (and sometimes confusing) to the casual installer --enable-nfsv3 enable support for NFSv3 [default=yes] --enable-nfsv4 enable support for NFSv4 [default=yes] + --enable-nfsv41 enable support for NFSv41 [default=no] --enable-gss enable support for rpcsec_gss [default=yes] --enable-kprefix install progs as rpc.knfsd etc --disable-uuid Exclude uuid support to avoid buggy libblkid @@ -1769,7 +1775,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -linux nfs-utils configure 1.2.1 +linux nfs-utils configure 1.2.2 generated by GNU Autoconf 2.63 Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, @@ -1783,7 +1789,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by linux nfs-utils $as_me 1.2.1, which was +It was created by linux nfs-utils $as_me 1.2.2, which was generated by GNU Autoconf 2.63. Invocation command line was $ $0 $@ @@ -2716,7 +2722,7 @@ fi # Define the identity of the package. PACKAGE='nfs-utils' - VERSION='1.2.1' + VERSION='1.2.2' cat >>confdefs.h <<_ACEOF @@ -2877,6 +2883,33 @@ else CONFIG_NFSV4_FALSE= fi + +# Check whether --enable-nfsv41 was given. +if test "${enable_nfsv41+set}" = set; then + enableval=$enable_nfsv41; enable_nfsv41=$enableval +else + enable_nfsv41=no +fi + + if test "$enable_nfsv41" = yes; then + +cat >>confdefs.h <<\_ACEOF +#define NFS41_SUPPORTED 1 +_ACEOF + + else + enable_nfsv4= + fi + + if test "$enable_nfsv41" = "yes"; then + CONFIG_NFSV41_TRUE= + CONFIG_NFSV41_FALSE='#' +else + CONFIG_NFSV41_TRUE='#' + CONFIG_NFSV41_FALSE= +fi + + # Check whether --enable-gss was given. if test "${enable_gss+set}" = set; then enableval=$enable_gss; enable_gss=$enableval @@ -5060,6 +5093,324 @@ done + + + { $as_echo "$as_me:$LINENO: checking for prctl" >&5 +$as_echo_n "checking for prctl... " >&6; } +if test "${ac_cv_func_prctl+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define prctl to an innocuous variant, in case declares prctl. + For example, HP-UX 11i declares gettimeofday. */ +#define prctl innocuous_prctl + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char prctl (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef prctl + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char prctl (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_prctl || defined __stub___prctl +choke me +#endif + +int +main () +{ +return prctl (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then + ac_cv_func_prctl=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_func_prctl=no +fi + +rm -rf conftest.dSYM +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_func_prctl" >&5 +$as_echo "$ac_cv_func_prctl" >&6; } + + + { $as_echo "$as_me:$LINENO: checking for cap_get_proc in -lcap" >&5 +$as_echo_n "checking for cap_get_proc in -lcap... " >&6; } +if test "${ac_cv_lib_cap_cap_get_proc+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lcap $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char cap_get_proc (); +int +main () +{ +return cap_get_proc (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then + ac_cv_lib_cap_cap_get_proc=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_lib_cap_cap_get_proc=no +fi + +rm -rf conftest.dSYM +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_cap_cap_get_proc" >&5 +$as_echo "$ac_cv_lib_cap_cap_get_proc" >&6; } +if test "x$ac_cv_lib_cap_cap_get_proc" = x""yes; then + LIBCAP=-lcap +fi + + + + +for ac_header in sys/capability.h +do +as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + { $as_echo "$as_me:$LINENO: checking for $ac_header" >&5 +$as_echo_n "checking for $ac_header... " >&6; } +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + $as_echo_n "(cached) " >&6 +fi +ac_res=`eval 'as_val=${'$as_ac_Header'} + $as_echo "$as_val"'` + { $as_echo "$as_me:$LINENO: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +else + # Is the header compilable? +{ $as_echo "$as_me:$LINENO: checking $ac_header usability" >&5 +$as_echo_n "checking $ac_header usability... " >&6; } +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_header_compiler=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_compiler=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +$as_echo "$ac_header_compiler" >&6; } + +# Is the header present? +{ $as_echo "$as_me:$LINENO: checking $ac_header presence" >&5 +$as_echo_n "checking $ac_header presence... " >&6; } +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then + ac_header_preproc=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi + +rm -f conftest.err conftest.$ac_ext +{ $as_echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +$as_echo "$ac_header_preproc" >&6; } + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +$as_echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +$as_echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +$as_echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +$as_echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +$as_echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +$as_echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { $as_echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +$as_echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( cat <<\_ASBOX +## ---------------------------------------- ## +## Report this to linux-nfs@vger.kernel.org ## +## ---------------------------------------- ## +_ASBOX + ) | sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +{ $as_echo "$as_me:$LINENO: checking for $ac_header" >&5 +$as_echo_n "checking for $ac_header... " >&6; } +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + $as_echo_n "(cached) " >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +ac_res=`eval 'as_val=${'$as_ac_Header'} + $as_echo "$as_val"'` + { $as_echo "$as_me:$LINENO: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + +fi +as_val=`eval 'as_val=${'$as_ac_Header'} + $as_echo "$as_val"'` + if test "x$as_val" = x""yes; then + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +else + { { $as_echo "$as_me:$LINENO: error: libcap headers not found." >&5 +$as_echo "$as_me: error: libcap headers not found." >&2;} + { (exit 1); exit 1; }; } +fi + +done + + + + # Check whether user wants TCP wrappers support TCPW_MSG="no" @@ -7232,8 +7583,8 @@ esac -macro_version='2.2.6' -macro_revision='1.3012' +macro_version='2.2.6b' +macro_revision='1.3017' @@ -7709,13 +8060,13 @@ if test "${lt_cv_nm_interface+set}" = set; then else lt_cv_nm_interface="BSD nm" echo "int some_variable = 0;" > conftest.$ac_ext - (eval echo "\"\$as_me:7712: $ac_compile\"" >&5) + (eval echo "\"\$as_me:8063: $ac_compile\"" >&5) (eval "$ac_compile" 2>conftest.err) cat conftest.err >&5 - (eval echo "\"\$as_me:7715: $NM \\\"conftest.$ac_objext\\\"\"" >&5) + (eval echo "\"\$as_me:8066: $NM \\\"conftest.$ac_objext\\\"\"" >&5) (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) cat conftest.err >&5 - (eval echo "\"\$as_me:7718: output\"" >&5) + (eval echo "\"\$as_me:8069: output\"" >&5) cat conftest.out >&5 if $GREP 'External.*some_variable' conftest.out > /dev/null; then lt_cv_nm_interface="MS dumpbin" @@ -8909,7 +9260,7 @@ ia64-*-hpux*) ;; *-*-irix6*) # Find out which ABI we are using. - echo '#line 8912 "configure"' > conftest.$ac_ext + echo '#line 9263 "configure"' > conftest.$ac_ext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? @@ -11012,11 +11363,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:11015: $lt_compile\"" >&5) + (eval echo "\"\$as_me:11366: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:11019: \$? = $ac_status" >&5 + echo "$as_me:11370: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. @@ -11351,11 +11702,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:11354: $lt_compile\"" >&5) + (eval echo "\"\$as_me:11705: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:11358: \$? = $ac_status" >&5 + echo "$as_me:11709: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. @@ -11456,11 +11807,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:11459: $lt_compile\"" >&5) + (eval echo "\"\$as_me:11810: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:11463: \$? = $ac_status" >&5 + echo "$as_me:11814: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized @@ -11511,11 +11862,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:11514: $lt_compile\"" >&5) + (eval echo "\"\$as_me:11865: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:11518: \$? = $ac_status" >&5 + echo "$as_me:11869: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized @@ -14314,7 +14665,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 14317 "configure" +#line 14668 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -14410,7 +14761,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 14413 "configure" +#line 14764 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -16430,11 +16781,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:16433: $lt_compile\"" >&5) + (eval echo "\"\$as_me:16784: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:16437: \$? = $ac_status" >&5 + echo "$as_me:16788: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. @@ -16529,11 +16880,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:16532: $lt_compile\"" >&5) + (eval echo "\"\$as_me:16883: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:16536: \$? = $ac_status" >&5 + echo "$as_me:16887: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized @@ -16581,11 +16932,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:16584: $lt_compile\"" >&5) + (eval echo "\"\$as_me:16935: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:16588: \$? = $ac_status" >&5 + echo "$as_me:16939: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized @@ -21455,7 +21806,8 @@ $as_echo "$as_me: error: '--enable-ipv6' requires TIRPC support." >&2;} -for ac_func in getnameinfo bindresvport_sa + +for ac_func in getifaddrs getnameinfo bindresvport_sa do as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` { $as_echo "$as_me:$LINENO: checking for $ac_func" >&5 @@ -21553,8 +21905,8 @@ as_val=`eval 'as_val=${'$as_ac_var'} _ACEOF else - { { $as_echo "$as_me:$LINENO: error: Missing functions needed for IPv6." >&5 -$as_echo "$as_me: error: Missing functions needed for IPv6." >&2;} + { { $as_echo "$as_me:$LINENO: error: Missing library functions needed for IPv6." >&5 +$as_echo "$as_me: error: Missing library functions needed for IPv6." >&2;} { (exit 1); exit 1; }; } fi done @@ -25528,11 +25880,12 @@ done + for ac_func in alarm atexit dup2 fdatasync ftruncate getcwd \ gethostbyaddr gethostbyname gethostname getmntent \ - getnameinfo getrpcbyname \ + getnameinfo getrpcbyname getifaddrs \ gettimeofday hasmntopt inet_ntoa innetgr memset mkdir pathconf \ realpath rmdir select socket strcasecmp strchr strdup \ strerror strrchr strtol strtoul sigprocmask @@ -27476,7 +27829,7 @@ AM_CFLAGS="$my_am_cflags" ACLOCAL_AMFLAGS="-I $ac_macro_dir \$(ACLOCAL_FLAGS)" -ac_config_files="$ac_config_files Makefile linux-nfs/Makefile support/Makefile support/export/Makefile support/include/nfs/Makefile support/include/rpcsvc/Makefile support/include/sys/fs/Makefile support/include/sys/Makefile support/include/Makefile support/misc/Makefile support/nfs/Makefile tools/Makefile tools/locktest/Makefile tools/nlmtest/Makefile tools/rpcdebug/Makefile tools/rpcgen/Makefile utils/Makefile utils/exportfs/Makefile utils/gssd/Makefile utils/idmapd/Makefile utils/mount/Makefile utils/mountd/Makefile utils/nfsd/Makefile utils/nfsstat/Makefile utils/showmount/Makefile utils/statd/Makefile" +ac_config_files="$ac_config_files Makefile linux-nfs/Makefile support/Makefile support/export/Makefile support/include/nfs/Makefile support/include/rpcsvc/Makefile support/include/sys/fs/Makefile support/include/sys/Makefile support/include/Makefile support/misc/Makefile support/nfs/Makefile support/nsm/Makefile tools/Makefile tools/locktest/Makefile tools/nlmtest/Makefile tools/rpcdebug/Makefile tools/rpcgen/Makefile utils/Makefile utils/exportfs/Makefile utils/gssd/Makefile utils/idmapd/Makefile utils/mount/Makefile utils/mountd/Makefile utils/nfsd/Makefile utils/nfsstat/Makefile utils/showmount/Makefile utils/statd/Makefile tests/Makefile tests/nsm_client/Makefile" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure @@ -27597,6 +27950,13 @@ $as_echo "$as_me: error: conditional \"CONFIG_NFSV4\" was never defined. Usually this means the macro was only invoked conditionally." >&2;} { (exit 1); exit 1; }; } fi +if test -z "${CONFIG_NFSV41_TRUE}" && test -z "${CONFIG_NFSV41_FALSE}"; then + { { $as_echo "$as_me:$LINENO: error: conditional \"CONFIG_NFSV41\" was never defined. +Usually this means the macro was only invoked conditionally." >&5 +$as_echo "$as_me: error: conditional \"CONFIG_NFSV41\" was never defined. +Usually this means the macro was only invoked conditionally." >&2;} + { (exit 1); exit 1; }; } +fi if test -z "${CONFIG_GSS_TRUE}" && test -z "${CONFIG_GSS_FALSE}"; then { { $as_echo "$as_me:$LINENO: error: conditional \"CONFIG_GSS\" was never defined. Usually this means the macro was only invoked conditionally." >&5 @@ -27996,7 +28356,7 @@ exec 6>&1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by linux nfs-utils $as_me 1.2.1, which was +This file was extended by linux nfs-utils $as_me 1.2.2, which was generated by GNU Autoconf 2.63. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -28059,7 +28419,7 @@ Report bugs to ." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_version="\\ -linux nfs-utils config.status 1.2.1 +linux nfs-utils config.status 1.2.2 configured by $0, generated by GNU Autoconf 2.63, with options \\"`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\" @@ -28547,6 +28907,7 @@ do "support/include/Makefile") CONFIG_FILES="$CONFIG_FILES support/include/Makefile" ;; "support/misc/Makefile") CONFIG_FILES="$CONFIG_FILES support/misc/Makefile" ;; "support/nfs/Makefile") CONFIG_FILES="$CONFIG_FILES support/nfs/Makefile" ;; + "support/nsm/Makefile") CONFIG_FILES="$CONFIG_FILES support/nsm/Makefile" ;; "tools/Makefile") CONFIG_FILES="$CONFIG_FILES tools/Makefile" ;; "tools/locktest/Makefile") CONFIG_FILES="$CONFIG_FILES tools/locktest/Makefile" ;; "tools/nlmtest/Makefile") CONFIG_FILES="$CONFIG_FILES tools/nlmtest/Makefile" ;; @@ -28562,6 +28923,8 @@ do "utils/nfsstat/Makefile") CONFIG_FILES="$CONFIG_FILES utils/nfsstat/Makefile" ;; "utils/showmount/Makefile") CONFIG_FILES="$CONFIG_FILES utils/showmount/Makefile" ;; "utils/statd/Makefile") CONFIG_FILES="$CONFIG_FILES utils/statd/Makefile" ;; + "tests/Makefile") CONFIG_FILES="$CONFIG_FILES tests/Makefile" ;; + "tests/nsm_client/Makefile") CONFIG_FILES="$CONFIG_FILES tests/nsm_client/Makefile" ;; *) { { $as_echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5 $as_echo "$as_me: error: invalid argument: $ac_config_target" >&2;} diff --git a/configure.ac b/configure.ac index 3ad415c..b7520d8 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ dnl Process this file with autoconf to produce a configure script. dnl -AC_INIT([linux nfs-utils],[1.2.1],[linux-nfs@vger.kernel.org],[nfs-utils]) +AC_INIT([linux nfs-utils],[1.2.2],[linux-nfs@vger.kernel.org],[nfs-utils]) AC_CANONICAL_BUILD([]) AC_CANONICAL_HOST([]) AC_CONFIG_MACRO_DIR(aclocal) @@ -72,6 +72,20 @@ AC_ARG_ENABLE(nfsv4, AC_SUBST(IDMAPD) AC_SUBST(enable_nfsv4) AM_CONDITIONAL(CONFIG_NFSV4, [test "$enable_nfsv4" = "yes"]) + +AC_ARG_ENABLE(nfsv41, + [AC_HELP_STRING([--enable-nfsv41], + [enable support for NFSv41 @<:@default=no@:>@])], + enable_nfsv41=$enableval, + enable_nfsv41=no) + if test "$enable_nfsv41" = yes; then + AC_DEFINE(NFS41_SUPPORTED, 1, [Define this if you want NFSv41 support compiled in]) + else + enable_nfsv4= + fi + AC_SUBST(enable_nfsv41) + AM_CONDITIONAL(CONFIG_NFSV41, [test "$enable_nfsv41" = "yes"]) + AC_ARG_ENABLE(gss, [AC_HELP_STRING([--enable-gss], [enable support for rpcsec_gss @<:@default=yes@:>@])], @@ -166,6 +180,9 @@ fi dnl Check for TI-RPC library and headers AC_LIBTIRPC +dnl Check for -lcap +AC_LIBCAP + # Check whether user wants TCP wrappers support AC_TCP_WRAPPERS @@ -327,7 +344,7 @@ AC_FUNC_STAT AC_FUNC_VPRINTF AC_CHECK_FUNCS([alarm atexit dup2 fdatasync ftruncate getcwd \ gethostbyaddr gethostbyname gethostname getmntent \ - getnameinfo getrpcbyname \ + getnameinfo getrpcbyname getifaddrs \ gettimeofday hasmntopt inet_ntoa innetgr memset mkdir pathconf \ realpath rmdir select socket strcasecmp strchr strdup \ strerror strrchr strtol strtoul sigprocmask]) @@ -402,6 +419,7 @@ AC_CONFIG_FILES([ support/include/Makefile support/misc/Makefile support/nfs/Makefile + support/nsm/Makefile tools/Makefile tools/locktest/Makefile tools/nlmtest/Makefile @@ -416,6 +434,8 @@ AC_CONFIG_FILES([ utils/nfsd/Makefile utils/nfsstat/Makefile utils/showmount/Makefile - utils/statd/Makefile]) + utils/statd/Makefile + tests/Makefile + tests/nsm_client/Makefile]) AC_OUTPUT diff --git a/linux-nfs/Makefile.in b/linux-nfs/Makefile.in index 3ea0b53..cd0f843 100644 --- a/linux-nfs/Makefile.in +++ b/linux-nfs/Makefile.in @@ -1,4 +1,4 @@ -# Makefile.in generated by automake 1.11 from Makefile.am. +# Makefile.in generated by automake 1.11.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, @@ -41,6 +41,7 @@ am__aclocal_m4_deps = $(top_srcdir)/aclocal/bsdsignals.m4 \ $(top_srcdir)/aclocal/ipv6.m4 \ $(top_srcdir)/aclocal/kerberos5.m4 \ $(top_srcdir)/aclocal/libblkid.m4 \ + $(top_srcdir)/aclocal/libcap.m4 \ $(top_srcdir)/aclocal/libevent.m4 \ $(top_srcdir)/aclocal/libnfsidmap.m4 \ $(top_srcdir)/aclocal/librpcsecgss.m4 \ @@ -115,6 +116,7 @@ LDFLAGS = @LDFLAGS@ LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@ LIBBLKID = @LIBBLKID@ LIBBSD = @LIBBSD@ +LIBCAP = @LIBCAP@ LIBCRYPT = @LIBCRYPT@ LIBNSL = @LIBNSL@ LIBOBJS = @LIBOBJS@ @@ -181,6 +183,7 @@ enable_ipv6 = @enable_ipv6@ enable_mountconfig = @enable_mountconfig@ enable_nfsv3 = @enable_nfsv3@ enable_nfsv4 = @enable_nfsv4@ +enable_nfsv41 = @enable_nfsv41@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ diff --git a/ltmain.sh b/ltmain.sh index 6939dcc..a72f2fd 100755 --- a/ltmain.sh +++ b/ltmain.sh @@ -1,6 +1,6 @@ # Generated from ltmain.m4sh. -# ltmain.sh (GNU libtool) 2.2.6 +# ltmain.sh (GNU libtool) 2.2.6b # Written by Gordon Matzigkeit , 1996 # Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006, 2007 2008 Free Software Foundation, Inc. @@ -65,7 +65,7 @@ # compiler: $LTCC # compiler flags: $LTCFLAGS # linker: $LD (gnu? $with_gnu_ld) -# $progname: (GNU libtool) 2.2.6 +# $progname: (GNU libtool) 2.2.6b # automake: $automake_version # autoconf: $autoconf_version # @@ -73,9 +73,9 @@ PROGRAM=ltmain.sh PACKAGE=libtool -VERSION=2.2.6 +VERSION=2.2.6b TIMESTAMP="" -package_revision=1.3012 +package_revision=1.3017 # Be Bourne compatible if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then diff --git a/support/Makefile.am b/support/Makefile.am index aa4d692..cb37733 100644 --- a/support/Makefile.am +++ b/support/Makefile.am @@ -1,6 +1,6 @@ ## Process this file with automake to produce Makefile.in -SUBDIRS = export include misc nfs +SUBDIRS = export include misc nfs nsm MAINTAINERCLEANFILES = Makefile.in diff --git a/support/Makefile.in b/support/Makefile.in index 6c469a0..a317d46 100644 --- a/support/Makefile.in +++ b/support/Makefile.in @@ -1,4 +1,4 @@ -# Makefile.in generated by automake 1.11 from Makefile.am. +# Makefile.in generated by automake 1.11.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, @@ -40,6 +40,7 @@ am__aclocal_m4_deps = $(top_srcdir)/aclocal/bsdsignals.m4 \ $(top_srcdir)/aclocal/ipv6.m4 \ $(top_srcdir)/aclocal/kerberos5.m4 \ $(top_srcdir)/aclocal/libblkid.m4 \ + $(top_srcdir)/aclocal/libcap.m4 \ $(top_srcdir)/aclocal/libevent.m4 \ $(top_srcdir)/aclocal/libnfsidmap.m4 \ $(top_srcdir)/aclocal/librpcsecgss.m4 \ @@ -154,6 +155,7 @@ LDFLAGS = @LDFLAGS@ LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@ LIBBLKID = @LIBBLKID@ LIBBSD = @LIBBSD@ +LIBCAP = @LIBCAP@ LIBCRYPT = @LIBCRYPT@ LIBNSL = @LIBNSL@ LIBOBJS = @LIBOBJS@ @@ -220,6 +222,7 @@ enable_ipv6 = @enable_ipv6@ enable_mountconfig = @enable_mountconfig@ enable_nfsv3 = @enable_nfsv3@ enable_nfsv4 = @enable_nfsv4@ +enable_nfsv41 = @enable_nfsv41@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ @@ -255,7 +258,7 @@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ -SUBDIRS = export include misc nfs +SUBDIRS = export include misc nfs nsm MAINTAINERCLEANFILES = Makefile.in all: all-recursive @@ -304,7 +307,7 @@ clean-libtool: # (which will cause the Makefiles to be regenerated when you run `make'); # (2) otherwise, pass the desired values on the `make' command line. $(RECURSIVE_TARGETS): - @failcom='exit 1'; \ + @fail= failcom='exit 1'; \ for f in x $$MAKEFLAGS; do \ case $$f in \ *=* | --[!k]*);; \ @@ -329,7 +332,7 @@ $(RECURSIVE_TARGETS): fi; test -z "$$fail" $(RECURSIVE_CLEAN_TARGETS): - @failcom='exit 1'; \ + @fail= failcom='exit 1'; \ for f in x $$MAKEFLAGS; do \ case $$f in \ *=* | --[!k]*);; \ diff --git a/support/export/Makefile.in b/support/export/Makefile.in index f2640c8..e77cbaa 100644 --- a/support/export/Makefile.in +++ b/support/export/Makefile.in @@ -1,4 +1,4 @@ -# Makefile.in generated by automake 1.11 from Makefile.am. +# Makefile.in generated by automake 1.11.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, @@ -43,6 +43,7 @@ am__aclocal_m4_deps = $(top_srcdir)/aclocal/bsdsignals.m4 \ $(top_srcdir)/aclocal/ipv6.m4 \ $(top_srcdir)/aclocal/kerberos5.m4 \ $(top_srcdir)/aclocal/libblkid.m4 \ + $(top_srcdir)/aclocal/libcap.m4 \ $(top_srcdir)/aclocal/libevent.m4 \ $(top_srcdir)/aclocal/libnfsidmap.m4 \ $(top_srcdir)/aclocal/librpcsecgss.m4 \ @@ -141,6 +142,7 @@ LDFLAGS = @LDFLAGS@ LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@ LIBBLKID = @LIBBLKID@ LIBBSD = @LIBBSD@ +LIBCAP = @LIBCAP@ LIBCRYPT = @LIBCRYPT@ LIBNSL = @LIBNSL@ LIBOBJS = @LIBOBJS@ @@ -207,6 +209,7 @@ enable_ipv6 = @enable_ipv6@ enable_mountconfig = @enable_mountconfig@ enable_nfsv3 = @enable_nfsv3@ enable_nfsv4 = @enable_nfsv4@ +enable_nfsv41 = @enable_nfsv41@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ diff --git a/support/export/client.c b/support/export/client.c index 5fcf355..6236561 100644 --- a/support/export/client.c +++ b/support/export/client.c @@ -297,7 +297,7 @@ name_cmp(char *a, char *b) /* compare strings a and b, but only upto ',' in a */ while (*a && *b && *a != ',' && *a == *b) a++, b++; - if (!*b && (!*a || !a == ',') ) + if (!*b && (!*a || *a == ',')) return 0; if (!*b) return 1; if (!*a || *a == ',') return -1; diff --git a/support/export/export.c b/support/export/export.c index e5e6cb0..2943466 100644 --- a/support/export/export.c +++ b/support/export/export.c @@ -28,6 +28,22 @@ static int export_check(nfs_export *, struct hostent *, char *); static nfs_export * export_allowed_internal(struct hostent *hp, char *path); +static void warn_duplicated_exports(nfs_export *exp, struct exportent *eep) +{ + if (exp->m_export.e_flags != eep->e_flags) { + xlog(L_ERROR, "incompatible duplicated export entries:"); + xlog(L_ERROR, "\t%s:%s (0x%x) [IGNORED]", eep->e_hostname, + eep->e_path, eep->e_flags); + xlog(L_ERROR, "\t%s:%s (0x%x)", exp->m_export.e_hostname, + exp->m_export.e_path, exp->m_export.e_flags); + } else { + xlog(L_ERROR, "duplicated export entries:"); + xlog(L_ERROR, "\t%s:%s", eep->e_hostname, eep->e_path); + xlog(L_ERROR, "\t%s:%s", exp->m_export.e_hostname, + exp->m_export.e_path); + } +} + int export_read(char *fname) { @@ -36,27 +52,13 @@ export_read(char *fname) setexportent(fname, "r"); while ((eep = getexportent(0,1)) != NULL) { - exp = export_lookup(eep->e_hostname, eep->e_path, 0); - if (!exp) - export_create(eep,0); - else { - if (exp->m_export.e_flags != eep->e_flags) { - xlog(L_ERROR, "incompatible duplicated export entries:"); - xlog(L_ERROR, "\t%s:%s (0x%x) [IGNORED]", eep->e_hostname, - eep->e_path, eep->e_flags); - xlog(L_ERROR, "\t%s:%s (0x%x)", exp->m_export.e_hostname, - exp->m_export.e_path, exp->m_export.e_flags); - } - else { - xlog(L_ERROR, "duplicated export entries:"); - xlog(L_ERROR, "\t%s:%s", eep->e_hostname, eep->e_path); - xlog(L_ERROR, "\t%s:%s", exp->m_export.e_hostname, - exp->m_export.e_path); - } - } + exp = export_lookup(eep->e_hostname, eep->e_path, 0); + if (!exp) + export_create(eep, 0); + else + warn_duplicated_exports(exp, eep); } endexportent(); - return 0; } diff --git a/support/export/xtab.c b/support/export/xtab.c index 3b1dcce..2a43193 100644 --- a/support/export/xtab.c +++ b/support/export/xtab.c @@ -19,7 +19,9 @@ #include "exportfs.h" #include "xio.h" #include "xlog.h" +#include "v4root.h" +int v4root_needed; static void cond_rename(char *newfile, char *oldfile); static int @@ -36,6 +38,8 @@ xtab_read(char *xtab, char *lockfn, int is_export) if ((lockid = xflock(lockfn, "r")) < 0) return 0; setexportent(xtab, "r"); + if (is_export == 1) + v4root_needed = 1; while ((xp = getexportent(is_export==0, 0)) != NULL) { if (!(exp = export_lookup(xp->e_hostname, xp->e_path, is_export != 1)) && !(exp = export_create(xp, is_export!=1))) { @@ -48,6 +52,8 @@ xtab_read(char *xtab, char *lockfn, int is_export) case 1: exp->m_xtabent = 1; exp->m_mayexport = 1; + if ((xp->e_flags & NFSEXP_FSID) && xp->e_fsid == 0) + v4root_needed = 0; break; case 2: exp->m_exported = -1;/* may be exported */ diff --git a/support/include/Makefile.am b/support/include/Makefile.am index f5a77ec..4b33ee9 100644 --- a/support/include/Makefile.am +++ b/support/include/Makefile.am @@ -9,6 +9,8 @@ noinst_HEADERS = \ nfs_mntent.h \ nfs_paths.h \ nfslib.h \ + nfsrpc.h \ + nsm.h \ rpcmisc.h \ tcpwrapper.h \ xio.h \ diff --git a/support/include/Makefile.in b/support/include/Makefile.in index cff3f34..5e3d74e 100644 --- a/support/include/Makefile.in +++ b/support/include/Makefile.in @@ -1,4 +1,4 @@ -# Makefile.in generated by automake 1.11 from Makefile.am. +# Makefile.in generated by automake 1.11.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, @@ -42,6 +42,7 @@ am__aclocal_m4_deps = $(top_srcdir)/aclocal/bsdsignals.m4 \ $(top_srcdir)/aclocal/ipv6.m4 \ $(top_srcdir)/aclocal/kerberos5.m4 \ $(top_srcdir)/aclocal/libblkid.m4 \ + $(top_srcdir)/aclocal/libcap.m4 \ $(top_srcdir)/aclocal/libevent.m4 \ $(top_srcdir)/aclocal/libnfsidmap.m4 \ $(top_srcdir)/aclocal/librpcsecgss.m4 \ @@ -157,6 +158,7 @@ LDFLAGS = @LDFLAGS@ LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@ LIBBLKID = @LIBBLKID@ LIBBSD = @LIBBSD@ +LIBCAP = @LIBCAP@ LIBCRYPT = @LIBCRYPT@ LIBNSL = @LIBNSL@ LIBOBJS = @LIBOBJS@ @@ -223,6 +225,7 @@ enable_ipv6 = @enable_ipv6@ enable_mountconfig = @enable_mountconfig@ enable_nfsv3 = @enable_nfsv3@ enable_nfsv4 = @enable_nfsv4@ +enable_nfsv41 = @enable_nfsv41@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ @@ -266,6 +269,8 @@ noinst_HEADERS = \ nfs_mntent.h \ nfs_paths.h \ nfslib.h \ + nfsrpc.h \ + nsm.h \ rpcmisc.h \ tcpwrapper.h \ xio.h \ @@ -340,7 +345,7 @@ clean-libtool: # (which will cause the Makefiles to be regenerated when you run `make'); # (2) otherwise, pass the desired values on the `make' command line. $(RECURSIVE_TARGETS): - @failcom='exit 1'; \ + @fail= failcom='exit 1'; \ for f in x $$MAKEFLAGS; do \ case $$f in \ *=* | --[!k]*);; \ @@ -365,7 +370,7 @@ $(RECURSIVE_TARGETS): fi; test -z "$$fail" $(RECURSIVE_CLEAN_TARGETS): - @failcom='exit 1'; \ + @fail= failcom='exit 1'; \ for f in x $$MAKEFLAGS; do \ case $$f in \ *=* | --[!k]*);; \ diff --git a/support/include/config.h.in b/support/include/config.h.in index b8b46e2..03c9ceb 100644 --- a/support/include/config.h.in +++ b/support/include/config.h.in @@ -95,6 +95,9 @@ /* Define to 1 if you have the `gethostname' function. */ #undef HAVE_GETHOSTNAME +/* Define to 1 if you have the `getifaddrs' function. */ +#undef HAVE_GETIFADDRS + /* Define to 1 if you have the `getmntent' function. */ #undef HAVE_GETMNTENT @@ -253,6 +256,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_SYSLOG_H +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_CAPABILITY_H + /* Define to 1 if you have the header file, and it defines `DIR'. */ #undef HAVE_SYS_DIR_H @@ -346,6 +352,9 @@ /* Define this if you want NFSv3 support compiled in */ #undef NFS3_SUPPORTED +/* Define this if you want NFSv41 support compiled in */ +#undef NFS41_SUPPORTED + /* Define this if you want NFSv4 support compiled in */ #undef NFS4_SUPPORTED diff --git a/support/include/exportfs.h b/support/include/exportfs.h index a5cf482..470b2ec 100644 --- a/support/include/exportfs.h +++ b/support/include/exportfs.h @@ -99,10 +99,19 @@ int xtab_mount_write(void); int xtab_export_write(void); void xtab_append(nfs_export *); +int secinfo_addflavor(struct flav_info *, struct exportent *); + int rmtab_read(void); struct nfskey * key_lookup(char *hname); +struct export_features { + unsigned int flags; + unsigned int secinfo_flags; +}; + +struct export_features *get_export_features(void); + /* Record export error. */ extern int export_errno; diff --git a/support/include/ha-callout.h b/support/include/ha-callout.h index efb70fb..1164336 100644 --- a/support/include/ha-callout.h +++ b/support/include/ha-callout.h @@ -53,11 +53,7 @@ ha_callout(char *event, char *arg1, char *arg2, int arg3) default: pid = waitpid(pid, &ret, 0); } sigaction(SIGCHLD, &oldact, &newact); -#ifdef dprintf - dprintf(N_DEBUG, "ha callout returned %d\n", WEXITSTATUS(ret)); -#else xlog(D_GENERAL, "ha callout returned %d\n", WEXITSTATUS(ret)); -#endif } #endif diff --git a/support/include/nfs/Makefile.in b/support/include/nfs/Makefile.in index a416091..0f0d09b 100644 --- a/support/include/nfs/Makefile.in +++ b/support/include/nfs/Makefile.in @@ -1,4 +1,4 @@ -# Makefile.in generated by automake 1.11 from Makefile.am. +# Makefile.in generated by automake 1.11.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, @@ -42,6 +42,7 @@ am__aclocal_m4_deps = $(top_srcdir)/aclocal/bsdsignals.m4 \ $(top_srcdir)/aclocal/ipv6.m4 \ $(top_srcdir)/aclocal/kerberos5.m4 \ $(top_srcdir)/aclocal/libblkid.m4 \ + $(top_srcdir)/aclocal/libcap.m4 \ $(top_srcdir)/aclocal/libevent.m4 \ $(top_srcdir)/aclocal/libnfsidmap.m4 \ $(top_srcdir)/aclocal/librpcsecgss.m4 \ @@ -119,6 +120,7 @@ LDFLAGS = @LDFLAGS@ LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@ LIBBLKID = @LIBBLKID@ LIBBSD = @LIBBSD@ +LIBCAP = @LIBCAP@ LIBCRYPT = @LIBCRYPT@ LIBNSL = @LIBNSL@ LIBOBJS = @LIBOBJS@ @@ -185,6 +187,7 @@ enable_ipv6 = @enable_ipv6@ enable_mountconfig = @enable_mountconfig@ enable_nfsv3 = @enable_nfsv3@ enable_nfsv4 = @enable_nfsv4@ +enable_nfsv41 = @enable_nfsv41@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ diff --git a/support/include/nfs/export.h b/support/include/nfs/export.h index f7a99ba..1547a87 100644 --- a/support/include/nfs/export.h +++ b/support/include/nfs/export.h @@ -24,6 +24,17 @@ #define NFSEXP_FSID 0x2000 #define NFSEXP_CROSSMOUNT 0x4000 #define NFSEXP_NOACL 0x8000 /* reserved for possible ACL related use */ -#define NFSEXP_ALLFLAGS 0xFFFF +#define NFSEXP_V4ROOT 0x10000 +/* + * All flags supported by the kernel before addition of the + * export_features interface: + */ +#define NFSEXP_OLDFLAGS 0x7E3F +/* + * Flags that can vary per flavor, for kernels before addition of the + * export_features interface: + */ +#define NFSEXP_OLD_SECINFO_FLAGS (NFSEXP_READONLY | NFSEXP_ROOTSQUASH \ + | NFSEXP_ALLSQUASH) #endif /* _NSF_EXPORT_H */ diff --git a/support/include/nfs/nfs.h b/support/include/nfs/nfs.h index a64eb0a..c939d78 100644 --- a/support/include/nfs/nfs.h +++ b/support/include/nfs/nfs.h @@ -1,6 +1,8 @@ #ifndef _NFS_NFS_H #define _NFS_NFS_H +#include + #include #include #include @@ -14,7 +16,11 @@ #define NFSD_MAXVERS 4 #define NFSD_MINMINORVERS4 1 +#ifdef NFS41_SUPPORTED #define NFSD_MAXMINORVERS4 1 +#else +#define NFSD_MAXMINORVERS4 0 +#endif struct nfs_fh_len { int fh_size; diff --git a/support/include/nfsrpc.h b/support/include/nfsrpc.h index dff6af7..4db35ab 100644 --- a/support/include/nfsrpc.h +++ b/support/include/nfsrpc.h @@ -58,16 +58,6 @@ static inline void nfs_clear_rpc_createerr(void) memset(&rpc_createerr, 0, sizeof(rpc_createerr)); } -/* - * Extract port value from a socket address - */ -extern uint16_t nfs_get_port(const struct sockaddr *); - -/* - * Set port value in a socket address - */ -extern void nfs_set_port(struct sockaddr *, const uint16_t); - /* * Look up an RPC program name in /etc/rpc */ @@ -89,6 +79,18 @@ extern CLIENT *nfs_get_priv_rpcclient( const struct sockaddr *, const rpcprog_t, const rpcvers_t, struct timeval *); +/* + * Convert a netid to a protocol number and protocol family + */ +extern int nfs_get_proto(const char *netid, sa_family_t *family, + unsigned long *protocol); + +/* + * Convert a protocol family and protocol name to a netid + */ +extern char *nfs_get_netid(const sa_family_t family, + const unsigned long protocol); + /* * Convert a socket address to a universal address */ @@ -158,4 +160,4 @@ extern int nfs_rpc_ping(const struct sockaddr *sap, const unsigned short protocol, const struct timeval *timeout); -#endif /* __NFS_UTILS_NFSRPC_H */ +#endif /* !__NFS_UTILS_NFSRPC_H */ diff --git a/support/include/nsm.h b/support/include/nsm.h new file mode 100644 index 0000000..fb4d823 --- /dev/null +++ b/support/include/nsm.h @@ -0,0 +1,93 @@ +/* + * 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 . + */ + +/* + * NSM for Linux. + */ + +#ifndef NFS_UTILS_SUPPORT_NSM_H +#define NFS_UTILS_SUPPORT_NSM_H + +#include +#include +#include + +#include +#include + +#include "sm_inter.h" + +typedef unsigned int + (*nsm_populate_t)(const char *hostname, + const struct sockaddr *sap, + const struct mon *mon, + const time_t timestamp); + +/* file.c */ + +extern _Bool nsm_setup_pathnames(const char *progname, + const char *parentdir); +extern _Bool nsm_is_default_parentdir(void); +extern _Bool nsm_drop_privileges(const int pidfd); + +extern int nsm_get_state(_Bool update); +extern void nsm_update_kernel_state(const int state); + +extern unsigned int + nsm_retire_monitored_hosts(void); +extern unsigned int + nsm_load_monitor_list(nsm_populate_t func); +extern unsigned int + nsm_load_notify_list(nsm_populate_t func); + +extern _Bool nsm_insert_monitored_host(const char *hostname, + const struct sockaddr *sap, const struct mon *m); +extern void nsm_delete_monitored_host(const char *hostname, + const char *mon_name, const char *my_name); +extern void nsm_delete_notified_host(const char *hostname, + const char *mon_name, const char *my_name); +extern size_t nsm_priv_to_hex(const char *priv, char *buf, + const size_t buflen); + +/* rpc.c */ + +#define NSM_MAXMSGSIZE (2048u) + +extern uint32_t nsm_xmit_getport(const int sock, + const struct sockaddr_in *sin, + const unsigned long program, + const unsigned long version); +extern uint32_t nsm_xmit_getaddr(const int sock, + const struct sockaddr_in6 *sin6, + const rpcprog_t program, const rpcvers_t version); +extern uint32_t nsm_xmit_rpcbind(const int sock, const struct sockaddr *sap, + const rpcprog_t program, const rpcvers_t version); +extern 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); +extern uint32_t nsm_xmit_nlmcall(const int sock, const struct sockaddr *sap, + const socklen_t salen, const struct mon *m, + const int state); +extern uint32_t nsm_parse_reply(XDR *xdrs); +extern unsigned long + nsm_recv_getport(XDR *xdrs); +extern uint16_t nsm_recv_getaddr(XDR *xdrs); +extern uint16_t nsm_recv_rpcbind(const sa_family_t family, XDR *xdrs); + +#endif /* !NFS_UTILS_SUPPORT_NSM_H */ diff --git a/support/include/rpcmisc.h b/support/include/rpcmisc.h index f551a85..1b8f411 100644 --- a/support/include/rpcmisc.h +++ b/support/include/rpcmisc.h @@ -41,7 +41,12 @@ struct rpc_dtable { (xdrproc_t)xdr_##res_type, sizeof(res_type), \ } - +void nfs_svc_unregister(const rpcprog_t program, + const rpcvers_t version); +unsigned int nfs_svc_create(char *name, const rpcprog_t program, + const rpcvers_t version, + void (*dispatch)(struct svc_req *, SVCXPRT *), + const uint16_t port); void rpc_init(char *name, int prog, int vers, void (*dispatch)(struct svc_req *, SVCXPRT *), int defport); diff --git a/support/include/rpcsvc/Makefile.in b/support/include/rpcsvc/Makefile.in index 935ed04..4a32740 100644 --- a/support/include/rpcsvc/Makefile.in +++ b/support/include/rpcsvc/Makefile.in @@ -1,4 +1,4 @@ -# Makefile.in generated by automake 1.11 from Makefile.am. +# Makefile.in generated by automake 1.11.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, @@ -42,6 +42,7 @@ am__aclocal_m4_deps = $(top_srcdir)/aclocal/bsdsignals.m4 \ $(top_srcdir)/aclocal/ipv6.m4 \ $(top_srcdir)/aclocal/kerberos5.m4 \ $(top_srcdir)/aclocal/libblkid.m4 \ + $(top_srcdir)/aclocal/libcap.m4 \ $(top_srcdir)/aclocal/libevent.m4 \ $(top_srcdir)/aclocal/libnfsidmap.m4 \ $(top_srcdir)/aclocal/librpcsecgss.m4 \ @@ -119,6 +120,7 @@ LDFLAGS = @LDFLAGS@ LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@ LIBBLKID = @LIBBLKID@ LIBBSD = @LIBBSD@ +LIBCAP = @LIBCAP@ LIBCRYPT = @LIBCRYPT@ LIBNSL = @LIBNSL@ LIBOBJS = @LIBOBJS@ @@ -185,6 +187,7 @@ enable_ipv6 = @enable_ipv6@ enable_mountconfig = @enable_mountconfig@ enable_nfsv3 = @enable_nfsv3@ enable_nfsv4 = @enable_nfsv4@ +enable_nfsv41 = @enable_nfsv41@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ diff --git a/support/include/sockaddr.h b/support/include/sockaddr.h new file mode 100644 index 0000000..9af2543 --- /dev/null +++ b/support/include/sockaddr.h @@ -0,0 +1,241 @@ +/* + * 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 . + */ + +#ifndef NFS_UTILS_SOCKADDR_H +#define NFS_UTILS_SOCKADDR_H + +#include +#include +#include +#include + +/* + * This type is for defining buffers that contain network socket + * addresses. + * + * Casting a "struct sockaddr *" to the address of a "struct + * sockaddr_storage" breaks C aliasing rules. The "union + * nfs_sockaddr" type follows C aliasing rules yet specifically + * allows converting pointers to it between "struct sockaddr *" + * and a few other network sockaddr-related pointer types. + * + * Note that this union is much smaller than a sockaddr_storage. + * It should be used only for AF_INET or AF_INET6 socket addresses. + * An AF_LOCAL sockaddr_un, for example, will clearly not fit into + * a buffer of this type. + */ +union nfs_sockaddr { + struct sockaddr sa; + struct sockaddr_in s4; + struct sockaddr_in6 s6; +}; + +#if SIZEOF_SOCKLEN_T - 0 == 0 +#define socklen_t unsigned int +#endif + +#define SIZEOF_SOCKADDR_UNKNOWN (socklen_t)0 +#define SIZEOF_SOCKADDR_IN (socklen_t)sizeof(struct sockaddr_in) + +#ifdef IPV6_SUPPORTED +#define SIZEOF_SOCKADDR_IN6 (socklen_t)sizeof(struct sockaddr_in6) +#else /* !IPV6_SUPPORTED */ +#define SIZEOF_SOCKADDR_IN6 SIZEOF_SOCKADDR_UNKNOWN +#endif /* !IPV6_SUPPORTED */ + +/** + * nfs_sockaddr_length - return the size in bytes of a socket address + * @sap: pointer to socket address + * + * Returns the size in bytes of @sap, or zero if the family is + * not recognized. + */ +static inline socklen_t +nfs_sockaddr_length(const struct sockaddr *sap) +{ + switch (sap->sa_family) { + case AF_INET: + return SIZEOF_SOCKADDR_IN; + case AF_INET6: + return SIZEOF_SOCKADDR_IN6; + } + return SIZEOF_SOCKADDR_UNKNOWN; +} + +static inline uint16_t +get_port4(const struct sockaddr *sap) +{ + const struct sockaddr_in *sin = (const struct sockaddr_in *)sap; + return ntohs(sin->sin_port); +} + +#ifdef IPV6_SUPPORTED +static inline uint16_t +get_port6(const struct sockaddr *sap) +{ + const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sap; + return ntohs(sin6->sin6_port); +} +#else /* !IPV6_SUPPORTED */ +static inline uint16_t +get_port6(__attribute__ ((unused)) const struct sockaddr *sap) +{ + return 0; +} +#endif /* !IPV6_SUPPORTED */ + +/** + * nfs_get_port - extract port value from a socket address + * @sap: pointer to socket address + * + * Returns port value in host byte order, or zero if the + * socket address contains an unrecognized family. + */ +static inline uint16_t +nfs_get_port(const struct sockaddr *sap) +{ + switch (sap->sa_family) { + case AF_INET: + return get_port4(sap); + case AF_INET6: + return get_port6(sap); + } + return 0; +} + +static inline void +set_port4(struct sockaddr *sap, const uint16_t port) +{ + struct sockaddr_in *sin = (struct sockaddr_in *)sap; + sin->sin_port = htons(port); +} + +#ifdef IPV6_SUPPORTED +static inline void +set_port6(struct sockaddr *sap, const uint16_t port) +{ + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap; + sin6->sin6_port = htons(port); +} +#else /* !IPV6_SUPPORTED */ +static inline void +set_port6(__attribute__ ((unused)) struct sockaddr *sap, + __attribute__ ((unused)) const uint16_t port) +{ +} +#endif /* !IPV6_SUPPORTED */ + +/** + * nfs_set_port - set port value in a socket address + * @sap: pointer to socket address + * @port: port value to set + * + */ +static inline void +nfs_set_port(struct sockaddr *sap, const uint16_t port) +{ + switch (sap->sa_family) { + case AF_INET: + set_port4(sap, port); + break; + case AF_INET6: + set_port6(sap, port); + break; + } +} + +/** + * nfs_is_v4_loopback - test to see if socket address is AF_INET loopback + * @sap: pointer to socket address + * + * Returns true if the socket address is the standard IPv4 loopback + * address; otherwise false is returned. + */ +static inline _Bool +nfs_is_v4_loopback(const struct sockaddr *sap) +{ + const struct sockaddr_in *sin = (const struct sockaddr_in *)sap; + + if (sin->sin_family != AF_INET) + return false; + if (sin->sin_addr.s_addr != htonl(INADDR_LOOPBACK)) + return false; + return true; +} + +static inline _Bool +compare_sockaddr4(const struct sockaddr *sa1, const struct sockaddr *sa2) +{ + const struct sockaddr_in *sin1 = (const struct sockaddr_in *)sa1; + const struct sockaddr_in *sin2 = (const struct sockaddr_in *)sa2; + return sin1->sin_addr.s_addr == sin2->sin_addr.s_addr; +} + +#ifdef IPV6_SUPPORTED +static inline _Bool +compare_sockaddr6(const struct sockaddr *sa1, const struct sockaddr *sa2) +{ + const struct sockaddr_in6 *sin1 = (const struct sockaddr_in6 *)sa1; + const struct sockaddr_in6 *sin2 = (const struct sockaddr_in6 *)sa2; + + if ((IN6_IS_ADDR_LINKLOCAL((char *)&sin1->sin6_addr) && + IN6_IS_ADDR_LINKLOCAL((char *)&sin2->sin6_addr)) || + (IN6_IS_ADDR_SITELOCAL((char *)&sin1->sin6_addr) && + IN6_IS_ADDR_SITELOCAL((char *)&sin2->sin6_addr))) + if (sin1->sin6_scope_id != sin2->sin6_scope_id) + return false; + + return IN6_ARE_ADDR_EQUAL((char *)&sin1->sin6_addr, + (char *)&sin2->sin6_addr); +} +#else /* !IPV6_SUPPORTED */ +static inline _Bool +compare_sockaddr6(__attribute__ ((unused)) const struct sockaddr *sa1, + __attribute__ ((unused)) const struct sockaddr *sa2) +{ + return false; +} +#endif /* !IPV6_SUPPORTED */ + +/** + * nfs_compare_sockaddr - compare two socket addresses for equality + * @sa1: pointer to a socket address + * @sa2: pointer to a socket address + * + * Returns true if the two socket addresses contain equivalent + * network addresses; otherwise false is returned. + */ +static inline _Bool +nfs_compare_sockaddr(const struct sockaddr *sa1, const struct sockaddr *sa2) +{ + if (sa1 == NULL || sa2 == NULL) + return false; + + if (sa1->sa_family == sa2->sa_family) + switch (sa1->sa_family) { + case AF_INET: + return compare_sockaddr4(sa1, sa2); + case AF_INET6: + return compare_sockaddr6(sa1, sa2); + } + + return false; +} + +#endif /* !NFS_UTILS_SOCKADDR_H */ diff --git a/support/include/sys/Makefile.in b/support/include/sys/Makefile.in index f81de6e..bab4711 100644 --- a/support/include/sys/Makefile.in +++ b/support/include/sys/Makefile.in @@ -1,4 +1,4 @@ -# Makefile.in generated by automake 1.11 from Makefile.am. +# Makefile.in generated by automake 1.11.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, @@ -40,6 +40,7 @@ am__aclocal_m4_deps = $(top_srcdir)/aclocal/bsdsignals.m4 \ $(top_srcdir)/aclocal/ipv6.m4 \ $(top_srcdir)/aclocal/kerberos5.m4 \ $(top_srcdir)/aclocal/libblkid.m4 \ + $(top_srcdir)/aclocal/libcap.m4 \ $(top_srcdir)/aclocal/libevent.m4 \ $(top_srcdir)/aclocal/libnfsidmap.m4 \ $(top_srcdir)/aclocal/librpcsecgss.m4 \ @@ -154,6 +155,7 @@ LDFLAGS = @LDFLAGS@ LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@ LIBBLKID = @LIBBLKID@ LIBBSD = @LIBBSD@ +LIBCAP = @LIBCAP@ LIBCRYPT = @LIBCRYPT@ LIBNSL = @LIBNSL@ LIBOBJS = @LIBOBJS@ @@ -220,6 +222,7 @@ enable_ipv6 = @enable_ipv6@ enable_mountconfig = @enable_mountconfig@ enable_nfsv3 = @enable_nfsv3@ enable_nfsv4 = @enable_nfsv4@ +enable_nfsv41 = @enable_nfsv41@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ @@ -304,7 +307,7 @@ clean-libtool: # (which will cause the Makefiles to be regenerated when you run `make'); # (2) otherwise, pass the desired values on the `make' command line. $(RECURSIVE_TARGETS): - @failcom='exit 1'; \ + @fail= failcom='exit 1'; \ for f in x $$MAKEFLAGS; do \ case $$f in \ *=* | --[!k]*);; \ @@ -329,7 +332,7 @@ $(RECURSIVE_TARGETS): fi; test -z "$$fail" $(RECURSIVE_CLEAN_TARGETS): - @failcom='exit 1'; \ + @fail= failcom='exit 1'; \ for f in x $$MAKEFLAGS; do \ case $$f in \ *=* | --[!k]*);; \ diff --git a/support/include/sys/fs/Makefile.in b/support/include/sys/fs/Makefile.in index 5428b03..82dc4fb 100644 --- a/support/include/sys/fs/Makefile.in +++ b/support/include/sys/fs/Makefile.in @@ -1,4 +1,4 @@ -# Makefile.in generated by automake 1.11 from Makefile.am. +# Makefile.in generated by automake 1.11.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, @@ -42,6 +42,7 @@ am__aclocal_m4_deps = $(top_srcdir)/aclocal/bsdsignals.m4 \ $(top_srcdir)/aclocal/ipv6.m4 \ $(top_srcdir)/aclocal/kerberos5.m4 \ $(top_srcdir)/aclocal/libblkid.m4 \ + $(top_srcdir)/aclocal/libcap.m4 \ $(top_srcdir)/aclocal/libevent.m4 \ $(top_srcdir)/aclocal/libnfsidmap.m4 \ $(top_srcdir)/aclocal/librpcsecgss.m4 \ @@ -119,6 +120,7 @@ LDFLAGS = @LDFLAGS@ LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@ LIBBLKID = @LIBBLKID@ LIBBSD = @LIBBSD@ +LIBCAP = @LIBCAP@ LIBCRYPT = @LIBCRYPT@ LIBNSL = @LIBNSL@ LIBOBJS = @LIBOBJS@ @@ -185,6 +187,7 @@ enable_ipv6 = @enable_ipv6@ enable_mountconfig = @enable_mountconfig@ enable_nfsv3 = @enable_nfsv3@ enable_nfsv4 = @enable_nfsv4@ +enable_nfsv41 = @enable_nfsv41@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ diff --git a/support/include/tcpwrapper.h b/support/include/tcpwrapper.h index 98cf806..f735106 100644 --- a/support/include/tcpwrapper.h +++ b/support/include/tcpwrapper.h @@ -5,14 +5,8 @@ #include #include -extern int verboselog; - -extern int allow_severity; -extern int deny_severity; - -extern int good_client(char *daemon, struct sockaddr_in *addr); -extern int from_local (struct sockaddr_in *addr); -extern int check_default(char *daemon, struct sockaddr_in *addr, - u_long proc, u_long prog); +extern int from_local(const struct sockaddr *sap); +extern int check_default(char *name, struct sockaddr *sap, + const unsigned long program); #endif /* TCP_WRAPPER_H */ diff --git a/support/include/v4root.h b/support/include/v4root.h new file mode 100644 index 0000000..706c15c --- /dev/null +++ b/support/include/v4root.h @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2009 Red Hat + * support/include/v4root.h + * + * Support routines for dynamic pseudo roots. + * + */ + +#ifndef V4ROOT_H +#define V4ROOT_H + +extern int v4root_needed; +extern void v4root_set(void); + +#endif /* V4ROOT_H */ diff --git a/support/misc/Makefile.in b/support/misc/Makefile.in index 9b62b27..94a9877 100644 --- a/support/misc/Makefile.in +++ b/support/misc/Makefile.in @@ -1,4 +1,4 @@ -# Makefile.in generated by automake 1.11 from Makefile.am. +# Makefile.in generated by automake 1.11.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, @@ -41,6 +41,7 @@ am__aclocal_m4_deps = $(top_srcdir)/aclocal/bsdsignals.m4 \ $(top_srcdir)/aclocal/ipv6.m4 \ $(top_srcdir)/aclocal/kerberos5.m4 \ $(top_srcdir)/aclocal/libblkid.m4 \ + $(top_srcdir)/aclocal/libcap.m4 \ $(top_srcdir)/aclocal/libevent.m4 \ $(top_srcdir)/aclocal/libnfsidmap.m4 \ $(top_srcdir)/aclocal/librpcsecgss.m4 \ @@ -137,6 +138,7 @@ LDFLAGS = @LDFLAGS@ LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@ LIBBLKID = @LIBBLKID@ LIBBSD = @LIBBSD@ +LIBCAP = @LIBCAP@ LIBCRYPT = @LIBCRYPT@ LIBNSL = @LIBNSL@ LIBOBJS = @LIBOBJS@ @@ -203,6 +205,7 @@ enable_ipv6 = @enable_ipv6@ enable_mountconfig = @enable_mountconfig@ enable_nfsv3 = @enable_nfsv3@ enable_nfsv4 = @enable_nfsv4@ +enable_nfsv41 = @enable_nfsv41@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ diff --git a/support/misc/from_local.c b/support/misc/from_local.c index 89ccc4a..e2de969 100644 --- a/support/misc/from_local.c +++ b/support/misc/from_local.c @@ -37,32 +37,100 @@ static char sccsid[] = "@(#) from_local.c 1.3 96/05/31 15:52:57"; #endif -#ifdef TEST -#undef perror +#ifdef HAVE_CONFIG_H +#include #endif #include #include +#include #include #include #include #include #include #include -#include #include #include +#include "sockaddr.h" +#include "tcpwrapper.h" +#include "xlog.h" + #ifndef TRUE #define TRUE 1 #define FALSE 0 #endif - /* - * With virtual hosting, each hardware network interface can have multiple - * network addresses. On such machines the number of machine addresses can - * be surprisingly large. - */ +#ifdef HAVE_GETIFADDRS + +#include +#include + +/** + * from_local - determine whether request comes from the local system + * @sap: pointer to socket address to check + * + * With virtual hosting, each hardware network interface can have + * multiple network addresses. On such machines the number of machine + * addresses can be surprisingly large. + * + * We also expect the local network configuration to change over time, + * so call getifaddrs(3) more than once, but not too often. + * + * Returns TRUE if the sockaddr contains an address of one of the local + * network interfaces. Otherwise FALSE is returned. + */ +int +from_local(const struct sockaddr *sap) +{ + static struct ifaddrs *ifaddr = NULL; + static time_t last_update = 0; + struct ifaddrs *ifa; + unsigned int count; + time_t now; + + if (time(&now) == ((time_t)-1)) { + xlog(L_ERROR, "%s: time(2): %m", __func__); + + /* If we don't know what time it is, use the + * existing ifaddr list, if one exists */ + now = last_update; + if (ifaddr == NULL) + now++; + } + if (now != last_update) { + xlog(D_GENERAL, "%s: updating local if addr list", __func__); + + if (ifaddr) + freeifaddrs(ifaddr); + + if (getifaddrs(&ifaddr) == -1) { + xlog(L_ERROR, "%s: getifaddrs(3): %m", __func__); + return FALSE; + } + + last_update = now; + } + + count = 0; + for (ifa = ifaddr; ifa; ifa = ifa->ifa_next) { + if ((ifa->ifa_flags & IFF_UP) && + nfs_compare_sockaddr(sap, ifa->ifa_addr)) { + xlog(D_GENERAL, "%s: incoming address matches " + "local interface address", __func__); + return TRUE; + } else + count++; + } + + xlog(D_GENERAL, "%s: checked %u local if addrs; " + "incoming address not found", __func__, count); + return FALSE; +} + +#else /* !HAVE_GETIFADDRS */ + static int num_local; static int num_addrs; static struct in_addr *addrs; @@ -81,7 +149,7 @@ static int grow_addrs(void) new_num = (addrs == 0) ? 1 : num_addrs + num_addrs; new_addrs = (struct in_addr *) malloc(sizeof(*addrs) * new_num); if (new_addrs == 0) { - perror("portmap: out of memory"); + xlog_warn("%s: out of memory", __func__); return (0); } else { if (addrs != 0) { @@ -112,13 +180,13 @@ find_local(void) */ if ((sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { - perror("socket"); + xlog_warn("%s: socket(2): %m", __func__); return (0); } ifc.ifc_len = sizeof(buf); ifc.ifc_buf = buf; if (ioctl(sock, SIOCGIFCONF, (char *) &ifc) < 0) { - perror("SIOCGIFCONF"); + xlog_warn("%s: ioctl(SIOCGIFCONF): %m", __func__); (void) close(sock); return (0); } @@ -130,10 +198,10 @@ find_local(void) if (ifr->ifr_addr.sa_family == AF_INET) { /* IP net interface */ ifreq = *ifr; if (ioctl(sock, SIOCGIFFLAGS, (char *) &ifreq) < 0) { - perror("SIOCGIFFLAGS"); + xlog_warn("%s: ioctl(SIOCGIFFLAGS): %m", __func__); } else if (ifreq.ifr_flags & IFF_UP) { /* active interface */ if (ioctl(sock, SIOCGIFADDR, (char *) &ifreq) < 0) { - perror("SIOCGIFADDR"); + xlog_warn("%s: ioctl(SIOCGIFADDR): %m", __func__); } else { if (num_local >= num_addrs) if (grow_addrs() == 0) @@ -153,14 +221,28 @@ find_local(void) return (num_local); } -/* from_local - determine whether request comes from the local system */ +/** + * from_local - determine whether request comes from the local system + * @sap: pointer to socket address to check + * + * With virtual hosting, each hardware network interface can have + * multiple network addresses. On such machines the number of machine + * addresses can be surprisingly large. + * + * Returns TRUE if the sockaddr contains an address of one of the local + * network interfaces. Otherwise FALSE is returned. + */ int -from_local(struct sockaddr_in *addr) +from_local(const struct sockaddr *sap) { + const struct sockaddr_in *addr = (const struct sockaddr_in *)sap; int i; + if (sap->sa_family != AF_INET) + return (FALSE); + if (addrs == 0 && find_local() == 0) - syslog(LOG_ERR, "cannot find any active local network interfaces"); + xlog(L_ERROR, "Cannot find any active local network interfaces"); for (i = 0; i < num_local; i++) { if (memcmp((char *) &(addr->sin_addr), (char *) &(addrs[i]), @@ -172,9 +254,8 @@ from_local(struct sockaddr_in *addr) #ifdef TEST -main() +int main(void) { - char *inet_ntoa(); int i; find_local(); @@ -182,4 +263,6 @@ main() printf("%s\n", inet_ntoa(addrs[i])); } -#endif +#endif /* TEST */ + +#endif /* !HAVE_GETIFADDRS */ diff --git a/support/misc/tcpwrapper.c b/support/misc/tcpwrapper.c index 1da6020..06b0a46 100644 --- a/support/misc/tcpwrapper.c +++ b/support/misc/tcpwrapper.c @@ -34,13 +34,12 @@ #ifdef HAVE_CONFIG_H #include #endif + #ifdef HAVE_LIBWRAP -#include #include #include #include #include -#include #include #include #include @@ -49,108 +48,146 @@ #include #include +#include "sockaddr.h" +#include "tcpwrapper.h" #include "xlog.h" #ifdef SYSV40 #include #include -#endif +#endif /* SYSV40 */ -static void logit(int severity, struct sockaddr_in *addr, - u_long procnum, u_long prognum, char *text); -static int check_files(void); +#define ALLOW 1 +#define DENY 0 -/* - * These need to exist since they are externed - * public header files. - */ -int verboselog = 0; -int allow_severity = LOG_INFO; -int deny_severity = LOG_WARNING; +#ifdef IPV6_SUPPORTED +static void +present_address(const struct sockaddr *sap, char *buf, const size_t buflen) +{ + const struct sockaddr_in *sin = (const struct sockaddr_in *)sap; + const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sap; + socklen_t len = (socklen_t)buflen; + + switch (sap->sa_family) { + case AF_INET: + if (inet_ntop(AF_INET, &sin->sin_addr, buf, len) != 0) + return; + case AF_INET6: + if (inet_ntop(AF_INET6, &sin6->sin6_addr, buf, len) != 0) + return; + } -#define log_bad_host(addr, proc, prog) \ - logit(deny_severity, addr, proc, prog, "request from unauthorized host") + memset(buf, 0, buflen); + strncpy(buf, "unrecognized caller", buflen); +} +#else /* !IPV6_SUPPORTED */ +static void +present_address(const struct sockaddr *sap, char *buf, const size_t buflen) +{ + const struct sockaddr_in *sin = (const struct sockaddr_in *)sap; + socklen_t len = (socklen_t)buflen; -#define ALLOW 1 -#define DENY 0 + if (sap->sa_family == AF_INET) + if (inet_ntop(AF_INET, &sin->sin_addr, buf, len) != 0) + return; + + memset(buf, 0, buflen); + strncpy(buf, "unrecognized caller", (size_t)buflen); +} +#endif /* !IPV6_SUPPORTED */ typedef struct _haccess_t { - TAILQ_ENTRY(_haccess_t) list; - int access; - struct in_addr addr; + TAILQ_ENTRY(_haccess_t) list; + int allowed; + union nfs_sockaddr address; } haccess_t; #define HASH_TABLE_SIZE 1021 typedef struct _hash_head { TAILQ_HEAD(host_list, _haccess_t) h_head; } hash_head; -hash_head haccess_tbl[HASH_TABLE_SIZE]; -static haccess_t *haccess_lookup(struct sockaddr_in *addr, u_long); -static void haccess_add(struct sockaddr_in *addr, u_long, int); -inline unsigned int strtoint(char *str) +static hash_head haccess_tbl[HASH_TABLE_SIZE]; + +static unsigned long +strtoint(const char *str) { - unsigned int n = 0; - int len = strlen(str); - int i; + unsigned long i, n = 0; + size_t len = strlen(str); - for (i=0; i < len; i++) - n+=((int)str[i])*i; + for (i = 0; i < len; i++) + n += (unsigned char)str[i] * i; return n; } -static inline int hashint(unsigned int num) + +static unsigned int +hashint(const unsigned long num) { - return num % HASH_TABLE_SIZE; + return (unsigned int)(num % HASH_TABLE_SIZE); } -#define HASH(_addr, _prog) \ - hashint((strtoint((_addr))+(_prog))) -void haccess_add(struct sockaddr_in *addr, u_long prog, int access) +static unsigned int +HASH(const char *addr, const unsigned long program) +{ + return hashint(strtoint(addr) + program); +} + +static void +haccess_add(const struct sockaddr *sap, const char *address, + const unsigned long program, const int allowed) { hash_head *head; - haccess_t *hptr; - int hash; + haccess_t *hptr; + unsigned int hash; hptr = (haccess_t *)malloc(sizeof(haccess_t)); if (hptr == NULL) return; - hash = HASH(inet_ntoa(addr->sin_addr), prog); + hash = HASH(address, program); head = &(haccess_tbl[hash]); - hptr->access = access; - hptr->addr.s_addr = addr->sin_addr.s_addr; + hptr->allowed = allowed; + memcpy(&hptr->address, sap, (size_t)nfs_sockaddr_length(sap)); if (TAILQ_EMPTY(&head->h_head)) TAILQ_INSERT_HEAD(&head->h_head, hptr, list); else TAILQ_INSERT_TAIL(&head->h_head, hptr, list); } -haccess_t *haccess_lookup(struct sockaddr_in *addr, u_long prog) + +static haccess_t * +haccess_lookup(const struct sockaddr *sap, const char *address, + const unsigned long program) { hash_head *head; - haccess_t *hptr; - int hash; + haccess_t *hptr; + unsigned int hash; - hash = HASH(inet_ntoa(addr->sin_addr), prog); + hash = HASH(address, program); head = &(haccess_tbl[hash]); TAILQ_FOREACH(hptr, &head->h_head, list) { - if (hptr->addr.s_addr == addr->sin_addr.s_addr) + if (nfs_compare_sockaddr(&hptr->address.sa, sap)) return hptr; } return NULL; } -int -good_client(daemon, addr) -char *daemon; -struct sockaddr_in *addr; +static void +logit(const char *address) +{ + xlog_warn("connect from %s denied: request from unauthorized host", + address); +} + +static int +good_client(char *name, struct sockaddr *sap) { struct request_info req; - request_init(&req, RQ_DAEMON, daemon, RQ_CLIENT_SIN, addr, 0); + request_init(&req, RQ_DAEMON, name, RQ_CLIENT_SIN, sap, 0); sock_methods(&req); if (hosts_access(&req)) @@ -159,9 +196,8 @@ struct sockaddr_in *addr; return DENY; } -/* check_files - check to see if either access files have changed */ - -static int check_files() +static int +check_files(void) { static time_t allow_mtime, deny_mtime; struct stat astat, dstat; @@ -186,45 +222,48 @@ static int check_files() return changed; } -/* check_default - additional checks for NULL, DUMP, GETPORT and unknown */ - +/** + * check_default - additional checks for NULL, DUMP, GETPORT and unknown + * @name: pointer to '\0'-terminated ASCII string containing name of the + * daemon requesting the access check + * @sap: pointer to sockaddr containing network address of caller + * @program: RPC program number caller is attempting to access + * + * Returns TRUE if the caller is allowed access; otherwise FALSE is returned. + */ int -check_default(daemon, addr, proc, prog) -char *daemon; -struct sockaddr_in *addr; -u_long proc; -u_long prog; +check_default(char *name, struct sockaddr *sap, const unsigned long program) { haccess_t *acc = NULL; int changed = check_files(); + char buf[INET6_ADDRSTRLEN]; + + present_address(sap, buf, sizeof(buf)); - acc = haccess_lookup(addr, prog); - if (acc && changed == 0) - return (acc->access); + acc = haccess_lookup(sap, buf, program); + if (acc != NULL && changed == 0) { + xlog(D_GENERAL, "%s: access by %s %s (cached)", __func__, + buf, acc->allowed ? "ALLOWED" : "DENIED"); + return acc->allowed; + } - if (!(from_local(addr) || good_client(daemon, addr))) { - log_bad_host(addr, proc, prog); - if (acc) - acc->access = FALSE; - else - haccess_add(addr, prog, FALSE); + if (!(from_local(sap) || good_client(name, sap))) { + logit(buf); + if (acc != NULL) + acc->allowed = FALSE; + else + haccess_add(sap, buf, program, FALSE); + xlog(D_GENERAL, "%s: access by %s DENIED", __func__, buf); return (FALSE); } - if (acc) - acc->access = TRUE; - else - haccess_add(addr, prog, TRUE); + if (acc != NULL) + acc->allowed = TRUE; + else + haccess_add(sap, buf, program, TRUE); + xlog(D_GENERAL, "%s: access by %s ALLOWED", __func__, buf); - return (TRUE); + return (TRUE); } -/* logit - report events of interest via the syslog daemon */ - -static void logit(int severity, struct sockaddr_in *addr, - u_long procnum, u_long prognum, char *text) -{ - syslog(severity, "connect from %s denied: %s", - inet_ntoa(addr->sin_addr), text); -} -#endif +#endif /* HAVE_LIBWRAP */ diff --git a/support/nfs/Makefile.am b/support/nfs/Makefile.am index e9462fc..60400b2 100644 --- a/support/nfs/Makefile.am +++ b/support/nfs/Makefile.am @@ -4,7 +4,8 @@ noinst_LIBRARIES = libnfs.a libnfs_a_SOURCES = exports.c rmtab.c xio.c rpcmisc.c rpcdispatch.c \ xlog.c xcommon.c wildmat.c nfsclient.c \ nfsexport.c getfh.c nfsctl.c rpc_socket.c getport.c \ - svc_socket.c cacheio.c closeall.c nfs_mntent.c conffile.c + svc_socket.c cacheio.c closeall.c nfs_mntent.c conffile.c \ + svc_create.c MAINTAINERCLEANFILES = Makefile.in diff --git a/support/nfs/Makefile.in b/support/nfs/Makefile.in index d6420ce..469abe7 100644 --- a/support/nfs/Makefile.in +++ b/support/nfs/Makefile.in @@ -1,4 +1,4 @@ -# Makefile.in generated by automake 1.11 from Makefile.am. +# Makefile.in generated by automake 1.11.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, @@ -41,6 +41,7 @@ am__aclocal_m4_deps = $(top_srcdir)/aclocal/bsdsignals.m4 \ $(top_srcdir)/aclocal/ipv6.m4 \ $(top_srcdir)/aclocal/kerberos5.m4 \ $(top_srcdir)/aclocal/libblkid.m4 \ + $(top_srcdir)/aclocal/libcap.m4 \ $(top_srcdir)/aclocal/libevent.m4 \ $(top_srcdir)/aclocal/libnfsidmap.m4 \ $(top_srcdir)/aclocal/librpcsecgss.m4 \ @@ -65,7 +66,7 @@ am_libnfs_a_OBJECTS = exports.$(OBJEXT) rmtab.$(OBJEXT) xio.$(OBJEXT) \ nfsexport.$(OBJEXT) getfh.$(OBJEXT) nfsctl.$(OBJEXT) \ rpc_socket.$(OBJEXT) getport.$(OBJEXT) svc_socket.$(OBJEXT) \ cacheio.$(OBJEXT) closeall.$(OBJEXT) nfs_mntent.$(OBJEXT) \ - conffile.$(OBJEXT) + conffile.$(OBJEXT) svc_create.$(OBJEXT) libnfs_a_OBJECTS = $(am_libnfs_a_OBJECTS) DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/support/include depcomp = $(SHELL) $(top_srcdir)/depcomp @@ -142,6 +143,7 @@ LDFLAGS = @LDFLAGS@ LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@ LIBBLKID = @LIBBLKID@ LIBBSD = @LIBBSD@ +LIBCAP = @LIBCAP@ LIBCRYPT = @LIBCRYPT@ LIBNSL = @LIBNSL@ LIBOBJS = @LIBOBJS@ @@ -208,6 +210,7 @@ enable_ipv6 = @enable_ipv6@ enable_mountconfig = @enable_mountconfig@ enable_nfsv3 = @enable_nfsv3@ enable_nfsv4 = @enable_nfsv4@ +enable_nfsv41 = @enable_nfsv41@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ @@ -247,7 +250,8 @@ noinst_LIBRARIES = libnfs.a libnfs_a_SOURCES = exports.c rmtab.c xio.c rpcmisc.c rpcdispatch.c \ xlog.c xcommon.c wildmat.c nfsclient.c \ nfsexport.c getfh.c nfsctl.c rpc_socket.c getport.c \ - svc_socket.c cacheio.c closeall.c nfs_mntent.c conffile.c + svc_socket.c cacheio.c closeall.c nfs_mntent.c conffile.c \ + svc_create.c MAINTAINERCLEANFILES = Makefile.in all: all-am @@ -312,6 +316,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rpc_socket.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rpcdispatch.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rpcmisc.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/svc_create.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/svc_socket.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wildmat.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xcommon.Po@am__quote@ diff --git a/support/nfs/cacheio.c b/support/nfs/cacheio.c index 6a6ed5a..bdf5d84 100644 --- a/support/nfs/cacheio.c +++ b/support/nfs/cacheio.c @@ -285,9 +285,8 @@ int readline(int fd, char **buf, int *lenp) int check_new_cache(void) { - struct stat stb; - return (stat("/proc/fs/nfs/filehandle", &stb) == 0) || - (stat("/proc/fs/nfsd/filehandle", &stb) == 0); + return (access("/proc/fs/nfs/filehandle", F_OK) == 0) || + (access("/proc/fs/nfsd/filehandle", F_OK) == 0); } diff --git a/support/nfs/exports.c b/support/nfs/exports.c index 1aaebf4..a93941c 100644 --- a/support/nfs/exports.c +++ b/support/nfs/exports.c @@ -84,6 +84,31 @@ setexportent(char *fname, char *type) first = 1; } +static void init_exportent (struct exportent *ee, int fromkernel) +{ + ee->e_flags = EXPORT_DEFAULT_FLAGS; + /* some kernels assume the default is sync rather than + * async. More recent kernels always report one or other, + * but this test makes sure we assume same as kernel + * Ditto for wgather + */ + if (fromkernel) { + ee->e_flags &= ~NFSEXP_ASYNC; + ee->e_flags &= ~NFSEXP_GATHERED_WRITES; + } + ee->e_anonuid = 65534; + ee->e_anongid = 65534; + ee->e_squids = NULL; + ee->e_sqgids = NULL; + ee->e_mountpoint = NULL; + ee->e_fslocmethod = FSLOC_NONE; + ee->e_fslocdata = NULL; + ee->e_secinfo[0].flav = NULL; + ee->e_nsquids = 0; + ee->e_nsqgids = 0; + ee->e_uuid = NULL; +} + struct exportent * getexportent(int fromkernel, int fromexports) { @@ -102,26 +127,7 @@ getexportent(int fromkernel, int fromexports) has_default_opts = 0; has_default_subtree_opts = 0; - def_ee.e_flags = EXPORT_DEFAULT_FLAGS; - /* some kernels assume the default is sync rather than - * async. More recent kernels always report one or other, - * but this test makes sure we assume same as kernel - * Ditto for wgather - */ - if (fromkernel) { - def_ee.e_flags &= ~NFSEXP_ASYNC; - def_ee.e_flags &= ~NFSEXP_GATHERED_WRITES; - } - def_ee.e_anonuid = 65534; - def_ee.e_anongid = 65534; - def_ee.e_squids = NULL; - def_ee.e_sqgids = NULL; - def_ee.e_mountpoint = NULL; - def_ee.e_fslocmethod = FSLOC_NONE; - def_ee.e_fslocdata = NULL; - def_ee.e_secinfo[0].flav = NULL; - def_ee.e_nsquids = 0; - def_ee.e_nsqgids = 0; + init_exportent(&def_ee, fromkernel); ok = getpath(def_ee.e_path, sizeof(def_ee.e_path)); if (ok <= 0) @@ -334,18 +340,7 @@ mkexportent(char *hname, char *path, char *options) { static struct exportent ee; - ee.e_flags = EXPORT_DEFAULT_FLAGS; - ee.e_anonuid = 65534; - ee.e_anongid = 65534; - ee.e_squids = NULL; - ee.e_sqgids = NULL; - ee.e_mountpoint = NULL; - ee.e_fslocmethod = FSLOC_NONE; - ee.e_fslocdata = NULL; - ee.e_secinfo[0].flav = NULL; - ee.e_nsquids = 0; - ee.e_nsqgids = 0; - ee.e_uuid = NULL; + init_exportent(&ee, 0); xfree(ee.e_hostname); ee.e_hostname = xstrdup(hname); @@ -385,7 +380,7 @@ static int valid_uuid(char *uuid) * do nothing if it's already there. Returns the index of flavor * in the resulting array in any case. */ -static int secinfo_addflavor(struct flav_info *flav, struct exportent *ep) +int secinfo_addflavor(struct flav_info *flav, struct exportent *ep) { struct sec_entry *p; @@ -467,9 +462,20 @@ static void clearflags(int mask, unsigned int active, struct exportent *ep) } } -/* options that can vary per flavor: */ -#define NFSEXP_SECINFO_FLAGS (NFSEXP_READONLY | NFSEXP_ROOTSQUASH \ - | NFSEXP_ALLSQUASH) +/* + * For those flags which are not allowed to vary by pseudoflavor, + * ensure that the export flags agree with the flags on each + * pseudoflavor: + */ +static void fix_pseudoflavor_flags(struct exportent *ep) +{ + struct export_features *ef; + struct sec_entry *p; + + ef = get_export_features(); + for (p = ep->e_secinfo; p->flav; p++) + p->flags |= ep->e_flags & ~ef->secinfo_flags; +} /* * Parse option string pointed to by cp and set mount options accordingly. @@ -477,7 +483,6 @@ static void clearflags(int mask, unsigned int active, struct exportent *ep) static int parseopts(char *cp, struct exportent *ep, int warn, int *had_subtree_opt_ptr) { - struct sec_entry *p; int had_subtree_opt = 0; char *flname = efname?efname:"command line"; int flline = efp?efp->x_line:0; @@ -507,25 +512,25 @@ parseopts(char *cp, struct exportent *ep, int warn, int *had_subtree_opt_ptr) else if (strcmp(opt, "rw") == 0) clearflags(NFSEXP_READONLY, active, ep); else if (!strcmp(opt, "secure")) - ep->e_flags &= ~NFSEXP_INSECURE_PORT; + clearflags(NFSEXP_INSECURE_PORT, active, ep); else if (!strcmp(opt, "insecure")) - ep->e_flags |= NFSEXP_INSECURE_PORT; + setflags(NFSEXP_INSECURE_PORT, active, ep); else if (!strcmp(opt, "sync")) - ep->e_flags &= ~NFSEXP_ASYNC; + clearflags(NFSEXP_ASYNC, active, ep); else if (!strcmp(opt, "async")) - ep->e_flags |= NFSEXP_ASYNC; + setflags(NFSEXP_ASYNC, active, ep); else if (!strcmp(opt, "nohide")) - ep->e_flags |= NFSEXP_NOHIDE; + setflags(NFSEXP_NOHIDE, active, ep); else if (!strcmp(opt, "hide")) - ep->e_flags &= ~NFSEXP_NOHIDE; + clearflags(NFSEXP_NOHIDE, active, ep); else if (!strcmp(opt, "crossmnt")) - ep->e_flags |= NFSEXP_CROSSMOUNT; + setflags(NFSEXP_CROSSMOUNT, active, ep); else if (!strcmp(opt, "nocrossmnt")) - ep->e_flags &= ~NFSEXP_CROSSMOUNT; + clearflags(NFSEXP_CROSSMOUNT, active, ep); else if (!strcmp(opt, "wdelay")) - ep->e_flags |= NFSEXP_GATHERED_WRITES; + setflags(NFSEXP_GATHERED_WRITES, active, ep); else if (!strcmp(opt, "no_wdelay")) - ep->e_flags &= ~NFSEXP_GATHERED_WRITES; + clearflags(NFSEXP_GATHERED_WRITES, active, ep); else if (strcmp(opt, "root_squash") == 0) setflags(NFSEXP_ROOTSQUASH, active, ep); else if (!strcmp(opt, "no_root_squash")) @@ -536,22 +541,22 @@ parseopts(char *cp, struct exportent *ep, int warn, int *had_subtree_opt_ptr) clearflags(NFSEXP_ALLSQUASH, active, ep); else if (strcmp(opt, "subtree_check") == 0) { had_subtree_opt = 1; - ep->e_flags &= ~NFSEXP_NOSUBTREECHECK; + clearflags(NFSEXP_NOSUBTREECHECK, active, ep); } else if (strcmp(opt, "no_subtree_check") == 0) { had_subtree_opt = 1; - ep->e_flags |= NFSEXP_NOSUBTREECHECK; + setflags(NFSEXP_NOSUBTREECHECK, active, ep); } else if (strcmp(opt, "auth_nlm") == 0) - ep->e_flags &= ~NFSEXP_NOAUTHNLM; + clearflags(NFSEXP_NOAUTHNLM, active, ep); else if (strcmp(opt, "no_auth_nlm") == 0) - ep->e_flags |= NFSEXP_NOAUTHNLM; + setflags(NFSEXP_NOAUTHNLM, active, ep); else if (strcmp(opt, "secure_locks") == 0) - ep->e_flags &= ~NFSEXP_NOAUTHNLM; + clearflags(NFSEXP_NOAUTHNLM, active, ep); else if (strcmp(opt, "insecure_locks") == 0) - ep->e_flags |= NFSEXP_NOAUTHNLM; + setflags(NFSEXP_NOAUTHNLM, active, ep); else if (strcmp(opt, "acl") == 0) - ep->e_flags &= ~NFSEXP_NOACL; + clearflags(NFSEXP_NOACL, active, ep); else if (strcmp(opt, "no_acl") == 0) - ep->e_flags |= NFSEXP_NOACL; + setflags(NFSEXP_NOACL, active, ep); else if (strncmp(opt, "anonuid=", 8) == 0) { char *oe; ep->e_anonuid = strtol(opt+8, &oe, 10); @@ -583,11 +588,11 @@ bad_option: char *oe; if (strcmp(opt+5, "root") == 0) { ep->e_fsid = 0; - ep->e_flags |= NFSEXP_FSID; + setflags(NFSEXP_FSID, active, ep); } else { ep->e_fsid = strtoul(opt+5, &oe, 0); if (opt[5]!='\0' && *oe == '\0') - ep->e_flags |= NFSEXP_FSID; + setflags(NFSEXP_FSID, active, ep); else if (valid_uuid(opt+5)) ep->e_uuid = strdup(opt+5); else { @@ -628,22 +633,15 @@ bad_option: } else { xlog(L_ERROR, "%s:%d: unknown keyword \"%s\"\n", flname, flline, opt); - ep->e_flags |= NFSEXP_ALLSQUASH | NFSEXP_READONLY; + setflags(NFSEXP_ALLSQUASH | NFSEXP_READONLY, active, ep); goto bad_option; } free(opt); while (isblank(*cp)) cp++; } - /* - * Turn on nohide which will allow this export to cross over - * the 'mount --bind' mount point. - */ - if (ep->e_fslocdata) - ep->e_flags |= NFSEXP_NOHIDE; - for (p = ep->e_secinfo; p->flav; p++) - p->flags |= ep->e_flags & ~NFSEXP_SECINFO_FLAGS; + fix_pseudoflavor_flags(ep); ep->e_squids = squids; ep->e_sqgids = sqgids; ep->e_nsquids = nsquids; @@ -760,4 +758,34 @@ syntaxerr(char *msg) xlog(L_ERROR, "%s:%d: syntax error: %s", efname, efp?efp->x_line:0, msg); } - +struct export_features *get_export_features(void) +{ + static char *path = "/proc/fs/nfsd/export_features"; + static struct export_features ef; + static int cached = 0; + char buf[50]; + int c; + int fd; + + if (cached) + return &ef; + + ef.flags = NFSEXP_OLDFLAGS; + ef.secinfo_flags = NFSEXP_OLD_SECINFO_FLAGS; + + fd = open(path, O_RDONLY); + if (fd == -1) + goto good; + fd = read(fd, buf, 50); + if (fd == -1) + goto err; + c = sscanf(buf, "%x %x", &ef.flags, &ef.secinfo_flags); + if (c != 2) + goto err; +good: + cached = 1; + return &ef; +err: + xlog(L_WARNING, "unexpected error reading %s", path); + return &ef; +} diff --git a/support/nfs/getport.c b/support/nfs/getport.c index 4bdf556..c930539 100644 --- a/support/nfs/getport.c +++ b/support/nfs/getport.c @@ -45,6 +45,7 @@ #include #endif +#include "sockaddr.h" #include "nfsrpc.h" /* @@ -199,7 +200,63 @@ static CLIENT *nfs_gp_get_rpcbclient(struct sockaddr *sap, return clnt; } -/* +/** + * nfs_get_proto - Convert a netid to an address family and protocol number + * @netid: C string containing a netid + * @family: OUT: address family + * @protocol: OUT: protocol number + * + * Returns 1 and fills in @protocol if the netid was recognized; + * otherwise zero is returned. + */ +#ifdef HAVE_LIBTIRPC +int +nfs_get_proto(const char *netid, sa_family_t *family, unsigned long *protocol) +{ + struct netconfig *nconf; + struct protoent *proto; + + nconf = getnetconfigent(netid); + if (nconf == NULL) + return 0; + + proto = getprotobyname(nconf->nc_proto); + if (proto == NULL) { + freenetconfigent(nconf); + return 0; + } + + *family = AF_UNSPEC; + if (strcmp(nconf->nc_protofmly, NC_INET) == 0) + *family = AF_INET; + if (strcmp(nconf->nc_protofmly, NC_INET6) == 0) + *family = AF_INET6; + freenetconfigent(nconf); + + *protocol = (unsigned long)proto->p_proto; + return 1; +} +#else /* !HAVE_LIBTIRPC */ +int +nfs_get_proto(const char *netid, sa_family_t *family, unsigned long *protocol) +{ + struct protoent *proto; + + proto = getprotobyname(netid); + if (proto == NULL) + return 0; + + *family = AF_INET; + *protocol = (unsigned long)proto->p_proto; + return 1; +} +#endif /* !HAVE_LIBTIRPC */ + +/** + * nfs_get_netid - Convert a protocol family and protocol name to a netid + * @family: protocol family + * @protocol: protocol number + * * One of the arguments passed when querying remote rpcbind services * via rpcbind v3 or v4 is a netid string. This replaces the pm_prot * field used in legacy PMAP_GETPORT calls. @@ -213,13 +270,12 @@ static CLIENT *nfs_gp_get_rpcbclient(struct sockaddr *sap, * first entry that matches @family and @protocol and whose netid string * fits in the provided buffer. * - * Returns a '\0'-terminated string if successful; otherwise NULL. + * Returns a '\0'-terminated string if successful. Caller must + * free the returned string. Otherwise NULL is returned, and * rpc_createerr.cf_stat is set to reflect the error. */ #ifdef HAVE_LIBTIRPC - -static char *nfs_gp_get_netid(const sa_family_t family, - const unsigned short protocol) +char *nfs_get_netid(const sa_family_t family, const unsigned long protocol) { char *nc_protofmly, *nc_proto, *nc_netid; struct netconfig *nconf; @@ -255,6 +311,9 @@ static char *nfs_gp_get_netid(const sa_family_t family, nc_netid = strdup(nconf->nc_netid); endnetconfig(handle); + + if (nc_netid == NULL) + rpc_createerr.cf_stat = RPC_SYSTEMERROR; return nc_netid; } endnetconfig(handle); @@ -263,8 +322,28 @@ out: rpc_createerr.cf_stat = RPC_UNKNOWNPROTO; return NULL; } +#else /* !HAVE_LIBTIRPC */ +char *nfs_get_netid(const sa_family_t family, const unsigned long protocol) +{ + struct protoent *proto; + char *netid; -#endif /* HAVE_LIBTIRPC */ + if (family != AF_INET) + goto out; + proto = getprotobynumber((int)protocol); + if (proto == NULL) + goto out; + + netid = strdup(proto->p_name); + if (netid == NULL) + rpc_createerr.cf_stat = RPC_SYSTEMERROR; + return netid; + +out: + rpc_createerr.cf_stat = RPC_UNKNOWNPROTO; + return NULL; +} +#endif /* !HAVE_LIBTIRPC */ /* * Extract a port number from a universal address, and terminate the @@ -421,7 +500,7 @@ static int nfs_gp_init_rpcb_parms(const struct sockaddr *sap, { char *netid, *addr; - netid = nfs_gp_get_netid(sap->sa_family, protocol); + netid = nfs_get_netid(sap->sa_family, protocol); if (netid == NULL) return 0; @@ -627,8 +706,8 @@ int nfs_rpc_ping(const struct sockaddr *sap, const socklen_t salen, const rpcprog_t program, const rpcvers_t version, const unsigned short protocol, const struct timeval *timeout) { - struct sockaddr_storage address; - struct sockaddr *saddr = (struct sockaddr *)&address; + union nfs_sockaddr address; + struct sockaddr *saddr = &address.sa; CLIENT *client; struct timeval tout = { -1, 0 }; int result = 0; @@ -696,8 +775,8 @@ unsigned short nfs_getport(const struct sockaddr *sap, const rpcvers_t version, const unsigned short protocol) { - struct sockaddr_storage address; - struct sockaddr *saddr = (struct sockaddr *)&address; + union nfs_sockaddr address; + struct sockaddr *saddr = &address.sa; struct timeval timeout = { -1, 0 }; unsigned short port = 0; CLIENT *client; @@ -755,8 +834,8 @@ int nfs_getport_ping(struct sockaddr *sap, const socklen_t salen, } if (port != 0) { - struct sockaddr_storage address; - struct sockaddr *saddr = (struct sockaddr *)&address; + union nfs_sockaddr address; + struct sockaddr *saddr = &address.sa; memcpy(saddr, sap, (size_t)salen); nfs_set_port(saddr, port); @@ -807,8 +886,8 @@ unsigned short nfs_getlocalport(const rpcprot_t program, const rpcvers_t version, const unsigned short protocol) { - struct sockaddr_storage address; - struct sockaddr *lb_addr = (struct sockaddr *)&address; + union nfs_sockaddr address; + struct sockaddr *lb_addr = &address.sa; socklen_t lb_len = sizeof(*lb_addr); unsigned short port = 0; @@ -891,8 +970,8 @@ unsigned short nfs_rpcb_getaddr(const struct sockaddr *sap, const unsigned short protocol, const struct timeval *timeout) { - struct sockaddr_storage address; - struct sockaddr *saddr = (struct sockaddr *)&address; + union nfs_sockaddr address; + struct sockaddr *saddr = &address.sa; CLIENT *client; struct rpcb parms; struct timeval tout = { -1, 0 }; diff --git a/support/nfs/rmtab.c b/support/nfs/rmtab.c index 4cbd285..a28abf3 100644 --- a/support/nfs/rmtab.c +++ b/support/nfs/rmtab.c @@ -117,11 +117,24 @@ void fendrmtabent(FILE *fp) { if (fp) { - /* If it was written to, we really want - * to flush to disk before returning - */ - fflush(fp); - fdatasync(fileno(fp)); + static int have_new_cache = -1; + if (have_new_cache == -1) /* check only once */ + have_new_cache = check_new_cache(); + + if (!have_new_cache) { + /* + * If we are using the old caching interface: exportfs + * uses the rmtab to determine what should be exported, + * so it is important that it be up-to-date. + * + * If we are using the new caching interface: the rmtab + * is ignored by exportfs and the fdatasync only serves + * to slow us down. + */ + fflush(fp); + fdatasync(fileno(fp)); + } + fclose(fp); } } diff --git a/support/nfs/rpc_socket.c b/support/nfs/rpc_socket.c index 9c20f61..0e20824 100644 --- a/support/nfs/rpc_socket.c +++ b/support/nfs/rpc_socket.c @@ -26,6 +26,8 @@ #include #include + +#include #include #include #include @@ -38,6 +40,7 @@ #include #include +#include "sockaddr.h" #include "nfsrpc.h" #ifdef HAVE_LIBTIRPC @@ -51,6 +54,7 @@ #define NFSRPC_TIMEOUT_UDP (3) #define NFSRPC_TIMEOUT_TCP (10) + /* * Set up an RPC client for communicating via a AF_LOCAL socket. * @@ -121,10 +125,10 @@ static int nfs_bind(const int sock, const sa_family_t family) switch (family) { case AF_INET: - return bind(sock, (struct sockaddr *)&sin, + return bind(sock, (struct sockaddr *)(char *)&sin, (socklen_t)sizeof(sin)); case AF_INET6: - return bind(sock, (struct sockaddr *)&sin6, + return bind(sock, (struct sockaddr *)(char *)&sin6, (socklen_t)sizeof(sin6)); } @@ -153,9 +157,9 @@ static int nfs_bindresvport(const int sock, const sa_family_t family) switch (family) { case AF_INET: - return bindresvport_sa(sock, (struct sockaddr *)&sin); + return bindresvport_sa(sock, (struct sockaddr *)(char *)&sin); case AF_INET6: - return bindresvport_sa(sock, (struct sockaddr *)&sin6); + return bindresvport_sa(sock, (struct sockaddr *)(char *)&sin6); } errno = EAFNOSUPPORT; @@ -415,49 +419,6 @@ static CLIENT *nfs_get_tcpclient(const struct sockaddr *sap, return client; } -/** - * nfs_get_port - extract port value from a socket address - * @sap: pointer to socket address - * - * Returns port value in host byte order. - */ -uint16_t -nfs_get_port(const struct sockaddr *sap) -{ - const struct sockaddr_in *sin = (const struct sockaddr_in *)sap; - const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sap; - - switch (sap->sa_family) { - case AF_INET: - return ntohs(sin->sin_port); - case AF_INET6: - return ntohs(sin6->sin6_port); - } - return 0; -} - -/** - * nfs_set_port - set port value in a socket address - * @sap: pointer to socket address - * @port: port value to set - * - */ -void -nfs_set_port(struct sockaddr *sap, const uint16_t port) -{ - struct sockaddr_in *sin = (struct sockaddr_in *)sap; - struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap; - - switch (sap->sa_family) { - case AF_INET: - sin->sin_port = htons(port); - break; - case AF_INET6: - sin6->sin6_port = htons(port); - break; - } -} - /** * nfs_get_rpcclient - acquire an RPC client * @sap: pointer to socket address of RPC server diff --git a/support/nfs/svc_create.c b/support/nfs/svc_create.c new file mode 100644 index 0000000..59ba505 --- /dev/null +++ b/support/nfs/svc_create.c @@ -0,0 +1,246 @@ +/* + * 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 . + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include + +#ifdef HAVE_TCP_WRAPPER +#include "tcpwrapper.h" +#endif + +#include "rpcmisc.h" +#include "xlog.h" + +#ifdef HAVE_LIBTIRPC + +/* + * Set up an appropriate bind address, given @port and @nconf. + * + * Returns getaddrinfo(3) results if successful. Caller must + * invoke freeaddrinfo(3) on these results. + * + * Otherwise NULL is returned if an error occurs. + */ +__attribute_malloc__ +static struct addrinfo * +svc_create_bindaddr(struct netconfig *nconf, const uint16_t port) +{ + struct addrinfo *ai = NULL; + struct addrinfo hint = { + .ai_flags = AI_PASSIVE | AI_NUMERICSERV, + }; + char buf[8]; + int error; + + if (strcmp(nconf->nc_protofmly, NC_INET) == 0) + hint.ai_family = AF_INET; +#ifdef IPV6_SUPPORTED + else if (strcmp(nconf->nc_protofmly, NC_INET6) == 0) + hint.ai_family = AF_INET6; +#endif /* IPV6_SUPPORTED */ + else { + xlog(D_GENERAL, "Unrecognized bind address family: %s", + nconf->nc_protofmly); + return NULL; + } + + if (strcmp(nconf->nc_proto, NC_UDP) == 0) + hint.ai_protocol = (int)IPPROTO_UDP; + else if (strcmp(nconf->nc_proto, NC_TCP) == 0) + hint.ai_protocol = (int)IPPROTO_TCP; + else { + xlog(D_GENERAL, "Unrecognized bind address protocol: %s", + nconf->nc_proto); + return NULL; + } + + (void)snprintf(buf, sizeof(buf), "%u", port); + error = getaddrinfo(NULL, buf, &hint, &ai); + if (error != 0) { + xlog(L_ERROR, "Failed to construct bind address: %s", + gai_strerror(error)); + return NULL; + } + + return ai; +} + +static unsigned int +svc_create_nconf(const char *name, const rpcprog_t program, + const rpcvers_t version, + void (*dispatch)(struct svc_req *, SVCXPRT *), + const uint16_t port, struct netconfig *nconf) +{ + struct t_bind bindaddr; + struct addrinfo *ai; + SVCXPRT *xprt; + + ai = svc_create_bindaddr(nconf, port); + if (ai == NULL) + return 0; + + bindaddr.addr.buf = ai->ai_addr; + bindaddr.qlen = SOMAXCONN; + + xprt = svc_tli_create(RPC_ANYFD, nconf, &bindaddr, 0, 0); + freeaddrinfo(ai); + if (xprt == NULL) { + xlog(D_GENERAL, "Failed to create listener xprt " + "(%s, %u, %s)", name, version, nconf->nc_netid); + return 0; + } + + if (!svc_reg(xprt, program, version, dispatch, nconf)) { + /* svc_reg(3) destroys @xprt in this case */ + xlog(D_GENERAL, "Failed to register (%s, %u, %s)", + name, version, nconf->nc_netid); + return 0; + } + + return 1; +} + +/** + * nfs_svc_create - start up RPC svc listeners + * @name: C string containing name of new service + * @program: RPC program number to register + * @version: RPC version number to register + * @dispatch: address of function that handles incoming RPC requests + * @port: if not zero, transport listens on this port + * + * Sets up network transports for receiving RPC requests, and starts + * the RPC dispatcher. Returns the number of started network transports. + */ +unsigned int +nfs_svc_create(__attribute__((unused)) char *name, + const rpcprog_t program, const rpcvers_t version, + void (*dispatch)(struct svc_req *, SVCXPRT *), + const uint16_t port) +{ + const struct sigaction create_sigaction = { + .sa_handler = SIG_IGN, + }; + unsigned int visible, up; + struct netconfig *nconf; + void *handlep; + + /* + * Ignore SIGPIPE to avoid exiting sideways when peers + * close their TCP connection while we're trying to reply + * to them. + */ + (void)sigaction(SIGPIPE, &create_sigaction, NULL); + + handlep = setnetconfig(); + if (handlep == NULL) { + xlog(L_ERROR, "Failed to access local netconfig database: %s", + nc_sperror()); + return 0; + } + + visible = 0; + up = 0; + while ((nconf = getnetconfig(handlep)) != NULL) { + if (!(nconf->nc_flag & NC_VISIBLE)) + continue; + visible++; + up += svc_create_nconf(name, program, version, dispatch, + port, nconf); + } + + if (visible == 0) + xlog(L_ERROR, "Failed to find any visible netconfig entries"); + + if (endnetconfig(handlep) == -1) + xlog(L_ERROR, "Failed to close local netconfig database: %s", + nc_sperror()); + + return up; +} + +/** + * nfs_svc_unregister - remove service registrations from local rpcbind database + * @program: RPC program number to unregister + * @version: RPC version number to unregister + * + * Removes all registrations for [ @program, @version ] . + */ +void +nfs_svc_unregister(const rpcprog_t program, const rpcvers_t version) +{ + if (rpcb_unset(program, version, NULL) == FALSE) + xlog(D_GENERAL, "Failed to unregister program %lu, version %lu", + (unsigned long)program, (unsigned long)version); +} + +#else /* !HAVE_LIBTIRPC */ + +/** + * nfs_svc_create - start up RPC svc listeners + * @name: C string containing name of new service + * @program: RPC program number to register + * @version: RPC version number to register + * @dispatch: address of function that handles incoming RPC requests + * @port: if not zero, transport listens on this port + * + * Sets up network transports for receiving RPC requests, and starts + * the RPC dispatcher. Returns the number of started network transports. + */ +unsigned int +nfs_svc_create(char *name, const rpcprog_t program, const rpcvers_t version, + void (*dispatch)(struct svc_req *, SVCXPRT *), + const uint16_t port) +{ + rpc_init(name, (int)program, (int)version, dispatch, (int)port); + return 1; +} + +/** + * nfs_svc_unregister - remove service registrations from local rpcbind database + * @program: RPC program number to unregister + * @version: RPC version number to unregister + * + * Removes all registrations for [ @program, @version ] . + */ +void +nfs_svc_unregister(const rpcprog_t program, const rpcvers_t version) +{ + if (pmap_unset((unsigned long)program, (unsigned long)version) == FALSE) + xlog(D_GENERAL, "Failed to unregister program %lu, version %lu", + (unsigned long)program, (unsigned long)version); +} + +#endif /* !HAVE_LIBTIRPC */ diff --git a/support/nfs/xio.c b/support/nfs/xio.c index 5e2e1e9..e3d27d2 100644 --- a/support/nfs/xio.c +++ b/support/nfs/xio.c @@ -44,16 +44,9 @@ xfclose(XFILE *xfp) xfree(xfp); } -static void -doalarm(int sig) -{ - return; -} - int xflock(char *fname, char *type) { - struct sigaction sa, oldsa; int readonly = !strcmp(type, "r"); struct flock fl = { readonly? F_RDLCK : F_WRLCK, SEEK_SET, 0, 0, 0 }; int fd; @@ -68,21 +61,12 @@ xflock(char *fname, char *type) return -1; } - sa.sa_handler = doalarm; - sa.sa_flags = 0; - sigemptyset(&sa.sa_mask); - sigaction(SIGALRM, &sa, &oldsa); - alarm(10); if (fcntl(fd, F_SETLKW, &fl) < 0) { - alarm(0); xlog(L_WARNING, "failed to lock %s: errno %d (%s)", fname, errno, strerror(errno)); close(fd); - fd = 0; - } else { - alarm(0); + fd = -1; } - sigaction(SIGALRM, &oldsa, NULL); return fd; } diff --git a/support/nsm/Makefile.am b/support/nsm/Makefile.am new file mode 100644 index 0000000..2038e68 --- /dev/null +++ b/support/nsm/Makefile.am @@ -0,0 +1,45 @@ +## 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 -o $@ $< + +$(GENFILES_H): %.h: %.x $(RPCGEN) + test -f $@ && rm -rf $@ || true + $(RPCGEN) -h -o $@ $< + 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..7ce9d88 --- /dev/null +++ b/support/nsm/Makefile.in @@ -0,0 +1,569 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 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@ +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 +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/aclocal/bsdsignals.m4 \ + $(top_srcdir)/aclocal/ipv6.m4 \ + $(top_srcdir)/aclocal/kerberos5.m4 \ + $(top_srcdir)/aclocal/libblkid.m4 \ + $(top_srcdir)/aclocal/libcap.m4 \ + $(top_srcdir)/aclocal/libevent.m4 \ + $(top_srcdir)/aclocal/libnfsidmap.m4 \ + $(top_srcdir)/aclocal/librpcsecgss.m4 \ + $(top_srcdir)/aclocal/libtirpc.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) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/support/include/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +LIBRARIES = $(noinst_LIBRARIES) +ARFLAGS = cru +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) +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/support/include +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +SOURCES = $(libnsm_a_SOURCES) +DIST_SOURCES = $(libnsm_a_SOURCES) +ETAGS = etags +CTAGS = ctags +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@ +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@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CXXFLAGS_FOR_BUILD = @CXXFLAGS_FOR_BUILD@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GREP = @GREP@ +GSSD = @GSSD@ +GSSGLUE_CFLAGS = @GSSGLUE_CFLAGS@ +GSSGLUE_LIBS = @GSSGLUE_LIBS@ +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@ +LIBNSL = @LIBNSL@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBSOCKET = @LIBSOCKET@ +LIBTOOL = @LIBTOOL@ +LIBWRAP = @LIBWRAP@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +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_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +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@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +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_nfsv3 = @enable_nfsv3@ +enable_nfsv4 = @enable_nfsv4@ +enable_nfsv41 = @enable_nfsv41@ +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@ +lt_ECHO = @lt_ECHO@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +mountfile = @mountfile@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +startstatd = @startstatd@ +statduser = @statduser@ +statedir = @statedir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +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 +.PRECIOUS: 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__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + 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) + -rm -f libnsm.a + $(libnsm_a_AR) libnsm.a $(libnsm_a_OBJECTS) $(libnsm_a_LIBADD) + $(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@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rpc.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sm_inter_clnt.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sm_inter_svc.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sm_inter_xdr.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + 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 +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + 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" + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(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: 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: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +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 -rf ./$(DEPDIR) + -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 -rf ./$(DEPDIR) + -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-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-libtool clean-noinstLIBRARIES ctags 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 uninstall uninstall-am + +@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 $@ $< + 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..d469219 --- /dev/null +++ b/support/nsm/file.c @@ -0,0 +1,1067 @@ +/* + * 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 . + */ + +/* + * 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 +#endif + +#include +#include +#include +#include + +#include +#include +#include +#ifndef S_SPLINT_S +#include +#endif +#include +#include +#include +#include +#include +#include + +#include "xlog.h" +#include "nsm.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" + +/* + * Some distributions place statd's files in a subdirectory + */ +#define NSM_PATH_EXTENSION +/* #define NSM_PATH_EXTENSION "/statd" */ + +#define NSM_DEFAULT_STATEDIR NFS_STATEDIR NSM_PATH_EXTENSION + +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) +{ + size_t size; + char *path; + int len; + + size = strlen(nsm_base_dirname) + strlen(directory) + 2; + if (size > PATH_MAX) + return NULL; + + path = malloc(size); + if (path == NULL) + return NULL; + + len = snprintf(path, size, "%s/%s", nsm_base_dirname, directory); + if (error_check(len, size)) { + 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_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) +{ + static char buf[PATH_MAX]; + struct stat st; + char *path; + + /* First: test length of name and whether it exists */ + if (lstat(parentdir, &st) == -1) { + (void)fprintf(stderr, "%s: Failed to stat %s: %s", + progname, parentdir, strerror(errno)); + return false; + } + + /* Ensure we have a clean directory pathname */ + strncpy(buf, parentdir, sizeof(buf)); + path = dirname(buf); + if (*path == '.') { + (void)fprintf(stderr, "%s: Unusable directory %s", + progname, parentdir); + return false; + } + + xlog(D_CALL, "Using %s as the state directory", parentdir); + strncpy(nsm_base_dirname, parentdir, sizeof(nsm_base_dirname)); + return true; +} + +/** + * 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. + */ +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; +} + +/** + * 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); + + /* + * XXX: If we can't stat dirname, or if dirname is owned by + * root, we should use "statduser" instead, which is set up + * by configure.ac. Nothing in nfs-utils seems to use + * "statduser," though. + */ + if (lstat(nsm_base_dirname, &st) == -1) { + xlog(L_ERROR, "Failed to stat %s: %m", nsm_base_dirname); + return false; + } + + if (st.st_uid == 0) { + xlog_warn("Running as root. " + "chown %s to choose different user", nsm_base_dirname); + return true; + } + + if (chdir(nsm_base_dirname) == -1) { + xlog(L_ERROR, "Failed to change working directory to %s: %m", + nsm_base_dirname); + return false; + } + + /* + * 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 0; + } + + 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: + (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"); + 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; + + if (de->d_type != (unsigned char)DT_REG) + continue; + 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; + } + + 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 NSM_MONITOR_DIR + * 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 (stat(path, &stb) == -1) { + xlog(L_ERROR, "Failed to stat %s: %m", 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_type != (unsigned char)DT_REG) + continue; + 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) +{ + 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) { + 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 + * + */ +void +nsm_delete_monitored_host(const char *hostname, const char *mon_name, + const char *my_name) +{ + nsm_delete_host(NSM_MONITOR_DIR, hostname, mon_name, my_name); +} + +/** + * 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); +} diff --git a/support/nsm/rpc.c b/support/nsm/rpc.c new file mode 100644 index 0000000..4e5f40e --- /dev/null +++ b/support/nsm/rpc.c @@ -0,0 +1,534 @@ +/* + * 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 . + */ + +/* + * 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 +#endif /* HAVE_CONFIG_H */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#ifdef HAVE_LIBTIRPC +#include +#include +#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 %u, %u, 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 +#endif + +program SM_PROG { + version SM_VERS { + /* res_stat = stat_succ if status monitor agrees to monitor */ + /* res_stat = stat_fail if status monitor cannot monitor */ + /* if res_stat == stat_succ, state = state number of site sm_name */ + struct sm_stat_res SM_STAT(struct sm_name) = 1; + + /* res_stat = stat_succ if status monitor agrees to monitor */ + /* res_stat = stat_fail if status monitor cannot monitor */ + /* stat consists of state number of local site */ + struct sm_stat_res SM_MON(struct mon) = 2; + + /* stat consists of state number of local site */ + struct sm_stat SM_UNMON(struct mon_id) = 3; + + /* stat consists of state number of local site */ + struct sm_stat SM_UNMON_ALL(struct my_id) = 4; + + void SM_SIMU_CRASH(void) = 5; + + void SM_NOTIFY(struct stat_chge) = 6; + + } = 1; +} = 100024; + +const SM_MAXSTRLEN = 1024; +const SM_PRIV_SIZE = 16; + +struct sm_name { + string mon_name; +}; + +struct my_id { + string my_name; /* name of the site iniates the monitoring request*/ + int my_prog; /* rpc program # of the requesting process */ + int my_vers; /* rpc version # of the requesting process */ + int my_proc; /* rpc procedure # of the requesting process */ +}; + +struct mon_id { + string mon_name; /* name of the site to be monitored */ + struct my_id my_id; +}; + + +struct mon { + struct mon_id mon_id; + opaque priv[SM_PRIV_SIZE]; /* private information to store at monitor for requesting process */ +}; + +struct stat_chge { + string mon_name; /* name of the site that had the state change */ + int state; +}; + +/* + * state # of status monitor monitonically increases each time + * status of the site changes: + * an even number (>= 0) indicates the site is down and + * an odd number (> 0) indicates the site is up; + */ +struct sm_stat { + int state; /* state # of status monitor */ +}; + +enum res { + stat_succ = 0, /* status monitor agrees to monitor */ + stat_fail = 1 /* status monitor cannot monitor */ +}; + +struct sm_stat_res { + res res_stat; + int state; +}; + +/* + * structure of the status message sent back by the status monitor + * when monitor site status changes + */ +struct status { + string mon_name; + int state; + opaque priv[SM_PRIV_SIZE]; /* stored private information */ +}; + +%#define SM_INTER_X diff --git a/tests/Makefile.am b/tests/Makefile.am new file mode 100644 index 0000000..faa8197 --- /dev/null +++ b/tests/Makefile.am @@ -0,0 +1,13 @@ +## Process this file with automake to produce Makefile.in + +check_PROGRAMS = statdb_dump +statdb_dump_SOURCES = statdb_dump.c + +statdb_dump_LDADD = ../support/nfs/libnfs.a \ + ../support/nsm/libnsm.a $(LIBCAP) + +SUBDIRS = nsm_client + +MAINTAINERCLEANFILES = Makefile.in + +TESTS = t0001-statd-basic-mon-unmon.sh diff --git a/tests/Makefile.in b/tests/Makefile.in new file mode 100644 index 0000000..8f4c9b5 --- /dev/null +++ b/tests/Makefile.in @@ -0,0 +1,781 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 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@ +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@ +check_PROGRAMS = statdb_dump$(EXEEXT) +subdir = tests +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/aclocal/bsdsignals.m4 \ + $(top_srcdir)/aclocal/ipv6.m4 \ + $(top_srcdir)/aclocal/kerberos5.m4 \ + $(top_srcdir)/aclocal/libblkid.m4 \ + $(top_srcdir)/aclocal/libcap.m4 \ + $(top_srcdir)/aclocal/libevent.m4 \ + $(top_srcdir)/aclocal/libnfsidmap.m4 \ + $(top_srcdir)/aclocal/librpcsecgss.m4 \ + $(top_srcdir)/aclocal/libtirpc.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) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/support/include/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am_statdb_dump_OBJECTS = statdb_dump.$(OBJEXT) +statdb_dump_OBJECTS = $(am_statdb_dump_OBJECTS) +am__DEPENDENCIES_1 = +statdb_dump_DEPENDENCIES = ../support/nfs/libnfs.a \ + ../support/nsm/libnsm.a $(am__DEPENDENCIES_1) +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/support/include +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +SOURCES = $(statdb_dump_SOURCES) +DIST_SOURCES = $(statdb_dump_SOURCES) +RECURSIVE_TARGETS = all-recursive check-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 uninstall-recursive +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +AM_RECURSIVE_TARGETS = $(RECURSIVE_TARGETS:-recursive=) \ + $(RECURSIVE_CLEAN_TARGETS:-recursive=) tags TAGS ctags CTAGS \ + distdir +ETAGS = etags +CTAGS = ctags +am__tty_colors = \ +red=; grn=; lgn=; blu=; std= +DIST_SUBDIRS = $(SUBDIRS) +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@ +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@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CXXFLAGS_FOR_BUILD = @CXXFLAGS_FOR_BUILD@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GREP = @GREP@ +GSSD = @GSSD@ +GSSGLUE_CFLAGS = @GSSGLUE_CFLAGS@ +GSSGLUE_LIBS = @GSSGLUE_LIBS@ +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@ +LIBNSL = @LIBNSL@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBSOCKET = @LIBSOCKET@ +LIBTOOL = @LIBTOOL@ +LIBWRAP = @LIBWRAP@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +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_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +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@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +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_nfsv3 = @enable_nfsv3@ +enable_nfsv4 = @enable_nfsv4@ +enable_nfsv41 = @enable_nfsv41@ +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@ +lt_ECHO = @lt_ECHO@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +mountfile = @mountfile@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +startstatd = @startstatd@ +statduser = @statduser@ +statedir = @statedir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +statdb_dump_SOURCES = statdb_dump.c +statdb_dump_LDADD = ../support/nfs/libnfs.a \ + ../support/nsm/libnsm.a $(LIBCAP) + +SUBDIRS = nsm_client +MAINTAINERCLEANFILES = Makefile.in +TESTS = t0001-statd-basic-mon-unmon.sh +all: all-recursive + +.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 tests/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu tests/Makefile +.PRECIOUS: 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__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + 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-checkPROGRAMS: + @list='$(check_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 +statdb_dump$(EXEEXT): $(statdb_dump_OBJECTS) $(statdb_dump_DEPENDENCIES) + @rm -f statdb_dump$(EXEEXT) + $(LINK) $(statdb_dump_OBJECTS) $(statdb_dump_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/statdb_dump.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< + +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. +$(RECURSIVE_TARGETS): + @fail= failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + list='$(SUBDIRS)'; 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" + +$(RECURSIVE_CLEAN_TARGETS): + @fail= failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + rev=''; for subdir in $$list; do \ + if test "$$subdir" = "."; then :; else \ + rev="$$subdir $$rev"; \ + fi; \ + done; \ + rev="$$rev ."; \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$rev; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done && test -z "$$fail" +tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done +ctags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \ + done + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + 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; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + 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 +CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + 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" + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +check-TESTS: $(TESTS) + @failed=0; all=0; xfail=0; xpass=0; skip=0; \ + srcdir=$(srcdir); export srcdir; \ + list=' $(TESTS) '; \ + $(am__tty_colors); \ + if test -n "$$list"; then \ + for tst in $$list; do \ + if test -f ./$$tst; then dir=./; \ + elif test -f $$tst; then dir=; \ + else dir="$(srcdir)/"; fi; \ + if $(TESTS_ENVIRONMENT) $${dir}$$tst; then \ + all=`expr $$all + 1`; \ + case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$tst[\ \ ]*) \ + xpass=`expr $$xpass + 1`; \ + failed=`expr $$failed + 1`; \ + col=$$red; res=XPASS; \ + ;; \ + *) \ + col=$$grn; res=PASS; \ + ;; \ + esac; \ + elif test $$? -ne 77; then \ + all=`expr $$all + 1`; \ + case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$tst[\ \ ]*) \ + xfail=`expr $$xfail + 1`; \ + col=$$lgn; res=XFAIL; \ + ;; \ + *) \ + failed=`expr $$failed + 1`; \ + col=$$red; res=FAIL; \ + ;; \ + esac; \ + else \ + skip=`expr $$skip + 1`; \ + col=$$blu; res=SKIP; \ + fi; \ + echo "$${col}$$res$${std}: $$tst"; \ + done; \ + if test "$$all" -eq 1; then \ + tests="test"; \ + All=""; \ + else \ + tests="tests"; \ + All="All "; \ + fi; \ + if test "$$failed" -eq 0; then \ + if test "$$xfail" -eq 0; then \ + banner="$$All$$all $$tests passed"; \ + else \ + if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \ + banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \ + fi; \ + else \ + if test "$$xpass" -eq 0; then \ + banner="$$failed of $$all $$tests failed"; \ + else \ + if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \ + banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \ + fi; \ + fi; \ + dashes="$$banner"; \ + skipped=""; \ + if test "$$skip" -ne 0; then \ + if test "$$skip" -eq 1; then \ + skipped="($$skip test was not run)"; \ + else \ + skipped="($$skip tests were not run)"; \ + fi; \ + test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \ + dashes="$$skipped"; \ + fi; \ + report=""; \ + if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \ + report="Please report to $(PACKAGE_BUGREPORT)"; \ + test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \ + dashes="$$report"; \ + fi; \ + dashes=`echo "$$dashes" | sed s/./=/g`; \ + if test "$$failed" -eq 0; then \ + echo "$$grn$$dashes"; \ + else \ + echo "$$red$$dashes"; \ + fi; \ + echo "$$banner"; \ + test -z "$$skipped" || echo "$$skipped"; \ + test -z "$$report" || echo "$$report"; \ + echo "$$dashes$$std"; \ + test "$$failed" -eq 0; \ + else :; fi + +distdir: $(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 \ + test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + 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 + $(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS) + $(MAKE) $(AM_MAKEFLAGS) check-TESTS +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: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +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-checkPROGRAMS clean-generic clean-libtool \ + mostlyclean-am + +distclean: distclean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile 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 -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: + +.MAKE: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) check-am \ + ctags-recursive install-am install-strip tags-recursive + +.PHONY: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) CTAGS GTAGS \ + all all-am check check-TESTS check-am clean \ + clean-checkPROGRAMS clean-generic clean-libtool ctags \ + ctags-recursive 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 installdirs-am maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-recursive uninstall uninstall-am + + +# 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/tests/nsm_client/Makefile.am b/tests/nsm_client/Makefile.am new file mode 100644 index 0000000..4bf0a45 --- /dev/null +++ b/tests/nsm_client/Makefile.am @@ -0,0 +1,45 @@ +## Process this file with automake to produce Makefile.in + +GENFILES_CLNT = nlm_sm_inter_clnt.c +GENFILES_SVC = nlm_sm_inter_svc.c +GENFILES_XDR = nlm_sm_inter_xdr.c +GENFILES_H = nlm_sm_inter.h + +GENFILES = $(GENFILES_CLNT) $(GENFILES_SVC) $(GENFILES_XDR) $(GENFILES_H) + + +check_PROGRAMS = nsm_client +nsm_client_SOURCES = $(GENFILES) nsm_client.c + +BUILT_SOURCES = $(GENFILES) +nsm_client_LDADD = ../../support/nfs/libnfs.a \ + ../../support/nsm/libnsm.a $(LIBCAP) + +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) + diff --git a/tests/nsm_client/Makefile.in b/tests/nsm_client/Makefile.in new file mode 100644 index 0000000..77b99e8 --- /dev/null +++ b/tests/nsm_client/Makefile.in @@ -0,0 +1,573 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 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@ +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@ +check_PROGRAMS = nsm_client$(EXEEXT) +subdir = tests/nsm_client +DIST_COMMON = README $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/aclocal/bsdsignals.m4 \ + $(top_srcdir)/aclocal/ipv6.m4 \ + $(top_srcdir)/aclocal/kerberos5.m4 \ + $(top_srcdir)/aclocal/libblkid.m4 \ + $(top_srcdir)/aclocal/libcap.m4 \ + $(top_srcdir)/aclocal/libevent.m4 \ + $(top_srcdir)/aclocal/libnfsidmap.m4 \ + $(top_srcdir)/aclocal/librpcsecgss.m4 \ + $(top_srcdir)/aclocal/libtirpc.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) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/support/include/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__objects_1 = nlm_sm_inter_clnt.$(OBJEXT) +am__objects_2 = nlm_sm_inter_svc.$(OBJEXT) +am__objects_3 = nlm_sm_inter_xdr.$(OBJEXT) +am__objects_4 = +am__objects_5 = $(am__objects_1) $(am__objects_2) $(am__objects_3) \ + $(am__objects_4) +am_nsm_client_OBJECTS = $(am__objects_5) nsm_client.$(OBJEXT) +nsm_client_OBJECTS = $(am_nsm_client_OBJECTS) +am__DEPENDENCIES_1 = +nsm_client_DEPENDENCIES = ../../support/nfs/libnfs.a \ + ../../support/nsm/libnsm.a $(am__DEPENDENCIES_1) +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/support/include +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +SOURCES = $(nsm_client_SOURCES) +DIST_SOURCES = $(nsm_client_SOURCES) +ETAGS = etags +CTAGS = ctags +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@ +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@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CXXFLAGS_FOR_BUILD = @CXXFLAGS_FOR_BUILD@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GREP = @GREP@ +GSSD = @GSSD@ +GSSGLUE_CFLAGS = @GSSGLUE_CFLAGS@ +GSSGLUE_LIBS = @GSSGLUE_LIBS@ +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@ +LIBNSL = @LIBNSL@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBSOCKET = @LIBSOCKET@ +LIBTOOL = @LIBTOOL@ +LIBWRAP = @LIBWRAP@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +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_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +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@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +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_nfsv3 = @enable_nfsv3@ +enable_nfsv4 = @enable_nfsv4@ +enable_nfsv41 = @enable_nfsv41@ +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@ +lt_ECHO = @lt_ECHO@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +mountfile = @mountfile@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +startstatd = @startstatd@ +statduser = @statduser@ +statedir = @statedir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +GENFILES_CLNT = nlm_sm_inter_clnt.c +GENFILES_SVC = nlm_sm_inter_svc.c +GENFILES_XDR = nlm_sm_inter_xdr.c +GENFILES_H = nlm_sm_inter.h +GENFILES = $(GENFILES_CLNT) $(GENFILES_SVC) $(GENFILES_XDR) $(GENFILES_H) +nsm_client_SOURCES = $(GENFILES) nsm_client.c +BUILT_SOURCES = $(GENFILES) +nsm_client_LDADD = ../../support/nfs/libnfs.a \ + ../../support/nsm/libnsm.a $(LIBCAP) + +@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 tests/nsm_client/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu tests/nsm_client/Makefile +.PRECIOUS: 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__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + 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-checkPROGRAMS: + @list='$(check_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 +nsm_client$(EXEEXT): $(nsm_client_OBJECTS) $(nsm_client_DEPENDENCIES) + @rm -f nsm_client$(EXEEXT) + $(LINK) $(nsm_client_OBJECTS) $(nsm_client_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nlm_sm_inter_clnt.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nlm_sm_inter_svc.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nlm_sm_inter_xdr.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nsm_client.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + 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 +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + 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" + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(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 + $(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS) +check: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) check-am +all-am: Makefile +installdirs: +install: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) 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: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +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-checkPROGRAMS clean-generic clean-libtool \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -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 -rf ./$(DEPDIR) + -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 check-am install install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean \ + clean-checkPROGRAMS clean-generic clean-libtool ctags \ + 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 uninstall uninstall-am + +@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 $@ $< + +# 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/tests/nsm_client/README b/tests/nsm_client/README new file mode 100644 index 0000000..85379dd --- /dev/null +++ b/tests/nsm_client/README @@ -0,0 +1,12 @@ +The nsm_client program is intended for testing statd. It has the ability +to act as a synthetic NSM client for sending artificial NSM calls to any +host you choose. + +It also has an NLM simulator that implements the call that statd uses to +communicate with lockd. The daemon simulator will start itself up, +register as an NLM service and listen for "downcalls" from statd. When +it gets one, it will log a message. + +Note that lockd will need to be down when using the daemon simulator. It +also does not implement the entire NLM protocol and is only really +useful for testing statd's downcall. diff --git a/tests/nsm_client/nlm_sm_inter.x b/tests/nsm_client/nlm_sm_inter.x new file mode 100644 index 0000000..95fa326 --- /dev/null +++ b/tests/nsm_client/nlm_sm_inter.x @@ -0,0 +1,43 @@ +/* + * Copyright (C) 1995, 1997-1999 Jeffrey A. Uphoff + * Modified by Olaf Kirch, 1996. + * Modified by H.J. Lu, 1998. + * Modified by Jeff Layton, 2010. + * + * NLM similator for Linux + */ + +#ifdef RPC_CLNT +%#include +#endif + +/* + * statd rejects monitor registrations for any non-lockd services, so pretend + * to be lockd when testing. Furthermore, the only call we care about from + * statd is #16, which is the downcall to notify the kernel of a host's status + * change. + */ +program NLM_SM_PROG { + /* version 3 of the NLM protocol */ + version NLM_SM_VERS3 { + void NLM_SM_NOTIFY(struct nlm_sm_notify) = 16; + } = 3; + + /* version 2 of NLM protocol */ + version NLM_SM_VERS4 { + void NLM_SM_NOTIFY(struct nlm_sm_notify) = 16; + } = 4; +} = 100021; + +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 + */ +struct nlm_sm_notify { + string mon_name; + int state; + opaque priv[SM_PRIV_SIZE]; /* stored private information */ +}; diff --git a/tests/nsm_client/nsm_client.c b/tests/nsm_client/nsm_client.c new file mode 100644 index 0000000..0d1159a --- /dev/null +++ b/tests/nsm_client/nsm_client.c @@ -0,0 +1,465 @@ +/* + * nsm_client.c -- synthetic client and lockd simulator for testing statd + * + * Copyright (C) 2010 Red Hat, Jeff Layton + * + * 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. + * + * Very loosely based on "simulator.c" in the statd directory. Original + * copyright for that program follows: + * + * Copyright (C) 1995-1997, 1999 Jeffrey A. Uphoff + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nfslib.h" +#include "nfsrpc.h" +#include "nsm.h" +#include "sm_inter.h" +#include "nlm_sm_inter.h" +#include "sockaddr.h" +#include "xcommon.h" + +static void daemon_simulator(void); +static void sim_killer(int sig); +static int nsm_client_crash(char *); +static int nsm_client_mon(char *, char *, char *, char *, int, int); +static int nsm_client_stat(char *, char *); +static int nsm_client_notify(char *, char *, char *); +static int nsm_client_unmon(char *, char *, char *, int, int); +static int nsm_client_unmon_all(char *, char *, int, int); + +extern void nlm_sm_prog_4(struct svc_req *rqstp, register SVCXPRT *transp); +extern void svc_exit(void); + +/* + * default to 15 retransmit interval, which seems to be the default for + * UDP clients w/ legacy glibc RPC + */ +static struct timeval retrans_interval = +{ + .tv_sec = 15, +}; + +static struct option longopts[] = +{ + { "help", 0, 0, 'h' }, + { "host", 0, 0, 'H' }, + { "name", 1, 0, 'n' }, + { "program", 1, 0, 'P' }, + { "version", 1, 0, 'v' }, + { NULL, 0, 0, 0 }, +}; + +static int +usage(char *program) +{ + printf("Usage:\n"); + printf("%s [options] [arg]...\n", program); + printf("where command is one of these with the specified args:\n"); + printf("crash\t\t\t\ttell host to simulate crash\n"); + printf("daemon\t\t\t\t\tstart up lockd daemon simulator\n"); + printf("notify \tsend a reboot notification to host\n"); + printf("stat \t\t\tget status of on host\n"); + printf("unmon_all\t\t\ttell host to unmon everything\n"); + printf("unmon \t\t\ttell host to unmon \n"); + printf("mon \t\ttell host to monitor with private \n"); + return 1; +} + +static int +hex2bin(char *dst, size_t dstlen, char *src) +{ + int i; + unsigned int tmp; + + for (i = 0; *src && i < dstlen; i++) { + if (sscanf(src, "%2x", &tmp) != 1) + return 0; + dst[i] = tmp; + src++; + if (!*src) + break; + src++; + } + + return 1; +} + +static void +bin2hex(char *dst, char *src, size_t srclen) +{ + int i; + + for (i = 0; i < srclen; i++) + dst += sprintf(dst, "%02x", 0xff & src[i]); +} + +int +main(int argc, char **argv) +{ + int arg, err = 0; + int remaining_args; + char my_name[NI_MAXHOST], host[NI_MAXHOST]; + char cookie[SM_PRIV_SIZE]; + int my_prog = NLM_SM_PROG; + int my_vers = NLM_SM_VERS4; + + my_name[0] = '\0'; + host[0] = '\0'; + + while ((arg = getopt_long(argc, argv, "hHn:P:v:", longopts, + NULL)) != EOF) { + switch (arg) { + case 'H': + strncpy(host, optarg, sizeof(host)); + case 'n': + strncpy(my_name, optarg, sizeof(my_name)); + case 'P': + my_prog = atoi(optarg); + case 'v': + my_vers = atoi(optarg); + } + } + + remaining_args = argc - optind; + if (remaining_args <= 0) + usage(argv[0]); + + if (!my_name[0]) + gethostname(my_name, sizeof(my_name)); + if (!host[0]) + strncpy(host, "127.0.0.1", sizeof(host)); + + if (!strcasecmp(argv[optind], "daemon")) { + daemon_simulator(); + } else if (!strcasecmp(argv[optind], "crash")) { + err = nsm_client_crash(host); + } else if (!strcasecmp(argv[optind], "stat")) { + if (remaining_args < 2) + usage(argv[0]); + err = nsm_client_stat(host, argv[optind + 2]); + } else if (!strcasecmp(argv[optind], "unmon_all")) { + err = nsm_client_unmon_all(host, my_name, my_prog, my_vers); + } else if (!strcasecmp(argv[optind], "unmon")) { + if (remaining_args < 2) + usage(argv[0]); + err = nsm_client_unmon(host, argv[optind + 1], my_name, my_prog, + my_vers); + } else if (!strcasecmp(argv[optind], "notify")) { + if (remaining_args < 2) + usage(argv[0]); + err = nsm_client_notify(host, argv[optind + 1], + argv[optind + 2]); + } else if (!strcasecmp(argv[optind], "mon")) { + if (remaining_args < 2) + usage(argv[0]); + + memset(cookie, '\0', SM_PRIV_SIZE); + if (!hex2bin(cookie, sizeof(cookie), argv[optind + 2])) { + fprintf(stderr, "SYS:%d\n", EINVAL); + printf("Unable to convert hex cookie %s to binary.\n", + argv[optind + 2]); + return 1; + } + + err = nsm_client_mon(host, argv[optind + 1], cookie, my_name, + my_prog, my_vers); + } else { + err = usage(argv[0]); + } + + return err; +} + +static CLIENT * +nsm_client_get_rpcclient(const char *node) +{ + unsigned short port; + struct addrinfo *ai; + struct addrinfo hints = { .ai_flags = AI_ADDRCONFIG }; + int err; + CLIENT *client = NULL; + +#ifndef IPV6_ENABLED + hints.ai_family = AF_INET; +#endif /* IPV6_ENABLED */ + + /* FIXME: allow support for providing port? */ + err = getaddrinfo(node, NULL, &hints, &ai); + if (err) { + fprintf(stderr, "EAI:%d\n", err); + if (err == EAI_SYSTEM) + fprintf(stderr, "SYS:%d\n", errno); + printf("Unable to translate host to address: %s\n", + err == EAI_SYSTEM ? strerror(errno) : + gai_strerror(err)); + return client; + } + + /* FIXME: allow for TCP too? */ + port = nfs_getport(ai->ai_addr, ai->ai_addrlen, SM_PROG, + SM_VERS, IPPROTO_UDP); + if (!port) { + fprintf(stderr, "RPC:%d\n", rpc_createerr.cf_stat); + printf("Unable to determine port for service\n"); + goto out; + } + + nfs_set_port(ai->ai_addr, port); + + client = nfs_get_rpcclient(ai->ai_addr, ai->ai_addrlen, IPPROTO_UDP, + SM_PROG, SM_VERS, &retrans_interval); + if (!client) { + fprintf(stderr, "RPC:%d\n", rpc_createerr.cf_stat); + printf("RPC client creation failed\n"); + } +out: + freeaddrinfo(ai); + return client; +} + +static int +nsm_client_mon(char *calling, char *monitoring, char *cookie, char *my_name, + int my_prog, int my_vers) +{ + CLIENT *client; + sm_stat_res *result; + mon mon; + int err = 0; + + printf("Calling %s (as %s) to monitor %s\n", calling, my_name, + monitoring); + + if ((client = nsm_client_get_rpcclient(calling)) == NULL) + return 1; + + memcpy(mon.priv, cookie, SM_PRIV_SIZE); + mon.mon_id.my_id.my_name = my_name; + mon.mon_id.my_id.my_prog = my_prog; + mon.mon_id.my_id.my_vers = my_vers; + mon.mon_id.my_id.my_proc = NLM_SM_NOTIFY; + mon.mon_id.mon_name = monitoring; + + if (!(result = sm_mon_1(&mon, client))) { + fprintf(stderr, "RPC:%d\n", rpc_createerr.cf_stat); + printf("%s\n", clnt_sperror(client, "sm_mon_1")); + err = 1; + goto mon_out; + } + + printf("SM_MON request %s, state: %d\n", + result->res_stat == stat_succ ? "successful" : "failed", + result->state); + + if (result->res_stat != stat_succ) { + fprintf(stderr, "RPC:%d\n", rpc_createerr.cf_stat); + err = 1; + } + +mon_out: + clnt_destroy(client); + return err; +} + +static int +nsm_client_unmon(char *calling, char *unmonitoring, char *my_name, int my_prog, + int my_vers) +{ + CLIENT *client; + sm_stat *result; + mon_id mon_id; + int err = 0; + + printf("Calling %s (as %s) to unmonitor %s\n", calling, my_name, + unmonitoring); + + if ((client = nsm_client_get_rpcclient(calling)) == NULL) + return 1; + + mon_id.my_id.my_name = my_name; + mon_id.my_id.my_prog = my_prog; + mon_id.my_id.my_vers = my_vers; + mon_id.my_id.my_proc = NLM_SM_NOTIFY; + mon_id.mon_name = unmonitoring; + + if (!(result = sm_unmon_1(&mon_id, client))) { + fprintf(stderr, "RPC:%d\n", rpc_createerr.cf_stat); + printf("%s\n", clnt_sperror(client, "sm_unmon_1")); + err = 1; + goto unmon_out; + } + + printf("SM_UNMON state: %d\n", result->state); + +unmon_out: + clnt_destroy(client); + return err; +} + +static int +nsm_client_unmon_all(char *calling, char *my_name, int my_prog, int my_vers) +{ + CLIENT *client; + sm_stat *result; + my_id my_id; + int err = 0; + + printf("Calling %s (as %s) to unmonitor all hosts\n", calling, my_name); + + if ((client = nsm_client_get_rpcclient(calling)) == NULL) { + printf("RPC client creation failed\n"); + return 1; + } + + my_id.my_name = my_name; + my_id.my_prog = my_prog; + my_id.my_vers = my_vers; + my_id.my_proc = NLM_SM_NOTIFY; + + if (!(result = sm_unmon_all_1(&my_id, client))) { + fprintf(stderr, "RPC:%d\n", rpc_createerr.cf_stat); + printf("%s\n", clnt_sperror(client, "sm_unmon_all_1")); + err = 1; + goto unmon_all_out; + } + + printf("SM_UNMON_ALL state: %d\n", result->state); + +unmon_all_out: + return err; +} + +static int +nsm_client_crash(char *host) +{ + CLIENT *client; + + if ((client = nsm_client_get_rpcclient(host)) == NULL) + return 1; + + if (!sm_simu_crash_1(NULL, client)) { + fprintf(stderr, "RPC:%d\n", rpc_createerr.cf_stat); + printf("%s\n", clnt_sperror(client, "sm_simu_crash_1")); + return 1; + } + + return 0; +} + +static int +nsm_client_stat(char *calling, char *monitoring) +{ + CLIENT *client; + sm_name checking; + sm_stat_res *result; + + if ((client = nsm_client_get_rpcclient(calling)) == NULL) + return 1; + + checking.mon_name = monitoring; + + if (!(result = sm_stat_1(&checking, client))) { + fprintf(stderr, "RPC:%d\n", rpc_createerr.cf_stat); + printf("%s\n", clnt_sperror(client, "sm_stat_1")); + return 1; + } + + if (result->res_stat != stat_succ) { + fprintf(stderr, "RPC:%d\n", rpc_createerr.cf_stat); + printf("stat_fail from %s for %s, state: %d\n", calling, + monitoring, result->state); + return 1; + } + + printf("stat_succ from %s for %s, state: %d\n", calling, + monitoring, result->state); + + return 0; +} + +static int +nsm_client_notify(char *calling, char *mon_name, char *statestr) +{ + CLIENT *client; + + stat_chge stat_chge = { .mon_name = mon_name }; + + stat_chge.state = atoi(statestr); + + if ((client = nsm_client_get_rpcclient(calling)) == NULL) + return 1; + + if (!sm_notify_1(&stat_chge, client)) { + fprintf(stderr, "RPC:%d\n", rpc_createerr.cf_stat); + printf("%s\n", clnt_sperror(client, "sm_notify_1")); + return 1; + } + + return 0; +} + +static void sim_killer(int sig) +{ +#ifdef HAVE_LIBTIRPC + (void) rpcb_unset(NLM_SM_PROG, NLM_SM_VERS4, NULL); +#else + (void) pmap_unset(NLM_SM_PROG, NLM_SM_VERS4); +#endif + exit(0); +} + +static void daemon_simulator(void) +{ + signal(SIGHUP, sim_killer); + signal(SIGINT, sim_killer); + signal(SIGTERM, sim_killer); + /* FIXME: allow for different versions? */ + nfs_svc_create("nlmsim", NLM_SM_PROG, NLM_SM_VERS4, nlm_sm_prog_4, 0); + svc_run(); +} + +void *nlm_sm_notify_4_svc(struct nlm_sm_notify *argp, struct svc_req *rqstp) +{ + static char *result; + char priv[SM_PRIV_SIZE * 2 + 1]; + + bin2hex(priv, argp->priv, SM_PRIV_SIZE); + + printf("state=%d:mon_name=%s:private=%s\n", argp->state, + argp->mon_name, priv); + return (void *) &result; +} + +void *nlm_sm_notify_3_svc(struct nlm_sm_notify *argp, struct svc_req *rqstp) +{ + return nlm_sm_notify_4_svc(argp, rqstp); +} diff --git a/tests/statdb_dump.c b/tests/statdb_dump.c new file mode 100644 index 0000000..92d63f2 --- /dev/null +++ b/tests/statdb_dump.c @@ -0,0 +1,99 @@ +/* + * statdb_dump.c -- dump contents of statd's monitor DB + * + * Copyright (C) 2010 Red Hat, Jeff Layton + * + * 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 +#include +#include + +#include "nsm.h" +#include "xlog.h" + +static char cookiebuf[(SM_PRIV_SIZE * 2) + 1]; +static char addrbuf[INET6_ADDRSTRLEN + 1]; + +static unsigned int +dump_host(const char *hostname, const struct sockaddr *sa, const struct mon *m, + const time_t timestamp) +{ + int ret; + const char *addr; + const struct sockaddr_in *sin; + const struct sockaddr_in6 *sin6; + + ret = nsm_priv_to_hex(m->priv, cookiebuf, sizeof(cookiebuf)); + if (!ret) { + xlog(L_ERROR, "Unable to convert cookie to hex string.\n"); + return ret; + } + + switch (sa->sa_family) { + case AF_INET: + sin = (struct sockaddr_in *)(char *)sa; + addr = inet_ntop(sa->sa_family, &sin->sin_addr.s_addr, addrbuf, + (socklen_t)sizeof(addrbuf)); + break; + case AF_INET6: + sin6 = (struct sockaddr_in6 *)(char *)sa; + addr = inet_ntop(sa->sa_family, &sin6->sin6_addr, addrbuf, + (socklen_t)sizeof(addrbuf)); + break; + default: + xlog(L_ERROR, "Unrecognized address family: %hu\n", + sa->sa_family); + return 0; + } + + if (addr == NULL) { + xlog(L_ERROR, "Unable to convert sockaddr to string: %s\n", + strerror(errno)); + return 0; + } + + /* + * Callers of this program should assume that in the future, extra + * fields may be added to the output. Anyone adding extra fields to + * the output should add them to the end of the line. + */ + printf("%s %s %s %s %s %d %d %d\n", + hostname, addr, cookiebuf, + m->mon_id.mon_name, + m->mon_id.my_id.my_name, + m->mon_id.my_id.my_prog, + m->mon_id.my_id.my_vers, + m->mon_id.my_id.my_proc); + + return 1; +} + +int +main(int argc, char **argv) +{ + xlog_syslog(0); + xlog_stderr(1); + xlog_open(argv[0]); + + nsm_load_monitor_list(dump_host); + return 0; +} diff --git a/tests/t0001-statd-basic-mon-unmon.sh b/tests/t0001-statd-basic-mon-unmon.sh new file mode 100644 index 0000000..00127fb --- /dev/null +++ b/tests/t0001-statd-basic-mon-unmon.sh @@ -0,0 +1,58 @@ +#!/bin/bash +# +# statd_basic_mon_unmon -- test basic mon/unmon functionality with statd +# +# Copyright (C) 2010 Red Hat, Jeff Layton +# +# 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. +# + +. ./test-lib.sh + +# This test needs root privileges +check_root + +start_statd +if [ $? -ne 0 ]; then + echo "FAIL: problem starting statd" + exit 1 +fi + +COOKIE=`echo $$ | md5sum | cut -d' ' -f1` +MON_NAME=`hostname` + +nsm_client mon $MON_NAME $COOKIE +if [ $? -ne 0 ]; then + echo "FAIL: mon failed" + kill_statd + exit 1 +fi + +statdb_dump | grep $MON_NAME | grep -q $COOKIE +if [ $? -ne 0 ]; then + echo "FAIL: monitor DB doesn't seem to contain entry" + kill_statd + exit 1 +fi + +nsm_client unmon $MON_NAME +if [ $? -ne 0 ]; then + echo "FAIL: unmon failed" + kill_statd + exit 1 +fi + +kill_statd + diff --git a/tests/test-lib.sh b/tests/test-lib.sh new file mode 100644 index 0000000..3d47264 --- /dev/null +++ b/tests/test-lib.sh @@ -0,0 +1,60 @@ +#!/bin/bash +# +# test-lib.sh -- library of functions for nfs-utils tests +# +# Copyright (C) 2010 Red Hat, Jeff Layton +# +# 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. +# + +# make sure $srcdir is set and sanity check it +srcdir=${srcdir-.} +if [ ! -d ${srcdir} ]; then + echo "***ERROR***: bad installation -- \$srcdir=${srcdir}" + exit 1 +fi + +export PATH=$PATH:${srcdir}:${srcdir}/nsm_client + +# Some tests require root privileges. Check for them and skip the test (exit 77) +# if the caller doesn't have them. +check_root() { + if [ $EUID -ne 0 ]; then + echo "*** Skipping this test as it requires root privs ***" + exit 77 + fi +} + +# is lockd registered as a service? +lockd_registered() { + rpcinfo -p | grep -q nlockmgr + return $? +} + +# start up statd +start_statd() { + rpcinfo -u 127.0.0.1 status 1 &> /dev/null + if [ $? -eq 0 ]; then + echo "***ERROR***: statd is already running and should " + echo " be down when starting this test" + return 1 + fi + $srcdir/../utils/statd/statd --no-notify +} + +# shut down statd +kill_statd() { + kill `cat /var/run/rpc.statd.pid` +} diff --git a/tools/Makefile.in b/tools/Makefile.in index 79aff24..231b77b 100644 --- a/tools/Makefile.in +++ b/tools/Makefile.in @@ -1,4 +1,4 @@ -# Makefile.in generated by automake 1.11 from Makefile.am. +# Makefile.in generated by automake 1.11.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, @@ -41,6 +41,7 @@ am__aclocal_m4_deps = $(top_srcdir)/aclocal/bsdsignals.m4 \ $(top_srcdir)/aclocal/ipv6.m4 \ $(top_srcdir)/aclocal/kerberos5.m4 \ $(top_srcdir)/aclocal/libblkid.m4 \ + $(top_srcdir)/aclocal/libcap.m4 \ $(top_srcdir)/aclocal/libevent.m4 \ $(top_srcdir)/aclocal/libnfsidmap.m4 \ $(top_srcdir)/aclocal/librpcsecgss.m4 \ @@ -155,6 +156,7 @@ LDFLAGS = @LDFLAGS@ LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@ LIBBLKID = @LIBBLKID@ LIBBSD = @LIBBSD@ +LIBCAP = @LIBCAP@ LIBCRYPT = @LIBCRYPT@ LIBNSL = @LIBNSL@ LIBOBJS = @LIBOBJS@ @@ -221,6 +223,7 @@ enable_ipv6 = @enable_ipv6@ enable_mountconfig = @enable_mountconfig@ enable_nfsv3 = @enable_nfsv3@ enable_nfsv4 = @enable_nfsv4@ +enable_nfsv41 = @enable_nfsv41@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ @@ -306,7 +309,7 @@ clean-libtool: # (which will cause the Makefiles to be regenerated when you run `make'); # (2) otherwise, pass the desired values on the `make' command line. $(RECURSIVE_TARGETS): - @failcom='exit 1'; \ + @fail= failcom='exit 1'; \ for f in x $$MAKEFLAGS; do \ case $$f in \ *=* | --[!k]*);; \ @@ -331,7 +334,7 @@ $(RECURSIVE_TARGETS): fi; test -z "$$fail" $(RECURSIVE_CLEAN_TARGETS): - @failcom='exit 1'; \ + @fail= failcom='exit 1'; \ for f in x $$MAKEFLAGS; do \ case $$f in \ *=* | --[!k]*);; \ diff --git a/tools/locktest/Makefile.in b/tools/locktest/Makefile.in index 31e6527..7afa042 100644 --- a/tools/locktest/Makefile.in +++ b/tools/locktest/Makefile.in @@ -1,4 +1,4 @@ -# Makefile.in generated by automake 1.11 from Makefile.am. +# Makefile.in generated by automake 1.11.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, @@ -42,6 +42,7 @@ am__aclocal_m4_deps = $(top_srcdir)/aclocal/bsdsignals.m4 \ $(top_srcdir)/aclocal/ipv6.m4 \ $(top_srcdir)/aclocal/kerberos5.m4 \ $(top_srcdir)/aclocal/libblkid.m4 \ + $(top_srcdir)/aclocal/libcap.m4 \ $(top_srcdir)/aclocal/libevent.m4 \ $(top_srcdir)/aclocal/libnfsidmap.m4 \ $(top_srcdir)/aclocal/librpcsecgss.m4 \ @@ -138,6 +139,7 @@ LDFLAGS = @LDFLAGS@ LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@ LIBBLKID = @LIBBLKID@ LIBBSD = @LIBBSD@ +LIBCAP = @LIBCAP@ LIBCRYPT = @LIBCRYPT@ LIBNSL = @LIBNSL@ LIBOBJS = @LIBOBJS@ @@ -204,6 +206,7 @@ enable_ipv6 = @enable_ipv6@ enable_mountconfig = @enable_mountconfig@ enable_nfsv3 = @enable_nfsv3@ enable_nfsv4 = @enable_nfsv4@ +enable_nfsv41 = @enable_nfsv41@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ diff --git a/tools/nlmtest/Makefile.in b/tools/nlmtest/Makefile.in index 6b59b27..b5d0a13 100644 --- a/tools/nlmtest/Makefile.in +++ b/tools/nlmtest/Makefile.in @@ -1,4 +1,4 @@ -# Makefile.in generated by automake 1.11 from Makefile.am. +# Makefile.in generated by automake 1.11.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, @@ -40,6 +40,7 @@ am__aclocal_m4_deps = $(top_srcdir)/aclocal/bsdsignals.m4 \ $(top_srcdir)/aclocal/ipv6.m4 \ $(top_srcdir)/aclocal/kerberos5.m4 \ $(top_srcdir)/aclocal/libblkid.m4 \ + $(top_srcdir)/aclocal/libcap.m4 \ $(top_srcdir)/aclocal/libevent.m4 \ $(top_srcdir)/aclocal/libnfsidmap.m4 \ $(top_srcdir)/aclocal/librpcsecgss.m4 \ @@ -114,6 +115,7 @@ LDFLAGS = @LDFLAGS@ LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@ LIBBLKID = @LIBBLKID@ LIBBSD = @LIBBSD@ +LIBCAP = @LIBCAP@ LIBCRYPT = @LIBCRYPT@ LIBNSL = @LIBNSL@ LIBOBJS = @LIBOBJS@ @@ -180,6 +182,7 @@ enable_ipv6 = @enable_ipv6@ enable_mountconfig = @enable_mountconfig@ enable_nfsv3 = @enable_nfsv3@ enable_nfsv4 = @enable_nfsv4@ +enable_nfsv41 = @enable_nfsv41@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ diff --git a/tools/rpcdebug/Makefile.in b/tools/rpcdebug/Makefile.in index 61cc0a4..455a88a 100644 --- a/tools/rpcdebug/Makefile.in +++ b/tools/rpcdebug/Makefile.in @@ -1,4 +1,4 @@ -# Makefile.in generated by automake 1.11 from Makefile.am. +# Makefile.in generated by automake 1.11.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, @@ -42,6 +42,7 @@ am__aclocal_m4_deps = $(top_srcdir)/aclocal/bsdsignals.m4 \ $(top_srcdir)/aclocal/ipv6.m4 \ $(top_srcdir)/aclocal/kerberos5.m4 \ $(top_srcdir)/aclocal/libblkid.m4 \ + $(top_srcdir)/aclocal/libcap.m4 \ $(top_srcdir)/aclocal/libevent.m4 \ $(top_srcdir)/aclocal/libnfsidmap.m4 \ $(top_srcdir)/aclocal/librpcsecgss.m4 \ @@ -163,6 +164,7 @@ LDFLAGS = @LDFLAGS@ LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@ LIBBLKID = @LIBBLKID@ LIBBSD = @LIBBSD@ +LIBCAP = @LIBCAP@ LIBCRYPT = @LIBCRYPT@ LIBNSL = @LIBNSL@ LIBOBJS = @LIBOBJS@ @@ -229,6 +231,7 @@ enable_ipv6 = @enable_ipv6@ enable_mountconfig = @enable_mountconfig@ enable_nfsv3 = @enable_nfsv3@ enable_nfsv4 = @enable_nfsv4@ +enable_nfsv41 = @enable_nfsv41@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ diff --git a/tools/rpcgen/Makefile.in b/tools/rpcgen/Makefile.in index 5350b85..4fe57a2 100644 --- a/tools/rpcgen/Makefile.in +++ b/tools/rpcgen/Makefile.in @@ -1,4 +1,4 @@ -# Makefile.in generated by automake 1.11 from Makefile.am. +# Makefile.in generated by automake 1.11.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, @@ -42,6 +42,7 @@ am__aclocal_m4_deps = $(top_srcdir)/aclocal/bsdsignals.m4 \ $(top_srcdir)/aclocal/ipv6.m4 \ $(top_srcdir)/aclocal/kerberos5.m4 \ $(top_srcdir)/aclocal/libblkid.m4 \ + $(top_srcdir)/aclocal/libcap.m4 \ $(top_srcdir)/aclocal/libevent.m4 \ $(top_srcdir)/aclocal/libnfsidmap.m4 \ $(top_srcdir)/aclocal/librpcsecgss.m4 \ @@ -143,6 +144,7 @@ LDFLAGS = @LDFLAGS@ LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@ LIBBLKID = @LIBBLKID@ LIBBSD = @LIBBSD@ +LIBCAP = @LIBCAP@ LIBCRYPT = @LIBCRYPT@ LIBNSL = @LIBNSL@ LIBOBJS = @LIBOBJS@ @@ -209,6 +211,7 @@ enable_ipv6 = @enable_ipv6@ enable_mountconfig = @enable_mountconfig@ enable_nfsv3 = @enable_nfsv3@ enable_nfsv4 = @enable_nfsv4@ +enable_nfsv41 = @enable_nfsv41@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ diff --git a/utils/Makefile.in b/utils/Makefile.in index 9340eea..56d87d2 100644 --- a/utils/Makefile.in +++ b/utils/Makefile.in @@ -1,4 +1,4 @@ -# Makefile.in generated by automake 1.11 from Makefile.am. +# Makefile.in generated by automake 1.11.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, @@ -43,6 +43,7 @@ am__aclocal_m4_deps = $(top_srcdir)/aclocal/bsdsignals.m4 \ $(top_srcdir)/aclocal/ipv6.m4 \ $(top_srcdir)/aclocal/kerberos5.m4 \ $(top_srcdir)/aclocal/libblkid.m4 \ + $(top_srcdir)/aclocal/libcap.m4 \ $(top_srcdir)/aclocal/libevent.m4 \ $(top_srcdir)/aclocal/libnfsidmap.m4 \ $(top_srcdir)/aclocal/librpcsecgss.m4 \ @@ -158,6 +159,7 @@ LDFLAGS = @LDFLAGS@ LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@ LIBBLKID = @LIBBLKID@ LIBBSD = @LIBBSD@ +LIBCAP = @LIBCAP@ LIBCRYPT = @LIBCRYPT@ LIBNSL = @LIBNSL@ LIBOBJS = @LIBOBJS@ @@ -224,6 +226,7 @@ enable_ipv6 = @enable_ipv6@ enable_mountconfig = @enable_mountconfig@ enable_nfsv3 = @enable_nfsv3@ enable_nfsv4 = @enable_nfsv4@ +enable_nfsv41 = @enable_nfsv41@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ @@ -317,7 +320,7 @@ clean-libtool: # (which will cause the Makefiles to be regenerated when you run `make'); # (2) otherwise, pass the desired values on the `make' command line. $(RECURSIVE_TARGETS): - @failcom='exit 1'; \ + @fail= failcom='exit 1'; \ for f in x $$MAKEFLAGS; do \ case $$f in \ *=* | --[!k]*);; \ @@ -342,7 +345,7 @@ $(RECURSIVE_TARGETS): fi; test -z "$$fail" $(RECURSIVE_CLEAN_TARGETS): - @failcom='exit 1'; \ + @fail= failcom='exit 1'; \ for f in x $$MAKEFLAGS; do \ case $$f in \ *=* | --[!k]*);; \ diff --git a/utils/exportfs/Makefile.in b/utils/exportfs/Makefile.in index 5c8352e..f1e0dee 100644 --- a/utils/exportfs/Makefile.in +++ b/utils/exportfs/Makefile.in @@ -1,4 +1,4 @@ -# Makefile.in generated by automake 1.11 from Makefile.am. +# Makefile.in generated by automake 1.11.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, @@ -42,6 +42,7 @@ am__aclocal_m4_deps = $(top_srcdir)/aclocal/bsdsignals.m4 \ $(top_srcdir)/aclocal/ipv6.m4 \ $(top_srcdir)/aclocal/kerberos5.m4 \ $(top_srcdir)/aclocal/libblkid.m4 \ + $(top_srcdir)/aclocal/libcap.m4 \ $(top_srcdir)/aclocal/libevent.m4 \ $(top_srcdir)/aclocal/libnfsidmap.m4 \ $(top_srcdir)/aclocal/librpcsecgss.m4 \ @@ -166,6 +167,7 @@ LDFLAGS = @LDFLAGS@ LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@ LIBBLKID = @LIBBLKID@ LIBBSD = @LIBBSD@ +LIBCAP = @LIBCAP@ LIBCRYPT = @LIBCRYPT@ LIBNSL = @LIBNSL@ LIBOBJS = @LIBOBJS@ @@ -232,6 +234,7 @@ enable_ipv6 = @enable_ipv6@ enable_mountconfig = @enable_mountconfig@ enable_nfsv3 = @enable_nfsv3@ enable_nfsv4 = @enable_nfsv4@ +enable_nfsv41 = @enable_nfsv41@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ diff --git a/utils/exportfs/exportfs.c b/utils/exportfs/exportfs.c index 593a8eb..331e57e 100644 --- a/utils/exportfs/exportfs.c +++ b/utils/exportfs/exportfs.c @@ -13,6 +13,7 @@ #endif #include +#include #include #include #include diff --git a/utils/gssd/Makefile.in b/utils/gssd/Makefile.in index 54778cd..d6e9dc0 100644 --- a/utils/gssd/Makefile.in +++ b/utils/gssd/Makefile.in @@ -1,4 +1,4 @@ -# Makefile.in generated by automake 1.11 from Makefile.am. +# Makefile.in generated by automake 1.11.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, @@ -43,6 +43,7 @@ am__aclocal_m4_deps = $(top_srcdir)/aclocal/bsdsignals.m4 \ $(top_srcdir)/aclocal/ipv6.m4 \ $(top_srcdir)/aclocal/kerberos5.m4 \ $(top_srcdir)/aclocal/libblkid.m4 \ + $(top_srcdir)/aclocal/libcap.m4 \ $(top_srcdir)/aclocal/libevent.m4 \ $(top_srcdir)/aclocal/libnfsidmap.m4 \ $(top_srcdir)/aclocal/librpcsecgss.m4 \ @@ -202,6 +203,7 @@ LDFLAGS = @LDFLAGS@ LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@ LIBBLKID = @LIBBLKID@ LIBBSD = @LIBBSD@ +LIBCAP = @LIBCAP@ LIBCRYPT = @LIBCRYPT@ LIBNSL = @LIBNSL@ LIBOBJS = @LIBOBJS@ @@ -268,6 +270,7 @@ enable_ipv6 = @enable_ipv6@ enable_mountconfig = @enable_mountconfig@ enable_nfsv3 = @enable_nfsv3@ enable_nfsv4 = @enable_nfsv4@ +enable_nfsv41 = @enable_nfsv41@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ diff --git a/utils/gssd/gssd.c b/utils/gssd/gssd.c index 40a2b4d..bd37a5f 100644 --- a/utils/gssd/gssd.c +++ b/utils/gssd/gssd.c @@ -56,7 +56,6 @@ #include "krb5_util.h" char pipefs_dir[PATH_MAX] = GSSD_PIPEFS_DIR; -char pipefs_nfsdir[PATH_MAX] = GSSD_PIPEFS_DIR; char keytabfile[PATH_MAX] = GSSD_DEFAULT_KEYTAB_FILE; char ccachedir[PATH_MAX] = GSSD_DEFAULT_CRED_DIR; char *ccachesearch[GSSD_MAX_CCACHE_SEARCH + 1]; @@ -159,11 +158,6 @@ main(int argc, char *argv[]) if (preferred_realm == NULL) gssd_k5_get_default_realm(&preferred_realm); - snprintf(pipefs_nfsdir, sizeof(pipefs_nfsdir), "%s/%s", - pipefs_dir, GSSD_SERVICE_NAME); - if (pipefs_nfsdir[sizeof(pipefs_nfsdir)-1] != '\0') - errx(1, "pipefs_nfsdir path name too long"); - if ((progname = strrchr(argv[0], '/'))) progname++; else diff --git a/utils/gssd/gssd.h b/utils/gssd/gssd.h index 3c52f46..465c305 100644 --- a/utils/gssd/gssd.h +++ b/utils/gssd/gssd.h @@ -60,7 +60,6 @@ enum {AUTHTYPE_KRB5, AUTHTYPE_SPKM3, AUTHTYPE_LIPKEY}; extern char pipefs_dir[PATH_MAX]; -extern char pipefs_nfsdir[PATH_MAX]; extern char keytabfile[PATH_MAX]; extern char *ccachesearch[]; extern int use_memcache; @@ -83,13 +82,24 @@ struct clnt_info { int krb5_poll_index; int spkm3_fd; int spkm3_poll_index; + int gssd_fd; + int gssd_poll_index; struct sockaddr_storage addr; }; +TAILQ_HEAD(topdirs_list_head, topdirs_info) topdirs_list; + +struct topdirs_info { + TAILQ_ENTRY(topdirs_info) list; + char *dirname; + int fd; +}; + void init_client_list(void); int update_client_list(void); void handle_krb5_upcall(struct clnt_info *clp); void handle_spkm3_upcall(struct clnt_info *clp); +void handle_gssd_upcall(struct clnt_info *clp); int gssd_acquire_cred(char *server_name); void gssd_run(void); diff --git a/utils/gssd/gssd_main_loop.c b/utils/gssd/gssd_main_loop.c index 917b662..f1a68d3 100644 --- a/utils/gssd/gssd_main_loop.c +++ b/utils/gssd/gssd_main_loop.c @@ -49,6 +49,7 @@ #include #include #include +#include #include "gssd.h" #include "err_util.h" @@ -73,6 +74,17 @@ scan_poll_results(int ret) for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next) { + i = clp->gssd_poll_index; + if (i >= 0 && pollarray[i].revents) { + if (pollarray[i].revents & POLLHUP) + dir_changed = 1; + if (pollarray[i].revents & POLLIN) + handle_gssd_upcall(clp); + pollarray[clp->gssd_poll_index].revents = 0; + ret--; + if (!ret) + break; + } i = clp->krb5_poll_index; if (i >= 0 && pollarray[i].revents) { if (pollarray[i].revents & POLLHUP) @@ -98,12 +110,85 @@ scan_poll_results(int ret) } }; +static int +topdirs_add_entry(struct dirent *dent) +{ + struct topdirs_info *tdi; + + tdi = calloc(sizeof(struct topdirs_info), 1); + if (tdi == NULL) { + printerr(0, "ERROR: Couldn't allocate struct topdirs_info\n"); + return -1; + } + tdi->dirname = malloc(PATH_MAX); + if (tdi->dirname == NULL) { + printerr(0, "ERROR: Couldn't allocate directory name\n"); + free(tdi); + return -1; + } + snprintf(tdi->dirname, PATH_MAX, "%s/%s", pipefs_dir, dent->d_name); + tdi->fd = open(tdi->dirname, O_RDONLY); + if (tdi->fd != -1) { + fcntl(tdi->fd, F_SETSIG, DNOTIFY_SIGNAL); + fcntl(tdi->fd, F_NOTIFY, + DN_CREATE|DN_DELETE|DN_MODIFY|DN_MULTISHOT); + } + + TAILQ_INSERT_HEAD(&topdirs_list, tdi, list); + return 0; +} + +static void +topdirs_free_list(void) +{ + struct topdirs_info *tdi; + + TAILQ_FOREACH(tdi, &topdirs_list, list) { + free(tdi->dirname); + if (tdi->fd != -1) + close(tdi->fd); + TAILQ_REMOVE(&topdirs_list, tdi, list); + free(tdi); + } +} + +static int +topdirs_init_list(void) +{ + DIR *pipedir; + struct dirent *dent; + int ret; + + TAILQ_INIT(&topdirs_list); + + pipedir = opendir(pipefs_dir); + if (pipedir == NULL) { + printerr(0, "ERROR: could not open rpc_pipefs directory '%s': " + "%s\n", pipefs_dir, strerror(errno)); + return -1; + } + for (dent = readdir(pipedir); dent != NULL; dent = readdir(pipedir)) { + if (dent->d_type != DT_DIR || + strcmp(dent->d_name, ".") == 0 || + strcmp(dent->d_name, "..") == 0) { + continue; + } + ret = topdirs_add_entry(dent); + if (ret) + goto out_err; + } + closedir(pipedir); + return 0; +out_err: + topdirs_free_list(); + return -1; +} + void gssd_run() { int ret; struct sigaction dn_act; - int fd; sigset_t set; /* Taken from linux/Documentation/dnotify.txt: */ @@ -117,13 +202,8 @@ gssd_run() sigaddset(&set, DNOTIFY_SIGNAL); sigprocmask(SIG_UNBLOCK, &set, NULL); - if ((fd = open(pipefs_nfsdir, O_RDONLY)) == -1) { - printerr(0, "ERROR: failed to open %s: %s\n", - pipefs_nfsdir, strerror(errno)); - exit(1); - } - fcntl(fd, F_SETSIG, DNOTIFY_SIGNAL); - fcntl(fd, F_NOTIFY, DN_CREATE|DN_DELETE|DN_MODIFY|DN_MULTISHOT); + if (topdirs_init_list() != 0) + return; init_client_list(); @@ -132,8 +212,7 @@ gssd_run() while (dir_changed) { dir_changed = 0; if (update_client_list()) { - printerr(0, "ERROR: couldn't update " - "client list\n"); + /* Error msg is already printed */ exit(1); } } @@ -151,6 +230,7 @@ gssd_run() scan_poll_results(ret); } } - close(fd); + topdirs_free_list(); + return; } diff --git a/utils/gssd/gssd_proc.c b/utils/gssd/gssd_proc.c index 37e2aa5..be4fb11 100644 --- a/utils/gssd/gssd_proc.c +++ b/utils/gssd/gssd_proc.c @@ -73,6 +73,7 @@ #include "krb5_util.h" #include "context.h" #include "nfsrpc.h" +#include "nfslib.h" /* * pollarray: @@ -83,20 +84,22 @@ * linked list of struct clnt_info which associates a clntXXX directory * with an index into pollarray[], and other basic data about that client. * - * Directory structure: created by the kernel nfs client - * {pipefs_nfsdir}/clntXX : one per rpc_clnt struct in the kernel - * {pipefs_nfsdir}/clntXX/krb5 : read uid for which kernel wants + * Directory structure: created by the kernel + * {rpc_pipefs}/{dir}/clntXX : one per rpc_clnt struct in the kernel + * {rpc_pipefs}/{dir}/clntXX/krb5 : read uid for which kernel wants * a context, write the resulting context - * {pipefs_nfsdir}/clntXX/info : stores info such as server name + * {rpc_pipefs}/{dir}/clntXX/info : stores info such as server name + * {rpc_pipefs}/{dir}/clntXX/gssd : pipe for all gss mechanisms using + * a text-based string of parameters * * Algorithm: - * Poll all {pipefs_nfsdir}/clntXX/krb5 files. When ready, data read - * is a uid; performs rpcsec_gss context initialization protocol to + * Poll all {rpc_pipefs}/{dir}/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 {pipefs_nfsdir} or in an of the clntXX directories, - * and rescan the whole {pipefs_nfsdir} when this happens. + * created or destroyed in {rpc_pipefs} or in any of the clntXX directories, + * and rescan the whole {rpc_pipefs} when this happens. */ struct pollfd * pollarray; @@ -105,7 +108,7 @@ int pollsize; /* the size of pollaray (in pollfd's) */ /* * convert a presentation address string to a sockaddr_storage struct. Returns - * true on success and false on failure. + * 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 @@ -117,26 +120,43 @@ int pollsize; /* the size of pollaray (in pollfd's) */ * not really feasible at present. */ static int -addrstr_to_sockaddr(struct sockaddr *sa, const char *addr, const int port) +addrstr_to_sockaddr(struct sockaddr *sa, const char *node, const char *port) { - struct sockaddr_in *s4 = (struct sockaddr_in *) sa; -#ifdef IPV6_SUPPORTED - struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) sa; -#endif /* IPV6_SUPPORTED */ + int rc; + struct addrinfo *res; + struct addrinfo hints = { .ai_flags = AI_NUMERICHOST | AI_NUMERICSERV }; - if (inet_pton(AF_INET, addr, &s4->sin_addr)) { - s4->sin_family = AF_INET; - s4->sin_port = htons(port); -#ifdef IPV6_SUPPORTED - } else if (inet_pton(AF_INET6, addr, &s6->sin6_addr)) { - s6->sin6_family = AF_INET6; - s6->sin6_port = htons(port); +#ifndef IPV6_SUPPORTED + hints.ai_family = AF_INET; #endif /* IPV6_SUPPORTED */ - } else { - printerr(0, "ERROR: unable to convert %s to address\n", addr); + + 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 0; } +#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); + freeaddrinfo(res); + return 0; + } + } +#endif /* IPV6_SUPPORTED */ + + memcpy(sa, res->ai_addr, res->ai_addrlen); + freeaddrinfo(res); return 1; } @@ -194,11 +214,10 @@ read_service_info(char *info_file_name, char **servicename, char **servername, char program[16]; char version[16]; char protoname[16]; - char cb_port[128]; + char port[128]; char *p; int fd = -1; int numfields; - int port = 0; *servicename = *servername = *protocol = NULL; @@ -227,20 +246,22 @@ read_service_info(char *info_file_name, char **servicename, char **servername, goto fail; } - cb_port[0] = '\0'; + port[0] = '\0'; if ((p = strstr(buf, "port")) != NULL) - sscanf(p, "port: %127s\n", cb_port); + sscanf(p, "port: %127s\n", port); /* check service, program, and version */ - if(memcmp(service, "nfs", 3)) return -1; + if (memcmp(service, "nfs", 3) != 0) + return -1; *prog = atoi(program + 1); /* skip open paren */ *vers = atoi(version); - if((*prog != 100003) || ((*vers != 2) && (*vers != 3) && (*vers != 4))) - goto fail; - if (cb_port[0] != '\0') { - port = atoi(cb_port); - if (port < 0 || port > 65535) + if (strlen(service) == 3 ) { + if ((*prog != 100003) || ((*vers != 2) && (*vers != 3) && + (*vers != 4))) + goto fail; + } else if (memcmp(service, "nfs4_cb", 7) == 0) { + if (*vers != 1) goto fail; } @@ -281,9 +302,13 @@ destroy_client(struct clnt_info *clp) if (clp->spkm3_poll_index != -1) memset(&pollarray[clp->spkm3_poll_index], 0, sizeof(struct pollfd)); + if (clp->gssd_poll_index != -1) + memset(&pollarray[clp->gssd_poll_index], 0, + sizeof(struct pollfd)); if (clp->dir_fd != -1) close(clp->dir_fd); if (clp->krb5_fd != -1) close(clp->krb5_fd); if (clp->spkm3_fd != -1) close(clp->spkm3_fd); + if (clp->gssd_fd != -1) close(clp->gssd_fd); free(clp->dirname); free(clp->servicename); free(clp->servername); @@ -303,8 +328,10 @@ insert_new_clnt(void) } clp->krb5_poll_index = -1; clp->spkm3_poll_index = -1; + clp->gssd_poll_index = -1; clp->krb5_fd = -1; clp->spkm3_fd = -1; + clp->gssd_fd = -1; clp->dir_fd = -1; TAILQ_INSERT_HEAD(&clnt_list, clp, list); @@ -315,19 +342,43 @@ out: static int process_clnt_dir_files(struct clnt_info * clp) { - char kname[32]; - char sname[32]; - char info_file_name[32]; + char name[PATH_MAX]; + char gname[PATH_MAX]; + char info_file_name[PATH_MAX]; - if (clp->krb5_fd == -1) { - snprintf(kname, sizeof(kname), "%s/krb5", clp->dirname); - clp->krb5_fd = open(kname, O_RDWR); + if (clp->gssd_fd == -1) { + snprintf(gname, sizeof(gname), "%s/gssd", clp->dirname); + clp->gssd_fd = open(gname, O_RDWR); } - if (clp->spkm3_fd == -1) { - snprintf(sname, sizeof(sname), "%s/spkm3", clp->dirname); - clp->spkm3_fd = open(sname, O_RDWR); + if (clp->gssd_fd == -1) { + if (clp->krb5_fd == -1) { + snprintf(name, sizeof(name), "%s/krb5", clp->dirname); + clp->krb5_fd = open(name, O_RDWR); + } + if (clp->spkm3_fd == -1) { + snprintf(name, sizeof(name), "%s/spkm3", clp->dirname); + clp->spkm3_fd = open(name, O_RDWR); + } + + /* If we opened a gss-specific pipe, let's try opening + * the new upcall pipe again. If we succeed, close + * gss-specific pipe(s). + */ + if (clp->krb5_fd != -1 || clp->spkm3_fd != -1) { + clp->gssd_fd = open(gname, O_RDWR); + if (clp->gssd_fd != -1) { + if (clp->krb5_fd != -1) + close(clp->krb5_fd); + clp->krb5_fd = -1; + if (clp->spkm3_fd != -1) + close(clp->spkm3_fd); + clp->spkm3_fd = -1; + } + } } - if((clp->krb5_fd == -1) && (clp->spkm3_fd == -1)) + + if ((clp->krb5_fd == -1) && (clp->spkm3_fd == -1) && + (clp->gssd_fd == -1)) return -1; snprintf(info_file_name, sizeof(info_file_name), "%s/info", clp->dirname); @@ -362,6 +413,15 @@ get_poll_index(int *ind) static int insert_clnt_poll(struct clnt_info *clp) { + if ((clp->gssd_fd != -1) && (clp->gssd_poll_index == -1)) { + if (get_poll_index(&clp->gssd_poll_index)) { + printerr(0, "ERROR: Too many gssd clients\n"); + return -1; + } + pollarray[clp->gssd_poll_index].fd = clp->gssd_fd; + pollarray[clp->gssd_poll_index].events |= POLLIN; + } + if ((clp->krb5_fd != -1) && (clp->krb5_poll_index == -1)) { if (get_poll_index(&clp->krb5_poll_index)) { printerr(0, "ERROR: Too many krb5 clients\n"); @@ -384,17 +444,18 @@ insert_clnt_poll(struct clnt_info *clp) } static void -process_clnt_dir(char *dir) +process_clnt_dir(char *dir, char *pdir) { struct clnt_info * clp; if (!(clp = insert_new_clnt())) goto fail_destroy_client; - if (!(clp->dirname = calloc(strlen(dir) + 1, 1))) { + /* An extra for the '/', and an extra for the null */ + if (!(clp->dirname = calloc(strlen(dir) + strlen(pdir) + 2, 1))) { goto fail_destroy_client; } - memcpy(clp->dirname, dir, strlen(dir)); + sprintf(clp->dirname, "%s/%s", pdir, dir); if ((clp->dir_fd = open(clp->dirname, O_RDONLY)) == -1) { printerr(0, "ERROR: can't open %s: %s\n", clp->dirname, strerror(errno)); @@ -438,16 +499,24 @@ init_client_list(void) * directories, since the DNOTIFY could have been in there. */ static void -update_old_clients(struct dirent **namelist, int size) +update_old_clients(struct dirent **namelist, int size, char *pdir) { struct clnt_info *clp; void *saveprev; int i, stillhere; + char fname[PATH_MAX]; for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next) { + /* only compare entries in the global list that are from the + * same pipefs parent directory as "pdir" + */ + if (strncmp(clp->dirname, pdir, strlen(pdir)) != 0) continue; + stillhere = 0; for (i=0; i < size; i++) { - if (!strcmp(clp->dirname, namelist[i]->d_name)) { + snprintf(fname, sizeof(fname), "%s/%s", + pdir, namelist[i]->d_name); + if (strcmp(clp->dirname, fname) == 0) { stillhere = 1; break; } @@ -468,48 +537,69 @@ update_old_clients(struct dirent **namelist, int size) /* Search for a client by directory name, return 1 if found, 0 otherwise */ static int -find_client(char *dirname) +find_client(char *dirname, char *pdir) { struct clnt_info *clp; + char fname[PATH_MAX]; - for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next) - if (!strcmp(clp->dirname, dirname)) + for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next) { + snprintf(fname, sizeof(fname), "%s/%s", pdir, dirname); + if (strcmp(clp->dirname, fname) == 0) return 1; + } return 0; } -/* Used to read (and re-read) list of clients, set up poll array. */ -int -update_client_list(void) +static int +process_pipedir(char *pipe_name) { struct dirent **namelist; int i, j; - if (chdir(pipefs_nfsdir) < 0) { + if (chdir(pipe_name) < 0) { printerr(0, "ERROR: can't chdir to %s: %s\n", - pipefs_nfsdir, strerror(errno)); + pipe_name, strerror(errno)); return -1; } - j = scandir(pipefs_nfsdir, &namelist, NULL, alphasort); + j = scandir(pipe_name, &namelist, NULL, alphasort); if (j < 0) { printerr(0, "ERROR: can't scandir %s: %s\n", - pipefs_nfsdir, strerror(errno)); + pipe_name, strerror(errno)); return -1; } - update_old_clients(namelist, j); + + update_old_clients(namelist, j, pipe_name); for (i=0; i < j; i++) { if (i < FD_ALLOC_BLOCK && !strncmp(namelist[i]->d_name, "clnt", 4) - && !find_client(namelist[i]->d_name)) - process_clnt_dir(namelist[i]->d_name); + && !find_client(namelist[i]->d_name, pipe_name)) + process_clnt_dir(namelist[i]->d_name, pipe_name); free(namelist[i]); } free(namelist); + return 0; } +/* Used to read (and re-read) list of clients, set up poll array. */ +int +update_client_list(void) +{ + int retval = -1; + struct topdirs_info *tdi; + + TAILQ_FOREACH(tdi, &topdirs_list, list) { + retval = process_pipedir(tdi->dirname); + if (retval) + printerr(1, "WARNING: error processing %s\n", + tdi->dirname); + + } + return retval; +} + static int do_downcall(int k5_fd, uid_t uid, struct authgss_private_data *pd, gss_buffer_desc *context_token) @@ -798,15 +888,14 @@ int create_auth_rpc_client(struct clnt_info *clp, goto out; } - /* * this code uses the userland rpcsec gss library to create a krb5 * context on behalf of the kernel */ -void -handle_krb5_upcall(struct clnt_info *clp) +static void +process_krb5_upcall(struct clnt_info *clp, uid_t uid, int fd, char *tgtname, + char *service) { - uid_t uid; CLIENT *rpc_clnt = NULL; AUTH *auth = NULL; struct authgss_private_data pd; @@ -815,23 +904,51 @@ handle_krb5_upcall(struct clnt_info *clp) char **ccname; char **dirname; int create_resp = -1; + int err, downcall_err = -EACCES; - printerr(1, "handling krb5 upcall\n"); + printerr(1, "handling krb5 upcall (%s)\n", clp->dirname); + if (tgtname) { + if (clp->servicename) { + free(clp->servicename); + clp->servicename = strdup(tgtname); + } + } token.length = 0; token.value = NULL; memset(&pd, 0, sizeof(struct authgss_private_data)); - if (read(clp->krb5_fd, &uid, sizeof(uid)) < sizeof(uid)) { - printerr(0, "WARNING: failed reading uid from krb5 " - "upcall pipe: %s\n", strerror(errno)); - goto out; - } - - if (uid != 0 || (uid == 0 && root_uses_machine_creds == 0)) { + /* + * 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. + * + */ + printerr(2, "%s: service is '%s'\n", __func__, + service ? service : ""); + if (uid != 0 || (uid == 0 && root_uses_machine_creds == 0 && + service == NULL)) { /* Tell krb5 gss which credentials cache to use */ for (dirname = ccachesearch; *dirname != NULL; dirname++) { - if (gssd_setup_krb5_user_gss_ccache(uid, clp->servername, *dirname) == 0) + err = gssd_setup_krb5_user_gss_ccache(uid, clp->servername, *dirname); + if (err == -EKEYEXPIRED) + downcall_err = -EKEYEXPIRED; + else if (!err) create_resp = create_auth_rpc_client(clp, &rpc_clnt, &auth, uid, AUTHTYPE_KRB5); if (create_resp == 0) @@ -839,12 +956,13 @@ handle_krb5_upcall(struct clnt_info *clp) } } if (create_resp != 0) { - if (uid == 0 && root_uses_machine_creds == 1) { + if (uid == 0 && (root_uses_machine_creds == 1 || + service != NULL)) { int nocache = 0; int success = 0; do { gssd_refresh_krb5_machine_credential(clp->servername, - NULL, nocache); + NULL, service); /* * Get a list of credential cache names and try each * of them until one works or we've tried them all @@ -904,7 +1022,7 @@ handle_krb5_upcall(struct clnt_info *clp) goto out_return_error; } - do_downcall(clp->krb5_fd, uid, &pd, &token); + do_downcall(fd, uid, &pd, &token); out: if (token.value) @@ -920,7 +1038,7 @@ out: return; out_return_error: - do_error_downcall(clp->krb5_fd, uid, -1); + do_error_downcall(fd, uid, downcall_err); goto out; } @@ -928,26 +1046,19 @@ out_return_error: * this code uses the userland rpcsec gss library to create an spkm3 * context on behalf of the kernel */ -void -handle_spkm3_upcall(struct clnt_info *clp) +static void +process_spkm3_upcall(struct clnt_info *clp, uid_t uid, int fd) { - uid_t uid; CLIENT *rpc_clnt = NULL; AUTH *auth = NULL; struct authgss_private_data pd; gss_buffer_desc token; - printerr(2, "handling spkm3 upcall\n"); + printerr(2, "handling spkm3 upcall (%s)\n", clp->dirname); token.length = 0; token.value = NULL; - if (read(clp->spkm3_fd, &uid, sizeof(uid)) < sizeof(uid)) { - printerr(0, "WARNING: failed reading uid from spkm3 " - "upcall pipe: %s\n", strerror(errno)); - goto out; - } - if (create_auth_rpc_client(clp, &rpc_clnt, &auth, uid, AUTHTYPE_SPKM3)) { printerr(0, "WARNING: Failed to create spkm3 context for " "user with uid %d\n", uid); @@ -968,7 +1079,7 @@ handle_spkm3_upcall(struct clnt_info *clp) goto out_return_error; } - do_downcall(clp->spkm3_fd, uid, &pd, &token); + do_downcall(fd, uid, &pd, &token); out: if (token.value) @@ -980,6 +1091,139 @@ out: return; out_return_error: - do_error_downcall(clp->spkm3_fd, uid, -1); + do_error_downcall(fd, uid, -1); goto out; } + +void +handle_krb5_upcall(struct clnt_info *clp) +{ + uid_t uid; + + if (read(clp->krb5_fd, &uid, sizeof(uid)) < sizeof(uid)) { + printerr(0, "WARNING: failed reading uid from krb5 " + "upcall pipe: %s\n", strerror(errno)); + return; + } + + return process_krb5_upcall(clp, uid, clp->krb5_fd, NULL, NULL); +} + +void +handle_spkm3_upcall(struct clnt_info *clp) +{ + uid_t uid; + + if (read(clp->spkm3_fd, &uid, sizeof(uid)) < sizeof(uid)) { + printerr(0, "WARNING: failed reading uid from spkm3 " + "upcall pipe: %s\n", strerror(errno)); + return; + } + + return process_spkm3_upcall(clp, uid, clp->spkm3_fd); +} + +void +handle_gssd_upcall(struct clnt_info *clp) +{ + uid_t uid; + char *lbuf = NULL; + int lbuflen = 0; + char *p; + char *mech = NULL; + char *target = NULL; + char *service = NULL; + + printerr(1, "handling gssd upcall (%s)\n", clp->dirname); + + if (readline(clp->gssd_fd, &lbuf, &lbuflen) != 1) { + printerr(0, "WARNING: handle_gssd_upcall: " + "failed reading request\n"); + return; + } + printerr(2, "%s: '%s'\n", __func__, lbuf); + + /* find the mechanism name */ + if ((p = strstr(lbuf, "mech=")) != NULL) { + mech = malloc(lbuflen); + if (!mech) + goto out; + if (sscanf(p, "mech=%s", mech) != 1) { + printerr(0, "WARNING: handle_gssd_upcall: " + "failed to parse gss mechanism name " + "in upcall string '%s'\n", lbuf); + goto out; + } + } else { + printerr(0, "WARNING: handle_gssd_upcall: " + "failed to find gss mechanism name " + "in upcall string '%s'\n", lbuf); + goto out; + } + + /* read uid */ + if ((p = strstr(lbuf, "uid=")) != NULL) { + if (sscanf(p, "uid=%d", &uid) != 1) { + printerr(0, "WARNING: handle_gssd_upcall: " + "failed to parse uid " + "in upcall string '%s'\n", lbuf); + goto out; + } + } else { + printerr(0, "WARNING: handle_gssd_upcall: " + "failed to find uid " + "in upcall string '%s'\n", lbuf); + goto out; + } + + /* read target name */ + if ((p = strstr(lbuf, "target=")) != NULL) { + target = malloc(lbuflen); + if (!target) + goto out; + if (sscanf(p, "target=%s", target) != 1) { + printerr(0, "WARNING: handle_gssd_upcall: " + "failed to parse target name " + "in upcall string '%s'\n", lbuf); + goto out; + } + } + + /* + * read the service name + * + * 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 ((p = strstr(lbuf, "service=")) != NULL) { + service = malloc(lbuflen); + if (!service) + goto out; + if (sscanf(p, "service=%s", service) != 1) { + printerr(0, "WARNING: handle_gssd_upcall: " + "failed to parse service type " + "in upcall string '%s'\n", lbuf); + goto out; + } + } + + if (strcmp(mech, "krb5") == 0) + process_krb5_upcall(clp, uid, clp->gssd_fd, target, service); + else if (strcmp(mech, "spkm3") == 0) + process_spkm3_upcall(clp, uid, clp->gssd_fd); + else + printerr(0, "WARNING: handle_gssd_upcall: " + "received unknown gss mech '%s'\n", mech); + +out: + free(lbuf); + free(mech); + free(target); + free(service); + return; +} + diff --git a/utils/gssd/krb5_util.c b/utils/gssd/krb5_util.c index 78e9775..1295f57 100644 --- a/utils/gssd/krb5_util.c +++ b/utils/gssd/krb5_util.c @@ -170,9 +170,8 @@ select_krb5_ccache(const struct dirent *d) * what we want. Otherwise, return zero and no dirent pointer. * The caller is responsible for freeing the dirent if one is returned. * - * Returns: - * 0 => could not find an existing entry - * 1 => found an existing entry + * Returns 0 if a valid-looking entry was found and a non-zero error + * code otherwise. */ static int gssd_find_existing_krb5_ccache(uid_t uid, char *dirname, struct dirent **d) @@ -186,7 +185,7 @@ gssd_find_existing_krb5_ccache(uid_t uid, char *dirname, struct dirent **d) char buf[1030]; char *princname = NULL; char *realm = NULL; - int score, best_match_score = 0; + int score, best_match_score = 0, err = -EACCES; memset(&best_match_stat, 0, sizeof(best_match_stat)); *d = NULL; @@ -229,6 +228,7 @@ gssd_find_existing_krb5_ccache(uid_t uid, char *dirname, struct dirent **d) printerr(3, "CC file '%s' is expired or corrupt\n", statname); free(namelist[i]); + err = -EKEYEXPIRED; continue; } @@ -284,11 +284,12 @@ gssd_find_existing_krb5_ccache(uid_t uid, char *dirname, struct dirent **d) } free(namelist); } - if (found) - { + if (found) { *d = best_match_dir; + return 0; } - return found; + + return err; } @@ -797,10 +798,9 @@ gssd_search_krb5_keytab(krb5_context context, krb5_keytab kt, */ static int find_keytab_entry(krb5_context context, krb5_keytab kt, const char *hostname, - krb5_keytab_entry *kte) + krb5_keytab_entry *kte, const char **svcnames) { krb5_error_code code; - const char *svcnames[] = { "root", "nfs", "host", NULL }; char **realmnames = NULL; char myhostname[NI_MAXHOST], targethostname[NI_MAXHOST]; int i, j, retval; @@ -1025,29 +1025,29 @@ err_cache: * given only a UID. We really need more information, but we * do the best we can. * - * Returns: - * 0 => a ccache was found - * 1 => no ccache was found + * Returns 0 if a ccache was found, and a non-zero error code otherwise. */ int gssd_setup_krb5_user_gss_ccache(uid_t uid, char *servername, char *dirname) { char buf[MAX_NETOBJ_SZ]; struct dirent *d; + int err; printerr(2, "getting credentials for client with uid %u for " "server %s\n", uid, servername); memset(buf, 0, sizeof(buf)); - if (gssd_find_existing_krb5_ccache(uid, dirname, &d)) { - snprintf(buf, sizeof(buf), "FILE:%s/%s", dirname, d->d_name); - free(d); - } - else - return 1; + err = gssd_find_existing_krb5_ccache(uid, dirname, &d); + if (err) + return err; + + snprintf(buf, sizeof(buf), "FILE:%s/%s", 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); gssd_set_krb5_ccache_name(buf); - return 0; + return err; } /* @@ -1096,7 +1096,8 @@ gssd_get_krb5_machine_cred_list(char ***list) for (ple = gssd_k5_kt_princ_list; ple; ple = ple->next) { if (ple->ccname) { /* Make sure cred is up-to-date before returning it */ - retval = gssd_refresh_krb5_machine_credential(NULL, ple, 0); + retval = gssd_refresh_krb5_machine_credential(NULL, ple, + NULL); if (retval) continue; if (i + 1 > listsize) { @@ -1186,14 +1187,24 @@ gssd_destroy_krb5_machine_creds(void) */ int gssd_refresh_krb5_machine_credential(char *hostname, - struct gssd_k5_kt_princ *ple, int nocache) + struct gssd_k5_kt_princ *ple, + char *service) { krb5_error_code code = 0; krb5_context context; krb5_keytab kt = NULL;; int retval = 0; char *k5err = NULL; + const char *svcnames[4] = { "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) return EINVAL; @@ -1216,7 +1227,7 @@ gssd_refresh_krb5_machine_credential(char *hostname, if (ple == NULL) { krb5_keytab_entry kte; - code = find_keytab_entry(context, kt, hostname, &kte); + code = find_keytab_entry(context, kt, hostname, &kte, svcnames); if (code) { printerr(0, "ERROR: %s: no usable keytab entry found " "in keytab %s for connection with host %s\n", @@ -1241,7 +1252,7 @@ gssd_refresh_krb5_machine_credential(char *hostname, goto out; } } - retval = gssd_get_single_krb5_cred(context, kt, ple, nocache); + retval = gssd_get_single_krb5_cred(context, kt, ple, 0); out: if (kt) krb5_kt_close(context, kt); diff --git a/utils/gssd/krb5_util.h b/utils/gssd/krb5_util.h index 4b6b281..4602cc3 100644 --- a/utils/gssd/krb5_util.h +++ b/utils/gssd/krb5_util.h @@ -30,7 +30,8 @@ void gssd_free_krb5_machine_cred_list(char **list); void gssd_setup_krb5_machine_gss_ccache(char *servername); void gssd_destroy_krb5_machine_creds(void); int gssd_refresh_krb5_machine_credential(char *hostname, - struct gssd_k5_kt_princ *ple, int nocache); + struct gssd_k5_kt_princ *ple, + char *service); char *gssd_k5_err_msg(krb5_context context, krb5_error_code code); void gssd_k5_get_default_realm(char **def_realm); diff --git a/utils/gssd/svcgssd_proc.c b/utils/gssd/svcgssd_proc.c index 6f2ba61..f1bfbef 100644 --- a/utils/gssd/svcgssd_proc.c +++ b/utils/gssd/svcgssd_proc.c @@ -56,6 +56,7 @@ #include "gss_util.h" #include "err_util.h" #include "context.h" +#include "gss_oids.h" extern char * mech2file(gss_OID mech); #define SVCGSSD_CONTEXT_CHANNEL "/proc/net/rpc/auth.rpcsec.context/channel" @@ -73,7 +74,7 @@ struct svc_cred { 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) + int32_t endtime, char *client_name) { FILE *f; int i; @@ -98,9 +99,10 @@ do_svc_downcall(gss_buffer_desc *out_handle, struct svc_cred *cred, qword_printint(f, cred->cr_gid); qword_printint(f, cred->cr_ngroups); printerr(2, "mech: %s, hndl len: %d, ctx len %d, timeout: %d (%d from now), " - "uid: %d, gid: %d, num aux grps: %d:\n", + "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 : "", cred->cr_uid, cred->cr_gid, cred->cr_ngroups); for (i=0; i < cred->cr_ngroups; i++) { qword_printint(f, cred->cr_groups[i]); @@ -108,6 +110,8 @@ do_svc_downcall(gss_buffer_desc *out_handle, struct svc_cred *cred, } qword_print(f, fname); qword_printhex(f, context_token->value, context_token->length); + if (client_name) + qword_print(f, client_name); err = qword_eol(f); if (err) { printerr(1, "WARNING: error writing to downcall channel " @@ -307,6 +311,75 @@ print_hexl(const char *description, unsigned char *cp, int length) } #endif +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; +} + +static 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) + *hostbased_name = cname; + } + + /* No support for SPKM3, just print a warning (for now) */ + if (g_OID_equal(&spkm3oid, mech)) { + printerr(1, "WARNING: get_hostbased_client_name: " + "no hostbased_name support for SPKM3\n"); + } + + res = 0; +out_rel_buf: + gss_release_buffer(&min_stat, &name); +out_err: + return res; +} + void handle_nullreq(FILE *f) { /* XXX initialize to a random integer to reduce chances of unnecessary @@ -325,7 +398,7 @@ handle_nullreq(FILE *f) { null_token = {.value = NULL}; u_int32_t ret_flags; gss_ctx_id_t ctx = GSS_C_NO_CONTEXT; - gss_name_t client_name; + 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; @@ -334,6 +407,7 @@ handle_nullreq(FILE *f) { static int lbuflen = 0; static char *cp; int32_t ctx_endtime; + char *hostbased_name = NULL; printerr(1, "handling null request\n"); @@ -396,11 +470,13 @@ handle_nullreq(FILE *f) { if (get_ids(client_name, mech, &cred)) { /* get_ids() prints error msg */ maj_stat = GSS_S_BAD_NAME; /* XXX ? */ - gss_release_name(&ignore_min_stat, &client_name); goto out_err; } - gss_release_name(&ignore_min_stat, &client_name); - + 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. */ @@ -419,7 +495,8 @@ handle_nullreq(FILE *f) { /* 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); + do_svc_downcall(&out_handle, &cred, mech, &ctx_token, ctx_endtime, + hostbased_name); continue_needed: send_response(f, &in_handle, &in_tok, maj_stat, min_stat, &out_handle, &out_tok); @@ -428,6 +505,9 @@ out: 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; diff --git a/utils/idmapd/Makefile.am b/utils/idmapd/Makefile.am index 4dabb3d..4218048 100644 --- a/utils/idmapd/Makefile.am +++ b/utils/idmapd/Makefile.am @@ -1,6 +1,5 @@ ## Process this file with automake to produce Makefile.in -man5_MANS = idmapd.conf.man man8_MANS = idmapd.man RPCPREFIX = rpc. @@ -8,7 +7,6 @@ KPREFIX = @kprefix@ sbin_PROGRAMS = idmapd EXTRA_DIST = \ - $(man5_MANS) \ $(man8_MANS) \ idmapd.conf @@ -48,8 +46,8 @@ uninstall-hook: # XXX This makes some assumptions about what automake does. # XXX But there is no install-man-hook or install-man-local. -install-man: install-man5 install-man8 install-man-links -uninstall-man: uninstall-man5 uninstall-man8 uninstall-man-links +install-man: install-man8 install-man-links +uninstall-man: uninstall-man8 uninstall-man-links install-man-links: (cd $(DESTDIR)$(man8dir) && \ diff --git a/utils/idmapd/Makefile.in b/utils/idmapd/Makefile.in index e47f0a2..3d2ffff 100644 --- a/utils/idmapd/Makefile.in +++ b/utils/idmapd/Makefile.in @@ -1,4 +1,4 @@ -# Makefile.in generated by automake 1.11 from Makefile.am. +# Makefile.in generated by automake 1.11.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, @@ -42,6 +42,7 @@ am__aclocal_m4_deps = $(top_srcdir)/aclocal/bsdsignals.m4 \ $(top_srcdir)/aclocal/ipv6.m4 \ $(top_srcdir)/aclocal/kerberos5.m4 \ $(top_srcdir)/aclocal/libblkid.m4 \ + $(top_srcdir)/aclocal/libcap.m4 \ $(top_srcdir)/aclocal/libevent.m4 \ $(top_srcdir)/aclocal/libnfsidmap.m4 \ $(top_srcdir)/aclocal/librpcsecgss.m4 \ @@ -56,8 +57,7 @@ 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)" +am__installdirs = "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man8dir)" PROGRAMS = $(sbin_PROGRAMS) am_idmapd_OBJECTS = atomicio.$(OBJEXT) idmapd.$(OBJEXT) \ strlcat.$(OBJEXT) strlcpy.$(OBJEXT) @@ -99,10 +99,9 @@ am__nobase_list = $(am__nobase_strip_setup); \ am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' -man5dir = $(mandir)/man5 man8dir = $(mandir)/man8 NROFF = nroff -MANS = $(man5_MANS) $(man8_MANS) +MANS = $(man8_MANS) ETAGS = etags CTAGS = ctags DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) @@ -163,6 +162,7 @@ LDFLAGS = @LDFLAGS@ LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@ LIBBLKID = @LIBBLKID@ LIBBSD = @LIBBSD@ +LIBCAP = @LIBCAP@ LIBCRYPT = @LIBCRYPT@ LIBNSL = @LIBNSL@ LIBOBJS = @LIBOBJS@ @@ -229,6 +229,7 @@ enable_ipv6 = @enable_ipv6@ enable_mountconfig = @enable_mountconfig@ enable_nfsv3 = @enable_nfsv3@ enable_nfsv4 = @enable_nfsv4@ +enable_nfsv41 = @enable_nfsv41@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ @@ -264,12 +265,10 @@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ -man5_MANS = idmapd.conf.man man8_MANS = idmapd.man RPCPREFIX = rpc. KPREFIX = @kprefix@ EXTRA_DIST = \ - $(man5_MANS) \ $(man8_MANS) \ idmapd.conf @@ -403,40 +402,6 @@ mostlyclean-libtool: clean-libtool: -rm -rf .libs _libs -install-man5: $(man5_MANS) - @$(NORMAL_INSTALL) - test -z "$(man5dir)" || $(MKDIR_P) "$(DESTDIR)$(man5dir)" - @list='$(man5_MANS)'; test -n "$(man5dir)" || exit 0; \ - { for i in $$list; do echo "$$i"; done; \ - } | 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,.,'`; \ - test -z "$$files" || { \ - echo " ( cd '$(DESTDIR)$(man5dir)' && rm -f" $$files ")"; \ - cd "$(DESTDIR)$(man5dir)" && rm -f $$files; } install-man8: $(man8_MANS) @$(NORMAL_INSTALL) test -z "$(man8dir)" || $(MKDIR_P) "$(DESTDIR)$(man8dir)" @@ -571,7 +536,7 @@ check-am: all-am check: check-am all-am: Makefile $(PROGRAMS) $(MANS) installdirs: - for dir in "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man5dir)" "$(DESTDIR)$(man8dir)"; do \ + for dir in "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man8dir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am @@ -680,14 +645,13 @@ uninstall-am: uninstall-man uninstall-sbinPROGRAMS 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 uninstall uninstall-am uninstall-hook \ - uninstall-man uninstall-man5 uninstall-man8 \ - uninstall-sbinPROGRAMS + 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 uninstall uninstall-am uninstall-hook uninstall-man \ + uninstall-man8 uninstall-sbinPROGRAMS ####################################################################### @@ -711,8 +675,8 @@ uninstall-hook: # XXX This makes some assumptions about what automake does. # XXX But there is no install-man-hook or install-man-local. -install-man: install-man5 install-man8 install-man-links -uninstall-man: uninstall-man5 uninstall-man8 uninstall-man-links +install-man: install-man8 install-man-links +uninstall-man: uninstall-man8 uninstall-man-links install-man-links: (cd $(DESTDIR)$(man8dir) && \ diff --git a/utils/idmapd/idmapd.conf.man b/utils/idmapd/idmapd.conf.man deleted file mode 100644 index 02722b1..0000000 --- a/utils/idmapd/idmapd.conf.man +++ /dev/null @@ -1,74 +0,0 @@ -.\" $OpenBSD: mdoc.template,v 1.6 2001/02/03 08:22:44 niklas Exp $ -.\" -.\" The following requests are required for all man pages. -.Dd July 16, 2003 -.Dt idmapd.conf 5 -.Os -.Sh NAME -.Nm idmapd.conf -.Nd configuration file for idmapd, the NFSv4 ID <-> Name Mapper -.Sh SYNOPSIS -Configuration file for idmapd, the NFSv4 ID <-> Name Mapper -.Sh DESCRIPTION -The -.Nm -configuration file has two sections, initiated by the strings -[General] and [Mapping]. Each section may contain lines of the form -.Dl "" -.Dl variable = value -.Dl "" -The variables allowed in the General section are -.Va Verbosity, -.Va Pipefs-Directory, -and -.Va Domain, -whose values have the same effect as the arguments to the -.Fl v, -.Fl p, -and -.Fl d -commandline options, respectively. The variables allowed in the -Mapping section are -.Va Nobody-User -and -.Va Nobody-Group, -which have the same effect as the -.Fl U -and -.Fl G -commandline options. -' -.Sh EXAMPLES -' -An example -.Pa /etc/idmapd.conf -file: -' -.Bd -literal -[General] - -Verbosity = 0 -Pipefs-Directory = /var/lib/nfs/rpc_pipefs -Domain = localdomain - -[Mapping] - -Nobody-User = nobody -Nobody-Group = nobody -.Ed -' -.Sh SEE ALSO -.Xr idmapd 8 -.\".Sh SEE ALSO -.\".Xr nylon.conf 4 -.\" .Sh COMPATIBILITY -.\".Sh STANDARDS -.\".Sh ACKNOWLEDGEMENTS -.Sh AUTHORS -The idmapd 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/mount/Makefile.in b/utils/mount/Makefile.in index e5a366e..d24c26f 100644 --- a/utils/mount/Makefile.in +++ b/utils/mount/Makefile.in @@ -1,4 +1,4 @@ -# Makefile.in generated by automake 1.11 from Makefile.am. +# Makefile.in generated by automake 1.11.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, @@ -45,6 +45,7 @@ am__aclocal_m4_deps = $(top_srcdir)/aclocal/bsdsignals.m4 \ $(top_srcdir)/aclocal/ipv6.m4 \ $(top_srcdir)/aclocal/kerberos5.m4 \ $(top_srcdir)/aclocal/libblkid.m4 \ + $(top_srcdir)/aclocal/libcap.m4 \ $(top_srcdir)/aclocal/libevent.m4 \ $(top_srcdir)/aclocal/libnfsidmap.m4 \ $(top_srcdir)/aclocal/librpcsecgss.m4 \ @@ -176,6 +177,7 @@ LDFLAGS = @LDFLAGS@ LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@ LIBBLKID = @LIBBLKID@ LIBBSD = @LIBBSD@ +LIBCAP = @LIBCAP@ LIBCRYPT = @LIBCRYPT@ LIBNSL = @LIBNSL@ LIBOBJS = @LIBOBJS@ @@ -242,6 +244,7 @@ enable_ipv6 = @enable_ipv6@ enable_mountconfig = @enable_mountconfig@ enable_nfsv3 = @enable_nfsv3@ enable_nfsv4 = @enable_nfsv4@ +enable_nfsv41 = @enable_nfsv41@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ diff --git a/utils/mount/configfile.c b/utils/mount/configfile.c index 28b722c..5cff009 100644 --- a/utils/mount/configfile.c +++ b/utils/mount/configfile.c @@ -194,18 +194,36 @@ void free_all(void) static char *versions[] = {"v2", "v3", "v4", "vers", "nfsvers", NULL}; int inline check_vers(char *mopt, char *field) { - int i; + int i, found=0; - if (strncmp("mountvers", field, strlen("mountvers")) != 0) { - for (i=0; versions[i]; i++) - if (strcasestr(mopt, versions[i]) != NULL) - return 1; + /* + * First check to see if the config setting is one + * of the many version settings + */ + for (i=0; versions[i]; i++) { + if (strcasestr(field, versions[i]) != NULL) { + found++; + break; + } + } + if (!found) + return 0; + /* + * It appears the version is being set, now see + * if the version appears on the command + */ + for (i=0; versions[i]; i++) { + if (strcasestr(mopt, versions[i]) != NULL) + return 1; } + return 0; } unsigned long 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 @@ -227,6 +245,10 @@ int inline default_value(char *mopt) 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"); } diff --git a/utils/mount/mount.c b/utils/mount/mount.c index 355df79..82b9169 100644 --- a/utils/mount/mount.c +++ b/utils/mount/mount.c @@ -24,6 +24,7 @@ #include #include +#include #include #include #include @@ -593,6 +594,9 @@ int main(int argc, char *argv[]) if (mnt_err == EX_BG) { printf(_("%s: backgrounding \"%s\"\n"), progname, spec); + printf(_("%s: mount options: \"%s\"\n"), + progname, extra_opts); + fflush(stdout); /* diff --git a/utils/mount/network.c b/utils/mount/network.c index 7b1152a..8dc183a 100644 --- a/utils/mount/network.c +++ b/utils/mount/network.c @@ -37,11 +37,13 @@ #include #include #include +#include #include #include #include #include +#include "sockaddr.h" #include "xcommon.h" #include "mount.h" #include "nls.h" @@ -56,10 +58,6 @@ #define CONNECT_TIMEOUT (20) #define MOUNT_TIMEOUT (30) -#if SIZEOF_SOCKLEN_T - 0 == 0 -#define socklen_t unsigned int -#endif - extern int nfs_mount_data_version; extern char *progname; extern int verbose; @@ -193,8 +191,18 @@ static const unsigned int *nfs_default_proto() } #endif /* MOUNT_CONFIG */ -static int nfs_lookup(const char *hostname, const sa_family_t family, - struct sockaddr *sap, socklen_t *salen) +/** + * 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 = { @@ -242,25 +250,6 @@ static int nfs_lookup(const char *hostname, const sa_family_t family, return ret; } -/** - * nfs_name_to_address - resolve hostname to an IPv4 or IPv6 socket address - * @hostname: pointer to C string containing DNS hostname to resolve - * @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_name_to_address(const char *hostname, - struct sockaddr *sap, socklen_t *salen) -{ -#ifdef IPV6_SUPPORTED - return nfs_lookup(hostname, AF_UNSPEC, sap, salen); -#else /* !IPV6_SUPPORTED */ - return nfs_lookup(hostname, AF_INET, sap, salen); -#endif /* !IPV6_SUPPORTED */ -} - /** * nfs_gethostbyname - resolve a hostname to an IPv4 address * @hostname: pointer to a C string containing a DNS hostname @@ -283,8 +272,8 @@ int nfs_gethostbyname(const char *hostname, struct sockaddr_in *sin) * OUT: length of converted socket address * * Convert a presentation format address string to a socket address. - * Similar to nfs_name_to_address(), but the DNS query is squelched, - * and won't make any noise if the getaddrinfo() call fails. + * 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. * @@ -549,8 +538,8 @@ static int nfs_probe_port(const struct sockaddr *sap, const socklen_t salen, struct pmap *pmap, const unsigned long *versions, const unsigned int *protos) { - struct sockaddr_storage address; - struct sockaddr *saddr = (struct sockaddr *)&address; + 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; @@ -840,8 +829,8 @@ int start_statd(void) int nfs_advise_umount(const struct sockaddr *sap, const socklen_t salen, const struct pmap *pmap, const dirpath *argp) { - struct sockaddr_storage address; - struct sockaddr *saddr = (struct sockaddr *)&address; + union nfs_sockaddr address; + struct sockaddr *saddr = &address.sa; struct pmap mnt_pmap = *pmap; struct timeval timeout = { .tv_sec = MOUNT_TIMEOUT >> 3, @@ -1284,11 +1273,13 @@ nfs_nfs_version(struct mount_options *options, unsigned long *version) /* * Returns TRUE if @protocol contains a valid value for this option, - * or FALSE if the option was specified with an invalid value. + * 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)) { @@ -1300,16 +1291,12 @@ nfs_nfs_protocol(struct mount_options *options, unsigned long *protocol) return 1; case 2: /* proto */ option = po_get(options, "proto"); - if (option) { - if (strcmp(option, "tcp") == 0) { - *protocol = IPPROTO_TCP; - return 1; - } - if (strcmp(option, "udp") == 0) { - *protocol = IPPROTO_UDP; - return 1; + if (option != NULL) { + if (!nfs_get_proto(option, &family, protocol)) { + errno = EPROTONOSUPPORT; + return 0; } - return 0; + return 1; } } @@ -1351,6 +1338,61 @@ nfs_nfs_port(struct mount_options *options, unsigned long *port) return 1; } +#ifdef IPV6_SUPPORTED +sa_family_t config_default_family = AF_UNSPEC; + +static int +nfs_verify_family(sa_family_t 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 */ + /* for compatibility; these are always AF_INET */ + *family = AF_INET; + return 1; + case 2: /* proto */ + option = po_get(options, "proto"); + if (option != NULL && + !nfs_get_proto(option, &tmp_family, &protocol)) + goto out_err; + } + + 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. @@ -1414,24 +1456,22 @@ nfs_mount_version(struct mount_options *options, unsigned long *version) /* * Returns TRUE if @protocol contains a valid value for this option, - * or FALSE if the option was specified with an invalid value. + * 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) { - if (strcmp(option, "tcp") == 0) { - *protocol = IPPROTO_TCP; - return 1; - } - if (strcmp(option, "udp") == 0) { - *protocol = IPPROTO_UDP; - return 1; + if (option != NULL) { + if (!nfs_get_proto(option, &family, protocol)) { + errno = EPROTONOSUPPORT; + return 0; } - return 0; + return 1; } /* @@ -1472,6 +1512,40 @@ nfs_mount_port(struct mount_options *options, unsigned long *port) 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)) + 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 diff --git a/utils/mount/network.h b/utils/mount/network.h index 7eb89b0..2a3a110 100644 --- a/utils/mount/network.h +++ b/utils/mount/network.h @@ -44,7 +44,8 @@ int nfs_probe_bothports(const struct sockaddr *, const socklen_t, struct pmap *, const struct sockaddr *, const socklen_t, struct pmap *); int nfs_gethostbyname(const char *, struct sockaddr_in *); -int nfs_name_to_address(const char *, struct sockaddr *, socklen_t *); +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); @@ -56,6 +57,8 @@ int clnt_ping(struct sockaddr_in *, const unsigned long, struct mount_options; +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(struct mount_options *options, unsigned long *version); int nfs_nfs_protocol(struct mount_options *options, unsigned long *protocol); diff --git a/utils/mount/nfs.man b/utils/mount/nfs.man index 2299637..c64de5f 100644 --- a/utils/mount/nfs.man +++ b/utils/mount/nfs.man @@ -58,9 +58,17 @@ 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, -or a dotted quad IPv4 address. +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 either "nfs" (for version 2 or version 3 NFS mounts) @@ -355,7 +363,11 @@ The number of minutes that the 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). +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. .TP 1.5i .BI sec= mode The RPCGSS security flavor to use for accessing files on this mount point. @@ -470,32 +482,38 @@ for mounting the .B nfs file system type. .TP 1.5i -.BI proto= transport -The transport the NFS client uses +.BI proto= netid +The transport protocol name and protocol family the NFS client uses to transmit requests to the NFS server for this mount point. -.I transport -can be either -.B udp -or -.BR tcp . -Each transport uses different default +If an NFS server has both an IPv4 and an IPv6 address, using a specific +netid will force the use of IPv4 or IPv6 networking to communicate +with that server. +.IP +If support for TI-RPC is built into the +.B mount.nfs +command, +.I netid +is a valid netid listed in +.IR /etc/netconfig . +Otherwise, +.I netid +is one of "tcp," "udp," or "rdma," and only IPv4 may be used. +.IP +Each transport protocol uses different default .B retrans and .B timeo -settings; refer to the description of these two mount options for details. +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 -.B proto=tcp -forces all traffic from the +Specifying a netid that uses TCP forces all traffic from the .BR mount (8) command and the NFS client to use TCP. -Specifying -.B proto=udp -forces all traffic types to use UDP. +Specifying a netid that uses UDP forces all traffic types to use UDP. .IP If the .B proto @@ -548,15 +566,20 @@ or the server's mountd service is not available on the advertised port. This option can be used when mounting an NFS server through a firewall that blocks the rpcbind protocol. .TP 1.5i -.BI mountproto= transport -The transport the NFS client uses +.BI mountproto= netid +The transport protocol name and protocol family 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. -.I transport -can be either -.B udp -or -.BR tcp . +.IP +If support for TI-RPC is built into the +.B mount.nfs +command, +.I netid +is a valid netid listed in +.IR /etc/netconfig . +Otherwise, +.I netid +is one of "tcp" or "udp," and only IPv4 may be used. .IP This option can be used when mounting an NFS server through a firewall that blocks a particular transport. @@ -566,6 +589,7 @@ 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 @@ -709,17 +733,26 @@ for mounting the .B nfs4 file system type. .TP 1.5i -.BI proto= transport -The transport the NFS client uses +.BI proto= netid +The transport protocol name and protocol family the NFS client uses to transmit requests to the NFS server for this mount point. -.I transport -can be either -.B udp -or -.BR tcp . +If an NFS server has both an IPv4 and an IPv6 address, using a specific +netid will force the use of IPv4 or IPv6 networking to communicate +with that server. +.IP +If support for TI-RPC is built into the +.B mount.nfs +command, +.I netid +is a valid netid listed in +.IR /etc/netconfig . +Otherwise, +.I netid +is one of "tcp" or "udp," and only IPv4 may be used. +.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 transport protocol. +uses the TCP protocol. Refer to the TRANSPORT METHODS section for more details. .TP 1.5i .BI port= n @@ -779,7 +812,8 @@ The DATA AND METADATA COHERENCE section discusses the behavior of this option in more detail. .TP 1.5i .BI clientaddr= n.n.n.n -Specifies a single IPv4 address (in dotted-quad form) +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 callback requests against files on this mount point. If the server is unable to @@ -855,6 +889,14 @@ This example can be used to mount /usr over NFS. .TA 2.5i +0.7i +0.7i +.7i 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 2.5i +0.7i +0.7i +.7i + [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 @@ -1498,6 +1540,8 @@ such as security negotiation, server referrals, and named attributes. .BR mount.nfs (5), .BR umount.nfs (5), .BR exports (5), +.BR netconfig (5), +.BR ipv6 (7), .BR nfsd (8), .BR sm-notify (8), .BR rpc.statd (8), diff --git a/utils/mount/nfs4mount.c b/utils/mount/nfs4mount.c index a2f318f..4a2fab7 100644 --- a/utils/mount/nfs4mount.c +++ b/utils/mount/nfs4mount.c @@ -217,8 +217,11 @@ int nfs4mount(const char *spec, const char *node, int flags, progname); goto fail; } - snprintf(new_opts, sizeof(new_opts), "%s%saddr=%s", - old_opts, *old_opts ? "," : "", s); + if (running_bg) + strncpy(new_opts, old_opts, sizeof(new_opts)); + else + snprintf(new_opts, sizeof(new_opts), "%s%saddr=%s", + old_opts, *old_opts ? "," : "", s); *extra_opts = xstrdup(new_opts); /* Set default options. @@ -434,15 +437,17 @@ int nfs4mount(const char *spec, const char *node, int flags, break; } - switch(rpc_createerr.cf_stat){ - case RPC_TIMEDOUT: - break; - case RPC_SYSTEMERROR: - if (errno == ETIMEDOUT) + if (!bg) { + switch(rpc_createerr.cf_stat) { + case RPC_TIMEDOUT: break; - default: - rpc_mount_errors(hostname, 0, bg); - goto fail; + case RPC_SYSTEMERROR: + if (errno == ETIMEDOUT) + break; + default: + rpc_mount_errors(hostname, 0, bg); + goto fail; + } } if (bg && !running_bg) { diff --git a/utils/mount/nfsmount.c b/utils/mount/nfsmount.c index 6355681..6b3356c 100644 --- a/utils/mount/nfsmount.c +++ b/utils/mount/nfsmount.c @@ -170,7 +170,7 @@ parse_options(char *old_opts, struct nfs_mount_data *data, struct pmap *mnt_pmap = &mnt_server->pmap; struct pmap *nfs_pmap = &nfs_server->pmap; int len; - char *opt, *opteq, *p, *opt_b; + char *opt, *opteq, *p, *opt_b, *tmp_opts; char *mounthost = NULL; char cbuf[128]; int open_quote = 0; @@ -179,7 +179,8 @@ parse_options(char *old_opts, struct nfs_mount_data *data, *bg = 0; len = strlen(new_opts); - for (p=old_opts, opt_b=NULL; p && *p; p++) { + 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 == '"') @@ -457,10 +458,12 @@ parse_options(char *old_opts, struct nfs_mount_data *data, 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); return 0; } diff --git a/utils/mount/nfsumount.c b/utils/mount/nfsumount.c index c5505b1..9d798a2 100644 --- a/utils/mount/nfsumount.c +++ b/utils/mount/nfsumount.c @@ -169,10 +169,15 @@ out: static int nfs_umount_do_umnt(struct mount_options *options, char **hostname, char **dirname) { - struct sockaddr_storage address; - struct sockaddr *sap = (struct sockaddr *)&address; + union { + struct sockaddr sa; + struct sockaddr_in s4; + struct sockaddr_in6 s6; + } 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)) { nfs_error(_("%s: bad mount options"), progname); @@ -189,8 +194,10 @@ static int nfs_umount_do_umnt(struct mount_options *options, return EX_FAIL; } - if (nfs_name_to_address(*hostname, sap, &salen) == 0) - /* nfs_name_to_address reports any errors */ + 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) diff --git a/utils/mount/stropts.c b/utils/mount/stropts.c index b595649..9b8c38f 100644 --- a/utils/mount/stropts.c +++ b/utils/mount/stropts.c @@ -35,9 +35,11 @@ #include #include +#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" @@ -47,6 +49,10 @@ #include "parse_dev.h" #include "conffile.h" +#ifndef HAVE_DECL_AI_ADDRCONFIG +#define AI_ADDRCONFIG 0 +#endif + #ifndef NFS_PROGRAM #define NFS_PROGRAM (100003) #endif @@ -81,8 +87,7 @@ struct nfsmount_info { *node, /* mounted-on dir */ *type; /* "nfs" or "nfs4" */ char *hostname; /* server's hostname */ - struct sockaddr_storage address; /* server's address */ - socklen_t salen; /* size of server's address */ + struct addrinfo *address; /* server's addresses */ struct mount_options *options; /* parsed mount options */ char **extra_opts; /* string for /etc/mtab */ @@ -204,9 +209,9 @@ static int nfs_append_clientaddr_option(const struct sockaddr *sap, socklen_t salen, struct mount_options *options) { - struct sockaddr_storage dummy; - struct sockaddr *my_addr = (struct sockaddr *)&dummy; - socklen_t my_len = sizeof(dummy); + union nfs_sockaddr address; + struct sockaddr *my_addr = &address.sa; + socklen_t my_len = sizeof(address); if (po_contains(options, "clientaddr") == PO_FOUND) return 1; @@ -218,21 +223,33 @@ static int nfs_append_clientaddr_option(const struct sockaddr *sap, } /* - * Resolve the 'mounthost=' hostname and append a new option using - * the resulting address. + * 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) +static int nfs_fix_mounthost_option(struct mount_options *options, + const char *nfs_hostname) { - struct sockaddr_storage dummy; - struct sockaddr *sap = (struct sockaddr *)&dummy; - socklen_t salen = sizeof(dummy); + 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) - return 1; + if (mounthost == NULL) { + if (nfs_family == mnt_family) + return 1; + mounthost = (char *)nfs_hostname; + } - if (!nfs_name_to_address(mounthost, sap, &salen)) { + if (!nfs_lookup(mounthost, mnt_family, sap, &salen)) { nfs_error(_("%s: unable to determine mount server's address"), progname); return 0; @@ -319,22 +336,36 @@ static int nfs_set_version(struct nfsmount_info *mi) */ static int nfs_validate_options(struct nfsmount_info *mi) { - struct sockaddr *sap = (struct sockaddr *)&mi->address; + struct addrinfo hint = { + .ai_protocol = (int)IPPROTO_UDP, + .ai_flags = AI_ADDRCONFIG, + }; + sa_family_t family; + int error; if (!nfs_parse_devname(mi->spec, &mi->hostname, NULL)) return 0; - mi->salen = sizeof(mi->address); - if (!nfs_name_to_address(mi->hostname, sap, &mi->salen)) + if (!nfs_nfs_proto_family(mi->options, &family)) return 0; + hint.ai_family = (int)family; + error = getaddrinfo(mi->hostname, NULL, &hint, &mi->address); + if (error != 0) { + nfs_error(_("%s: Failed to resolve server %s: %s"), + progname, mi->hostname, gai_strerror(error)); + mi->address = NULL; + return 0; + } + if (!nfs_set_version(mi)) return 0; if (!nfs_append_sloppy_option(mi->options)) return 0; - if (!nfs_append_addr_option(sap, mi->salen, mi->options)) + if (!nfs_append_addr_option(mi->address->ai_addr, + mi->address->ai_addrlen, mi->options)) return 0; return 1; @@ -371,10 +402,13 @@ static int nfs_extract_server_addresses(struct mount_options *options, } 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"); @@ -391,20 +425,14 @@ static int nfs_construct_new_options(struct mount_options *options, po_remove_all(options, "proto"); po_remove_all(options, "udp"); po_remove_all(options, "tcp"); - switch (nfs_pmap->pm_prot) { - case IPPROTO_TCP: - snprintf(new_option, sizeof(new_option) - 1, - "proto=tcp"); - if (po_append(options, new_option) == PO_FAILED) - return 0; - break; - case IPPROTO_UDP: - snprintf(new_option, sizeof(new_option) - 1, - "proto=udp"); - if (po_append(options, new_option) == PO_FAILED) - return 0; - break; - } + 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; po_remove_all(options, "port"); if (nfs_pmap->pm_port != NFS_PORT) { @@ -421,20 +449,14 @@ static int nfs_construct_new_options(struct mount_options *options, return 0; po_remove_all(options, "mountproto"); - switch (mnt_pmap->pm_prot) { - case IPPROTO_TCP: - snprintf(new_option, sizeof(new_option) - 1, - "mountproto=tcp"); - if (po_append(options, new_option) == PO_FAILED) - return 0; - break; - case IPPROTO_UDP: - snprintf(new_option, sizeof(new_option) - 1, - "mountproto=udp"); - if (po_append(options, new_option) == PO_FAILED) - return 0; - break; - } + 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, @@ -461,12 +483,12 @@ static int nfs_construct_new_options(struct mount_options *options, static int nfs_rewrite_pmap_mount_options(struct mount_options *options) { - struct sockaddr_storage nfs_address; - struct sockaddr *nfs_saddr = (struct sockaddr *)&nfs_address; + union nfs_sockaddr nfs_address; + struct sockaddr *nfs_saddr = &nfs_address.sa; socklen_t nfs_salen = sizeof(nfs_address); struct pmap nfs_pmap; - struct sockaddr_storage mnt_address; - struct sockaddr *mnt_saddr = (struct sockaddr *)&mnt_address; + union nfs_sockaddr mnt_address; + struct sockaddr *mnt_saddr = &mnt_address.sa; socklen_t mnt_salen = sizeof(mnt_address); struct pmap mnt_pmap; char *option; @@ -507,10 +529,15 @@ nfs_rewrite_pmap_mount_options(struct mount_options *options) if (!nfs_probe_bothports(mnt_saddr, mnt_salen, &mnt_pmap, nfs_saddr, nfs_salen, &nfs_pmap)) { errno = ESPIPE; + if (rpc_createerr.cf_stat == RPC_PROGNOTREGISTERED) + errno = EOPNOTSUPP; + 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_pmap, &mnt_pmap)) { + if (!nfs_construct_new_options(options, nfs_saddr, &nfs_pmap, + mnt_saddr, &mnt_pmap)) { errno = EINVAL; return 0; } @@ -536,10 +563,6 @@ static int nfs_sys_mount(struct nfsmount_info *mi, struct mount_options *opts) return 0; } - if (verbose) - printf(_("%s: trying text-based options '%s'\n"), - progname, options); - if (mi->fake) return 1; @@ -553,10 +576,8 @@ static int nfs_sys_mount(struct nfsmount_info *mi, struct mount_options *opts) return !result; } -/* - * For "-t nfs vers=2" or "-t nfs vers=3" mounts. - */ -static int nfs_try_mount_v3v2(struct nfsmount_info *mi) +static int nfs_do_mount_v3v2(struct nfsmount_info *mi, + struct sockaddr *sap, socklen_t salen) { struct mount_options *options = po_dup(mi->options); int result = 0; @@ -566,7 +587,12 @@ static int nfs_try_mount_v3v2(struct nfsmount_info *mi) return result; } - if (!nfs_fix_mounthost_option(options)) { + if (!nfs_append_addr_option(sap, salen, options)) { + errno = EINVAL; + goto out_fail; + } + + if (!nfs_fix_mounthost_option(options, mi->hostname)) { errno = EINVAL; goto out_fail; } @@ -586,6 +612,10 @@ static int nfs_try_mount_v3v2(struct nfsmount_info *mi) goto out_fail; } + if (verbose) + printf(_("%s: trying text-based options '%s'\n"), + progname, *mi->extra_opts); + if (!nfs_rewrite_pmap_mount_options(options)) goto out_fail; @@ -597,11 +627,36 @@ out_fail: } /* - * For "-t nfs -o vers=4" or "-t nfs4" mounts. + * 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_v4(struct nfsmount_info *mi) +static int nfs_try_mount_v3v2(struct nfsmount_info *mi) +{ + struct addrinfo *ai; + int ret; + + for (ai = mi->address; ai != NULL; ai = ai->ai_next) { + ret = nfs_do_mount_v3v2(mi, ai->ai_addr, ai->ai_addrlen); + if (ret != 0) + return ret; + + switch (errno) { + case ECONNREFUSED: + case EOPNOTSUPP: + case EHOSTUNREACH: + continue; + default: + break; + } + } + return ret; +} + +static int nfs_do_mount_v4(struct nfsmount_info *mi, + struct sockaddr *sap, socklen_t salen) { - struct sockaddr *sap = (struct sockaddr *)&mi->address; struct mount_options *options = po_dup(mi->options); int result = 0; @@ -611,13 +666,30 @@ static int nfs_try_mount_v4(struct nfsmount_info *mi) } if (mi->version == 0) { + if (po_contains(options, "mounthost") || + po_contains(options, "mountaddr") || + po_contains(options, "mountvers") || + 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. + */ + errno = EPROTONOSUPPORT; + goto out_fail; + } if (po_append(options, "vers=4") == PO_FAILED) { errno = EINVAL; goto out_fail; } } - if (!nfs_append_clientaddr_option(sap, mi->salen, options)) { + 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; } @@ -630,6 +702,10 @@ static int nfs_try_mount_v4(struct nfsmount_info *mi) goto out_fail; } + if (verbose) + printf(_("%s: trying text-based options '%s'\n"), + progname, *mi->extra_opts); + result = nfs_sys_mount(mi, options); out_fail: @@ -637,6 +713,33 @@ out_fail: 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; + + 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: + continue; + default: + break; + } + } + return ret; +} + /* * This is a single pass through the fg/bg loop. * @@ -656,9 +759,10 @@ static int nfs_try_mount(struct nfsmount_info *mi) /* * To deal with legacy Linux servers that don't * automatically export a pseudo root, retry - * ENOENT errors using version 3 + * ENOENT errors using version 3. And for + * Linux servers prior to 2.6.25, retry EPERM */ - if (errno != ENOENT) + if (errno != ENOENT && errno != EPERM) break; } } @@ -856,6 +960,7 @@ int nfsmount_string(const char *spec, const char *node, const char *type, struct nfsmount_info mi = { .spec = spec, .node = node, + .address = NULL, .type = type, .extra_opts = extra_opts, .flags = flags, @@ -871,6 +976,7 @@ int nfsmount_string(const char *spec, const char *node, const char *type, } else nfs_error(_("%s: internal option parsing error"), progname); + freeaddrinfo(mi.address); free(mi.hostname); return retval; } diff --git a/utils/mountd/Makefile.am b/utils/mountd/Makefile.am index 1e76cf8..eba81fc 100644 --- a/utils/mountd/Makefile.am +++ b/utils/mountd/Makefile.am @@ -8,7 +8,7 @@ KPREFIX = @kprefix@ sbin_PROGRAMS = mountd mountd_SOURCES = mountd.c mount_dispatch.c auth.c rmtab.c cache.c \ - svc_run.c fsloc.c mountd.h + svc_run.c fsloc.c v4root.c mountd.h mountd_LDADD = ../../support/export/libexport.a \ ../../support/nfs/libnfs.a \ ../../support/misc/libmisc.a \ diff --git a/utils/mountd/Makefile.in b/utils/mountd/Makefile.in index bab01c7..34a6dca 100644 --- a/utils/mountd/Makefile.in +++ b/utils/mountd/Makefile.in @@ -1,4 +1,4 @@ -# Makefile.in generated by automake 1.11 from Makefile.am. +# Makefile.in generated by automake 1.11.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, @@ -42,6 +42,7 @@ am__aclocal_m4_deps = $(top_srcdir)/aclocal/bsdsignals.m4 \ $(top_srcdir)/aclocal/ipv6.m4 \ $(top_srcdir)/aclocal/kerberos5.m4 \ $(top_srcdir)/aclocal/libblkid.m4 \ + $(top_srcdir)/aclocal/libcap.m4 \ $(top_srcdir)/aclocal/libevent.m4 \ $(top_srcdir)/aclocal/libnfsidmap.m4 \ $(top_srcdir)/aclocal/librpcsecgss.m4 \ @@ -61,7 +62,8 @@ PROGRAMS = $(sbin_PROGRAMS) am_mountd_OBJECTS = mountd-mountd.$(OBJEXT) \ mountd-mount_dispatch.$(OBJEXT) mountd-auth.$(OBJEXT) \ mountd-rmtab.$(OBJEXT) mountd-cache.$(OBJEXT) \ - mountd-svc_run.$(OBJEXT) mountd-fsloc.$(OBJEXT) + mountd-svc_run.$(OBJEXT) mountd-fsloc.$(OBJEXT) \ + mountd-v4root.$(OBJEXT) mountd_OBJECTS = $(am_mountd_OBJECTS) am__DEPENDENCIES_1 = mountd_DEPENDENCIES = ../../support/export/libexport.a \ @@ -167,6 +169,7 @@ LDFLAGS = @LDFLAGS@ LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@ LIBBLKID = @LIBBLKID@ LIBBSD = @LIBBSD@ +LIBCAP = @LIBCAP@ LIBCRYPT = @LIBCRYPT@ LIBNSL = @LIBNSL@ LIBOBJS = @LIBOBJS@ @@ -233,6 +236,7 @@ enable_ipv6 = @enable_ipv6@ enable_mountconfig = @enable_mountconfig@ enable_nfsv3 = @enable_nfsv3@ enable_nfsv4 = @enable_nfsv4@ +enable_nfsv41 = @enable_nfsv41@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ @@ -273,7 +277,7 @@ EXTRA_DIST = $(man8_MANS) RPCPREFIX = rpc. KPREFIX = @kprefix@ mountd_SOURCES = mountd.c mount_dispatch.c auth.c rmtab.c cache.c \ - svc_run.c fsloc.c mountd.h + svc_run.c fsloc.c v4root.c mountd.h mountd_LDADD = ../../support/export/libexport.a \ ../../support/nfs/libnfs.a \ @@ -379,6 +383,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mountd-mountd.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mountd-rmtab.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mountd-svc_run.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mountd-v4root.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @@ -499,6 +504,20 @@ mountd-fsloc.obj: fsloc.c @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mountd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mountd-fsloc.obj `if test -f 'fsloc.c'; then $(CYGPATH_W) 'fsloc.c'; else $(CYGPATH_W) '$(srcdir)/fsloc.c'; fi` +mountd-v4root.o: v4root.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mountd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mountd-v4root.o -MD -MP -MF $(DEPDIR)/mountd-v4root.Tpo -c -o mountd-v4root.o `test -f 'v4root.c' || echo '$(srcdir)/'`v4root.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/mountd-v4root.Tpo $(DEPDIR)/mountd-v4root.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='v4root.c' object='mountd-v4root.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mountd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mountd-v4root.o `test -f 'v4root.c' || echo '$(srcdir)/'`v4root.c + +mountd-v4root.obj: v4root.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mountd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mountd-v4root.obj -MD -MP -MF $(DEPDIR)/mountd-v4root.Tpo -c -o mountd-v4root.obj `if test -f 'v4root.c'; then $(CYGPATH_W) 'v4root.c'; else $(CYGPATH_W) '$(srcdir)/v4root.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/mountd-v4root.Tpo $(DEPDIR)/mountd-v4root.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='v4root.c' object='mountd-v4root.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mountd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mountd-v4root.obj `if test -f 'v4root.c'; then $(CYGPATH_W) 'v4root.c'; else $(CYGPATH_W) '$(srcdir)/v4root.c'; fi` + mostlyclean-libtool: -rm -f *.lo diff --git a/utils/mountd/auth.c b/utils/mountd/auth.c index 575f207..13eba70 100644 --- a/utils/mountd/auth.c +++ b/utils/mountd/auth.c @@ -20,6 +20,7 @@ #include "exportfs.h" #include "mountd.h" #include "xmalloc.h" +#include "v4root.h" enum auth_error { @@ -102,75 +103,91 @@ auth_reload() memset(&my_client, 0, sizeof(my_client)); xtab_export_read(); check_useipaddr(); + v4root_set(); + ++counter; return counter; } +static char *get_client_hostname(struct sockaddr_in *caller, struct hostent *hp, enum auth_error *error) +{ + char *n; + + if (use_ipaddr) + return strdup(inet_ntoa(caller->sin_addr)); + n = client_compose(hp); + *error = unknown_host; + if (!n) + return NULL; + if (*n) + return n; + free(n); + return strdup("DEFAULT"); +} + +/* return static nfs_export with details filled in */ static nfs_export * -auth_authenticate_internal(char *what, struct sockaddr_in *caller, +auth_authenticate_newcache(char *what, struct sockaddr_in *caller, char *path, struct hostent *hp, enum auth_error *error) { - nfs_export *exp; + nfs_export *exp; + int i; - if (new_cache) { - int i; - /* return static nfs_export with details filled in */ - char *n; - free(my_client.m_hostname); - if (use_ipaddr) { - my_client.m_hostname = - strdup(inet_ntoa(caller->sin_addr)); - } else { - n = client_compose(hp); - *error = unknown_host; - if (!n) - my_client.m_hostname = NULL; - else if (*n) - my_client.m_hostname = n; - else { - free(n); - my_client.m_hostname = strdup("DEFAULT"); - } + free(my_client.m_hostname); + + my_client.m_hostname = get_client_hostname(caller, hp, error); + if (my_client.m_hostname == NULL) + return NULL; + + my_client.m_naddr = 1; + my_client.m_addrlist[0] = caller->sin_addr; + my_exp.m_client = &my_client; + + exp = NULL; + for (i = 0; !exp && i < MCL_MAXTYPES; i++) + for (exp = exportlist[i].p_head; exp; exp = exp->m_next) { + if (strcmp(path, exp->m_export.e_path)) + continue; + if (!use_ipaddr && !client_member(my_client.m_hostname, exp->m_client->m_hostname)) + continue; + if (use_ipaddr && !client_check(exp->m_client, hp)) + continue; + break; } - if (my_client.m_hostname == NULL) - return NULL; - my_client.m_naddr = 1; - my_client.m_addrlist[0] = caller->sin_addr; - my_exp.m_client = &my_client; - - exp = NULL; - for (i = 0; !exp && i < MCL_MAXTYPES; i++) - for (exp = exportlist[i].p_head; exp; exp = exp->m_next) { - if (strcmp(path, exp->m_export.e_path)) - continue; - if (!use_ipaddr && !client_member(my_client.m_hostname, exp->m_client->m_hostname)) - continue; - if (use_ipaddr && !client_check(exp->m_client, hp)) - continue; - break; - } - *error = not_exported; - if (!exp) - return exp; + *error = not_exported; + if (!exp) + return NULL; - my_exp.m_export = exp->m_export; - exp = &my_exp; + my_exp.m_export = exp->m_export; + exp = &my_exp; + return exp; +} + +static nfs_export * +auth_authenticate_internal(char *what, struct sockaddr_in *caller, + char *path, struct hostent *hp, + enum auth_error *error) +{ + nfs_export *exp; + if (new_cache) { + exp = auth_authenticate_newcache(what, caller, path, hp, error); + if (!exp) + return NULL; } else { if (!(exp = export_find(hp, path))) { *error = no_entry; return NULL; } - if (!exp->m_mayexport) { - *error = not_exported; - return NULL; - } + } + if (exp->m_export.e_flags & NFSEXP_V4ROOT) { + *error = no_entry; + return NULL; } if (!(exp->m_export.e_flags & NFSEXP_INSECURE_PORT) && - (ntohs(caller->sin_port) < IPPORT_RESERVED/2 || - ntohs(caller->sin_port) >= IPPORT_RESERVED)) { + ntohs(caller->sin_port) >= IPPORT_RESERVED) { *error = illegal_port; return NULL; } diff --git a/utils/mountd/cache.c b/utils/mountd/cache.c index e4e2f22..d63e10a 100644 --- a/utils/mountd/cache.c +++ b/utils/mountd/cache.c @@ -614,73 +614,54 @@ static int dump_to_cache(FILE *f, char *domain, char *path, struct exportent *ex return qword_eol(f); } -void nfsd_export(FILE *f) +static int is_subdirectory(char *subpath, char *path) { - /* requests are: - * domain path - * determine export options and return: - * domain path expiry flags anonuid anongid fsid - */ - - char *cp; - int i; - char *dom, *path; - nfs_export *exp, *found = NULL; - int found_type = 0; - struct in_addr addr; - struct hostent *he = NULL; - - - if (readline(fileno(f), &lbuf, &lbuflen) != 1) - return; + int l = strlen(path); - xlog(D_CALL, "nfsd_export: inbuf '%s'", lbuf); + return strcmp(subpath, path) == 0 + || (strncmp(subpath, path, l) == 0 && path[l] == '/'); +} - cp = lbuf; - dom = malloc(strlen(cp)); - path = malloc(strlen(cp)); +static int path_matches(nfs_export *exp, char *path) +{ + if (exp->m_export.e_flags & NFSEXP_CROSSMOUNT) + return is_subdirectory(path, exp->m_export.e_path); + return strcmp(path, exp->m_export.e_path) == 0; +} - if (!dom || !path) - goto out; +static int client_matches(nfs_export *exp, char *dom, struct hostent *he) +{ + if (use_ipaddr) + return client_check(exp->m_client, he); + return client_member(dom, exp->m_client->m_hostname); +} - if (qword_get(&cp, dom, strlen(lbuf)) <= 0) - goto out; - if (qword_get(&cp, path, strlen(lbuf)) <= 0) - goto out; +static int export_matches(nfs_export *exp, char *dom, char *path, struct hostent *he) +{ + return path_matches(exp, path) && client_matches(exp, dom, he); +} - auth_reload(); +static nfs_export *lookup_export(char *dom, char *path, struct hostent *he) +{ + nfs_export *exp; + nfs_export *found = NULL; + int found_type = 0; + int i; - /* now find flags for this export point in this domain */ for (i=0 ; i < MCL_MAXTYPES; i++) { for (exp = exportlist[i].p_head; exp; exp = exp->m_next) { - if (!use_ipaddr && !client_member(dom, exp->m_client->m_hostname)) - continue; - if (exp->m_export.e_flags & NFSEXP_CROSSMOUNT) { - /* if path is a mountpoint below e_path, then OK */ - int l = strlen(exp->m_export.e_path); - if (strcmp(path, exp->m_export.e_path) == 0 || - (strncmp(path, exp->m_export.e_path, l) == 0 && - path[l] == '/' && - is_mountpoint(path))) - /* ok */; - else - continue; - } else if (strcmp(path, exp->m_export.e_path) != 0) + if (!export_matches(exp, dom, path, he)) continue; - if (use_ipaddr) { - if (he == NULL) { - if (!inet_aton(dom, &addr)) - goto out; - he = client_resolve(addr); - } - if (!client_check(exp->m_client, he)) - continue; - } if (!found) { found = exp; found_type = i; continue; } + + /* Always prefer non-V4ROOT mounts */ + if (found->m_export.e_flags & NFSEXP_V4ROOT) + continue; + /* If one is a CROSSMOUNT, then prefer the longest path */ if (((found->m_export.e_flags & NFSEXP_CROSSMOUNT) || (exp->m_export.e_flags & NFSEXP_CROSSMOUNT)) && @@ -703,6 +684,50 @@ void nfsd_export(FILE *f) } } } + return found; +} + +void nfsd_export(FILE *f) +{ + /* requests are: + * domain path + * determine export options and return: + * domain path expiry flags anonuid anongid fsid + */ + + char *cp; + char *dom, *path; + nfs_export *found = NULL; + struct in_addr addr; + struct hostent *he = NULL; + + + if (readline(fileno(f), &lbuf, &lbuflen) != 1) + return; + + xlog(D_CALL, "nfsd_export: inbuf '%s'", lbuf); + + cp = lbuf; + dom = malloc(strlen(cp)); + path = malloc(strlen(cp)); + + if (!dom || !path) + goto out; + + if (qword_get(&cp, dom, strlen(lbuf)) <= 0) + goto out; + if (qword_get(&cp, path, strlen(lbuf)) <= 0) + goto out; + + auth_reload(); + + if (use_ipaddr) { + if (!inet_aton(dom, &addr)) + goto out; + he = client_resolve(addr); + } + + found = lookup_export(dom, path, he); if (found) { if (dump_to_cache(f, dom, path, &found->m_export) < 0) { diff --git a/utils/mountd/mount_dispatch.c b/utils/mountd/mount_dispatch.c index 199fcec..ba6981d 100644 --- a/utils/mountd/mount_dispatch.c +++ b/utils/mountd/mount_dispatch.c @@ -70,12 +70,10 @@ mount_dispatch(struct svc_req *rqstp, SVCXPRT *transp) { union mountd_arguments argument; union mountd_results result; -#ifdef HAVE_TCP_WRAPPER - struct sockaddr_in *sin = nfs_getrpccaller_in(transp); +#ifdef HAVE_TCP_WRAPPER /* remote host authorization check */ - if (sin->sin_family == AF_INET && - !check_default("mountd", sin, rqstp->rq_proc, MOUNTPROG)) { + if (!check_default("mountd", nfs_getrpccaller(transp), MOUNTPROG)) { svcerr_auth (transp, AUTH_FAILED); return; } diff --git a/utils/mountd/mountd.c b/utils/mountd/mountd.c index 888fd8c..a0a1f2d 100644 --- a/utils/mountd/mountd.c +++ b/utils/mountd/mountd.c @@ -509,12 +509,89 @@ get_rootfh(struct svc_req *rqstp, dirpath *path, nfs_export **expret, 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 hostent *hp; + struct groupnode *c, **cp; + + cp = &e->ex_groups; + while ((c = *cp) != NULL) { + if (client_gettype(c->gr_name) == MCL_FQDN + && (hp = gethostbyname(c->gr_name))) { + hp = hostent_dup(hp); + if (client_check(exp->m_client, hp)) { + *cp = c->gr_next; + xfree(c->gr_name); + xfree(c); + xfree (hp); + continue; + } + xfree (hp); + } + 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, *ne; - struct groupnode *g, *ng, *c, **cp; + struct exportnode *e; nfs_export *exp; int i; static unsigned int ecounter; @@ -526,77 +603,26 @@ get_exportlist(void) ecounter = acounter; - for (e = elist; e != NULL; e = ne) { - ne = e->ex_next; - for (g = e->ex_groups; g != NULL; g = ng) { - ng = g->gr_next; - xfree(g->gr_name); - xfree(g); - } - xfree(e->ex_dir); - xfree(e); - } - elist = NULL; + free_exportlist(&elist); for (i = 0; i < MCL_MAXTYPES; i++) { for (exp = exportlist[i].p_head; exp; exp = exp->m_next) { - for (e = elist; e != NULL; e = e->ex_next) { - if (!strcmp(exp->m_export.e_path, e->ex_dir)) - break; - } - if (!e) { - e = (struct exportnode *) xmalloc(sizeof(*e)); - e->ex_next = elist; - e->ex_groups = NULL; - e->ex_dir = xstrdup(exp->m_export.e_path); - elist = e; - } - - /* We need to check if we should remove - previous ones. */ - if (i == MCL_ANONYMOUS && e->ex_groups) { - for (g = e->ex_groups; g; g = ng) { - ng = g->gr_next; - xfree(g->gr_name); - xfree(g); - } - e->ex_groups = NULL; + /* Don't show pseudo exports */ + if (exp->m_export.e_flags & NFSEXP_V4ROOT) continue; - } - - if (i != MCL_FQDN && e->ex_groups) { - struct hostent *hp; + e = lookup_or_create_elist_entry(&elist, exp); - cp = &e->ex_groups; - while ((c = *cp) != NULL) { - if (client_gettype (c->gr_name) == MCL_FQDN - && (hp = gethostbyname(c->gr_name))) { - hp = hostent_dup (hp); - if (client_check(exp->m_client, hp)) { - *cp = c->gr_next; - xfree(c->gr_name); - xfree(c); - xfree (hp); + /* exports to "*" absorb any others */ + if (i == MCL_ANONYMOUS && e->ex_groups) { + remove_all_clients(e); continue; - } - xfree (hp); - } - cp = &(c->gr_next); - } } + /* 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') { - for (g = e->ex_groups; g; g = g->gr_next) - if (strcmp (exp->m_export.e_hostname, - g->gr_name) == 0) - break; - if (g) - continue; - g = (struct groupnode *) xmalloc(sizeof(*g)); - g->gr_name = xstrdup(exp->m_export.e_hostname); - g->gr_next = e->ex_groups; - e->ex_groups = g; - } + if (exp->m_export.e_hostname[0] != '\0') + insert_group(e, exp->m_export.e_hostname); } } diff --git a/utils/mountd/rmtab.c b/utils/mountd/rmtab.c index c371f8d..19b22ee 100644 --- a/utils/mountd/rmtab.c +++ b/utils/mountd/rmtab.c @@ -24,6 +24,7 @@ #include "ha-callout.h" #include /* PATH_MAX */ +#include extern int reverse_resolve; @@ -143,23 +144,16 @@ mountlist_del_all(struct sockaddr_in *sin) return; if (!(hp = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET))) { xlog(L_ERROR, "can't get hostname of %s", inet_ntoa(addr)); - xfunlock(lockid); - return; + goto out_unlock; } - else - hp = hostent_dup (hp); + hp = hostent_dup (hp); + + if (!setrmtabent("r")) + goto out_free; + + if (!(fp = fsetrmtabent(_PATH_RMTABTMP, "w"))) + goto out_close; - if (!setrmtabent("r")) { - xfunlock(lockid); - free (hp); - return; - } - if (!(fp = fsetrmtabent(_PATH_RMTABTMP, "w"))) { - endrmtabent(); - xfunlock(lockid); - free (hp); - return; - } while ((rep = getrmtabent(1, NULL)) != NULL) { if (strcmp(rep->r_client, hp->h_name) == 0 && (exp = auth_authenticate("umountall", sin, rep->r_path))) @@ -170,10 +164,13 @@ mountlist_del_all(struct sockaddr_in *sin) xlog(L_ERROR, "couldn't rename %s to %s", _PATH_RMTABTMP, _PATH_RMTAB); } - endrmtabent(); /* close & unlink */ fendrmtabent(fp); - xfunlock(lockid); +out_close: + endrmtabent(); /* close & unlink */ +out_free: free (hp); +out_unlock: + xfunlock(lockid); } mountlist @@ -191,7 +188,9 @@ mountlist_list(void) if ((lockid = xflock(_PATH_RMTABLCK, "r")) < 0) return NULL; if (stat(_PATH_RMTAB, &stb) < 0) { - xlog(L_ERROR, "can't stat %s", _PATH_RMTAB); + xlog(L_ERROR, "can't stat %s: %s", + _PATH_RMTAB, strerror(errno)); + xfunlock(lockid); return NULL; } if (stb.st_mtime != last_mtime) { diff --git a/utils/mountd/v4root.c b/utils/mountd/v4root.c new file mode 100644 index 0000000..7fd6af3 --- /dev/null +++ b/utils/mountd/v4root.c @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2009 Red Hat + * + * support/export/v4root.c + * + * Routines used to support NFSv4 pseudo roots + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "xlog.h" +#include "exportfs.h" +#include "nfslib.h" +#include "misc.h" +#include "v4root.h" + +int v4root_needed; + +static nfs_export pseudo_root = { + .m_next = NULL, + .m_client = NULL, + .m_export = { + .e_hostname = "*", + .e_path = "/", + .e_flags = NFSEXP_READONLY | NFSEXP_ROOTSQUASH + | NFSEXP_NOSUBTREECHECK | NFSEXP_FSID + | NFSEXP_V4ROOT, + .e_anonuid = 65534, + .e_anongid = 65534, + .e_squids = NULL, + .e_nsquids = 0, + .e_sqgids = NULL, + .e_nsqgids = 0, + .e_fsid = 0, + .e_mountpoint = NULL, + }, + .m_exported = 0, + .m_xtabent = 1, + .m_mayexport = 1, + .m_changed = 0, + .m_warned = 0, +}; + +void set_pseudofs_security(struct exportent *pseudo, struct exportent *source) +{ + struct sec_entry *se; + int i; + + if (source->e_flags & NFSEXP_INSECURE_PORT) + pseudo->e_flags |= NFSEXP_INSECURE_PORT; + for (se = source->e_secinfo; se->flav; se++) { + struct sec_entry *new; + + i = secinfo_addflavor(se->flav, pseudo); + new = &pseudo->e_secinfo[i]; + + if (se->flags & NFSEXP_INSECURE_PORT) + new->flags |= NFSEXP_INSECURE_PORT; + } +} + +/* + * Create a pseudo export + */ +static struct exportent * +v4root_create(char *path, nfs_export *export) +{ + nfs_export *exp; + struct exportent eep; + struct exportent *curexp = &export->m_export; + + dupexportent(&eep, &pseudo_root.m_export); + eep.e_hostname = strdup(curexp->e_hostname); + strncpy(eep.e_path, path, sizeof(eep.e_path)); + if (strcmp(path, "/") != 0) + eep.e_flags &= ~NFSEXP_FSID; + set_pseudofs_security(&eep, curexp); + exp = export_create(&eep, 0); + if (exp == NULL) + return NULL; + xlog(D_CALL, "v4root_create: path '%s'", exp->m_export.e_path); + return &exp->m_export; +} + +/* + * Make sure the kernel has pseudo root support. + */ +static int +v4root_support(void) +{ + struct export_features *ef; + static int warned = 0; + + ef = get_export_features(); + + if (ef->flags & NFSEXP_V4ROOT) + return 1; + if (!warned) { + xlog(L_WARNING, "Kernel does not have pseudo root support."); + xlog(L_WARNING, "NFS v4 mounts will be disabled unless fsid=0"); + xlog(L_WARNING, "is specfied in /etc/exports file."); + warned++; + } + return 0; +} + +int pseudofs_update(char *hostname, char *path, nfs_export *source) +{ + nfs_export *exp; + + exp = export_lookup(hostname, path, 0); + if (exp && !(exp->m_export.e_flags & NFSEXP_V4ROOT)) + return 0; + if (!exp) { + if (v4root_create(path, source) == NULL) { + xlog(L_WARNING, "v4root_set: Unable to create " + "pseudo export for '%s'", path); + return -ENOMEM; + } + return 0; + } + /* Update an existing V4ROOT export: */ + set_pseudofs_security(&exp->m_export, &source->m_export); + return 0; +} + +static int v4root_add_parents(nfs_export *exp) +{ + char *hostname = exp->m_export.e_hostname; + char *path; + char *ptr; + + path = strdup(exp->m_export.e_path); + if (!path) + return -ENOMEM; + for (ptr = path + 1; ptr; ptr = strchr(ptr, '/')) { + int ret; + char saved; + + saved = *ptr; + *ptr = '\0'; + ret = pseudofs_update(hostname, path, exp); + if (ret) + return ret; + *ptr = saved; + ptr++; + } + free(path); + return 0; +} + +/* + * Create pseudo exports by running through the real export + * looking at the components of the path that make up the export. + * Those path components, if not exported, will become pseudo + * exports allowing them to be found when the kernel does an upcall + * looking for components of the v4 mount. + */ +void +v4root_set() +{ + nfs_export *exp; + int i, ret; + + if (!v4root_needed) + return; + if (!v4root_support()) + return; + + for (i = 0; i < MCL_MAXTYPES; i++) { + for (exp = exportlist[i].p_head; exp; exp = exp->m_next) { + if (exp->m_export.e_flags & NFSEXP_V4ROOT) + /* + * We just added this one, so its + * parents are already dealt with! + */ + continue; + + ret = v4root_add_parents(exp); + /* XXX: error handling! */ + } + } +} diff --git a/utils/nfsd/Makefile.in b/utils/nfsd/Makefile.in index b7d91ea..6c87bad 100644 --- a/utils/nfsd/Makefile.in +++ b/utils/nfsd/Makefile.in @@ -1,4 +1,4 @@ -# Makefile.in generated by automake 1.11 from Makefile.am. +# Makefile.in generated by automake 1.11.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, @@ -42,6 +42,7 @@ am__aclocal_m4_deps = $(top_srcdir)/aclocal/bsdsignals.m4 \ $(top_srcdir)/aclocal/ipv6.m4 \ $(top_srcdir)/aclocal/kerberos5.m4 \ $(top_srcdir)/aclocal/libblkid.m4 \ + $(top_srcdir)/aclocal/libcap.m4 \ $(top_srcdir)/aclocal/libevent.m4 \ $(top_srcdir)/aclocal/libnfsidmap.m4 \ $(top_srcdir)/aclocal/librpcsecgss.m4 \ @@ -160,6 +161,7 @@ LDFLAGS = @LDFLAGS@ LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@ LIBBLKID = @LIBBLKID@ LIBBSD = @LIBBSD@ +LIBCAP = @LIBCAP@ LIBCRYPT = @LIBCRYPT@ LIBNSL = @LIBNSL@ LIBOBJS = @LIBOBJS@ @@ -226,6 +228,7 @@ enable_ipv6 = @enable_ipv6@ enable_mountconfig = @enable_mountconfig@ enable_nfsv3 = @enable_nfsv3@ enable_nfsv4 = @enable_nfsv4@ +enable_nfsv41 = @enable_nfsv41@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ diff --git a/utils/nfsd/nfssvc.c b/utils/nfsd/nfssvc.c index 12d3253..60232b8 100644 --- a/utils/nfsd/nfssvc.c +++ b/utils/nfsd/nfssvc.c @@ -212,7 +212,7 @@ int nfssvc_set_sockets(const int family, const unsigned int protobits, const char *host, const char *port) { - struct addrinfo hints = { .ai_flags = AI_PASSIVE | AI_ADDRCONFIG }; + struct addrinfo hints = { .ai_flags = AI_PASSIVE }; hints.ai_family = family; @@ -238,17 +238,17 @@ nfssvc_setvers(unsigned int ctlbits, int minorvers4) if (fd < 0) return; + n = minorvers4 >= 0 ? minorvers4 : -minorvers4; + if (n >= NFSD_MINMINORVERS4 && n <= NFSD_MAXMINORVERS4) + off += snprintf(ptr+off, sizeof(buf) - off, "%c4.%d ", + minorvers4 > 0 ? '+' : '-', + n); for (n = NFSD_MINVERS; n <= NFSD_MAXVERS; n++) { if (NFSCTL_VERISSET(ctlbits, n)) off += snprintf(ptr+off, sizeof(buf) - off, "+%d ", n); else off += snprintf(ptr+off, sizeof(buf) - off, "-%d ", n); } - n = minorvers4 >= 0 ? minorvers4 : -minorvers4; - if (n >= NFSD_MINMINORVERS4 && n <= NFSD_MAXMINORVERS4) - off += snprintf(ptr+off, sizeof(buf) - off, "%c4.%d", - minorvers4 > 0 ? '+' : '-', - n); xlog(D_GENERAL, "Writing version string to kernel: %s", buf); snprintf(ptr+off, sizeof(buf) - off, "\n"); if (write(fd, buf, strlen(buf)) != strlen(buf)) diff --git a/utils/nfsstat/Makefile.in b/utils/nfsstat/Makefile.in index 69e6e2d..fd147bb 100644 --- a/utils/nfsstat/Makefile.in +++ b/utils/nfsstat/Makefile.in @@ -1,4 +1,4 @@ -# Makefile.in generated by automake 1.11 from Makefile.am. +# Makefile.in generated by automake 1.11.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, @@ -42,6 +42,7 @@ am__aclocal_m4_deps = $(top_srcdir)/aclocal/bsdsignals.m4 \ $(top_srcdir)/aclocal/ipv6.m4 \ $(top_srcdir)/aclocal/kerberos5.m4 \ $(top_srcdir)/aclocal/libblkid.m4 \ + $(top_srcdir)/aclocal/libcap.m4 \ $(top_srcdir)/aclocal/libevent.m4 \ $(top_srcdir)/aclocal/libnfsidmap.m4 \ $(top_srcdir)/aclocal/librpcsecgss.m4 \ @@ -161,6 +162,7 @@ LDFLAGS = @LDFLAGS@ LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@ LIBBLKID = @LIBBLKID@ LIBBSD = @LIBBSD@ +LIBCAP = @LIBCAP@ LIBCRYPT = @LIBCRYPT@ LIBNSL = @LIBNSL@ LIBOBJS = @LIBOBJS@ @@ -227,6 +229,7 @@ enable_ipv6 = @enable_ipv6@ enable_mountconfig = @enable_mountconfig@ enable_nfsv3 = @enable_nfsv3@ enable_nfsv4 = @enable_nfsv4@ +enable_nfsv41 = @enable_nfsv41@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ diff --git a/utils/nfsstat/nfsstat.c b/utils/nfsstat/nfsstat.c index fa46d5d..99d77c9 100644 --- a/utils/nfsstat/nfsstat.c +++ b/utils/nfsstat/nfsstat.c @@ -25,13 +25,30 @@ #define MAXNRVALS 32 -static unsigned int srvproc2info[20], srvproc2info_old[20]; /* NFSv2 call counts ([0] == 18) */ -static unsigned int cltproc2info[20], cltproc2info_old[20]; /* NFSv2 call counts ([0] == 18) */ -static unsigned int srvproc3info[24], srvproc3info_old[24]; /* NFSv3 call counts ([0] == 22) */ -static unsigned int cltproc3info[24], cltproc3info_old[24]; /* NFSv3 call counts ([0] == 22) */ -static unsigned int srvproc4info[4], srvproc4info_old[4]; /* NFSv4 call counts ([0] == 2) */ -static unsigned int cltproc4info[49], cltproc4info_old[49]; /* NFSv4 call counts ([0] == 35) */ -static unsigned int srvproc4opsinfo[61], srvproc4opsinfo_old[61]; /* NFSv4 call counts ([0] == 40) */ +enum { + SRVPROC2_SZ = 18, + CLTPROC2_SZ = 18, + SRVPROC3_SZ = 22, + CLTPROC3_SZ = 22, + SRVPROC4_SZ = 2, + CLTPROC4_SZ = 48, + SRVPROC4OPS_SZ = 59, +}; + +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] == 48) */ +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 @@ -75,25 +92,25 @@ static unsigned int srvfhinfo[7], srvfhinfo_old[7]; /* (for kernels >= 2.4.0) * compatability. */ -static const char * nfsv2name[18] = { +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[22] = { +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[2] = { +static const char * nfssrvproc4name[SRVPROC4_SZ] = { "null", "compound", }; -static const char * nfscltproc4name[47] = { +static const char * nfscltproc4name[CLTPROC4_SZ] = { "null", "read", "write", "commit", "open", "open_conf", "open_noat", "open_dgrd", "close", "setattr", "fsinfo", "renew", "setclntid", "confirm", "lock", @@ -107,6 +124,7 @@ static const char * nfscltproc4name[47] = { "destroy_ses", "sequence", "get_lease_t", + "reclaim_comp", "layoutget", "layoutcommit", "layoutreturn", @@ -117,7 +135,7 @@ static const char * nfscltproc4name[47] = { "ds_commit", }; -static const char * nfssrvproc4opname[59] = { +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", diff --git a/utils/showmount/Makefile.in b/utils/showmount/Makefile.in index b342bb8..8eeae8e 100644 --- a/utils/showmount/Makefile.in +++ b/utils/showmount/Makefile.in @@ -1,4 +1,4 @@ -# Makefile.in generated by automake 1.11 from Makefile.am. +# Makefile.in generated by automake 1.11.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, @@ -42,6 +42,7 @@ am__aclocal_m4_deps = $(top_srcdir)/aclocal/bsdsignals.m4 \ $(top_srcdir)/aclocal/ipv6.m4 \ $(top_srcdir)/aclocal/kerberos5.m4 \ $(top_srcdir)/aclocal/libblkid.m4 \ + $(top_srcdir)/aclocal/libcap.m4 \ $(top_srcdir)/aclocal/libevent.m4 \ $(top_srcdir)/aclocal/libnfsidmap.m4 \ $(top_srcdir)/aclocal/librpcsecgss.m4 \ @@ -161,6 +162,7 @@ LDFLAGS = @LDFLAGS@ LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@ LIBBLKID = @LIBBLKID@ LIBBSD = @LIBBSD@ +LIBCAP = @LIBCAP@ LIBCRYPT = @LIBCRYPT@ LIBNSL = @LIBNSL@ LIBOBJS = @LIBOBJS@ @@ -227,6 +229,7 @@ enable_ipv6 = @enable_ipv6@ enable_mountconfig = @enable_mountconfig@ enable_nfsv3 = @enable_nfsv3@ enable_nfsv4 = @enable_nfsv4@ +enable_nfsv41 = @enable_nfsv41@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ diff --git a/utils/showmount/showmount.c b/utils/showmount/showmount.c index 418e8b9..f567093 100644 --- a/utils/showmount/showmount.c +++ b/utils/showmount/showmount.c @@ -78,29 +78,36 @@ static void usage(FILE *fp, int n) exit(n); } -static const char *nfs_sm_pgmtbl[] = { +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) +static CLIENT *nfs_get_mount_client(const char *hostname, rpcvers_t vers) { - rpcprog_t program = nfs_getrpcbyname(MOUNTPROG, nfs_sm_pgmtbl); + rpcprog_t program = nfs_getrpcbyname(MOUNTPROG, mount_pgm_tbl); CLIENT *client; - client = clnt_create(hostname, program, MOUNTVERS, "tcp"); + client = clnt_create(hostname, program, vers, "tcp"); if (client) return client; - - client = clnt_create(hostname, program, MOUNTVERS, "udp"); + client = clnt_create(hostname, program, vers, "udp"); if (client) return client; @@ -123,6 +130,7 @@ int main(int argc, char **argv) int i; int n; int maxlen; + int unsigned vers=0; char **dumpv; program_name = argv[0]; @@ -185,11 +193,12 @@ int main(int argc, char **argv) break; } - mclient = nfs_get_mount_client(hostname); + mclient = nfs_get_mount_client(hostname, mount_vers_tbl[vers]); mclient->cl_auth = authunix_create_default(); total_timeout.tv_sec = TOTAL_TIMEOUT; total_timeout.tv_usec = 0; +again: if (eflag) { memset(&exportlist, '\0', sizeof(exportlist)); @@ -197,6 +206,13 @@ int main(int argc, char **argv) (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); @@ -232,6 +248,13 @@ int main(int argc, char **argv) (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); diff --git a/utils/statd/Makefile.am b/utils/statd/Makefile.am index 8a3ba4e..1744791 100644 --- a/utils/statd/Makefile.am +++ b/utils/statd/Makefile.am @@ -2,31 +2,25 @@ man8_MANS = statd.man sm-notify.man -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) - RPCPREFIX = rpc. KPREFIX = @kprefix@ sbin_PROGRAMS = statd sm-notify dist_sbin_SCRIPTS = start-statd -statd_SOURCES = callback.c notlist.c log.c misc.c monitor.c \ +statd_SOURCES = callback.c notlist.c misc.c monitor.c hostname.c \ simu.c stat.c statd.c svc_run.c rmtcall.c \ - sm_inter_clnt.c sm_inter_svc.c sm_inter_xdr.c log.h \ - notlist.h statd.h system.h version.h sm_inter.h + notlist.h statd.h system.h version.h sm_notify_SOURCES = sm-notify.c BUILT_SOURCES = $(GENFILES) -statd_LDADD = ../../support/export/libexport.a \ +statd_LDADD = ../../support/nsm/libnsm.a \ ../../support/nfs/libnfs.a \ ../../support/misc/libmisc.a \ - $(LIBWRAP) $(LIBNSL) -sm_notify_LDADD = $(LIBNSL) + $(LIBWRAP) $(LIBNSL) $(LIBCAP) +sm_notify_LDADD = ../../support/nsm/libnsm.a \ + ../../support/nfs/libnfs.a \ + $(LIBNSL) $(LIBCAP) -EXTRA_DIST = sim_sm_inter.x sm_inter.x $(man8_MANS) COPYRIGHT simulate.c +EXTRA_DIST = sim_sm_inter.x $(man8_MANS) COPYRIGHT simulate.c if CONFIG_RPCGEN RPCGEN = $(top_builddir)/tools/rpcgen/rpcgen diff --git a/utils/statd/Makefile.in b/utils/statd/Makefile.in index c158d82..ff9d37d 100644 --- a/utils/statd/Makefile.in +++ b/utils/statd/Makefile.in @@ -1,4 +1,4 @@ -# Makefile.in generated by automake 1.11 from Makefile.am. +# Makefile.in generated by automake 1.11.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, @@ -44,6 +44,7 @@ am__aclocal_m4_deps = $(top_srcdir)/aclocal/bsdsignals.m4 \ $(top_srcdir)/aclocal/ipv6.m4 \ $(top_srcdir)/aclocal/kerberos5.m4 \ $(top_srcdir)/aclocal/libblkid.m4 \ + $(top_srcdir)/aclocal/libcap.m4 \ $(top_srcdir)/aclocal/libevent.m4 \ $(top_srcdir)/aclocal/libnfsidmap.m4 \ $(top_srcdir)/aclocal/librpcsecgss.m4 \ @@ -64,16 +65,18 @@ PROGRAMS = $(sbin_PROGRAMS) am_sm_notify_OBJECTS = sm-notify.$(OBJEXT) sm_notify_OBJECTS = $(am_sm_notify_OBJECTS) am__DEPENDENCIES_1 = -sm_notify_DEPENDENCIES = $(am__DEPENDENCIES_1) -am_statd_OBJECTS = callback.$(OBJEXT) notlist.$(OBJEXT) log.$(OBJEXT) \ - misc.$(OBJEXT) monitor.$(OBJEXT) simu.$(OBJEXT) stat.$(OBJEXT) \ - statd.$(OBJEXT) svc_run.$(OBJEXT) rmtcall.$(OBJEXT) \ - sm_inter_clnt.$(OBJEXT) sm_inter_svc.$(OBJEXT) \ - sm_inter_xdr.$(OBJEXT) +sm_notify_DEPENDENCIES = ../../support/nsm/libnsm.a \ + ../../support/nfs/libnfs.a $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_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/export/libexport.a \ +statd_DEPENDENCIES = ../../support/nsm/libnsm.a \ ../../support/nfs/libnfs.a ../../support/misc/libmisc.a \ - $(am__DEPENDENCIES_1) $(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/||"`;; \ @@ -174,6 +177,7 @@ LDFLAGS = @LDFLAGS@ LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@ LIBBLKID = @LIBBLKID@ LIBBSD = @LIBBSD@ +LIBCAP = @LIBCAP@ LIBCRYPT = @LIBCRYPT@ LIBNSL = @LIBNSL@ LIBOBJS = @LIBOBJS@ @@ -240,6 +244,7 @@ enable_ipv6 = @enable_ipv6@ enable_mountconfig = @enable_mountconfig@ enable_nfsv3 = @enable_nfsv3@ enable_nfsv4 = @enable_nfsv4@ +enable_nfsv41 = @enable_nfsv41@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ @@ -276,28 +281,25 @@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ man8_MANS = statd.man sm-notify.man -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) RPCPREFIX = rpc. KPREFIX = @kprefix@ dist_sbin_SCRIPTS = start-statd -statd_SOURCES = callback.c notlist.c log.c misc.c monitor.c \ +statd_SOURCES = callback.c notlist.c misc.c monitor.c hostname.c \ simu.c stat.c statd.c svc_run.c rmtcall.c \ - sm_inter_clnt.c sm_inter_svc.c sm_inter_xdr.c log.h \ - notlist.h statd.h system.h version.h sm_inter.h + notlist.h statd.h system.h version.h sm_notify_SOURCES = sm-notify.c BUILT_SOURCES = $(GENFILES) -statd_LDADD = ../../support/export/libexport.a \ +statd_LDADD = ../../support/nsm/libnsm.a \ ../../support/nfs/libnfs.a \ ../../support/misc/libmisc.a \ - $(LIBWRAP) $(LIBNSL) + $(LIBWRAP) $(LIBNSL) $(LIBCAP) -sm_notify_LDADD = $(LIBNSL) -EXTRA_DIST = sim_sm_inter.x sm_inter.x $(man8_MANS) COPYRIGHT simulate.c +sm_notify_LDADD = ../../support/nsm/libnsm.a \ + ../../support/nfs/libnfs.a \ + $(LIBNSL) $(LIBCAP) + +EXTRA_DIST = sim_sm_inter.x $(man8_MANS) COPYRIGHT simulate.c @CONFIG_RPCGEN_FALSE@RPCGEN = @RPCGEN_PATH@ @CONFIG_RPCGEN_TRUE@RPCGEN = $(top_builddir)/tools/rpcgen/rpcgen MAINTAINERCLEANFILES = Makefile.in @@ -428,16 +430,13 @@ distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/callback.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/log.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hostname.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/misc.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/monitor.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/notlist.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rmtcall.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/simu.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sm-notify.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sm_inter_clnt.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sm_inter_svc.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sm_inter_xdr.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stat.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/statd.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/svc_run.Po@am__quote@ diff --git a/utils/statd/callback.c b/utils/statd/callback.c index 8885238..d1cc139 100644 --- a/utils/statd/callback.c +++ b/utils/statd/callback.c @@ -10,10 +10,9 @@ #include #endif -#include +#include #include "rpcmisc.h" -#include "misc.h" #include "statd.h" #include "notlist.h" @@ -21,30 +20,85 @@ /* notify_list *cbnl = NULL; ... never used */ -/* +/* * Services SM_NOTIFY requests. - * Any clients that have asked us to monitor that host are put on - * the global callback list, which is processed as soon as statd - * returns to svc_run. + * + * 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_in *sin = nfs_getrpccaller_in(rqstp->rq_xprt); - char *ip_addr = xstrdup(inet_ntoa(sin->sin_addr)); + struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt); + char ip_addr[INET6_ADDRSTRLEN]; - dprintf(N_DEBUG, "Received SM_NOTIFY from %s, state: %d", + xlog(D_CALL, "Received SM_NOTIFY from %s, state: %d", argp->mon_name, argp->state); /* quick check - don't bother if we're not monitoring anyone */ if (rtnl == NULL) { - note(N_WARNING, "SM_NOTIFY from %s while not monitoring any hosts.", + xlog_warn("SM_NOTIFY from %s while not monitoring any hosts", argp->mon_name); return ((void *) &result); } + if (!statd_present_address(sap, ip_addr, sizeof(ip_addr))) { + xlog_warn("Unrecognized sender address"); + 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 @@ -52,8 +106,8 @@ sm_notify_1_svc(struct stat_chge *argp, struct svc_req *rqstp) */ for (lp = rtnl ; lp ; lp = lp->next) if (NL_STATE(lp) != argp->state && - (matchhostname(argp->mon_name, lp->dns_name) || - matchhostname(ip_addr, lp->dns_name))) { + (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); diff --git a/utils/statd/hostname.c b/utils/statd/hostname.c new file mode 100644 index 0000000..7d704cc --- /dev/null +++ b/utils/statd/hostname.c @@ -0,0 +1,284 @@ +/* + * 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 . + */ + +/* + * NSM for Linux. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "sockaddr.h" +#include "statd.h" +#include "xlog.h" + +#ifndef HAVE_DECL_AI_ADDRCONFIG +#define AI_ADDRCONFIG 0 +#endif + +/** + * 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. + * + * We won't monitor peers that don't have a reverse map. The canonical + * name gives us a key for our monitor list. + */ +__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)); + freeaddrinfo(ai); + if (!result) + return NULL; + 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); + freeaddrinfo(ai); + + return strdup(buf); +} + +/** + * 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; + struct addrinfo hint = { + .ai_family = AF_UNSPEC, + .ai_flags = AI_CANONNAME, + .ai_protocol = (int)IPPROTO_UDP, + }; + _Bool result = false; + + if (strcasecmp(hostname1, hostname2) == 0) { + result = true; + goto out; + } + + results1 = get_addrinfo(hostname1, &hint); + if (results1 == NULL) + goto out; + results2 = get_addrinfo(hostname2, &hint); + 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: + freeaddrinfo(results2); + freeaddrinfo(results1); + + xlog(D_CALL, "%s: hostnames %s", __func__, + (result ? "matched" : "did not match")); + return result; +} diff --git a/utils/statd/log.c b/utils/statd/log.c deleted file mode 100644 index a6ca996..0000000 --- a/utils/statd/log.c +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (C) 1995 Olaf Kirch - * Modified by Jeffrey A. Uphoff, 1995, 1997, 1999. - * Modified by H.J. Lu, 1998. - * Modified by Lon Hohberger, Oct. 2000 - * - * NSM for Linux. - */ - -/* - * log.c - logging functions for lockd/statd - * 260295 okir started with simply syslog logging. - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include "log.h" -#include "statd.h" - -static pid_t mypid; - /* Turns on logging to console/stderr. */ -#if 0 -static int opt_debug = 0; /* Will be command-line option, eventually */ -#endif - -void log_init(void) -{ - if (!(run_mode & MODE_LOG_STDERR)) - openlog(name_p, LOG_PID | LOG_NDELAY, LOG_DAEMON); - - mypid = getpid(); - - note(N_WARNING,"Version %s Starting",version_p); -} - -void log_background(void) -{ - /* NOP */ -} - -void die(char *fmt, ...) -{ - char buffer[1024]; - va_list ap; - - va_start(ap, fmt); - vsnprintf (buffer, 1024, fmt, ap); - va_end(ap); - buffer[1023]=0; - - note(N_FATAL, "%s", buffer); - -#ifndef DEBUG - exit (2); -#else - abort(); /* make a core */ -#endif -} - -void note(int level, char *fmt, ...) -{ - char buffer[1024]; - va_list ap; - - va_start(ap, fmt); - vsnprintf (buffer, 1024, fmt, ap); - va_end(ap); - buffer[1023]=0; - - if ((!(run_mode & MODE_LOG_STDERR)) && (level < N_DEBUG)) { - syslog(level, "%s", buffer); - } else if (run_mode & MODE_LOG_STDERR) { - /* Log everything, including dprintf() stuff to stderr */ - time_t now; - struct tm * tm; - - time(&now); - tm = localtime(&now); - fprintf (stderr, "%02d/%02d/%04d %02d:%02d:%02d %s[%d]: %s\n", - tm->tm_mon + 1, tm->tm_mday, tm->tm_year + 1900, - tm->tm_hour, tm->tm_min, tm->tm_sec, - name_p, mypid, - buffer); - } -} diff --git a/utils/statd/log.h b/utils/statd/log.h deleted file mode 100644 index fc55d3c..0000000 --- a/utils/statd/log.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 1995 Olaf Kirch - * Modified by Jeffrey A. Uphoff, 1996, 1997, 1999. - * Modified by Lon Hohberger, Oct. 2000 - * - * NSM for Linux. - */ - -/* - * logging functionality - * 260295 okir - */ - -#ifndef _LOCKD_LOG_H_ -#define _LOCKD_LOG_H_ - -#include - -void log_init(void); -void log_background(void); -void log_enable(int facility); -int log_enabled(int facility); -void note(int level, char *fmt, ...); -void die(char *fmt, ...); - -/* - * Map per-application severity to system severity. What's fatal for - * lockd is merely an itching spot from the universe's point of view. - */ -#define N_CRIT LOG_CRIT -#define N_FATAL LOG_ERR -#define N_ERROR LOG_WARNING -#define N_WARNING LOG_NOTICE -#define N_DEBUG LOG_DEBUG - -#ifdef DEBUG -#define dprintf note -#else -#define dprintf if (run_mode & MODE_LOG_STDERR) note -#endif - -#endif /* _LOCKD_LOG_H_ */ diff --git a/utils/statd/misc.c b/utils/statd/misc.c index 7256291..f2a086f 100644 --- a/utils/statd/misc.c +++ b/utils/statd/misc.c @@ -29,8 +29,7 @@ xmalloc (size_t size) return ((void *)NULL); if (!(ptr = malloc (size))) - /* SHIT! SHIT! SHIT! */ - die ("malloc failed"); + xlog_err ("malloc failed"); return (ptr); } @@ -46,32 +45,7 @@ xstrdup (const char *string) /* Will only fail if underlying malloc() fails (ENOMEM). */ if (!(result = strdup (string))) - die ("strdup failed"); + xlog_err ("strdup failed"); return (result); } - - -/* - * Unlinking a file. - */ -void -xunlink (char *path, char *host) -{ - char *tozap; - - tozap = malloc(strlen(path)+strlen(host)+2); - if (tozap == NULL) { - note(N_ERROR, "xunlink: malloc failed: errno %d (%s)", - errno, strerror(errno)); - return; - } - sprintf (tozap, "%s/%s", path, host); - - if (unlink (tozap) == -1) - note(N_ERROR, "unlink (%s): %s", tozap, strerror (errno)); - else - dprintf (N_DEBUG, "Unlinked %s", tozap); - - free(tozap); -} diff --git a/utils/statd/monitor.c b/utils/statd/monitor.c index a2c9e2b..325dfd3 100644 --- a/utils/statd/monitor.c +++ b/utils/statd/monitor.c @@ -21,34 +21,38 @@ #include #include +#include "sockaddr.h" #include "rpcmisc.h" -#include "misc.h" +#include "nsm.h" #include "statd.h" #include "notlist.h" #include "ha-callout.h" notify_list * rtnl = NULL; /* Run-time notify list. */ -#define LINELEN (4*(8+1)+SM_PRIV_SIZE*2+1) - /* * 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 int +static _Bool caller_is_localhost(struct svc_req *rqstp) { - struct sockaddr_in *sin = nfs_getrpccaller_in(rqstp->rq_xprt); - struct in_addr caller; - - caller = sin->sin_addr; - if (caller.s_addr != htonl(INADDR_LOOPBACK)) { - note(N_WARNING, - "Call to statd from non-local host %s", - inet_ntoa(caller)); - return 0; - } - return 1; + 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; } /* @@ -61,13 +65,15 @@ sm_mon_1_svc(struct mon *argp, struct svc_req *rqstp) char *mon_name = argp->mon_id.mon_name, *my_name = argp->mon_id.my_id.my_name; struct my_id *id = &argp->mon_id.my_id; - char *path; char *cp; - int fd; notify_list *clnt; - struct in_addr my_addr; - char *dnsname; - struct hostent *hostinfo = NULL; + struct sockaddr_in my_addr = { + .sin_family = AF_INET, + .sin_addr.s_addr = htonl(INADDR_LOOPBACK), + }; + char *dnsname = NULL; + + xlog(D_CALL, "Received SM_MON for %s from %s", mon_name, my_name); /* Assume that we'll fail. */ result.res_stat = STAT_FAIL; @@ -79,7 +85,6 @@ sm_mon_1_svc(struct mon *argp, struct svc_req *rqstp) */ if (!caller_is_localhost(rqstp)) goto failure; - my_addr.s_addr = htonl(INADDR_LOOPBACK); /* 2. Reject any registrations for non-lockd services. * @@ -92,8 +97,7 @@ sm_mon_1_svc(struct mon *argp, struct svc_req *rqstp) if (id->my_prog != 100021 || (id->my_proc != 16 && id->my_proc != 24)) { - note(N_WARNING, - "Attempt to register callback to %d/%d", + xlog_warn("Attempt to register callback to %d/%d", id->my_prog, id->my_proc); goto failure; } @@ -105,12 +109,9 @@ sm_mon_1_svc(struct mon *argp, struct svc_req *rqstp) /* must check for /'s in hostname! See CERT's CA-96.09 for details. */ if (strchr(mon_name, '/') || mon_name[0] == '.') { - note(N_CRIT, "SM_MON request for hostname containing '/' " + xlog(L_ERROR, "SM_MON request for hostname containing '/' " "or starting '.': %s", mon_name); - note(N_CRIT, "POSSIBLE SPOOF/ATTACK ATTEMPT!"); - goto failure; - } else if ((hostinfo = gethostbyname(mon_name)) == NULL) { - note(N_WARNING, "gethostbyname error for %s", mon_name); + xlog(L_ERROR, "POSSIBLE SPOOF/ATTACK ATTEMPT!"); goto failure; } @@ -124,15 +125,13 @@ sm_mon_1_svc(struct mon *argp, struct svc_req *rqstp) * 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 + * FQDN now, and use that for matching. */ - hostinfo = gethostbyaddr(hostinfo->h_addr, - hostinfo->h_length, - hostinfo->h_addrtype); - if (hostinfo) - dnsname = xstrdup(hostinfo->h_name); - else - dnsname = xstrdup(my_name); + 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 @@ -146,18 +145,19 @@ sm_mon_1_svc(struct mon *argp, struct svc_req *rqstp) clnt = rtnl; while ((clnt = nlist_gethost(clnt, mon_name, 0))) { - if (matchhostname(NL_MY_NAME(clnt), my_name) && + 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 && memcmp(NL_PRIV(clnt), argp->priv, SM_PRIV_SIZE) == 0) { /* Hey! We already know you guys! */ - dprintf(N_DEBUG, + 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); @@ -168,11 +168,11 @@ sm_mon_1_svc(struct mon *argp, struct svc_req *rqstp) * doesn't fail. (I should probably fix this assumption.) */ if (!(clnt = nlist_new(my_name, mon_name, 0))) { - note(N_WARNING, "out of memory"); + free(dnsname); + xlog_warn("out of memory"); goto failure; } - NL_ADDR(clnt) = my_addr; NL_MY_PROG(clnt) = id->my_prog; NL_MY_VERS(clnt) = id->my_vers; NL_MY_PROC(clnt) = id->my_proc; @@ -182,40 +182,16 @@ sm_mon_1_svc(struct mon *argp, struct svc_req *rqstp) /* * Now, Create file on stable storage for host. */ - - path=xmalloc(strlen(SM_DIR)+strlen(dnsname)+2); - sprintf(path, "%s/%s", SM_DIR, dnsname); - if ((fd = open(path, O_WRONLY|O_SYNC|O_CREAT|O_APPEND, - S_IRUSR|S_IWUSR)) < 0) { - /* Didn't fly. We won't monitor. */ - note(N_ERROR, "creat(%s) failed: %s", path, strerror (errno)); + if (!nsm_insert_monitored_host(dnsname, + (struct sockaddr *)(char *)&my_addr, argp)) { nlist_free(NULL, clnt); - free(path); goto failure; } - { - char buf[LINELEN + 1 + SM_MAXSTRLEN*2 + 4]; - char *e; - int i; - e = buf + sprintf(buf, "%08x %08x %08x %08x ", - my_addr.s_addr, id->my_prog, - id->my_vers, id->my_proc); - for (i=0; ipriv[i])); - if (e+1-buf != LINELEN) abort(); - e += sprintf(e, " %s %s\n", mon_name, my_name); - if (write(fd, buf, e-buf) != (e-buf)) { - note(N_WARNING, "writing to %s failed: errno %d (%s)", - path, errno, strerror(errno)); - } - } - free(path); /* PRC: do the HA callout: */ ha_callout("add-client", mon_name, my_name, -1); nlist_insert(&rtnl, clnt); - close(fd); - dprintf(N_DEBUG, "MONITORING %s for %s", mon_name, my_name); + 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". @@ -232,75 +208,49 @@ sm_mon_1_svc(struct mon *argp, struct svc_req *rqstp) return (&result); failure: - note(N_WARNING, "STAT_FAIL to %s for SM_MON of %s", my_name, mon_name); + xlog_warn("STAT_FAIL to %s for SM_MON of %s", my_name, mon_name); return (&result); } -void load_state(void) +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) { - DIR *d; - struct dirent *de; - char buf[LINELEN + 1 + SM_MAXSTRLEN + 2]; - - d = opendir(SM_DIR); - if (!d) - return; - while ((de = readdir(d))) { - char *path; - FILE *f; - int p; - - if (de->d_name[0] == '.') - continue; - path = xmalloc(strlen(SM_DIR)+strlen(de->d_name)+2); - sprintf(path, "%s/%s", SM_DIR, de->d_name); - f = fopen(path, "r"); - free(path); - if (f == NULL) - continue; - while (fgets(buf, sizeof(buf), f) != NULL) { - int addr, proc, prog, vers; - char priv[SM_PRIV_SIZE]; - char *monname, *myname; - char *b; - int i; - notify_list *clnt; - - buf[sizeof(buf)-1] = 0; - b = strchr(buf, '\n'); - if (b) *b = 0; - sscanf(buf, "%x %x %x %x ", - &addr, &prog, &vers, &proc); - b = buf+36; - for (i=0; idns_name = xstrdup(de->d_name); - memcpy(NL_PRIV(clnt), priv, SM_PRIV_SIZE); - nlist_insert(&rtnl, clnt); - } - fclose(f); + 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); + return 0; } - closedir(d); -} + 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"); +} /* * Services SM_UNMON requests. @@ -320,6 +270,8 @@ sm_unmon_1_svc(struct mon_id *argp, struct svc_req *rqstp) 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)) @@ -333,9 +285,8 @@ sm_unmon_1_svc(struct mon_id *argp, struct svc_req *rqstp) /* Check if we're monitoring anyone. */ if (rtnl == NULL) { - note(N_WARNING, - "Received SM_UNMON request from %s for %s while not " - "monitoring any hosts.", my_name, argp->mon_name); + xlog_warn("Received SM_UNMON request from %s for %s while not " + "monitoring any hosts", my_name, argp->mon_name); return (&result); } clnt = rtnl; @@ -347,18 +298,19 @@ sm_unmon_1_svc(struct mon_id *argp, struct svc_req *rqstp) * entry winds up in the list the way I'm currently handling them.) */ while ((clnt = nlist_gethost(clnt, mon_name, 0))) { - if (matchhostname(NL_MY_NAME(clnt), my_name) && + 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! */ - dprintf(N_DEBUG, "UNMONITORING %s for %s", + 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); - xunlink(SM_DIR, clnt->dns_name); + nsm_delete_monitored_host(clnt->dns_name, + mon_name, my_name); nlist_free(&rtnl, clnt); return (&result); @@ -367,7 +319,7 @@ sm_unmon_1_svc(struct mon_id *argp, struct svc_req *rqstp) } failure: - note(N_WARNING, "Received erroneous SM_UNMON request from %s for %s", + xlog_warn("Received erroneous SM_UNMON request from %s for %s", my_name, mon_name); return (&result); } @@ -381,13 +333,15 @@ sm_unmon_all_1_svc(struct my_id *argp, struct svc_req *rqstp) 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) { - note(N_WARNING, "Received SM_UNMON_ALL request from %s " + xlog_warn("Received SM_UNMON_ALL request from %s " "while not monitoring any hosts", my_name); return (&result); } @@ -401,7 +355,7 @@ sm_unmon_all_1_svc(struct my_id *argp, struct svc_req *rqstp) char mon_name[SM_MAXSTRLEN + 1]; notify_list *temp; - dprintf(N_DEBUG, + 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), @@ -410,7 +364,8 @@ sm_unmon_all_1_svc(struct my_id *argp, struct svc_req *rqstp) temp = NL_NEXT(clnt); /* PRC: do the HA callout: */ ha_callout("del-client", mon_name, my_name, -1); - xunlink(SM_DIR, clnt->dns_name); + nsm_delete_monitored_host(clnt->dns_name, + mon_name, my_name); nlist_free(&rtnl, clnt); ++count; clnt = temp; @@ -419,8 +374,8 @@ sm_unmon_all_1_svc(struct my_id *argp, struct svc_req *rqstp) } if (!count) { - dprintf(N_DEBUG, "SM_UNMON_ALL request from %s with no " - "SM_MON requests from it.", my_name); + xlog(D_GENERAL, "SM_UNMON_ALL request from %s with no " + "SM_MON requests from it", my_name); } failure: diff --git a/utils/statd/notlist.c b/utils/statd/notlist.c index 1698c26..0341c15 100644 --- a/utils/statd/notlist.c +++ b/utils/statd/notlist.c @@ -17,7 +17,6 @@ #endif #include -#include "misc.h" #include "statd.h" #include "notlist.h" @@ -190,7 +189,6 @@ nlist_clone(notify_list *entry) NL_MY_PROG(new) = NL_MY_PROG(entry); NL_MY_VERS(new) = NL_MY_VERS(entry); NL_MY_PROC(new) = NL_MY_PROC(entry); - NL_ADDR(new) = NL_ADDR(entry); memcpy(NL_PRIV(new), NL_PRIV(entry), SM_PRIV_SIZE); return new; @@ -234,7 +232,8 @@ nlist_gethost(notify_list *list, char *host, int myname) notify_list *lp; for (lp = list; lp; lp = lp->next) { - if (matchhostname(host, myname? NL_MY_NAME(lp) : NL_MON_NAME(lp))) + if (statd_matchhostname(host, + myname? NL_MY_NAME(lp) : NL_MON_NAME(lp))) return lp; } diff --git a/utils/statd/notlist.h b/utils/statd/notlist.h index 664c9d8..6ed0da8 100644 --- a/utils/statd/notlist.h +++ b/utils/statd/notlist.h @@ -12,15 +12,14 @@ */ struct notify_list { mon mon; /* Big honkin' NSM structure. */ - struct in_addr addr; /* IP address for callback. */ - unsigned short port; /* port number for callback */ + 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. */ - u_int32_t xid; /* XID of MS_NOTIFY RPC call */ + uint32_t xid; /* XID of MS_NOTIFY RPC call */ time_t when; /* notify: timeout for re-xmit */ }; @@ -53,7 +52,6 @@ extern notify_list * nlist_gethost(notify_list *, char *, int); #define NL_FIRST NL_NEXT #define NL_PREV(L) ((L)->prev) #define NL_DATA(L) ((L)->mon) -#define NL_ADDR(L) ((L)->addr) #define NL_STATE(L) ((L)->state) #define NL_TIMES(L) ((L)->times) #define NL_MON_ID(L) (NL_DATA((L)).mon_id) diff --git a/utils/statd/rmtcall.c b/utils/statd/rmtcall.c index cc1a4a4..0e52fe2 100644 --- a/utils/statd/rmtcall.c +++ b/utils/statd/rmtcall.c @@ -37,22 +37,19 @@ #include #include #include -#ifdef HAVE_IFADDRS_H -#include -#endif /* HAVE_IFADDRS_H */ + #include "sm_inter.h" #include "statd.h" #include "notlist.h" -#include "log.h" #include "ha-callout.h" +#include "nsm.h" +#include "nfsrpc.h" + #if SIZEOF_SOCKLEN_T - 0 == 0 #define socklen_t int #endif -#define MAXMSGSIZE (2048 / sizeof(unsigned int)) - -static unsigned long xid = 0; /* RPC XID counter */ static int sockfd = -1; /* notify socket */ /* @@ -81,7 +78,7 @@ statd_get_socket(void) if (sockfd >= 0) close(sockfd); if ((sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { - note(N_CRIT, "%s: Can't create socket: %m", __func__); + xlog(L_ERROR, "%s: Can't create socket: %m", __func__); return -1; } @@ -91,7 +88,7 @@ statd_get_socket(void) sin.sin_addr.s_addr = INADDR_ANY; if (bindresvport(sockfd, &sin) < 0) { - dprintf(N_WARNING, "%s: can't bind to reserved port", + xlog(D_GENERAL, "%s: can't bind to reserved port", __func__); break; } @@ -104,112 +101,37 @@ statd_get_socket(void) return sockfd; } -static unsigned long -xmit_call(struct sockaddr_in *sin, - u_int32_t prog, u_int32_t vers, u_int32_t proc, - xdrproc_t func, void *obj) -/* __u32 prog, __u32 vers, __u32 proc, xdrproc_t func, void *obj) */ -{ - unsigned int msgbuf[MAXMSGSIZE], msglen; - struct rpc_msg mesg; - struct pmap pmap; - XDR xdr, *xdrs = &xdr; - int err; - - if (!xid) - xid = getpid() + time(NULL); - - mesg.rm_xid = ++xid; - mesg.rm_direction = CALL; - mesg.rm_call.cb_rpcvers = 2; - if (sin->sin_port == 0) { - sin->sin_port = htons(PMAPPORT); - mesg.rm_call.cb_prog = PMAPPROG; - mesg.rm_call.cb_vers = PMAPVERS; - mesg.rm_call.cb_proc = PMAPPROC_GETPORT; - pmap.pm_prog = prog; - pmap.pm_vers = vers; - pmap.pm_prot = IPPROTO_UDP; - pmap.pm_port = 0; - func = (xdrproc_t) xdr_pmap; - obj = &pmap; - } else { - mesg.rm_call.cb_prog = prog; - mesg.rm_call.cb_vers = vers; - mesg.rm_call.cb_proc = proc; - } - mesg.rm_call.cb_cred.oa_flavor = AUTH_NULL; - mesg.rm_call.cb_cred.oa_base = (caddr_t) NULL; - mesg.rm_call.cb_cred.oa_length = 0; - mesg.rm_call.cb_verf.oa_flavor = AUTH_NULL; - mesg.rm_call.cb_verf.oa_base = (caddr_t) NULL; - mesg.rm_call.cb_verf.oa_length = 0; - - /* Create XDR memory object for encoding */ - xdrmem_create(xdrs, (caddr_t) msgbuf, sizeof(msgbuf), XDR_ENCODE); - - /* Encode the RPC header part and payload */ - if (!xdr_callmsg(xdrs, &mesg) || !func(xdrs, obj)) { - dprintf(N_WARNING, "%s: can't encode RPC message!", __func__); - xdr_destroy(xdrs); - return 0; - } - - /* Get overall length of datagram */ - msglen = xdr_getpos(xdrs); - - if ((err = sendto(sockfd, msgbuf, msglen, 0, - (struct sockaddr *) sin, sizeof(*sin))) < 0) { - dprintf(N_WARNING, "%s: sendto failed: %m", __func__); - } else if (err != msglen) { - dprintf(N_WARNING, "%s: short write: %m", __func__); - } - - xdr_destroy(xdrs); - - return err == msglen? xid : 0; -} - static notify_list * -recv_rply(struct sockaddr_in *sin, u_long *portp) +recv_rply(u_long *portp) { - unsigned int msgbuf[MAXMSGSIZE], msglen; - struct rpc_msg mesg; + char msgbuf[NSM_MAXMSGSIZE]; + ssize_t msglen; notify_list *lp = NULL; - XDR xdr, *xdrs = &xdr; - socklen_t alen = sizeof(*sin); - - /* Receive message */ - if ((msglen = recvfrom(sockfd, msgbuf, sizeof(msgbuf), 0, - (struct sockaddr *) sin, &alen)) < 0) { - dprintf(N_WARNING, "%s: recvfrom failed: %m", __func__); + 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; } - /* Create XDR object for decoding buffer */ - xdrmem_create(xdrs, (caddr_t) msgbuf, msglen, XDR_DECODE); - - memset(&mesg, 0, sizeof(mesg)); - mesg.rm_reply.rp_acpt.ar_results.where = NULL; - mesg.rm_reply.rp_acpt.ar_results.proc = (xdrproc_t) xdr_void; - - if (!xdr_replymsg(xdrs, &mesg)) { - note(N_WARNING, "%s: can't decode RPC message!", __func__); + 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]; - if (mesg.rm_reply.rp_stat != 0) { - note(N_WARNING, "%s: [%s] RPC status %d", - __func__, - inet_ntoa(sin->sin_addr), - mesg.rm_reply.rp_stat); - goto done; - } - if (mesg.rm_reply.rp_acpt.ar_stat != 0) { - note(N_WARNING, "%s: [%s] RPC status %d", - __func__, - inet_ntoa(sin->sin_addr), - mesg.rm_reply.rp_acpt.ar_stat); + xlog_warn("%s: Unrecognized reply from %s", __func__, + inet_ntop(AF_INET, &addr, buf, + (socklen_t)sizeof(buf))); goto done; } @@ -217,32 +139,15 @@ recv_rply(struct sockaddr_in *sin, u_long *portp) /* 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 != mesg.rm_xid) + if (lp->xid != xid) continue; - if (lp->addr.s_addr != sin->sin_addr.s_addr) { - char addr [18]; - strncpy (addr, inet_ntoa(lp->addr), - sizeof (addr) - 1); - addr [sizeof (addr) - 1] = '\0'; - dprintf(N_WARNING, "%s: address mismatch: " - "expected %s, got %s", __func__, - addr, inet_ntoa(sin->sin_addr)); - } - if (lp->port == 0) { - if (!xdr_u_long(xdrs, portp)) { - note(N_WARNING, - "%s: [%s] can't decode reply body!", - __func__, - inet_ntoa(sin->sin_addr)); - lp = NULL; - goto done; - } - } + if (lp->port == 0) + *portp = nsm_recv_getport(&xdr); break; } done: - xdr_destroy(xdrs); + xdr_destroy(&xdr); return lp; } @@ -253,15 +158,10 @@ static int process_entry(notify_list *lp) { struct sockaddr_in sin; - struct status new_status; - xdrproc_t func; - void *objp; - u_int32_t proc, vers, prog; -/* __u32 proc, vers, prog; */ if (NL_TIMES(lp) == 0) { - note(N_DEBUG, "%s: Cannot notify %s, giving up.", - __func__, inet_ntoa(NL_ADDR(lp))); + xlog(D_GENERAL, "%s: Cannot notify localhost, giving up", + __func__); return 0; } @@ -270,23 +170,31 @@ process_entry(notify_list *lp) sin.sin_port = lp->port; /* LH - moved address into switch */ - prog = NL_MY_PROG(lp); - vers = NL_MY_VERS(lp); - proc = NL_MY_PROC(lp); - /* __FORCE__ loopback for callbacks to lockd ... */ /* Just in case we somehow ignored it thus far */ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - func = (xdrproc_t) xdr_status; - objp = &new_status; - new_status.mon_name = NL_MON_NAME(lp); - new_status.state = NL_STATE(lp); - memcpy(new_status.priv, NL_PRIV(lp), SM_PRIV_SIZE); + 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); - lp->xid = xmit_call(&sin, prog, vers, proc, func, objp); - if (!lp->xid) { - note(N_WARNING, "%s: failed to notify port %d", + 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; @@ -300,14 +208,13 @@ process_entry(notify_list *lp) int process_reply(FD_SET_TYPE *rfds) { - struct sockaddr_in sin; notify_list *lp; u_long port; if (sockfd == -1 || !FD_ISSET(sockfd, rfds)) return 0; - if (!(lp = recv_rply(&sin, &port))) + if (!(lp = recv_rply(&port))) return 1; if (lp->port == 0) { @@ -319,10 +226,10 @@ process_reply(FD_SET_TYPE *rfds) nlist_insert_timer(¬ify, lp); return 1; } - note(N_WARNING, "%s: [%s] service %d not registered", - __func__, inet_ntoa(lp->addr), NL_MY_PROG(lp)); + xlog_warn("%s: service %d not registered on localhost", + __func__, NL_MY_PROG(lp)); } else { - dprintf(N_DEBUG, "%s: Callback to %s (for %d) succeeded.", + xlog(D_GENERAL, "%s: Callback to %s (for %d) succeeded", __func__, NL_MY_NAME(lp), NL_MON_NAME(lp)); } nlist_free(¬ify, lp); @@ -346,8 +253,8 @@ process_notify_list(void) nlist_remove(¬ify, entry); nlist_insert_timer(¬ify, entry); } else { - note(N_ERROR, - "%s: Can't callback %s (%d,%d), giving up.", + xlog(L_ERROR, + "%s: Can't callback %s (%d,%d), giving up", __func__, NL_MY_NAME(entry), NL_MY_PROG(entry), diff --git a/utils/statd/simu.c b/utils/statd/simu.c index a7ecb85..f1d0bf8 100644 --- a/utils/statd/simu.c +++ b/utils/statd/simu.c @@ -8,8 +8,10 @@ #include #endif +#include #include +#include "sockaddr.h" #include "rpcmisc.h" #include "statd.h" #include "notlist.h" @@ -19,32 +21,28 @@ 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 (void *argp, struct svc_req *rqstp) +sm_simu_crash_1_svc (__attribute__ ((unused)) void *argp, struct svc_req *rqstp) { - struct sockaddr_in *sin = nfs_getrpccaller_in(rqstp->rq_xprt); + struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt); + char buf[INET6_ADDRSTRLEN]; static char *result = NULL; - struct in_addr caller; - if (sin->sin_family != AF_INET) { - note(N_WARNING, "Call to statd from non-AF_INET address"); - goto failure; - } + xlog(D_CALL, "Received SM_SIMU_CRASH"); - caller = sin->sin_addr; - if (caller.s_addr != htonl(INADDR_LOOPBACK)) { - note(N_WARNING, "Call to statd from non-local host %s", - inet_ntoa(caller)); - goto failure; - } + if (!nfs_is_v4_loopback(sap)) + goto out_nonlocal; - if (ntohs(sin->sin_port) >= 1024) { - note(N_WARNING, "Call to statd-simu-crash from unprivileged port"); + if ((int)nfs_get_port(sap) >= IPPORT_RESERVED) { + xlog_warn("SM_SIMU_CRASH call from unprivileged port"); goto failure; } - note (N_WARNING, "*** SIMULATING CRASH! ***"); my_svc_exit (); if (rtnl) @@ -52,4 +50,10 @@ sm_simu_crash_1_svc (void *argp, struct svc_req *rqstp) 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 index de8f1c9..4ed1468 100644 --- a/utils/statd/simulate.c +++ b/utils/statd/simulate.c @@ -38,7 +38,9 @@ extern void svc_exit (void); void simulator (int argc, char **argv) { - log_enable (1); + xlog_stderr (1); + xlog_syslog (0); + xlog_open ("statd simulator"); if (argc == 2) if (!strcasecmp (*argv, "crash")) @@ -61,7 +63,7 @@ simulator (int argc, char **argv) simulate_mon (*(&argv[1]), *(&argv[2]), *(&argv[3]), *(&argv[4]), *(&argv[5])); } - die ("WTF? Give me something I can use!"); + xlog_err ("WTF? Give me something I can use!"); } static void @@ -72,11 +74,11 @@ simulate_mon (char *calling, char *monitoring, char *as, char *proggy, sm_stat_res *result; mon mon; - dprintf (N_DEBUG, "Calling %s (as %s) to monitor %s", calling, as, + xlog (D_GENERAL, "Calling %s (as %s) to monitor %s", calling, as, monitoring); if ((client = clnt_create (calling, SM_PROG, SM_VERS, "udp")) == NULL) - die ("%s", clnt_spcreateerror ("clnt_create")); + xlog_err ("%s", clnt_spcreateerror ("clnt_create")); memcpy (mon.priv, fool, SM_PRIV_SIZE); mon.mon_id.my_id.my_name = xstrdup (as); @@ -87,16 +89,15 @@ simulate_mon (char *calling, char *monitoring, char *as, char *proggy, mon.mon_id.mon_name = monitoring; if (!(result = sm_mon_1 (&mon, client))) - die ("%s", clnt_sperror (client, "sm_mon_1")); + xlog_err ("%s", clnt_sperror (client, "sm_mon_1")); free (mon.mon_id.my_id.my_name); if (result->res_stat != STAT_SUCC) { - note (N_FATAL, "SM_MON request failed, state: %d", result->state); - exit (0); + xlog_err ("SM_MON request failed, state: %d", result->state); } else { - dprintf (N_DEBUG, "SM_MON result successful, state: %d\n", result->state); - dprintf (N_DEBUG, "Waiting for callback."); + xlog (D_GENERAL, "SM_MON result successful, state: %d\n", result->state); + xlog (D_GENERAL, "Waiting for callback"); daemon_simulator (); exit (0); } @@ -109,11 +110,11 @@ simulate_unmon (char *calling, char *unmonitoring, char *as, char *proggy) sm_stat *result; mon_id mon_id; - dprintf (N_DEBUG, "Calling %s (as %s) to unmonitor %s", calling, as, + xlog (D_GENERAL, "Calling %s (as %s) to unmonitor %s", calling, as, unmonitoring); if ((client = clnt_create (calling, SM_PROG, SM_VERS, "udp")) == NULL) - die ("%s", clnt_spcreateerror ("clnt_create")); + 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; @@ -122,10 +123,10 @@ simulate_unmon (char *calling, char *unmonitoring, char *as, char *proggy) mon_id.mon_name = unmonitoring; if (!(result = sm_unmon_1 (&mon_id, client))) - die ("%s", clnt_sperror (client, "sm_unmon_1")); + xlog_err ("%s", clnt_sperror (client, "sm_unmon_1")); free (mon_id.my_id.my_name); - dprintf (N_DEBUG, "SM_UNMON request returned state: %d\n", result->state); + xlog (D_GENERAL, "SM_UNMON request returned state: %d\n", result->state); exit (0); } @@ -136,10 +137,10 @@ simulate_unmon_all (char *calling, char *as, char *proggy) sm_stat *result; my_id my_id; - dprintf (N_DEBUG, "Calling %s (as %s) to unmonitor all hosts", calling, as); + xlog (D_GENERAL, "Calling %s (as %s) to unmonitor all hosts", calling, as); if ((client = clnt_create (calling, SM_PROG, SM_VERS, "udp")) == NULL) - die ("%s", clnt_spcreateerror ("clnt_create")); + xlog_err ("%s", clnt_spcreateerror ("clnt_create")); my_id.my_name = xstrdup (as); my_id.my_prog = atoi (proggy) * SIM_SM_PROG; @@ -147,10 +148,10 @@ simulate_unmon_all (char *calling, char *as, char *proggy) my_id.my_proc = SIM_SM_MON; if (!(result = sm_unmon_all_1 (&my_id, client))) - die ("%s", clnt_sperror (client, "sm_unmon_all_1")); + xlog_err ("%s", clnt_sperror (client, "sm_unmon_all_1")); free (my_id.my_name); - dprintf (N_DEBUG, "SM_UNMON_ALL request returned state: %d\n", result->state); + xlog (D_GENERAL, "SM_UNMON_ALL request returned state: %d\n", result->state); exit (0); } @@ -160,10 +161,10 @@ simulate_crash (char *host) CLIENT *client; if ((client = clnt_create (host, SM_PROG, SM_VERS, "udp")) == NULL) - die ("%s", clnt_spcreateerror ("clnt_create")); + xlog_err ("%s", clnt_spcreateerror ("clnt_create")); if (!sm_simu_crash_1 (NULL, client)) - die ("%s", clnt_sperror (client, "sm_simu_crash_1")); + xlog_err ("%s", clnt_sperror (client, "sm_simu_crash_1")); exit (0); } @@ -176,18 +177,18 @@ simulate_stat (char *calling, char *monitoring) sm_stat_res *result; if ((client = clnt_create (calling, SM_PROG, SM_VERS, "udp")) == NULL) - die ("%s", clnt_spcreateerror ("clnt_create")); + xlog_err ("%s", clnt_spcreateerror ("clnt_create")); checking.mon_name = monitoring; if (!(result = sm_stat_1 (&checking, client))) - die ("%s", clnt_sperror (client, "sm_stat_1")); + xlog_err ("%s", clnt_sperror (client, "sm_stat_1")); if (result->res_stat == STAT_SUCC) - dprintf (N_DEBUG, "STAT_SUCC from %s for %s, state: %d", calling, + xlog (D_GENERAL, "STAT_SUCC from %s for %s, state: %d", calling, monitoring, result->state); else - dprintf (N_DEBUG, "STAT_FAIL from %s for %s, state: %d", calling, + xlog (D_GENERAL, "STAT_FAIL from %s for %s, state: %d", calling, monitoring, result->state); exit (0); @@ -196,9 +197,8 @@ simulate_stat (char *calling, char *monitoring) static void sim_killer (int sig) { - note (N_FATAL, "Simulator caught signal %d, un-registering and exiting.", sig); pmap_unset (sim_port, SIM_SM_VERS); - exit (0); + xlog_err ("Simulator caught signal %d, un-registering and exiting", sig); } static void @@ -219,7 +219,7 @@ sim_sm_mon_1_svc (struct status *argp, struct svc_req *rqstp) { static char *result; - dprintf (N_DEBUG, "Recieved state %d for mon_name %s (opaque \"%s\")", + 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 index 72dcff4..3259a3e 100644 --- a/utils/statd/sm-notify.c +++ b/utils/statd/sm-notify.c @@ -8,6 +8,7 @@ #include #endif +#include #include #include #include @@ -28,129 +29,114 @@ #include #include -#ifndef BASEDIR -# ifdef NFS_STATEDIR -# define BASEDIR NFS_STATEDIR -# else -# define BASEDIR "/var/lib/nfs" -# endif -#endif - -#define DEFAULT_SM_STATE_PATH BASEDIR "/state" -#define DEFAULT_SM_DIR_PATH BASEDIR "/sm" -#define DEFAULT_SM_BAK_PATH DEFAULT_SM_DIR_PATH ".bak" +#include "sockaddr.h" +#include "xlog.h" +#include "nsm.h" +#include "nfsrpc.h" -char *_SM_BASE_PATH = BASEDIR; -char *_SM_STATE_PATH = DEFAULT_SM_STATE_PATH; -char *_SM_DIR_PATH = DEFAULT_SM_DIR_PATH; -char *_SM_BAK_PATH = DEFAULT_SM_BAK_PATH; +#ifndef HAVE_DECL_AI_ADDRCONFIG +#define AI_ADDRCONFIG 0 +#endif -#define NSM_PROG 100024 -#define NSM_PROGRAM 100024 -#define NSM_VERSION 1 #define NSM_TIMEOUT 2 -#define NSM_NOTIFY 6 #define NSM_MAX_TIMEOUT 120 /* don't make this too big */ -#define MAXMSGSIZE 256 struct nsm_host { struct nsm_host * next; char * name; - char * path; - struct sockaddr_storage addr; + char * mon_name; + char * my_name; struct addrinfo *ai; time_t last_used; time_t send_next; unsigned int timeout; unsigned int retries; - unsigned int xid; + uint32_t xid; }; static char nsm_hostname[256]; -static uint32_t nsm_state; +static int nsm_state; +static int nsm_family = AF_INET; static int opt_debug = 0; -static int opt_quiet = 0; -static int opt_update_state = 1; +static _Bool opt_update_state = true; static unsigned int opt_max_retry = 15 * 60; -static char * opt_srcaddr = 0; -static uint16_t opt_srcport = 0; -static int log_syslog = 0; +static char * opt_srcaddr = NULL; +static char * opt_srcport = NULL; -static unsigned int nsm_get_state(int); -static void notify(void); +static void notify(const int sock); static int notify_host(int, struct nsm_host *); static void recv_reply(int); -static void backup_hosts(const char *, const char *); -static void get_hosts(const char *); static void insert_host(struct nsm_host *); static struct nsm_host *find_host(uint32_t); -static void nsm_log(int fac, const char *fmt, ...); static int record_pid(void); -static void drop_privs(void); -static void set_kernel_nsm_state(int state); static struct nsm_host * hosts = NULL; -/* - * Address handling utilities - */ - -static unsigned short smn_get_port(const struct sockaddr *sap) +__attribute_malloc__ +static struct addrinfo * +smn_lookup(const char *name) { - switch (sap->sa_family) { - case AF_INET: - return ntohs(((struct sockaddr_in *)sap)->sin_port); - case AF_INET6: - return ntohs(((struct sockaddr_in6 *)sap)->sin6_port); - } - return 0; -} + struct addrinfo *ai = NULL; + struct addrinfo hint = { + .ai_flags = AI_ADDRCONFIG, + .ai_family = (nsm_family == AF_INET ? AF_INET: AF_UNSPEC), + .ai_protocol = (int)IPPROTO_UDP, + }; + int error; -static void smn_set_port(struct sockaddr *sap, const unsigned short port) -{ - switch (sap->sa_family) { - case AF_INET: - ((struct sockaddr_in *)sap)->sin_port = htons(port); - break; - case AF_INET6: - ((struct sockaddr_in6 *)sap)->sin6_port = htons(port); - break; + error = getaddrinfo(name, NULL, &hint, &ai); + if (error != 0) { + xlog(D_GENERAL, "getaddrinfo(3): %s", gai_strerror(error)); + return NULL; } + + return ai; } -static struct addrinfo *smn_lookup(const char *name) +__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 addrinfo *ai, hint = { -#if HAVE_DECL_AI_ADDRCONFIG - .ai_flags = AI_ADDRCONFIG, -#endif /* HAVE_DECL_AI_ADDRCONFIG */ - .ai_family = AF_INET, - .ai_protocol = IPPROTO_UDP, - }; - int error; + struct nsm_host *host; - error = getaddrinfo(name, NULL, &hint, &ai); - switch (error) { - case 0: - return ai; - case EAI_SYSTEM: - if (opt_debug) - nsm_log(LOG_ERR, "getaddrinfo(3): %s", - strerror(errno)); - break; - default: - if (opt_debug) - nsm_log(LOG_ERR, "getaddrinfo(3): %s", - gai_strerror(error)); + host = calloc(1, sizeof(*host)); + if (host == NULL) + goto out_nomem; + + host->name = strdup(hostname); + host->mon_name = strdup(mon_name); + host->my_name = strdup(my_name); + if (host->name == NULL || + host->mon_name == NULL || + host->my_name == NULL) { + free(host->my_name); + free(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) { - unlink(host->path); - free(host->path); + 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->my_name); + free(host->mon_name); free(host->name); if (host->ai) freeaddrinfo(host->ai); @@ -158,13 +144,219 @@ static void smn_forget_host(struct nsm_host *host) 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); + xlog(D_GENERAL, "Added host %s to notify list", hostname); + 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_family = nsm_family, + .ai_protocol = (int)IPPROTO_UDP, + }; + int error; + + if (srcaddr == NULL) + hint.ai_flags |= AI_PASSIVE; + + 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"); + 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"); + 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++; + freeaddrinfo(ai); + (void)close(sock); + goto retry; + } + } + + freeaddrinfo(ai); + return sock; +} + int main(int argc, char **argv) { - int c; - int force = 0; + int c, sock, force = 0; + char * progname; - while ((c = getopt(argc, argv, "dm:np:v:qP:f")) != -1) { + progname = strrchr(argv[0], '/'); + if (progname != NULL) + progname++; + else + progname = argv[0]; + + while ((c = getopt(argc, argv, "dm:np:v:P:f")) != -1) { switch (c) { case 'f': force = 1; @@ -176,32 +368,17 @@ main(int argc, char **argv) opt_max_retry = atoi(optarg) * 60; break; case 'n': - opt_update_state = 0; + opt_update_state = false; break; case 'p': - opt_srcport = atoi(optarg); + opt_srcport = optarg; break; case 'v': opt_srcaddr = optarg; break; - case 'q': - opt_quiet = 1; - break; case 'P': - _SM_BASE_PATH = strdup(optarg); - _SM_STATE_PATH = malloc(strlen(optarg)+1+sizeof("state")); - _SM_DIR_PATH = malloc(strlen(optarg)+1+sizeof("sm")); - _SM_BAK_PATH = malloc(strlen(optarg)+1+sizeof("sm.bak")); - if (_SM_BASE_PATH == NULL || - _SM_STATE_PATH == NULL || - _SM_DIR_PATH == NULL || - _SM_BAK_PATH == NULL) { - nsm_log(LOG_ERR, "unable to allocate memory"); + if (!nsm_setup_pathnames(argv[0], optarg)) exit(1); - } - strcat(strcpy(_SM_STATE_PATH, _SM_BASE_PATH), "/state"); - strcat(strcpy(_SM_DIR_PATH, _SM_BASE_PATH), "/sm"); - strcat(strcpy(_SM_BAK_PATH, _SM_BASE_PATH), "/sm.bak"); break; default: @@ -211,18 +388,26 @@ main(int argc, char **argv) if (optind < argc) { usage: fprintf(stderr, - "Usage: sm-notify [-dfq] [-m max-retry-minutes] [-p srcport]\n" - " [-P /path/to/state/directory] [-v my_host_name]\n"); + "Usage: %s -notify [-dfq] [-m max-retry-minutes] [-p srcport]\n" + " [-P /path/to/state/directory] [-v my_host_name]\n", + progname); exit(1); } - log_syslog = 1; - openlog("sm-notify", LOG_PID, LOG_DAEMON); + xlog_syslog(1); + if (opt_debug) { + xlog_stderr(1); + xlog_config(D_ALL, 1); + } else + xlog_stderr(0); + + xlog_open(progname); + xlog(L_NOTICE, "Version " VERSION " starting"); - if (strcmp(_SM_BASE_PATH, BASEDIR) == 0) { - if (record_pid() == 0 && force == 0 && opt_update_state == 1) { + if (nsm_is_default_parentdir()) { + if (record_pid() == 0 && force == 0 && opt_update_state) { /* already run, don't try again */ - nsm_log(LOG_NOTICE, "Already notifying clients; Exiting!"); + xlog(L_NOTICE, "Already notifying clients; Exiting!"); exit(0); } } @@ -231,31 +416,26 @@ usage: fprintf(stderr, strncpy(nsm_hostname, opt_srcaddr, sizeof(nsm_hostname)-1); } else if (gethostname(nsm_hostname, sizeof(nsm_hostname)) < 0) { - nsm_log(LOG_ERR, "Failed to obtain name of local host: %s", - strerror(errno)); + xlog(L_ERROR, "Failed to obtain name of local host: %m"); exit(1); } - backup_hosts(_SM_DIR_PATH, _SM_BAK_PATH); - get_hosts(_SM_BAK_PATH); - - /* If there are not hosts to notify, just exit */ - if (!hosts) { - nsm_log(LOG_DEBUG, "No hosts to notify; exiting"); + (void)nsm_retire_monitored_hosts(); + if (nsm_load_notify_list(smn_get_host) == 0) { + xlog(D_GENERAL, "No hosts to notify; exiting"); return 0; } - /* Get and update the NSM state. This will call sync() */ nsm_state = nsm_get_state(opt_update_state); - set_kernel_nsm_state(nsm_state); + if (nsm_state == 0) + exit(1); + nsm_update_kernel_state(nsm_state); if (!opt_debug) { - if (!opt_quiet) - printf("Backgrounding to notify hosts...\n"); + xlog(L_NOTICE, "Backgrounding to notify hosts...\n"); if (daemon(0, 0) < 0) { - nsm_log(LOG_ERR, "unable to background: %s", - strerror(errno)); + xlog(L_ERROR, "unable to background: %m"); exit(1); } @@ -264,15 +444,21 @@ usage: fprintf(stderr, close(2); } - notify(); + 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; - nsm_log(LOG_NOTICE, - "Unable to notify %s, giving up", + xlog(L_NOTICE, "Unable to notify %s, giving up", hp->name); } exit(1); @@ -285,69 +471,13 @@ usage: fprintf(stderr, * Notify hosts */ static void -notify(void) +notify(const int sock) { - struct sockaddr_storage address; - struct sockaddr *local_addr = (struct sockaddr *)&address; time_t failtime = 0; - int sock = -1; - int retry_cnt = 0; - - retry: - sock = socket(AF_INET, SOCK_DGRAM, 0); - if (sock < 0) { - nsm_log(LOG_ERR, "Failed to create RPC socket: %s", - strerror(errno)); - exit(1); - } - fcntl(sock, F_SETFL, O_NONBLOCK); - - memset(&address, 0, sizeof(address)); - local_addr->sa_family = AF_INET; /* Default to IPv4 */ - - /* Bind source IP if provided on command line */ - if (opt_srcaddr) { - struct addrinfo *ai = smn_lookup(opt_srcaddr); - if (!ai) { - nsm_log(LOG_ERR, - "Not a valid hostname or address: \"%s\"", - opt_srcaddr); - exit(1); - } - - /* We know it's IPv4 at this point */ - memcpy(local_addr, ai->ai_addr, ai->ai_addrlen); - - freeaddrinfo(ai); - } - - /* Use source port if provided on the command line, - * otherwise use bindresvport */ - if (opt_srcport) { - smn_set_port(local_addr, opt_srcport); - if (bind(sock, local_addr, sizeof(struct sockaddr_in)) < 0) { - nsm_log(LOG_ERR, "Failed to bind RPC socket: %s", - strerror(errno)); - exit(1); - } - } else { - struct servent *se; - struct sockaddr_in *sin = (struct sockaddr_in *)local_addr; - (void) bindresvport(sock, sin); - /* try to avoid known ports */ - se = getservbyport(sin->sin_port, "udp"); - if (se && retry_cnt < 100) { - retry_cnt++; - close(sock); - goto retry; - } - } if (opt_max_retry) failtime = time(NULL) + opt_max_retry; - drop_privs(); - while (hosts) { struct pollfd pfd; time_t now = time(NULL); @@ -383,7 +513,7 @@ notify(void) if (hosts == NULL) return; - nsm_log(LOG_DEBUG, "Host %s due in %ld seconds", + xlog(D_GENERAL, "Host %s due in %ld seconds", hosts->name, wait); pfd.fd = sock; @@ -405,34 +535,18 @@ notify(void) static int notify_host(int sock, struct nsm_host *host) { - struct sockaddr_storage address; - struct sockaddr *dest = (struct sockaddr *)&address; - socklen_t destlen = sizeof(address); - static unsigned int xid = 0; - uint32_t msgbuf[MAXMSGSIZE], *p; - unsigned int len; - - if (!xid) - xid = getpid() + time(NULL); - if (!host->xid) - host->xid = xid++; + struct sockaddr *sap; + socklen_t salen; if (host->ai == NULL) { host->ai = smn_lookup(host->name); if (host->ai == NULL) { - nsm_log(LOG_WARNING, - "DNS resolution of %s failed; " + xlog_warn("DNS resolution of %s failed; " "retrying later", host->name); return 0; } } - memset(msgbuf, 0, sizeof(msgbuf)); - p = msgbuf; - *p++ = htonl(host->xid); - *p++ = 0; - *p++ = htonl(2); - /* 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 @@ -440,10 +554,7 @@ notify_host(int sock, struct nsm_host *host) */ if (host->retries >= 4) { /* don't rotate if there is only one addrinfo */ - if (host->ai->ai_next == NULL) - memcpy(&host->addr, host->ai->ai_addr, - host->ai->ai_addrlen); - else { + if (host->ai->ai_next != NULL) { struct addrinfo *first = host->ai; struct addrinfo **next = &host->ai; @@ -456,213 +567,100 @@ notify_host(int sock, struct nsm_host *host) next = & (*next)->ai_next; /* put first entry at end */ *next = first; - memcpy(&host->addr, first->ai_addr, - first->ai_addrlen); } - smn_set_port((struct sockaddr *)&host->addr, 0); + nfs_set_port(host->ai->ai_addr, 0); host->retries = 0; } - memcpy(dest, &host->addr, destlen); - if (smn_get_port(dest) == 0) { - /* Build a PMAP packet */ - nsm_log(LOG_DEBUG, "Sending portmap query to %s", host->name); + sap = host->ai->ai_addr; + salen = host->ai->ai_addrlen; - smn_set_port(dest, 111); - *p++ = htonl(100000); - *p++ = htonl(2); - *p++ = htonl(3); - - /* Auth and verf */ - *p++ = 0; *p++ = 0; - *p++ = 0; *p++ = 0; - - *p++ = htonl(NSM_PROGRAM); - *p++ = htonl(NSM_VERSION); - *p++ = htonl(IPPROTO_UDP); - *p++ = 0; - } else { - /* Build an SM_NOTIFY packet */ - nsm_log(LOG_DEBUG, "Sending SM_NOTIFY to %s", host->name); - - *p++ = htonl(NSM_PROGRAM); - *p++ = htonl(NSM_VERSION); - *p++ = htonl(NSM_NOTIFY); - - /* Auth and verf */ - *p++ = 0; *p++ = 0; - *p++ = 0; *p++ = 0; - - /* state change */ - len = strlen(nsm_hostname); - *p++ = htonl(len); - memcpy(p, nsm_hostname, len); - p += (len + 3) >> 2; - *p++ = htonl(nsm_state); - } - len = (p - msgbuf) << 2; - - if (sendto(sock, msgbuf, len, 0, dest, destlen) < 0) - nsm_log(LOG_WARNING, "Sending Reboot Notification to " - "'%s' failed: errno %d (%s)", host->name, errno, strerror(errno)); + 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, nsm_hostname, nsm_state); return 0; } /* - * Receive reply from remote host + * Extract the returned port number and set up the SM_NOTIFY call. */ static void -recv_reply(int sock) +recv_rpcbind_reply(struct sockaddr *sap, struct nsm_host *host, XDR *xdr) { - struct nsm_host *hp; - struct sockaddr *sap; - uint32_t msgbuf[MAXMSGSIZE], *p, *end; - uint32_t xid; - int res; - - res = recv(sock, msgbuf, sizeof(msgbuf), 0); - if (res < 0) - return; - - nsm_log(LOG_DEBUG, "Received packet..."); + uint16_t port = nsm_recv_rpcbind(sap->sa_family, xdr); - p = msgbuf; - end = p + (res >> 2); - - xid = ntohl(*p++); - if (*p++ != htonl(1) /* must be REPLY */ - || *p++ != htonl(0) /* must be ACCEPTED */ - || *p++ != htonl(0) /* must be NULL verifier */ - || *p++ != htonl(0) - || *p++ != htonl(0)) /* must be SUCCESS */ - return; + host->send_next = time(NULL); + host->xid = 0; - /* Before we look at the data, find the host struct for - this reply */ - if ((hp = find_host(xid)) == NULL) - return; - sap = (struct sockaddr *)&hp->addr; - - if (smn_get_port(sap) == 0) { - /* This was a portmap request */ - unsigned int port; - - port = ntohl(*p++); - if (p > end) - goto fail; - - hp->send_next = time(NULL); - if (port == 0) { - /* No binding for statd. Delay the next - * portmap query for max timeout */ - nsm_log(LOG_DEBUG, "No statd on %s", hp->name); - hp->timeout = NSM_MAX_TIMEOUT; - hp->send_next += NSM_MAX_TIMEOUT; - } else { - smn_set_port(sap, port); - if (hp->timeout >= NSM_MAX_TIMEOUT / 4) - hp->timeout = NSM_MAX_TIMEOUT / 4; - } - hp->xid = 0; + if (port == 0) { + /* No binding for statd... */ + xlog(D_GENERAL, "No statd on host %s", host->name); + host->timeout = NSM_MAX_TIMEOUT; + host->send_next += NSM_MAX_TIMEOUT; } else { - /* Successful NOTIFY call. Server returns void, - * so nothing we need to do here (except - * check that we didn't read past the end of the - * packet) - */ - if (p <= end) { - nsm_log(LOG_DEBUG, "Host %s notified successfully", - hp->name); - smn_forget_host(hp); - return; - } + nfs_set_port(sap, port); + if (host->timeout >= NSM_MAX_TIMEOUT / 4) + host->timeout = NSM_MAX_TIMEOUT / 4; } -fail: /* Re-insert the host */ - insert_host(hp); + insert_host(host); } /* - * Back up all hosts from the sm directory to sm.bak + * Successful NOTIFY call. Server returns void, so nothing + * we need to do here. */ static void -backup_hosts(const char *dirname, const char *bakname) +recv_notify_reply(struct nsm_host *host) { - struct dirent *de; - DIR *dir; - - if (!(dir = opendir(dirname))) { - nsm_log(LOG_WARNING, - "Failed to open %s: %s", dirname, strerror(errno)); - return; - } + xlog(D_GENERAL, "Host %s notified successfully", host->name); - while ((de = readdir(dir)) != NULL) { - char src[1024], dst[1024]; - - if (de->d_name[0] == '.') - continue; - - snprintf(src, sizeof(src), "%s/%s", dirname, de->d_name); - snprintf(dst, sizeof(dst), "%s/%s", bakname, de->d_name); - if (rename(src, dst) < 0) { - nsm_log(LOG_WARNING, - "Failed to rename %s -> %s: %m", - src, dst); - } - } - closedir(dir); + smn_forget_host(host); } /* - * Get all entries from sm.bak and convert them to host entries + * Receive reply from remote host */ static void -get_hosts(const char *dirname) +recv_reply(int sock) { - struct nsm_host *host; - struct dirent *de; - DIR *dir; + struct nsm_host *hp; + struct sockaddr *sap; + char msgbuf[NSM_MAXMSGSIZE]; + uint32_t xid; + ssize_t msglen; + XDR xdr; - if (!(dir = opendir(dirname))) { - nsm_log(LOG_WARNING, - "Failed to open %s: %s", dirname, strerror(errno)); + memset(msgbuf, 0 , sizeof(msgbuf)); + msglen = recv(sock, msgbuf, sizeof(msgbuf), 0); + if (msglen < 0) return; - } - - host = NULL; - while ((de = readdir(dir)) != NULL) { - struct stat stb; - char path[1024]; - if (de->d_name[0] == '.') - continue; - if (host == NULL) - host = calloc(1, sizeof(*host)); - if (host == NULL) { - nsm_log(LOG_WARNING, "Unable to allocate memory"); - return; - } + xlog(D_GENERAL, "Received packet..."); - snprintf(path, sizeof(path), "%s/%s", dirname, de->d_name); - if (stat(path, &stb) < 0) - continue; + memset(&xdr, 0, sizeof(xdr)); + xdrmem_create(&xdr, msgbuf, (unsigned int)msglen, XDR_DECODE); + xid = nsm_parse_reply(&xdr); + if (xid == 0) + goto out; - host->last_used = stb.st_mtime; - host->timeout = NSM_TIMEOUT; - host->path = strdup(path); - host->name = strdup(de->d_name); - host->retries = 100; /* force address retry */ + /* Before we look at the data, find the host struct for + this reply */ + if ((hp = find_host(xid)) == NULL) + goto out; - insert_host(host); - host = NULL; - } - closedir(dir); + sap = hp->ai->ai_addr; + if (nfs_get_port(sap) == 0) + recv_rpcbind_reply(sap, hp, &xdr); + else + recv_notify_reply(hp); - if (host) - free(host); +out: + xdr_destroy(&xdr); } /* @@ -712,84 +710,6 @@ find_host(uint32_t xid) return NULL; } - -/* - * Retrieve the current NSM state - */ -static unsigned int -nsm_get_state(int update) -{ - char newfile[PATH_MAX]; - int fd, state; - - if ((fd = open(_SM_STATE_PATH, O_RDONLY)) < 0) { - if (!opt_quiet) { - nsm_log(LOG_WARNING, "%s: %m", _SM_STATE_PATH); - nsm_log(LOG_WARNING, "Creating %s, set initial state 1", - _SM_STATE_PATH); - } - state = 1; - update = 1; - } else { - if (read(fd, &state, sizeof(state)) != sizeof(state)) { - nsm_log(LOG_WARNING, - "%s: bad file size, setting state = 1", - _SM_STATE_PATH); - state = 1; - update = 1; - } else { - if (!(state & 1)) - state += 1; - } - close(fd); - } - - if (update) { - state += 2; - snprintf(newfile, sizeof(newfile), - "%s.new", _SM_STATE_PATH); - if ((fd = open(newfile, O_CREAT|O_WRONLY, 0644)) < 0) { - nsm_log(LOG_ERR, "Cannot create %s: %m", newfile); - exit(1); - } - if (write(fd, &state, sizeof(state)) != sizeof(state)) { - nsm_log(LOG_ERR, - "Failed to write state to %s", newfile); - exit(1); - } - close(fd); - if (rename(newfile, _SM_STATE_PATH) < 0) { - nsm_log(LOG_ERR, - "Cannot create %s: %m", _SM_STATE_PATH); - exit(1); - } - sync(); - } - - return state; -} - -/* - * Log a message - */ -static void -nsm_log(int fac, const char *fmt, ...) -{ - va_list ap; - - if (fac == LOG_DEBUG && !opt_debug) - return; - - va_start(ap, fmt); - if (log_syslog) - vsyslog(fac, fmt, ap); - else { - vfprintf(stderr, fmt, ap); - fputs("\n", stderr); - } - va_end(ap); -} - /* * Record pid in /var/run/sm-notify.pid * This file should remain until a reboot, even if the @@ -799,61 +719,20 @@ nsm_log(int fac, const char *fmt, ...) static int record_pid(void) { char pid[20]; + ssize_t len; int fd; - snprintf(pid, 20, "%d\n", getpid()); + (void)snprintf(pid, sizeof(pid), "%d\n", (int)getpid()); fd = open("/var/run/sm-notify.pid", O_CREAT|O_EXCL|O_WRONLY, 0600); if (fd < 0) return 0; - if (write(fd, pid, strlen(pid)) != strlen(pid)) { - nsm_log(LOG_WARNING, "Writing to pid file failed: errno %d(%s)", - errno, strerror(errno)); - } - close(fd); - return 1; -} -/* Drop privileges to match owner of state-directory - * (in case a reply triggers some unknown bug). - */ -static void drop_privs(void) -{ - struct stat st; - - if (stat(_SM_DIR_PATH, &st) == -1 && - stat(_SM_BASE_PATH, &st) == -1) { - st.st_uid = 0; - st.st_gid = 0; - } - - if (st.st_uid == 0) { - nsm_log(LOG_WARNING, - "sm-notify running as root. chown %s to choose different user", - _SM_DIR_PATH); - return; - } - - setgroups(0, NULL); - if (setgid(st.st_gid) == -1 - || setuid(st.st_uid) == -1) { - nsm_log(LOG_ERR, "Fail to drop privileges"); - exit(1); + 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); } -} -static void set_kernel_nsm_state(int state) -{ - int fd; - const char *file = "/proc/sys/fs/nfs/nsm_local_state"; - - fd = open(file ,O_WRONLY); - if (fd >= 0) { - char buf[20]; - snprintf(buf, sizeof(buf), "%d", state); - if (write(fd, buf, strlen(buf)) != strlen(buf)) { - nsm_log(LOG_WARNING, "Writing to '%s' failed: errno %d (%s)", - file, errno, strerror(errno)); - } - close(fd); - } + (void)close(fd); + return 1; } diff --git a/utils/statd/sm-notify.man b/utils/statd/sm-notify.man index dd03b8d..163713e 100644 --- a/utils/statd/sm-notify.man +++ b/utils/statd/sm-notify.man @@ -1,166 +1,315 @@ -.\" -.\" sm-notify(8) +.\"@(#)sm-notify.8" .\" .\" Copyright (C) 2004 Olaf Kirch -.TH sm-notify 8 "19 Mar 2007 +.\" +.\" Rewritten by Chuck Lever , 2009. +.\" Copyright 2009 Oracle. All rights reserved. +.\" +.TH SM-NOTIFY 8 "1 November 2009 .SH NAME -sm-notify \- Send out NSM reboot notifications +sm-notify \- send reboot notifications to NFS peers .SH SYNOPSIS -.BI "/sbin/sm-notify [-dfq] [-m " time "] [-p " port "] [-P " path "] [-v " my_name " ] +.BI "/usr/sbin/sm-notify [-dfn] [-m " minutes "] [-v " name "] [-p " notify-port "] [-P " path "] .SH DESCRIPTION -File locking over NFS (v2 and v3) requires a facility to notify peers in -case of a reboot, so that clients can reclaim locks after -a server crash, and/or -servers can release locks held by the rebooted client. +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 -This is a two-step process: during normal -operations, a mechanism is required to keep track of which -hosts need to be informed of a reboot. And of course, -notifications need to be sent out during reboot. -The protocol used for this is called NSM, for -.IR "Network Status Monitor" . +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 -This implementation separates these into separate program. +The local NFS lock manager alerts its local .B rpc.statd -tracks hosts which need to be notified and this +of each remote peer that should be monitored. +When the local system reboots, the .B sm-notify -performs the notification. When +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 -is started it will typically started +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 -but this is configurable. -.SS Operation -For each NFS client or server machine to be monitored, +command normally sends the results of +.BR gethostname (3) +as the +.I my_name +string. +The remote .B rpc.statd -creates a file in -.BR /var/lib/nfs/sm ", " -and removes the file if monitoring is no longer required. +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 -When the machine is rebooted, +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 -iterates through these files and notifies the peer -.B statd -server on those machines. +command. +.B rpc.statd +uses this number to distinguish between actual reboots +and replayed notifications. .PP -Each machine has an -.I "NSM state" , -which is basically an integer counter that is incremented -each time the machine reboots. This counter is stored -in -.BR /var/lib/nfs/state , -and updated by -.BR sm-notify . -.SS Security -.B sm-notify -has little need for root privileges and so drops them as soon as -possible. -It continues to need to make changes to the -.B sm -and -.B sm.bak -directories so to be able to drop privileges, these must be writable -by a non-privileged user. If these directories are owned by a -non-root user, -.B sm-notify -will drop privilege to match that user once it has created sockets for -sending out request (for which it needs privileged) but before it -processes any reply (which is the most likely source of possible -privilege abuse). +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 -.BI -m " failtime -When notifying hosts, +.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 -will try to contact each host for up to 15 minutes, -and will give up if unable to reach it within this time -frame. +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 -Using the -.B -m -option, you can override this. A value of 0 tells -sm-notify to retry indefinitely; any other value is -interpreted as the maximum retry time in minutes. +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 -.BI -v " ipaddr-or-hostname -This option tells -.B sm-notify -to bind to the specified -.IR ipaddr , -(or the ipaddr of the given -.IR hostname ) -so that all notification packets originate from this address. -This is useful for NFS failover. The given name is also used as the -.I name -of this host in the NSM request. +.B -n +Prevents +.B sm-notify +from updating the local system's NSM state number. .TP .BI -p " port -instructs +Specifies the source port number .B sm-notify -to bind to the indicated IP -.IR port -number. If this option is not given, it will try to bind to -a randomly chosen privileged port below 1024. -.TP -.B -q -Be quiet. This suppresses all messages except error -messages while collecting the list of hosts. +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 " /path/to/state/directory -If +.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 -should look in a no-standard place of state file, the path can be -given here. The directories -.B sm -and -.B sm.bak -and the file -.B state -must exist in that directory with the standard names. +attempts to set its effective UID and GID to the owner +and group of this directory. .TP -.B -f -If the state path has not been reset with -.BR -P , +.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 -will normally create a file in -.B /var/run -to indicate that it has been -run. If this file is found when +uses a wildcard address as the transport bind address, +and uses the results of +.BR gethostname (3) +as the +.I mon_name +argument. +.IP +The +.I ipaddr +form can be expressed as either an IPv4 or an IPv6 presentation address. +.IP +This option can be useful in multi-homed configurations where +the remote requires notification from a specific network address. +.SH SECURITY +The .B sm-notify -starts, it will not run again (as it is normally only needed once per -reboot). -If -.B -f -(for -.BR force ) -is given, +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 +.IP +The use of network addresses as a +.I mon_name +or a +.I my_name +string should be avoided when +interoperating with non-Linux NFS implementations. +.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 -will run even if the file in -.B /var/run -is present. -.TP -.B -n -Do not update the NSM state. This is for testing only. Setting this -flag implies -.BR -f . -.TP -.B -d -Enables debugging. -By default, +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 -forks and puts itself in the background after obtaining the -list of hosts from -.BR /var/lib/nfs/sm . +command supports sending notification only via datagram transport protocols. .SH FILES -.BR /var/lib/nfs/state -.br -.BR /var/lib/nfs/sm/* +.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 -.BR /var/lib/nfs/sm.bak/* +RFC 1813 - "NFS Version 3 Protocol Specification" .br -.BR /var/run/sm-notify.pid -.SH SEE ALSO -.BR rpc.nfsd(8), -.BR portmap(8) +OpenGroup Protocols for Interworking: XNFS, Version 3W - Chapter 11 .SH AUTHORS -.br Olaf Kirch +.br +Chuck Lever diff --git a/utils/statd/sm_inter.x b/utils/statd/sm_inter.x deleted file mode 100644 index d8e0ad7..0000000 --- a/utils/statd/sm_inter.x +++ /dev/null @@ -1,131 +0,0 @@ -/* - * 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 -#endif - -program SM_PROG { - version SM_VERS { - /* res_stat = stat_succ if status monitor agrees to monitor */ - /* res_stat = stat_fail if status monitor cannot monitor */ - /* if res_stat == stat_succ, state = state number of site sm_name */ - struct sm_stat_res SM_STAT(struct sm_name) = 1; - - /* res_stat = stat_succ if status monitor agrees to monitor */ - /* res_stat = stat_fail if status monitor cannot monitor */ - /* stat consists of state number of local site */ - struct sm_stat_res SM_MON(struct mon) = 2; - - /* stat consists of state number of local site */ - struct sm_stat SM_UNMON(struct mon_id) = 3; - - /* stat consists of state number of local site */ - struct sm_stat SM_UNMON_ALL(struct my_id) = 4; - - void SM_SIMU_CRASH(void) = 5; - - void SM_NOTIFY(struct stat_chge) = 6; - - } = 1; -} = 100024; - -const SM_MAXSTRLEN = 1024; -const SM_PRIV_SIZE = 16; - -struct sm_name { - string mon_name; -}; - -struct my_id { - string my_name; /* name of the site iniates the monitoring request*/ - int my_prog; /* rpc program # of the requesting process */ - int my_vers; /* rpc version # of the requesting process */ - int my_proc; /* rpc procedure # of the requesting process */ -}; - -struct mon_id { - string mon_name; /* name of the site to be monitored */ - struct my_id my_id; -}; - - -struct mon { - struct mon_id mon_id; - opaque priv[SM_PRIV_SIZE]; /* private information to store at monitor for requesting process */ -}; - -struct stat_chge { - string mon_name; /* name of the site that had the state change */ - int state; -}; - -/* - * state # of status monitor monitonically increases each time - * status of the site changes: - * an even number (>= 0) indicates the site is down and - * an odd number (> 0) indicates the site is up; - */ -struct sm_stat { - int state; /* state # of status monitor */ -}; - -enum res { - stat_succ = 0, /* status monitor agrees to monitor */ - stat_fail = 1 /* status monitor cannot monitor */ -}; - -struct sm_stat_res { - res res_stat; - int state; -}; - -/* - * structure of the status message sent back by the status monitor - * when monitor site status changes - */ -struct status { - string mon_name; - int state; - opaque priv[SM_PRIV_SIZE]; /* stored private information */ -}; - -%#define SM_INTER_X diff --git a/utils/statd/stat.c b/utils/statd/stat.c index 799239f..8d8b65e 100644 --- a/utils/statd/stat.c +++ b/utils/statd/stat.c @@ -12,7 +12,7 @@ #include #include "statd.h" -/* +/* * Services SM_STAT requests. * * According the the X/Open spec's on this procedure: "Implementations @@ -37,18 +37,23 @@ * 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, struct svc_req *rqstp) +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); - if (gethostbyname (argp->mon_name) == NULL) { - note (N_WARNING, "gethostbyname error for %s", argp->mon_name); + name = statd_canonical_name(argp->mon_name); + if (name == NULL) { result.res_stat = STAT_FAIL; - dprintf (N_DEBUG, "STAT_FAIL for %s", argp->mon_name); + xlog (D_GENERAL, "STAT_FAIL for %s", argp->mon_name); } else { result.res_stat = STAT_SUCC; - dprintf (N_DEBUG, "STAT_SUCC for %s", argp->mon_name); + 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 index 1c5247e..01fdb41 100644 --- a/utils/statd/statd.c +++ b/utils/statd/statd.c @@ -25,33 +25,21 @@ #include #include #include + #include "statd.h" -#include "version.h" #include "nfslib.h" +#include "nsm.h" /* Socket operations */ #include #include -/* Added to enable specification of state directory path at run-time - * j_carlos_gomez@yahoo.com - */ - -char * DIR_BASE = DEFAULT_DIR_BASE; - -char * SM_DIR = DEFAULT_SM_DIR; -char * SM_BAK_DIR = DEFAULT_SM_BAK_DIR; -char * SM_STAT_PATH = DEFAULT_SM_STAT_PATH; - -/* ----- end of state directory path stuff ------- */ - 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 */ -char *name_p = NULL; -const char *version_p = NULL; +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 @@ -75,7 +63,6 @@ static struct option longopts[] = }; extern void sm_prog_1 (struct svc_req *, register SVCXPRT *); -static void load_state_number(void); #ifdef SIMULATIONS extern void simulator (int, char **); @@ -88,11 +75,8 @@ extern void simulator (int, char **); static void sm_prog_1_wrapper (struct svc_req *rqstp, register SVCXPRT *transp) { - struct sockaddr_in *sin = nfs_getrpccaller_in(transp); - /* remote host authorization check */ - if (sin->sin_family == AF_INET && - !check_default("statd", sin, rqstp->rq_proc, SM_PROG)) { + if (!check_default("statd", nfs_getrpccaller(transp), SM_PROG)) { svcerr_auth (transp, AUTH_FAILED); return; } @@ -103,23 +87,26 @@ sm_prog_1_wrapper (struct svc_req *rqstp, register SVCXPRT *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) { - note (N_FATAL, "Caught signal %d, un-registering and exiting.", sig); - pmap_unset (SM_PROG, SM_VERS); - - exit (0); + statd_unregister (); + xlog_err ("Caught signal %d, un-registering and exiting", sig); } static void sigusr (int sig) { extern void my_svc_exit (void); - dprintf (N_DEBUG, "Caught signal %d, re-notifying (state %d).", sig, + xlog(D_GENERAL, "Caught signal %d, re-notifying (state %d)", sig, MY_STATE); my_svc_exit(); } @@ -140,8 +127,11 @@ static void log_modes(void) strcat(buf,"No-Daemon "); if (run_mode & MODE_LOG_STDERR) strcat(buf,"Log-STDERR "); +#ifdef HAVE_LIBTIRPC + strcat(buf, "TI-RPC "); +#endif - note(N_WARNING,buf); + xlog_warn(buf); } /* @@ -175,13 +165,12 @@ static void create_pidfile(void) unlink(pidfile); fp = fopen(pidfile, "w"); if (!fp) - die("Opening %s failed: %s\n", - pidfile, strerror(errno)); + xlog_err("Opening %s failed: %m\n", pidfile); fprintf(fp, "%d\n", getpid()); pidfd = dup(fileno(fp)); if (fclose(fp) < 0) { - note(N_WARNING, "Flushing pid file failed: errno %d (%s)\n", - errno, strerror(errno)); + xlog_warn("Flushing pid file failed: errno %d (%m)\n", + errno); } } @@ -189,42 +178,10 @@ static void truncate_pidfile(void) { if (pidfd >= 0) { if (ftruncate(pidfd, 0) < 0) { - note(N_WARNING, "truncating pid file failed: errno %d (%s)\n", - errno, strerror(errno)); - } - } -} - -static void drop_privs(void) -{ - struct stat st; - - if (stat(SM_DIR, &st) == -1 && - stat(DIR_BASE, &st) == -1) { - st.st_uid = 0; - st.st_gid = 0; - } - - if (st.st_uid == 0) { - note(N_WARNING, "statd running as root. chown %s to choose different user\n", - SM_DIR); - return; - } - /* better chown the pid file before dropping, as if it - * if over nfs we might loose access - */ - if (pidfd >= 0) { - if (fchown(pidfd, st.st_uid, st.st_gid) < 0) { - note(N_ERROR, "Unable to change owner of %s: %d (%s)", - SM_DIR, strerror (errno)); + xlog_warn("truncating pid file failed: errno %d (%m)\n", + errno); } } - setgroups(0, NULL); - if (setgid(st.st_gid) == -1 - || setuid(st.st_uid) == -1) { - note(N_ERROR, "Fail to drop privileges"); - exit(1); - } } static void run_sm_notify(int outport) @@ -266,6 +223,8 @@ int main (int argc, char **argv) /* Default: daemon mode, no other options */ run_mode = 0; + xlog_stderr(0); + xlog_syslog(1); /* Set the basename */ if ((name_p = strrchr(argv[0],'/')) != NULL) { @@ -274,13 +233,6 @@ int main (int argc, char **argv) name_p = argv[0]; } - /* Get the version */ - if ((version_p = strrchr(VERSION,' ')) != NULL) { - version_p++; - } else { - version_p = VERSION; - } - /* Set hostname */ MY_NAME = NULL; @@ -289,7 +241,7 @@ int main (int argc, char **argv) switch (arg) { case 'V': /* Version */ case 'v': - printf("%s version %s\n",name_p,version_p); + printf("%s version " VERSION "\n",name_p); exit(0); case 'F': /* Foreground/nodaemon mode */ run_mode |= MODE_NODAEMON; @@ -326,34 +278,8 @@ int main (int argc, char **argv) MY_NAME = xstrdup(optarg); break; case 'P': - - if ((DIR_BASE = xstrdup(optarg)) == NULL) { - fprintf(stderr, "%s: xstrdup(%s) failed!\n", - argv[0], optarg); - exit(1); - } - - SM_DIR = xmalloc(strlen(DIR_BASE) + 1 + sizeof("sm")); - SM_BAK_DIR = xmalloc(strlen(DIR_BASE) + 1 + sizeof("sm.bak")); - SM_STAT_PATH = xmalloc(strlen(DIR_BASE) + 1 + sizeof("state")); - - if ((SM_DIR == NULL) - || (SM_BAK_DIR == NULL) - || (SM_STAT_PATH == NULL)) { - - fprintf(stderr, "%s: xmalloc() failed!\n", - argv[0]); + if (!nsm_setup_pathnames(argv[0], optarg)) exit(1); - } - if (DIR_BASE[strlen(DIR_BASE)-1] == '/') { - sprintf(SM_DIR, "%ssm", DIR_BASE ); - sprintf(SM_BAK_DIR, "%ssm.bak", DIR_BASE ); - sprintf(SM_STAT_PATH, "%sstate", DIR_BASE ); - } else { - sprintf(SM_DIR, "%s/sm", DIR_BASE ); - sprintf(SM_BAK_DIR, "%s/sm.bak", DIR_BASE ); - sprintf(SM_STAT_PATH, "%s/state", DIR_BASE ); - } break; case 'H': /* PRC: specify the ha-callout program */ if ((ha_callout_prog = xstrdup(optarg)) == NULL) { @@ -383,7 +309,6 @@ int main (int argc, char **argv) run_sm_notify(out_port); } - if (!(run_mode & MODE_NODAEMON)) { run_mode &= ~MODE_LOG_STDERR; /* Never log to console in daemon mode. */ @@ -432,10 +357,6 @@ int main (int argc, char **argv) /* Child. */ close(pipefds[0]); setsid (); - if (chdir (DIR_BASE) == -1) { - perror("statd: Could not chdir"); - exit(1); - } while (pipefds[1] <= 2) { pipefds[1] = dup(pipefds[1]); @@ -455,7 +376,13 @@ int main (int argc, char **argv) /* Child. */ - log_init (/*name_p,version_p*/); + if (run_mode & MODE_LOG_STDERR) { + xlog_syslog(0); + xlog_stderr(1); + xlog_config(D_ALL, 1); + } + xlog_open(name_p); + xlog(L_NOTICE, "Version " VERSION " starting"); log_modes(); @@ -495,25 +422,48 @@ int main (int argc, char **argv) * pass on any SM_NOTIFY that arrives */ load_state(); - load_state_number(); - pmap_unset (SM_PROG, SM_VERS); - /* this registers both UDP and TCP services */ - rpc_init("statd", SM_PROG, SM_VERS, sm_prog_1, port); + 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, so notify parent */ if (pipefds[1] > 0) { status = 0; if (write(pipefds[1], &status, 1) != 1) { - note(N_WARNING, "writing to parent pipe failed: errno %d (%s)\n", + xlog_warn("writing to parent pipe failed: errno %d (%s)\n", errno, strerror(errno)); } close(pipefds[1]); pipefds[1] = -1; } - drop_privs(); - for (;;) { /* * Handle incoming requests: SM_NOTIFY socket requests, as @@ -541,29 +491,3 @@ int main (int argc, char **argv) } return 0; } - -static void -load_state_number(void) -{ - int fd; - const char *file = "/proc/sys/fs/nfs/nsm_local_state"; - - if ((fd = open(SM_STAT_PATH, O_RDONLY)) == -1) - return; - - if (read(fd, &MY_STATE, sizeof(MY_STATE)) != sizeof(MY_STATE)) { - note(N_WARNING, "Unable to read state from '%s': errno %d (%s)", - SM_STAT_PATH, errno, strerror(errno)); - } - close(fd); - fd = open(file, O_WRONLY); - if (fd >= 0) { - char buf[20]; - snprintf(buf, sizeof(buf), "%d", MY_STATE); - if (write(fd, buf, strlen(buf)) != strlen(buf)) - note(N_WARNING, "Writing to '%s' failed: errno %d (%s)", - file, errno, strerror(errno)); - close(fd); - } - -} diff --git a/utils/statd/statd.h b/utils/statd/statd.h index 88ba208..e89e666 100644 --- a/utils/statd/statd.h +++ b/utils/statd/statd.h @@ -11,29 +11,7 @@ #include "sm_inter.h" #include "system.h" -#include "log.h" - -/* - * Paths and filenames. - */ -#if defined(NFS_STATEDIR) -# define DEFAULT_DIR_BASE NFS_STATEDIR "/" -#else -# define DEFAULT_DIR_BASE "/var/lib/nfs/" -#endif - -#define DEFAULT_SM_DIR DEFAULT_DIR_BASE "sm" -#define DEFAULT_SM_BAK_DIR DEFAULT_DIR_BASE "sm.bak" -#define DEFAULT_SM_STAT_PATH DEFAULT_DIR_BASE "state" - -/* Added to support run-time specification of state directory path. - * j_carlos_gomez@yahoo.com - */ - -extern char * DIR_BASE; -extern char * SM_DIR; -extern char * SM_BAK_DIR; -extern char * SM_STAT_PATH; +#include "xlog.h" /* * Status definitions. @@ -44,7 +22,12 @@ extern char * SM_STAT_PATH; /* * Function prototypes. */ -extern void change_state(void); +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(void); extern void notify_hosts(void); extern void shuffle_dirs(void); @@ -53,7 +36,6 @@ extern int process_notify_list(void); extern int process_reply(FD_SET_TYPE *); extern char * xstrdup(const char *); extern void * xmalloc(size_t); -extern void xunlink (char *, char *); extern void load_state(void); /* @@ -84,10 +66,3 @@ extern int run_mode; * 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 */ -/* - * Program name and version pointers -- See statd.c for the reasoning - * as to why they're global. - */ -extern char *name_p; /* program basename */ -extern const char *version_p; /* program version */ - diff --git a/utils/statd/statd.man b/utils/statd/statd.man index e8be9f3..ffc5e95 100644 --- a/utils/statd/statd.man +++ b/utils/statd/statd.man @@ -1,191 +1,400 @@ -.\" -.\" statd(8) +.\"@(#)rpc.statd.8" .\" .\" Copyright (C) 1999 Olaf Kirch .\" Modified by Jeffrey A. Uphoff, 1999, 2002, 2005. .\" Modified by Lon Hohberger, 2000. .\" Modified by Paul Clements, 2004. -.TH rpc.statd 8 "31 Aug 2004" +.\" +.\" Rewritten by Chuck Lever , 2009. +.\" Copyright 2009 Oracle. All rights reserved. +.\" +.TH RPC.STATD 8 "1 November 2009 .SH NAME -rpc.statd \- NSM status monitor +rpc.statd \- NSM service daemon .SH SYNOPSIS -.B "rpc.statd [-FNL] [-d] [-?] [-n " name "] [-o " port "] [-p " port "] [-H " prog "] [-V]" +.BI "rpc.statd [-dh?FLNvVw] [-H " prog "] [-n " my-name "] [-o " outgoing-port "] [-p " listener-port "] [-P " path " ] .SH DESCRIPTION -The +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 -server implements the NSM (Network Status Monitor) RPC protocol. -This service is somewhat misnamed, since it doesn't actually provide -active monitoring as one might suspect; instead, NSM implements a -reboot notification service. It is used by the NFS file locking service, -.BR rpc.lockd , -to implement lock recovery when the NFS server machine crashes and -reboots. -.SS Operation -For each NFS client or server machine to be monitored, -.B rpc.statd -creates a file in -.BR /var/lib/nfs/sm . -When starting, it normally runs +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 -to iterate through these files and notify the -peer +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 -on those machines. +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 normally sends the results of +.BR gethostname (3) +as the +.I my_name +string. +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 -F -By default, +.BR -d , " --no-syslog +Causes .B rpc.statd -forks and puts itself in the background when started. The -.B -F -argument tells it to remain in the foreground. This option is -mainly for debugging purposes. -.TP -.B -d -By default, -.B rpc.statd -sends logging messages via -.BR syslog (3) -to system log. The -.B -d -argument forces it to log verbose output to -.B stderr -instead. This option is mainly for debugging purposes, and may only -be used in conjunction with the +to write log messages on +.I stderr +instead of to the system log, +if the .B -F -parameter. +option was also specified. .TP -.BI "\-n," "" " \-\-name " name -specify a name for -.B rpc.statd -to use as the local hostname. By default, -.BR rpc.statd -will call -.BR gethostname (2) -to get the local hostname. Specifying -a local hostname may be useful for machines with more than one -interfaces. +.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 -.BI "\-o," "" " \-\-outgoing\-port " port -specify a port for -.B rpc.statd -to send outgoing status requests from. By default, -.BR rpc.statd -will ask -.BR portmap (8) -to assign it a port number. As of this writing, there is not -a standard port number that -.BR portmap -always or usually assigns. Specifying -a port may be useful when implementing a firewall. +.BR -h , " -?" , " --help +Causes +.B rpc.statd +to display usage information on +.I stderr +and then exit. .TP -.BI "\-p," "" " \-\-port " port -specify a port for -.B rpc.statd -to listen on. By default, -.BR rpc.statd -will ask -.BR portmap (8) -to assign it a port number. As of this writing, there is not -a standard port number that -.BR portmap -always or usually assigns. Specifying -a port may be useful when implementing a firewall. +.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 -.BI "\-P," "" " \-\-state\-directory\-path " directory -specify a directory in which to place statd state information. -If this option is not specified the default of -.BR /var/lib/nfs -is used. +.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 -.B -N -Causes statd to run in the notify-only mode. When started in this mode, the -statd program will check its state directory, send notifications to any -monitored nodes, and exit once the notifications have been sent. This mode is -used to enable Highly Available NFS implementations (i.e. HA-NFS). -This mode is deprecated \- +.BI "\-n, " "" "\-\-name " ipaddr " | " hostname +Specifies the bind address used for RPC listener sockets. +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. +.IP +This string is also passed to the .B sm-notify -should be used directly instead. +command to be used as the source address from which +to send reboot notification requests. +See +.BR sm-notify (8) +for details. .TP -.BR -L , " --no-notify -Inhibits the running of -.BR sm-notify . -If +.BR -N +Causes +.B rpc.statd +to run the .B sm-notify -is run by some other script at boot time, there is no need for -.B statd -to start sm-notify itself. This can be appropriate if starting of -statd needs to be delayed until it is actually need. In such cases +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 -should still be run at boot time. +command should use when sending reboot notifications. +See +.BR sm-notify (8) +for details. .TP -.BI "\-H, " "" " \-\-ha-callout " prog -Specify a high availability callout program, which will receive callouts -for all client monitor and unmonitor requests. This allows +.BI "\-p," "" " \-\-port " port +Specifies the port number used for RPC listener sockets. +If this option is not specified, .B rpc.statd -to be used in a High Availability NFS (HA-NFS) environment. The -program will be run with 3 arguments: The first is either -.B add-client -or -.B del-client -depending on the reason for the callout. -The second will be the name of the client. -The third will be the name of the server as known to the client. +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 -.B -? -Causes +.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 -to print out command-line help and exit. +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 this directory. .TP -.B -V +.BR -v ", " -V ", " --version Causes .B rpc.statd -to print out version information and exit. - - - -.SH TCP_WRAPPERS SUPPORT -This +to display version information on +.I stderr +and then exit. +.SH SECURITY +The .B rpc.statd -version is protected by the +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. You have to give the clients access to -.B rpc.statd -if they should be allowed to use it. To allow connects from clients of -the .bar.com domain you could use the following line in /etc/hosts.allow: - -statd: .bar.com - -You have to use the daemon name +library, add the hostnames of peers that should be allowed access to +.IR /etc/hosts.allow . +Use the daemon name .B statd -for the daemon name (even if the binary has a different name). - -For further information please have a look at the +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) -manual pages. - -.SH SIGNALS -.BR SIGUSR1 -causes -.B rpc.statd -to re-read the notify list from disk -and send notifications to clients. This can be used in High Availability NFS -(HA-NFS) environments to notify clients to reacquire file locks upon takeover -of an NFS export from another server. - +man pages. +.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 +.IP +The use of network addresses as a +.I mon_name +or a +.I my_name +string should be avoided when +interoperating with non-Linux NFS implementations. +.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. +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 +or +.B del-client +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. +.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 FILES -.BR /var/lib/nfs/state +.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 /var/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 -.BR /var/lib/nfs/sm/* +RFC 1813 - "NFS Version 3 Protocol Specification" .br -.BR /var/lib/nfs/sm.bak/* -.SH SEE ALSO -.BR rpc.nfsd(8), -.BR portmap(8) +OpenGroup Protocols for Interworking: XNFS, Version 3W - Chapter 11 .SH AUTHORS -.br Jeff Uphoff .br Olaf Kirch @@ -195,3 +404,5 @@ H.J. Lu Lon Hohberger .br Paul Clements +.br +Chuck Lever diff --git a/utils/statd/svc_run.c b/utils/statd/svc_run.c index 14ee663..d98ecee 100644 --- a/utils/statd/svc_run.c +++ b/utils/statd/svc_run.c @@ -101,12 +101,12 @@ my_svc_run(void) tv.tv_sec = NL_WHEN(notify) - now; tv.tv_usec = 0; - dprintf(N_DEBUG, "Waiting for reply... (timeo %d)", + xlog(D_GENERAL, "Waiting for reply... (timeo %d)", tv.tv_sec); selret = select(FD_SETSIZE, &readfds, (void *) 0, (void *) 0, &tv); } else { - dprintf(N_DEBUG, "Waiting for client connections."); + xlog(D_GENERAL, "Waiting for client connections"); selret = select(FD_SETSIZE, &readfds, (void *) 0, (void *) 0, (struct timeval *) 0); } @@ -116,8 +116,7 @@ my_svc_run(void) if (errno == EINTR || errno == ECONNREFUSED || errno == ENETUNREACH || errno == EHOSTUNREACH) continue; - note(N_ERROR, "my_svc_run() - select: %s", - strerror (errno)); + xlog(L_ERROR, "my_svc_run() - select: %m"); return; case 0: diff --git a/utils/statd/version.h b/utils/statd/version.h deleted file mode 100644 index 12f16bd..0000000 --- a/utils/statd/version.h +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright (C) 1997-1999 Jeffrey A. Uphoff - * - * NSM for Linux. - */ - -#define STATD_RELEASE "1.1.1"