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
AUTOMAKE_OPTIONS = foreign
-SUBDIRS = tools support utils linux-nfs
+SUBDIRS = tools support utils linux-nfs tests
MAINTAINERCLEANFILES = Makefile.in
-# 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,
$(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 \
LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
LIBBLKID = @LIBBLKID@
LIBBSD = @LIBBSD@
+LIBCAP = @LIBCAP@
LIBCRYPT = @LIBCRYPT@
LIBNSL = @LIBNSL@
LIBOBJS = @LIBOBJS@
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@
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 \
# (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]*);; \
fi; test -z "$$fail"
$(RECURSIVE_CLEAN_TARGETS):
- @failcom='exit 1'; \
+ @fail= failcom='exit 1'; \
for f in x $$MAKEFLAGS; do \
case $$f in \
*=* | --[!k]*);; \
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 {} {} \; \
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
-# 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.
# 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)
])
[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
])
# 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]))])
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])
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
--- /dev/null
+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
# 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)
])
#! /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.
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"
#! /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
# the same distribution terms that you use for the rest of that program.
-# Originally written by Per Bothner <per@bothner.com>.
-# Please send patches to <config-patches@gnu.org>. Submit a context
-# diff and a properly formatted ChangeLog entry.
+# Originally written by Per Bothner. Please send patches (context
+# diff format) to <config-patches@gnu.org> 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,.*/,,'`
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?
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"
# => 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
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)
[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
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 \
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 ;;
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
*) 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
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 <features.h>
- #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
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:*:*)
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:*:*)
*: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}
#! /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
# Please send patches to <config-patches@gnu.org>. 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.
-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
| 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 \
| 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
| 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-* \
| 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-* \
| tahoe-* | thumb-* \
| tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* | tile-* \
| tron-* \
+ | ubicom32-* \
| v850-* | v850e-* | vax-* \
| we32k-* \
| x86-* | x86_64-* | xc16x-* | xps100-* | xscale-* | xscalee[bl]-* \
basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'`
os=-linux
;;
+ bluegene*)
+ basic_machine=powerpc-ibm
+ os=-cnk
+ ;;
c90)
basic_machine=c90-cray
os=-unicos
basic_machine=ns32k-utek
os=-sysv
;;
+ microblaze)
+ basic_machine=microblaze-xilinx
+ ;;
mingw32)
basic_machine=i386-pc
os=-mingw32
# 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|'`
;;
# 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* \
| -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*)
-sunos*)
vendor=sun
;;
- -aix*)
+ -cnk*|-aix*)
vendor=ibm
;;
-beos*)
#! /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 <linux-nfs@vger.kernel.org>.
#
# 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
HAVE_TCP_WRAPPER
HAVE_LIBWRAP
LIBWRAP
+LIBCAP
EGREP
GREP
CPP
enable_gss
SVCGSSD
GSSD
+CONFIG_NFSV41_FALSE
+CONFIG_NFSV41_TRUE
+enable_nfsv41
CONFIG_NFSV4_FALSE
CONFIG_NFSV4_TRUE
enable_nfsv4
with_start_statd
enable_nfsv3
enable_nfsv4
+enable_nfsv41
enable_gss
enable_kprefix
with_rpcgen
# 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]...
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
(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
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,
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 $@
# Define the identity of the package.
PACKAGE='nfs-utils'
- VERSION='1.2.1'
+ VERSION='1.2.2'
cat >>confdefs.h <<_ACEOF
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
+
+
+ { $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 <limits.h> declares prctl.
+ For example, HP-UX 11i <limits.h> declares gettimeofday. */
+#define prctl innocuous_prctl
+
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char prctl (); below.
+ Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ <limits.h> exists even on freestanding compilers. */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#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"
-macro_version='2.2.6'
-macro_revision='1.3012'
+macro_version='2.2.6b'
+macro_revision='1.3017'
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"
;;
*-*-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=$?
-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.
-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.
-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
-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
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
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
-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.
-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
-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
-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
_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
+
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
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
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
# 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
_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'`\\"
"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" ;;
"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;}
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)
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@:>@])],
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
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])
support/include/Makefile
support/misc/Makefile
support/nfs/Makefile
+ support/nsm/Makefile
tools/Makefile
tools/locktest/Makefile
tools/nlmtest/Makefile
utils/nfsd/Makefile
utils/nfsstat/Makefile
utils/showmount/Makefile
- utils/statd/Makefile])
+ utils/statd/Makefile
+ tests/Makefile
+ tests/nsm_client/Makefile])
AC_OUTPUT
-# 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,
$(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 \
LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
LIBBLKID = @LIBBLKID@
LIBBSD = @LIBBSD@
+LIBCAP = @LIBCAP@
LIBCRYPT = @LIBCRYPT@
LIBNSL = @LIBNSL@
LIBOBJS = @LIBOBJS@
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@
# Generated from ltmain.m4sh.
-# ltmain.sh (GNU libtool) 2.2.6
+# ltmain.sh (GNU libtool) 2.2.6b
# Written by Gordon Matzigkeit <gord@gnu.ai.mit.edu>, 1996
# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006, 2007 2008 Free Software Foundation, Inc.
# 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
#
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
## Process this file with automake to produce Makefile.in
-SUBDIRS = export include misc nfs
+SUBDIRS = export include misc nfs nsm
MAINTAINERCLEANFILES = Makefile.in
-# 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,
$(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 \
LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
LIBBLKID = @LIBBLKID@
LIBBSD = @LIBBSD@
+LIBCAP = @LIBCAP@
LIBCRYPT = @LIBCRYPT@
LIBNSL = @LIBNSL@
LIBOBJS = @LIBOBJS@
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@
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
# (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]*);; \
fi; test -z "$$fail"
$(RECURSIVE_CLEAN_TARGETS):
- @failcom='exit 1'; \
+ @fail= failcom='exit 1'; \
for f in x $$MAKEFLAGS; do \
case $$f in \
*=* | --[!k]*);; \
-# 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,
$(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 \
LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
LIBBLKID = @LIBBLKID@
LIBBSD = @LIBBSD@
+LIBCAP = @LIBCAP@
LIBCRYPT = @LIBCRYPT@
LIBNSL = @LIBNSL@
LIBOBJS = @LIBOBJS@
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@
/* 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;
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)
{
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;
}
#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
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))) {
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 */
nfs_mntent.h \
nfs_paths.h \
nfslib.h \
+ nfsrpc.h \
+ nsm.h \
rpcmisc.h \
tcpwrapper.h \
xio.h \
-# 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,
$(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 \
LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
LIBBLKID = @LIBBLKID@
LIBBSD = @LIBBSD@
+LIBCAP = @LIBCAP@
LIBCRYPT = @LIBCRYPT@
LIBNSL = @LIBNSL@
LIBOBJS = @LIBOBJS@
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@
nfs_mntent.h \
nfs_paths.h \
nfslib.h \
+ nfsrpc.h \
+ nsm.h \
rpcmisc.h \
tcpwrapper.h \
xio.h \
# (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]*);; \
fi; test -z "$$fail"
$(RECURSIVE_CLEAN_TARGETS):
- @failcom='exit 1'; \
+ @fail= failcom='exit 1'; \
for f in x $$MAKEFLAGS; do \
case $$f in \
*=* | --[!k]*);; \
/* 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
/* Define to 1 if you have the <syslog.h> header file. */
#undef HAVE_SYSLOG_H
+/* Define to 1 if you have the <sys/capability.h> header file. */
+#undef HAVE_SYS_CAPABILITY_H
+
/* Define to 1 if you have the <sys/dir.h> header file, and it defines `DIR'.
*/
#undef HAVE_SYS_DIR_H
/* 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
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;
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
-# 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,
$(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 \
LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
LIBBLKID = @LIBBLKID@
LIBBSD = @LIBBSD@
+LIBCAP = @LIBCAP@
LIBCRYPT = @LIBCRYPT@
LIBNSL = @LIBNSL@
LIBOBJS = @LIBOBJS@
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@
#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 */
#ifndef _NFS_NFS_H
#define _NFS_NFS_H
+#include <config.h>
+
#include <linux/posix_types.h>
#include <sys/types.h>
#include <netinet/in.h>
#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;
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
*/
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
*/
const unsigned short protocol,
const struct timeval *timeout);
-#endif /* __NFS_UTILS_NFSRPC_H */
+#endif /* !__NFS_UTILS_NFSRPC_H */
--- /dev/null
+/*
+ * Copyright 2009 Oracle. All rights reserved.
+ *
+ * This file is part of nfs-utils.
+ *
+ * nfs-utils is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * nfs-utils is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with nfs-utils. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * NSM for Linux.
+ */
+
+#ifndef NFS_UTILS_SUPPORT_NSM_H
+#define NFS_UTILS_SUPPORT_NSM_H
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <stdbool.h>
+
+#include <netdb.h>
+#include <time.h>
+
+#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 */
(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);
-# 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,
$(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 \
LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
LIBBLKID = @LIBBLKID@
LIBBSD = @LIBBSD@
+LIBCAP = @LIBCAP@
LIBCRYPT = @LIBCRYPT@
LIBNSL = @LIBNSL@
LIBOBJS = @LIBOBJS@
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@
--- /dev/null
+/*
+ * Copyright 2009 Oracle. All rights reserved.
+ *
+ * This file is part of nfs-utils.
+ *
+ * nfs-utils is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * nfs-utils is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with nfs-utils. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NFS_UTILS_SOCKADDR_H
+#define NFS_UTILS_SOCKADDR_H
+
+#include <libio.h>
+#include <stdbool.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+/*
+ * 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 */
-# 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,
$(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 \
LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
LIBBLKID = @LIBBLKID@
LIBBSD = @LIBBSD@
+LIBCAP = @LIBCAP@
LIBCRYPT = @LIBCRYPT@
LIBNSL = @LIBNSL@
LIBOBJS = @LIBOBJS@
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@
# (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]*);; \
fi; test -z "$$fail"
$(RECURSIVE_CLEAN_TARGETS):
- @failcom='exit 1'; \
+ @fail= failcom='exit 1'; \
for f in x $$MAKEFLAGS; do \
case $$f in \
*=* | --[!k]*);; \
-# 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,
$(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 \
LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
LIBBLKID = @LIBBLKID@
LIBBSD = @LIBBSD@
+LIBCAP = @LIBCAP@
LIBCRYPT = @LIBCRYPT@
LIBNSL = @LIBNSL@
LIBOBJS = @LIBOBJS@
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@
#include <netinet/in.h>
#include <arpa/inet.h>
-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 */
--- /dev/null
+/*
+ * Copyright (C) 2009 Red Hat <nfs@redhat.com>
+ * 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 */
-# 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,
$(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 \
LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
LIBBLKID = @LIBBLKID@
LIBBSD = @LIBBSD@
+LIBCAP = @LIBCAP@
LIBCRYPT = @LIBCRYPT@
LIBNSL = @LIBNSL@
LIBOBJS = @LIBOBJS@
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@
static char sccsid[] = "@(#) from_local.c 1.3 96/05/31 15:52:57";
#endif
-#ifdef TEST
-#undef perror
+#ifdef HAVE_CONFIG_H
+#include <config.h>
#endif
#include <sys/types.h>
#include <sys/socket.h>
+#include <stdbool.h>
#include <stdio.h>
#include <unistd.h>
#include <netdb.h>
#include <netinet/in.h>
#include <net/if.h>
#include <sys/ioctl.h>
-#include <syslog.h>
#include <stdlib.h>
#include <string.h>
+#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 <ifaddrs.h>
+#include <time.h>
+
+/**
+ * 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;
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) {
*/
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);
}
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)
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]),
#ifdef TEST
-main()
+int main(void)
{
- char *inet_ntoa();
int i;
find_local();
printf("%s\n", inet_ntoa(addrs[i]));
}
-#endif
+#endif /* TEST */
+
+#endif /* !HAVE_GETIFADDRS */
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
+
#ifdef HAVE_LIBWRAP
-#include <tcpwrapper.h>
#include <unistd.h>
#include <string.h>
#include <rpc/rpc.h>
#include <rpc/pmap_prot.h>
-#include <syslog.h>
#include <netdb.h>
#include <pwd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <tcpd.h>
+#include "sockaddr.h"
+#include "tcpwrapper.h"
#include "xlog.h"
#ifdef SYSV40
#include <netinet/in.h>
#include <rpc/rpcent.h>
-#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))
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;
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 */
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
-# 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,
$(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 \
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
LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
LIBBLKID = @LIBBLKID@
LIBBSD = @LIBBSD@
+LIBCAP = @LIBCAP@
LIBCRYPT = @LIBCRYPT@
LIBNSL = @LIBNSL@
LIBOBJS = @LIBOBJS@
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@
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
@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@
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);
}
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)
{
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)
{
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);
* 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;
}
}
-/* 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.
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;
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"))
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);
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 {
} 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;
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;
+}
#include <rpc/rpcb_prot.h>
#endif
+#include "sockaddr.h"
#include "nfsrpc.h"
/*
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.
* 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;
nc_netid = strdup(nconf->nc_netid);
endnetconfig(handle);
+
+ if (nc_netid == NULL)
+ rpc_createerr.cf_stat = RPC_SYSTEMERROR;
return nc_netid;
}
endnetconfig(handle);
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
{
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;
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;
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;
}
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);
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;
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 };
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);
}
}
#include <sys/types.h>
#include <sys/time.h>
+
+#include <stdbool.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <rpc/rpc.h>
#include <rpc/pmap_prot.h>
+#include "sockaddr.h"
#include "nfsrpc.h"
#ifdef HAVE_LIBTIRPC
#define NFSRPC_TIMEOUT_UDP (3)
#define NFSRPC_TIMEOUT_TCP (10)
+
/*
* Set up an RPC client for communicating via a AF_LOCAL socket.
*
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));
}
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;
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
--- /dev/null
+/*
+ * Copyright 2009 Oracle. All rights reserved.
+ *
+ * This file is part of nfs-utils.
+ *
+ * nfs-utils is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * nfs-utils is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with nfs-utils. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <memory.h>
+#include <signal.h>
+#include <unistd.h>
+#include <netdb.h>
+
+#include <netinet/in.h>
+
+#include <sys/socket.h>
+#include <sys/resource.h>
+
+#include <rpc/rpc.h>
+#include <rpc/svc.h>
+
+#ifdef HAVE_TCP_WRAPPER
+#include "tcpwrapper.h"
+#endif
+
+#include "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 */
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;
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;
}
--- /dev/null
+## 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
--- /dev/null
+# 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:
--- /dev/null
+/*
+ * Copyright 2009 Oracle. All rights reserved.
+ *
+ * This file is part of nfs-utils.
+ *
+ * nfs-utils is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * nfs-utils is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with nfs-utils. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * NSM for Linux.
+ *
+ * Callback information and NSM state is stored in files, usually
+ * under /var/lib/nfs. A database of information contained in local
+ * files stores NLM callback data and what remote peers to notify of
+ * reboots.
+ *
+ * For each monitored remote peer, a text file is created under the
+ * directory specified by NSM_MONITOR_DIR. The name of the file
+ * is a valid DNS hostname. The hostname string must be a valid
+ * ASCII DNS name, and must not contain slash characters, white space,
+ * or '\0' (ie. anything that might have some special meaning in a
+ * path name).
+ *
+ * The contents of each file include seven blank-separated fields of
+ * text, finished with '\n'. The first field contains the network
+ * address of the NLM service to call back. The current implementation
+ * supports using only IPv4 addresses, so the only contents of this
+ * field are a network order IPv4 address expressed in 8 hexadecimal
+ * characters.
+ *
+ * The next four fields are text strings of hexadecimal characters,
+ * representing:
+ *
+ * 2. A 4 byte RPC program number of the NLM service to call back
+ * 3. A 4 byte RPC version number of the NLM service to call back
+ * 4. A 4 byte RPC procedure number of the NLM service to call back
+ * 5. A 16 byte opaque cookie that the NLM service uses to identify
+ * the monitored host
+ *
+ * The sixth field is the monitored host's mon_name, passed to statd
+ * via an SM_MON request.
+ *
+ * The seventh field is the my_name for this peer, which is the
+ * hostname of the local NLM (currently on Linux, the result of
+ * `uname -n`). This can be used as the source address/hostname
+ * when sending SM_NOTIFY requests.
+ *
+ * The NSM protocol does not limit the contents of these strings
+ * in any way except that they must fit into 1024 bytes. Our
+ * implementation requires that these strings not contain
+ * white space or '\0'.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/capability.h>
+#include <sys/prctl.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <string.h>
+#include <stdint.h>
+#ifndef S_SPLINT_S
+#include <unistd.h>
+#endif
+#include <libgen.h>
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <grp.h>
+
+#include "xlog.h"
+#include "nsm.h"
+
+#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);
+}
--- /dev/null
+/*
+ * Copyright 2009 Oracle. All rights reserved.
+ *
+ * This file is part of nfs-utils.
+ *
+ * nfs-utils is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * nfs-utils is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with nfs-utils. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * NSM for Linux.
+ *
+ * Instead of using ONC or TI RPC library calls, statd constructs
+ * RPC calls directly in socket buffers. This allows a single
+ * socket to be concurrently shared among several different RPC
+ * programs and versions using a simple RPC request dispatcher.
+ *
+ * This file contains the details of RPC header and call
+ * construction and reply parsing, and a method for creating a
+ * socket for use with these functions.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+
+#include <time.h>
+#include <stdbool.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <netinet/in.h>
+#include <net/if.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <rpc/rpc.h>
+#include <rpc/pmap_prot.h>
+#include <rpc/pmap_rmt.h>
+
+#ifdef HAVE_LIBTIRPC
+#include <netconfig.h>
+#include <rpc/rpcb_prot.h>
+#endif /* HAVE_LIBTIRPC */
+
+#include "xlog.h"
+#include "nfsrpc.h"
+#include "nsm.h"
+#include "sm_inter.h"
+
+/*
+ * Returns a fresh XID appropriate for RPC over UDP -- never zero.
+ */
+static uint32_t
+nsm_next_xid(void)
+{
+ static uint32_t nsm_xid = 0;
+ struct timeval now;
+
+ if (nsm_xid == 0) {
+ (void)gettimeofday(&now, NULL);
+ nsm_xid = (uint32_t)getpid() ^
+ (uint32_t)now.tv_sec ^ (uint32_t)now.tv_usec;
+ }
+
+ return nsm_xid++;
+}
+
+/*
+ * Select a fresh XID and construct an RPC header in @mesg.
+ * Always use AUTH_NULL credentials and verifiers.
+ *
+ * Returns the new XID.
+ */
+static uint32_t
+nsm_init_rpc_header(const rpcprog_t program, const rpcvers_t version,
+ const rpcproc_t procedure, struct rpc_msg *mesg)
+{
+ struct call_body *cb = &mesg->rm_call;
+ uint32_t xid = nsm_next_xid();
+
+ memset(mesg, 0, sizeof(*mesg));
+
+ mesg->rm_xid = (unsigned long)xid;
+ mesg->rm_direction = CALL;
+
+ cb->cb_rpcvers = RPC_MSG_VERSION;
+ cb->cb_prog = program;
+ cb->cb_vers = version;
+ cb->cb_proc = procedure;
+
+ cb->cb_cred.oa_flavor = AUTH_NULL;
+ cb->cb_cred.oa_base = (caddr_t) NULL;
+ cb->cb_cred.oa_length = 0;
+ cb->cb_verf.oa_flavor = AUTH_NULL;
+ cb->cb_verf.oa_base = (caddr_t) NULL;
+ cb->cb_verf.oa_length = 0;
+
+ return xid;
+}
+
+/*
+ * Initialize the network send buffer and XDR memory for encoding.
+ */
+static void
+nsm_init_xdrmem(char *msgbuf, const unsigned int msgbuflen,
+ XDR *xdrp)
+{
+ memset(msgbuf, 0, (size_t)msgbuflen);
+ memset(xdrp, 0, sizeof(*xdrp));
+ xdrmem_create(xdrp, msgbuf, msgbuflen, XDR_ENCODE);
+}
+
+/*
+ * Send a completed RPC call on a socket.
+ *
+ * Returns true if all the bytes were sent successfully; otherwise
+ * false if any error occurred.
+ */
+static _Bool
+nsm_rpc_sendto(const int sock, const struct sockaddr *sap,
+ const socklen_t salen, XDR *xdrs, void *buf)
+{
+ const size_t buflen = (size_t)xdr_getpos(xdrs);
+ ssize_t err;
+
+ err = sendto(sock, buf, buflen, 0, sap, salen);
+ if ((err < 0) || ((size_t)err != buflen)) {
+ xlog(L_ERROR, "%s: sendto failed: %m", __func__);
+ return false;
+ }
+ return true;
+}
+
+/**
+ * nsm_xmit_getport - post a PMAP_GETPORT call on a socket descriptor
+ * @sock: datagram socket descriptor
+ * @sin: pointer to AF_INET socket address of server
+ * @program: RPC program number to query
+ * @version: RPC version number to query
+ *
+ * Send a PMAP_GETPORT call to the portmap daemon at @sin using
+ * socket descriptor @sock. This request queries the RPC program
+ * [program, version, IPPROTO_UDP].
+ *
+ * NB: PMAP_GETPORT works only for IPv4 hosts. This implementation
+ * works only over UDP, and queries only UDP registrations.
+ *
+ * Returns the XID of the call, or zero if an error occurred.
+ */
+uint32_t
+nsm_xmit_getport(const int sock, const struct sockaddr_in *sin,
+ const unsigned long program,
+ const unsigned long version)
+{
+ char msgbuf[NSM_MAXMSGSIZE];
+ struct sockaddr_in addr;
+ struct rpc_msg mesg;
+ _Bool sent = false;
+ struct pmap parms = {
+ .pm_prog = program,
+ .pm_vers = version,
+ .pm_prot = (unsigned long)IPPROTO_UDP,
+ };
+ uint32_t xid;
+ XDR xdr;
+
+ xlog(D_CALL, "Sending PMAP_GETPORT for %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;
+}
--- /dev/null
+/*
+ * Copyright (C) 1986 Sun Microsystems, Inc.
+ * Modified by Jeffrey A. Uphoff, 1995, 1997-1999.
+ * Modified by Olaf Kirch, 1996.
+ * Modified by H.J. Lu, 1998.
+ *
+ * NSM for Linux.
+ */
+
+/*
+ * Copyright (c) 2009, Sun Microsystems, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * - Neither the name of Sun Microsystems, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Status monitor protocol specification
+ */
+
+#ifdef RPC_CLNT
+%#include <string.h>
+#endif
+
+program SM_PROG {
+ version SM_VERS {
+ /* res_stat = stat_succ if status monitor agrees to monitor */
+ /* res_stat = stat_fail if status monitor cannot monitor */
+ /* if res_stat == stat_succ, state = state number of site sm_name */
+ struct sm_stat_res SM_STAT(struct sm_name) = 1;
+
+ /* res_stat = stat_succ if status monitor agrees to monitor */
+ /* res_stat = stat_fail if status monitor cannot monitor */
+ /* stat consists of state number of local site */
+ struct sm_stat_res SM_MON(struct mon) = 2;
+
+ /* stat consists of state number of local site */
+ struct sm_stat SM_UNMON(struct mon_id) = 3;
+
+ /* stat consists of state number of local site */
+ struct sm_stat SM_UNMON_ALL(struct my_id) = 4;
+
+ void SM_SIMU_CRASH(void) = 5;
+
+ void SM_NOTIFY(struct stat_chge) = 6;
+
+ } = 1;
+} = 100024;
+
+const SM_MAXSTRLEN = 1024;
+const SM_PRIV_SIZE = 16;
+
+struct sm_name {
+ string mon_name<SM_MAXSTRLEN>;
+};
+
+struct my_id {
+ string my_name<SM_MAXSTRLEN>; /* name of the site iniates the monitoring request*/
+ int my_prog; /* rpc program # of the requesting process */
+ int my_vers; /* rpc version # of the requesting process */
+ int my_proc; /* rpc procedure # of the requesting process */
+};
+
+struct mon_id {
+ string mon_name<SM_MAXSTRLEN>; /* name of the site to be monitored */
+ struct my_id my_id;
+};
+
+
+struct mon {
+ struct mon_id mon_id;
+ opaque priv[SM_PRIV_SIZE]; /* private information to store at monitor for requesting process */
+};
+
+struct stat_chge {
+ string mon_name<SM_MAXSTRLEN>; /* name of the site that had the state change */
+ int state;
+};
+
+/*
+ * state # of status monitor monitonically increases each time
+ * status of the site changes:
+ * an even number (>= 0) indicates the site is down and
+ * an odd number (> 0) indicates the site is up;
+ */
+struct sm_stat {
+ int state; /* state # of status monitor */
+};
+
+enum res {
+ stat_succ = 0, /* status monitor agrees to monitor */
+ stat_fail = 1 /* status monitor cannot monitor */
+};
+
+struct sm_stat_res {
+ res res_stat;
+ int state;
+};
+
+/*
+ * structure of the status message sent back by the status monitor
+ * when monitor site status changes
+ */
+struct status {
+ string mon_name<SM_MAXSTRLEN>;
+ int state;
+ opaque priv[SM_PRIV_SIZE]; /* stored private information */
+};
+
+%#define SM_INTER_X
--- /dev/null
+## 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
--- /dev/null
+# 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:
--- /dev/null
+## 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)
+
--- /dev/null
+# 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:
--- /dev/null
+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.
--- /dev/null
+/*
+ * 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 <string.h>
+#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<SM_MAXSTRLEN>;
+ int state;
+ opaque priv[SM_PRIV_SIZE]; /* stored private information */
+};
--- /dev/null
+/*
+ * nsm_client.c -- synthetic client and lockd simulator for testing statd
+ *
+ * Copyright (C) 2010 Red Hat, Jeff Layton <jlayton@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * 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 <errno.h>
+#include <getopt.h>
+#include <netdb.h>
+#include <signal.h>
+#include <string.h>
+#include <rpc/rpc.h>
+#include <rpc/pmap_clnt.h>
+#include <rpcmisc.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#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] <command> [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 <mon_name> <state>\tsend a reboot notification to host\n");
+ printf("stat <mon_name>\t\t\tget status of <mon_name> on host\n");
+ printf("unmon_all\t\t\ttell host to unmon everything\n");
+ printf("unmon <mon_name>\t\t\ttell host to unmon <mon_name>\n");
+ printf("mon <mon_name> <cookie>\t\ttell host to monitor <mon_name> with private <cookie>\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);
+}
--- /dev/null
+/*
+ * statdb_dump.c -- dump contents of statd's monitor DB
+ *
+ * Copyright (C) 2010 Red Hat, Jeff Layton <jlayton@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <arpa/inet.h>
+
+#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;
+}
--- /dev/null
+#!/bin/bash
+#
+# statd_basic_mon_unmon -- test basic mon/unmon functionality with statd
+#
+# Copyright (C) 2010 Red Hat, Jeff Layton <jlayton@redhat.com>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+. ./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
+
--- /dev/null
+#!/bin/bash
+#
+# test-lib.sh -- library of functions for nfs-utils tests
+#
+# Copyright (C) 2010 Red Hat, Jeff Layton <jlayton@redhat.com>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+# 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`
+}
-# 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,
$(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 \
LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
LIBBLKID = @LIBBLKID@
LIBBSD = @LIBBSD@
+LIBCAP = @LIBCAP@
LIBCRYPT = @LIBCRYPT@
LIBNSL = @LIBNSL@
LIBOBJS = @LIBOBJS@
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@
# (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]*);; \
fi; test -z "$$fail"
$(RECURSIVE_CLEAN_TARGETS):
- @failcom='exit 1'; \
+ @fail= failcom='exit 1'; \
for f in x $$MAKEFLAGS; do \
case $$f in \
*=* | --[!k]*);; \
-# 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,
$(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 \
LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
LIBBLKID = @LIBBLKID@
LIBBSD = @LIBBSD@
+LIBCAP = @LIBCAP@
LIBCRYPT = @LIBCRYPT@
LIBNSL = @LIBNSL@
LIBOBJS = @LIBOBJS@
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@
-# 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,
$(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 \
LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
LIBBLKID = @LIBBLKID@
LIBBSD = @LIBBSD@
+LIBCAP = @LIBCAP@
LIBCRYPT = @LIBCRYPT@
LIBNSL = @LIBNSL@
LIBOBJS = @LIBOBJS@
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@
-# 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,
$(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 \
LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
LIBBLKID = @LIBBLKID@
LIBBSD = @LIBBSD@
+LIBCAP = @LIBCAP@
LIBCRYPT = @LIBCRYPT@
LIBNSL = @LIBNSL@
LIBOBJS = @LIBOBJS@
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@
-# 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,
$(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 \
LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
LIBBLKID = @LIBBLKID@
LIBBSD = @LIBBSD@
+LIBCAP = @LIBCAP@
LIBCRYPT = @LIBCRYPT@
LIBNSL = @LIBNSL@
LIBOBJS = @LIBOBJS@
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@
-# 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,
$(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 \
LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
LIBBLKID = @LIBBLKID@
LIBBSD = @LIBBSD@
+LIBCAP = @LIBCAP@
LIBCRYPT = @LIBCRYPT@
LIBNSL = @LIBNSL@
LIBOBJS = @LIBOBJS@
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@
# (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]*);; \
fi; test -z "$$fail"
$(RECURSIVE_CLEAN_TARGETS):
- @failcom='exit 1'; \
+ @fail= failcom='exit 1'; \
for f in x $$MAKEFLAGS; do \
case $$f in \
*=* | --[!k]*);; \
-# 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,
$(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 \
LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
LIBBLKID = @LIBBLKID@
LIBBSD = @LIBBSD@
+LIBCAP = @LIBCAP@
LIBCRYPT = @LIBCRYPT@
LIBNSL = @LIBNSL@
LIBOBJS = @LIBOBJS@
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@
#endif
#include <sys/vfs.h>
+#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
-# 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,
$(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 \
LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
LIBBLKID = @LIBBLKID@
LIBBSD = @LIBBSD@
+LIBCAP = @LIBCAP@
LIBCRYPT = @LIBCRYPT@
LIBNSL = @LIBNSL@
LIBOBJS = @LIBOBJS@
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@
#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];
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
extern char pipefs_dir[PATH_MAX];
-extern char pipefs_nfsdir[PATH_MAX];
extern char keytabfile[PATH_MAX];
extern char *ccachesearch[];
extern int use_memcache;
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);
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
+#include <dirent.h>
#include "gssd.h"
#include "err_util.h"
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)
}
};
+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: */
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();
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);
}
}
scan_poll_results(ret);
}
}
- close(fd);
+ topdirs_free_list();
+
return;
}
#include "krb5_util.h"
#include "context.h"
#include "nfsrpc.h"
+#include "nfslib.h"
/*
* pollarray:
* 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;
/*
* 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
* 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;
}
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;
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;
}
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);
}
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);
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);
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");
}
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));
* 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;
}
/* 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)
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;
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 : "<null>");
+ 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)
}
}
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
goto out_return_error;
}
- do_downcall(clp->krb5_fd, uid, &pd, &token);
+ do_downcall(fd, uid, &pd, &token);
out:
if (token.value)
return;
out_return_error:
- do_error_downcall(clp->krb5_fd, uid, -1);
+ do_error_downcall(fd, uid, downcall_err);
goto out;
}
* 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);
goto out_return_error;
}
- do_downcall(clp->spkm3_fd, uid, &pd, &token);
+ do_downcall(fd, uid, &pd, &token);
out:
if (token.value)
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;
+}
+
* 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)
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;
printerr(3, "CC file '%s' is expired or corrupt\n",
statname);
free(namelist[i]);
+ err = -EKEYEXPIRED;
continue;
}
}
free(namelist);
}
- if (found)
- {
+ if (found) {
*d = best_match_dir;
+ return 0;
}
- return found;
+
+ return err;
}
*/
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;
* 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;
}
/*
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) {
*/
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;
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",
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);
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);
#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"
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;
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 : "<null>",
cred->cr_uid, cred->cr_gid, cred->cr_ngroups);
for (i=0; i < cred->cr_ngroups; i++) {
qword_printint(f, cred->cr_groups[i]);
}
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 "
}
#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
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;
static int lbuflen = 0;
static char *cp;
int32_t ctx_endtime;
+ char *hostbased_name = NULL;
printerr(1, "handling null request\n");
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. */
/* 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);
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;
## Process this file with automake to produce Makefile.in
-man5_MANS = idmapd.conf.man
man8_MANS = idmapd.man
RPCPREFIX = rpc.
sbin_PROGRAMS = idmapd
EXTRA_DIST = \
- $(man5_MANS) \
$(man8_MANS) \
idmapd.conf
# 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) && \
-# 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,
$(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 \
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)
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)
LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
LIBBLKID = @LIBBLKID@
LIBBSD = @LIBBSD@
+LIBCAP = @LIBCAP@
LIBCRYPT = @LIBCRYPT@
LIBNSL = @LIBNSL@
LIBOBJS = @LIBOBJS@
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@
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
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)"
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
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
#######################################################################
# 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) && \
+++ /dev/null
-.\" $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
-# 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,
$(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 \
LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
LIBBLKID = @LIBBLKID@
LIBBSD = @LIBBSD@
+LIBCAP = @LIBCAP@
LIBCRYPT = @LIBCRYPT@
LIBNSL = @LIBNSL@
LIBOBJS = @LIBOBJS@
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@
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
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");
}
#include <unistd.h>
#include <sys/types.h>
+#include <sys/stat.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
if (mnt_err == EX_BG) {
printf(_("%s: backgrounding \"%s\"\n"),
progname, spec);
+ printf(_("%s: mount options: \"%s\"\n"),
+ progname, extra_opts);
+
fflush(stdout);
/*
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
+#include <sys/stat.h>
#include <netinet/in.h>
#include <rpc/rpc.h>
#include <rpc/pmap_prot.h>
#include <rpc/pmap_clnt.h>
+#include "sockaddr.h"
#include "xcommon.h"
#include "mount.h"
#include "nls.h"
#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;
}
#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 = {
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
* 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.
*
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;
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,
/*
* 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)) {
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;
}
}
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.
/*
* 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;
}
/*
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
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);
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);
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)
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.
.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
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.
can be specified.
If the server's mountd service is not available via the specified
transport, the mount request fails.
+.IP
Refer to the TRANSPORT METHODS section for more on how the
.B mountproto
mount option interacts with the
.B 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
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
.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
.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),
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.
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) {
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;
*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 == '"')
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;
}
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);
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)
#include <netinet/in.h>
#include <arpa/inet.h>
+#include "sockaddr.h"
#include "xcommon.h"
#include "mount.h"
#include "nls.h"
+#include "nfsrpc.h"
#include "mount_constants.h"
#include "stropts.h"
#include "error.h"
#include "parse_dev.h"
#include "conffile.h"
+#ifndef HAVE_DECL_AI_ADDRCONFIG
+#define AI_ADDRCONFIG 0
+#endif
+
#ifndef NFS_PROGRAM
#define NFS_PROGRAM (100003)
#endif
*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 */
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;
}
/*
- * 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;
*/
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;
}
static int nfs_construct_new_options(struct mount_options *options,
+ struct sockaddr *nfs_saddr,
struct pmap *nfs_pmap,
+ struct sockaddr *mnt_saddr,
struct pmap *mnt_pmap)
{
char new_option[64];
+ char *netid;
po_remove_all(options, "nfsprog");
po_remove_all(options, "mountprog");
po_remove_all(options, "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) {
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,
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;
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;
}
return 0;
}
- if (verbose)
- printf(_("%s: trying text-based options '%s'\n"),
- progname, options);
-
if (mi->fake)
return 1;
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;
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;
}
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;
}
/*
- * 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;
}
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;
}
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:
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.
*
/*
* 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;
}
}
struct nfsmount_info mi = {
.spec = spec,
.node = node,
+ .address = NULL,
.type = type,
.extra_opts = extra_opts,
.flags = flags,
} else
nfs_error(_("%s: internal option parsing error"), progname);
+ freeaddrinfo(mi.address);
free(mi.hostname);
return retval;
}
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 \
-# 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,
$(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 \
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 \
LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
LIBBLKID = @LIBBLKID@
LIBBSD = @LIBBSD@
+LIBCAP = @LIBCAP@
LIBCRYPT = @LIBCRYPT@
LIBNSL = @LIBNSL@
LIBOBJS = @LIBOBJS@
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@
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 \
@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 $@ $<
@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
#include "exportfs.h"
#include "mountd.h"
#include "xmalloc.h"
+#include "v4root.h"
enum auth_error
{
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;
}
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)) &&
}
}
}
+ 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) {
{
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;
}
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;
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);
}
}
#include "ha-callout.h"
#include <limits.h> /* PATH_MAX */
+#include <errno.h>
extern int reverse_resolve;
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)))
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
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) {
--- /dev/null
+/*
+ * Copyright (C) 2009 Red Hat <nfs@redhat.com>
+ *
+ * support/export/v4root.c
+ *
+ * Routines used to support NFSv4 pseudo roots
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/queue.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include <unistd.h>
+#include <errno.h>
+
+#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! */
+ }
+ }
+}
-# 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,
$(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 \
LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
LIBBLKID = @LIBBLKID@
LIBBSD = @LIBBSD@
+LIBCAP = @LIBCAP@
LIBCRYPT = @LIBCRYPT@
LIBNSL = @LIBNSL@
LIBOBJS = @LIBOBJS@
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@
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;
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))
-# 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,
$(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 \
LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
LIBBLKID = @LIBBLKID@
LIBBSD = @LIBBSD@
+LIBCAP = @LIBCAP@
LIBCRYPT = @LIBCRYPT@
LIBNSL = @LIBNSL@
LIBOBJS = @LIBOBJS@
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@
#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
* 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",
"destroy_ses",
"sequence",
"get_lease_t",
+ "reclaim_comp",
"layoutget",
"layoutcommit",
"layoutreturn",
"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",
-# 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,
$(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 \
LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
LIBBLKID = @LIBBLKID@
LIBBSD = @LIBBSD@
+LIBCAP = @LIBCAP@
LIBCRYPT = @LIBCRYPT@
LIBNSL = @LIBNSL@
LIBOBJS = @LIBOBJS@
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@
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;
int i;
int n;
int maxlen;
+ int unsigned vers=0;
char **dumpv;
program_name = argv[0];
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));
(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);
(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);
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
-# 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,
$(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 \
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/||"`;; \
LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
LIBBLKID = @LIBBLKID@
LIBBSD = @LIBBSD@
+LIBCAP = @LIBCAP@
LIBCRYPT = @LIBCRYPT@
LIBNSL = @LIBNSL@
LIBOBJS = @LIBOBJS@
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@
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
-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@
#include <config.h>
#endif
-#include <arpa/inet.h>
+#include <netdb.h>
#include "rpcmisc.h"
-#include "misc.h"
#include "statd.h"
#include "notlist.h"
/* 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
*/
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);
--- /dev/null
+/*
+ * Copyright 2009 Oracle. All rights reserved.
+ *
+ * This file is part of nfs-utils.
+ *
+ * nfs-utils is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * nfs-utils is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with nfs-utils. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * NSM for Linux.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <strings.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+
+#include "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;
+}
+++ /dev/null
-/*
- * 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 <config.h>
-#endif
-
-#include <syslog.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdarg.h>
-#include <time.h>
-#include <sys/types.h>
-#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);
- }
-}
+++ /dev/null
-/*
- * 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 <syslog.h>
-
-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_ */
return ((void *)NULL);
if (!(ptr = malloc (size)))
- /* SHIT! SHIT! SHIT! */
- die ("malloc failed");
+ xlog_err ("malloc failed");
return (ptr);
}
/* 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);
-}
#include <arpa/inet.h>
#include <dirent.h>
+#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;
}
/*
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;
*/
if (!caller_is_localhost(rqstp))
goto failure;
- my_addr.s_addr = htonl(INADDR_LOOPBACK);
/* 2. Reject any registrations for non-lockd services.
*
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;
}
/* 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;
}
* 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
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);
* 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;
/*
* 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; i<SM_PRIV_SIZE; i++)
- e += sprintf(e, "%02x", 0xff & (argp->priv[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".
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; i<SM_PRIV_SIZE; i++) {
- sscanf(b, "%2x", &p);
- priv[i] = p;
- b += 2;
- }
- b++;
- monname = b;
- while (*b && *b != ' ') b++;
- if (*b) *b++ = '\0';
- while (*b == ' ') b++;
- myname = b;
- clnt = nlist_new(myname, monname, 0);
- if (!clnt)
- break;
- NL_ADDR(clnt).s_addr = addr;
- NL_MY_PROG(clnt) = prog;
- NL_MY_VERS(clnt) = vers;
- NL_MY_PROC(clnt) = proc;
- clnt->dns_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.
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))
/* 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;
* 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);
}
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);
}
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);
}
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),
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;
}
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:
#endif
#include <string.h>
-#include "misc.h"
#include "statd.h"
#include "notlist.h"
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;
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;
}
*/
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 */
};
#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)
#include <netdb.h>
#include <string.h>
#include <unistd.h>
-#ifdef HAVE_IFADDRS_H
-#include <ifaddrs.h>
-#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 */
/*
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;
}
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;
}
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;
}
/* 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;
}
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;
}
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;
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) {
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);
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),
#include <config.h>
#endif
+#include <netdb.h>
#include <arpa/inet.h>
+#include "sockaddr.h"
#include "rpcmisc.h"
#include "statd.h"
#include "notlist.h"
/*
* 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)
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;
}
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"))
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
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);
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);
}
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;
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);
}
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;
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);
}
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);
}
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);
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
{
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);
#include <config.h>
#endif
+#include <err.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <errno.h>
#include <grp.h>
-#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);
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;
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:
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);
}
}
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);
}
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);
* 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);
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;
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
*/
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;
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);
}
/*
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
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;
}
-.\"
-.\" sm-notify(8)
+.\"@(#)sm-notify.8"
.\"
.\" Copyright (C) 2004 Olaf Kirch <okir@suse.de>
-.TH sm-notify 8 "19 Mar 2007
+.\"
+.\" Rewritten by Chuck Lever <chuck.lever@oracle.com>, 2009.
+.\" Copyright 2009 Oracle. All rights reserved.
+.\"
+.TH SM-NOTIFY 8 "1 November 2009
.SH NAME
-sm-notify \- Send 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 <okir@suse.de>
+.br
+Chuck Lever <chuck.lever@oracle.com>
+++ /dev/null
-/*
- * Copyright (C) 1986 Sun Microsystems, Inc.
- * Modified by Jeffrey A. Uphoff, 1995, 1997-1999.
- * Modified by Olaf Kirch, 1996.
- * Modified by H.J. Lu, 1998.
- *
- * NSM for Linux.
- */
-
-/*
- * Copyright (c) 2009, Sun Microsystems, Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- * - Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- * - Neither the name of Sun Microsystems, Inc. nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-/*
- * Status monitor protocol specification
- */
-
-#ifdef RPC_CLNT
-%#include <string.h>
-#endif
-
-program SM_PROG {
- version SM_VERS {
- /* res_stat = stat_succ if status monitor agrees to monitor */
- /* res_stat = stat_fail if status monitor cannot monitor */
- /* if res_stat == stat_succ, state = state number of site sm_name */
- struct sm_stat_res SM_STAT(struct sm_name) = 1;
-
- /* res_stat = stat_succ if status monitor agrees to monitor */
- /* res_stat = stat_fail if status monitor cannot monitor */
- /* stat consists of state number of local site */
- struct sm_stat_res SM_MON(struct mon) = 2;
-
- /* stat consists of state number of local site */
- struct sm_stat SM_UNMON(struct mon_id) = 3;
-
- /* stat consists of state number of local site */
- struct sm_stat SM_UNMON_ALL(struct my_id) = 4;
-
- void SM_SIMU_CRASH(void) = 5;
-
- void SM_NOTIFY(struct stat_chge) = 6;
-
- } = 1;
-} = 100024;
-
-const SM_MAXSTRLEN = 1024;
-const SM_PRIV_SIZE = 16;
-
-struct sm_name {
- string mon_name<SM_MAXSTRLEN>;
-};
-
-struct my_id {
- string my_name<SM_MAXSTRLEN>; /* name of the site iniates the monitoring request*/
- int my_prog; /* rpc program # of the requesting process */
- int my_vers; /* rpc version # of the requesting process */
- int my_proc; /* rpc procedure # of the requesting process */
-};
-
-struct mon_id {
- string mon_name<SM_MAXSTRLEN>; /* name of the site to be monitored */
- struct my_id my_id;
-};
-
-
-struct mon {
- struct mon_id mon_id;
- opaque priv[SM_PRIV_SIZE]; /* private information to store at monitor for requesting process */
-};
-
-struct stat_chge {
- string mon_name<SM_MAXSTRLEN>; /* name of the site that had the state change */
- int state;
-};
-
-/*
- * state # of status monitor monitonically increases each time
- * status of the site changes:
- * an even number (>= 0) indicates the site is down and
- * an odd number (> 0) indicates the site is up;
- */
-struct sm_stat {
- int state; /* state # of status monitor */
-};
-
-enum res {
- stat_succ = 0, /* status monitor agrees to monitor */
- stat_fail = 1 /* status monitor cannot monitor */
-};
-
-struct sm_stat_res {
- res res_stat;
- int state;
-};
-
-/*
- * structure of the status message sent back by the status monitor
- * when monitor site status changes
- */
-struct status {
- string mon_name<SM_MAXSTRLEN>;
- int state;
- opaque priv[SM_PRIV_SIZE]; /* stored private information */
-};
-
-%#define SM_INTER_X
#include <netdb.h>
#include "statd.h"
-/*
+/*
* Services SM_STAT requests.
*
* According the the X/Open spec's on this procedure: "Implementations
* 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);
#include <sys/resource.h>
#include <sys/wait.h>
#include <grp.h>
+
#include "statd.h"
-#include "version.h"
#include "nfslib.h"
+#include "nsm.h"
/* Socket operations */
#include <sys/types.h>
#include <sys/socket.h>
-/* 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
};
extern void sm_prog_1 (struct svc_req *, register SVCXPRT *);
-static void load_state_number(void);
#ifdef SIMULATIONS
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;
}
#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();
}
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);
}
/*
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);
}
}
{
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)
/* 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) {
name_p = argv[0];
}
- /* Get the version */
- if ((version_p = strrchr(VERSION,' ')) != NULL) {
- version_p++;
- } else {
- version_p = VERSION;
- }
-
/* Set hostname */
MY_NAME = NULL;
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;
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) {
run_sm_notify(out_port);
}
-
if (!(run_mode & MODE_NODAEMON)) {
run_mode &= ~MODE_LOG_STDERR; /* Never log to console in
daemon mode. */
/* 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]);
/* 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();
* 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
}
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);
- }
-
-}
#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.
/*
* 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);
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);
/*
* 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 */
-
-.\"
-.\" statd(8)
+.\"@(#)rpc.statd.8"
.\"
.\" Copyright (C) 1999 Olaf Kirch <okir@monad.swb.de>
.\" Modified by Jeffrey A. Uphoff, 1999, 2002, 2005.
.\" Modified by Lon Hohberger, 2000.
.\" Modified by Paul Clements, 2004.
-.TH rpc.statd 8 "31 Aug 2004"
+.\"
+.\" Rewritten by Chuck Lever <chuck.lever@oracle.com>, 2009.
+.\" Copyright 2009 Oracle. All rights reserved.
+.\"
+.TH RPC.STATD 8 "1 November 2009
.SH NAME
-rpc.statd \- NSM 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 <juphoff@users.sourceforge.net>
.br
Olaf Kirch <okir@monad.swb.de>
Lon Hohberger <hohberger@missioncriticallinux.com>
.br
Paul Clements <paul.clements@steeleye.com>
+.br
+Chuck Lever <chuck.lever@oracle.com>
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);
}
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:
+++ /dev/null
-/*
- * Copyright (C) 1997-1999 Jeffrey A. Uphoff
- *
- * NSM for Linux.
- */
-
-#define STATD_RELEASE "1.1.1"