diff --git a/nfs-utils-1.2.2-rc7.patch b/nfs-utils-1.2.2-rc7.patch
deleted file mode 100644
index 418d182..0000000
--- a/nfs-utils-1.2.2-rc7.patch
+++ /dev/null
@@ -1,11581 +0,0 @@
-diff --git a/.gitignore b/.gitignore
-index 632609e..4bff9e3 100644
---- a/.gitignore
-+++ b/.gitignore
-@@ -55,10 +55,15 @@ support/export/mount.h
- support/export/mount_clnt.c
- support/export/mount_xdr.c
- support/include/mount.h
--utils/statd/sm_inter.h
--utils/statd/sm_inter_clnt.c
--utils/statd/sm_inter_svc.c
--utils/statd/sm_inter_xdr.c
-+support/nsm/sm_inter.h
-+support/nsm/sm_inter_clnt.c
-+support/nsm/sm_inter_svc.c
-+support/nsm/sm_inter_xdr.c
-+support/include/sm_inter.h
-+tests/nsm_client/nlm_sm_inter.h
-+tests/nsm_client/nlm_sm_inter_clnt.c
-+tests/nsm_client/nlm_sm_inter_svc.c
-+tests/nsm_client/nlm_sm_inter_xdr.c
- # cscope database files
- cscope.*
- # generic editor backup et al
-diff --git a/Makefile.am b/Makefile.am
-index b3a6e91..ae7cd16 100644
---- a/Makefile.am
-+++ b/Makefile.am
-@@ -2,7 +2,7 @@
-
- AUTOMAKE_OPTIONS = foreign
-
--SUBDIRS = tools support utils linux-nfs
-+SUBDIRS = tools support utils linux-nfs tests
-
- MAINTAINERCLEANFILES = Makefile.in
-
-diff --git a/aclocal/libcap.m4 b/aclocal/libcap.m4
-new file mode 100644
-index 0000000..eabe507
---- /dev/null
-+++ b/aclocal/libcap.m4
-@@ -0,0 +1,15 @@
-+dnl Checks for libcap.so
-+dnl
-+AC_DEFUN([AC_LIBCAP], [
-+
-+ dnl look for prctl
-+ AC_CHECK_FUNC([prctl], , )
-+
-+ dnl look for the library; do not add to LIBS if found
-+ AC_CHECK_LIB([cap], [cap_get_proc], [LIBCAP=-lcap], ,)
-+ AC_SUBST(LIBCAP)
-+
-+ AC_CHECK_HEADERS([sys/capability.h], ,
-+ [AC_MSG_ERROR([libcap headers not found.])])
-+
-+])dnl
-diff --git a/configure.ac b/configure.ac
-index 3ad415c..c77c5ba 100644
---- a/configure.ac
-+++ b/configure.ac
-@@ -166,6 +166,9 @@ fi
- dnl Check for TI-RPC library and headers
- AC_LIBTIRPC
-
-+dnl Check for -lcap
-+AC_LIBCAP
-+
- # Check whether user wants TCP wrappers support
- AC_TCP_WRAPPERS
-
-@@ -402,6 +405,7 @@ AC_CONFIG_FILES([
- support/include/Makefile
- support/misc/Makefile
- support/nfs/Makefile
-+ support/nsm/Makefile
- tools/Makefile
- tools/locktest/Makefile
- tools/nlmtest/Makefile
-@@ -416,6 +420,8 @@ AC_CONFIG_FILES([
- utils/nfsd/Makefile
- utils/nfsstat/Makefile
- utils/showmount/Makefile
-- utils/statd/Makefile])
-+ utils/statd/Makefile
-+ tests/Makefile
-+ tests/nsm_client/Makefile])
- AC_OUTPUT
-
-diff --git a/support/Makefile.am b/support/Makefile.am
-index aa4d692..cb37733 100644
---- a/support/Makefile.am
-+++ b/support/Makefile.am
-@@ -1,6 +1,6 @@
- ## Process this file with automake to produce Makefile.in
-
--SUBDIRS = export include misc nfs
-+SUBDIRS = export include misc nfs nsm
-
- MAINTAINERCLEANFILES = Makefile.in
-
-diff --git a/support/export/client.c b/support/export/client.c
-index 5fcf355..6236561 100644
---- a/support/export/client.c
-+++ b/support/export/client.c
-@@ -297,7 +297,7 @@ name_cmp(char *a, char *b)
- /* compare strings a and b, but only upto ',' in a */
- while (*a && *b && *a != ',' && *a == *b)
- a++, b++;
-- if (!*b && (!*a || !a == ',') )
-+ if (!*b && (!*a || *a == ','))
- return 0;
- if (!*b) return 1;
- if (!*a || *a == ',') return -1;
-diff --git a/support/export/export.c b/support/export/export.c
-index e5e6cb0..2943466 100644
---- a/support/export/export.c
-+++ b/support/export/export.c
-@@ -28,6 +28,22 @@ static int export_check(nfs_export *, struct hostent *, char *);
- static nfs_export *
- export_allowed_internal(struct hostent *hp, char *path);
-
-+static void warn_duplicated_exports(nfs_export *exp, struct exportent *eep)
-+{
-+ if (exp->m_export.e_flags != eep->e_flags) {
-+ xlog(L_ERROR, "incompatible duplicated export entries:");
-+ xlog(L_ERROR, "\t%s:%s (0x%x) [IGNORED]", eep->e_hostname,
-+ eep->e_path, eep->e_flags);
-+ xlog(L_ERROR, "\t%s:%s (0x%x)", exp->m_export.e_hostname,
-+ exp->m_export.e_path, exp->m_export.e_flags);
-+ } else {
-+ xlog(L_ERROR, "duplicated export entries:");
-+ xlog(L_ERROR, "\t%s:%s", eep->e_hostname, eep->e_path);
-+ xlog(L_ERROR, "\t%s:%s", exp->m_export.e_hostname,
-+ exp->m_export.e_path);
-+ }
-+}
-+
- int
- export_read(char *fname)
- {
-@@ -36,27 +52,13 @@ export_read(char *fname)
-
- setexportent(fname, "r");
- while ((eep = getexportent(0,1)) != NULL) {
-- exp = export_lookup(eep->e_hostname, eep->e_path, 0);
-- if (!exp)
-- export_create(eep,0);
-- else {
-- if (exp->m_export.e_flags != eep->e_flags) {
-- xlog(L_ERROR, "incompatible duplicated export entries:");
-- xlog(L_ERROR, "\t%s:%s (0x%x) [IGNORED]", eep->e_hostname,
-- eep->e_path, eep->e_flags);
-- xlog(L_ERROR, "\t%s:%s (0x%x)", exp->m_export.e_hostname,
-- exp->m_export.e_path, exp->m_export.e_flags);
-- }
-- else {
-- xlog(L_ERROR, "duplicated export entries:");
-- xlog(L_ERROR, "\t%s:%s", eep->e_hostname, eep->e_path);
-- xlog(L_ERROR, "\t%s:%s", exp->m_export.e_hostname,
-- exp->m_export.e_path);
-- }
-- }
-+ exp = export_lookup(eep->e_hostname, eep->e_path, 0);
-+ if (!exp)
-+ export_create(eep, 0);
-+ else
-+ warn_duplicated_exports(exp, eep);
- }
- endexportent();
--
- return 0;
- }
-
-diff --git a/support/export/xtab.c b/support/export/xtab.c
-index 3b1dcce..2a43193 100644
---- a/support/export/xtab.c
-+++ b/support/export/xtab.c
-@@ -19,7 +19,9 @@
- #include "exportfs.h"
- #include "xio.h"
- #include "xlog.h"
-+#include "v4root.h"
-
-+int v4root_needed;
- static void cond_rename(char *newfile, char *oldfile);
-
- static int
-@@ -36,6 +38,8 @@ xtab_read(char *xtab, char *lockfn, int is_export)
- if ((lockid = xflock(lockfn, "r")) < 0)
- return 0;
- setexportent(xtab, "r");
-+ if (is_export == 1)
-+ v4root_needed = 1;
- while ((xp = getexportent(is_export==0, 0)) != NULL) {
- if (!(exp = export_lookup(xp->e_hostname, xp->e_path, is_export != 1)) &&
- !(exp = export_create(xp, is_export!=1))) {
-@@ -48,6 +52,8 @@ xtab_read(char *xtab, char *lockfn, int is_export)
- case 1:
- exp->m_xtabent = 1;
- exp->m_mayexport = 1;
-+ if ((xp->e_flags & NFSEXP_FSID) && xp->e_fsid == 0)
-+ v4root_needed = 0;
- break;
- case 2:
- exp->m_exported = -1;/* may be exported */
-diff --git a/support/include/Makefile.am b/support/include/Makefile.am
-index f5a77ec..4b33ee9 100644
---- a/support/include/Makefile.am
-+++ b/support/include/Makefile.am
-@@ -9,6 +9,8 @@ noinst_HEADERS = \
- nfs_mntent.h \
- nfs_paths.h \
- nfslib.h \
-+ nfsrpc.h \
-+ nsm.h \
- rpcmisc.h \
- tcpwrapper.h \
- xio.h \
-diff --git a/support/include/exportfs.h b/support/include/exportfs.h
-index a5cf482..470b2ec 100644
---- a/support/include/exportfs.h
-+++ b/support/include/exportfs.h
-@@ -99,10 +99,19 @@ int xtab_mount_write(void);
- int xtab_export_write(void);
- void xtab_append(nfs_export *);
-
-+int secinfo_addflavor(struct flav_info *, struct exportent *);
-+
- int rmtab_read(void);
-
- struct nfskey * key_lookup(char *hname);
-
-+struct export_features {
-+ unsigned int flags;
-+ unsigned int secinfo_flags;
-+};
-+
-+struct export_features *get_export_features(void);
-+
- /* Record export error. */
- extern int export_errno;
-
-diff --git a/support/include/ha-callout.h b/support/include/ha-callout.h
-index efb70fb..1164336 100644
---- a/support/include/ha-callout.h
-+++ b/support/include/ha-callout.h
-@@ -53,11 +53,7 @@ ha_callout(char *event, char *arg1, char *arg2, int arg3)
- default: pid = waitpid(pid, &ret, 0);
- }
- sigaction(SIGCHLD, &oldact, &newact);
--#ifdef dprintf
-- dprintf(N_DEBUG, "ha callout returned %d\n", WEXITSTATUS(ret));
--#else
- xlog(D_GENERAL, "ha callout returned %d\n", WEXITSTATUS(ret));
--#endif
- }
-
- #endif
-diff --git a/support/include/nfs/export.h b/support/include/nfs/export.h
-index f7a99ba..1547a87 100644
---- a/support/include/nfs/export.h
-+++ b/support/include/nfs/export.h
-@@ -24,6 +24,17 @@
- #define NFSEXP_FSID 0x2000
- #define NFSEXP_CROSSMOUNT 0x4000
- #define NFSEXP_NOACL 0x8000 /* reserved for possible ACL related use */
--#define NFSEXP_ALLFLAGS 0xFFFF
-+#define NFSEXP_V4ROOT 0x10000
-+/*
-+ * All flags supported by the kernel before addition of the
-+ * export_features interface:
-+ */
-+#define NFSEXP_OLDFLAGS 0x7E3F
-+/*
-+ * Flags that can vary per flavor, for kernels before addition of the
-+ * export_features interface:
-+ */
-+#define NFSEXP_OLD_SECINFO_FLAGS (NFSEXP_READONLY | NFSEXP_ROOTSQUASH \
-+ | NFSEXP_ALLSQUASH)
-
- #endif /* _NSF_EXPORT_H */
-diff --git a/support/include/nfsrpc.h b/support/include/nfsrpc.h
-index dff6af7..4db35ab 100644
---- a/support/include/nfsrpc.h
-+++ b/support/include/nfsrpc.h
-@@ -59,16 +59,6 @@ static inline void nfs_clear_rpc_createerr(void)
- }
-
- /*
-- * 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
- */
- extern rpcprog_t nfs_getrpcbyname(const rpcprog_t, const char *table[]);
-@@ -90,6 +80,18 @@ extern CLIENT *nfs_get_priv_rpcclient( const struct sockaddr *,
- 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
- */
- extern char *nfs_sockaddr2universal(const struct sockaddr *);
-@@ -158,4 +160,4 @@ extern int nfs_rpc_ping(const struct sockaddr *sap,
- const unsigned short protocol,
- const struct timeval *timeout);
-
--#endif /* __NFS_UTILS_NFSRPC_H */
-+#endif /* !__NFS_UTILS_NFSRPC_H */
-diff --git a/support/include/nsm.h b/support/include/nsm.h
-new file mode 100644
-index 0000000..fb4d823
---- /dev/null
-+++ b/support/include/nsm.h
-@@ -0,0 +1,93 @@
-+/*
-+ * Copyright 2009 Oracle. All rights reserved.
-+ *
-+ * This file is part of nfs-utils.
-+ *
-+ * nfs-utils is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * nfs-utils is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with nfs-utils. If not, see .
-+ */
-+
-+/*
-+ * NSM for Linux.
-+ */
-+
-+#ifndef NFS_UTILS_SUPPORT_NSM_H
-+#define NFS_UTILS_SUPPORT_NSM_H
-+
-+#include
-+#include
-+#include
-+
-+#include
-+#include
-+
-+#include "sm_inter.h"
-+
-+typedef unsigned int
-+ (*nsm_populate_t)(const char *hostname,
-+ const struct sockaddr *sap,
-+ const struct mon *mon,
-+ const time_t timestamp);
-+
-+/* file.c */
-+
-+extern _Bool nsm_setup_pathnames(const char *progname,
-+ const char *parentdir);
-+extern _Bool nsm_is_default_parentdir(void);
-+extern _Bool nsm_drop_privileges(const int pidfd);
-+
-+extern int nsm_get_state(_Bool update);
-+extern void nsm_update_kernel_state(const int state);
-+
-+extern unsigned int
-+ nsm_retire_monitored_hosts(void);
-+extern unsigned int
-+ nsm_load_monitor_list(nsm_populate_t func);
-+extern unsigned int
-+ nsm_load_notify_list(nsm_populate_t func);
-+
-+extern _Bool nsm_insert_monitored_host(const char *hostname,
-+ const struct sockaddr *sap, const struct mon *m);
-+extern void nsm_delete_monitored_host(const char *hostname,
-+ const char *mon_name, const char *my_name);
-+extern void nsm_delete_notified_host(const char *hostname,
-+ const char *mon_name, const char *my_name);
-+extern size_t nsm_priv_to_hex(const char *priv, char *buf,
-+ const size_t buflen);
-+
-+/* rpc.c */
-+
-+#define NSM_MAXMSGSIZE (2048u)
-+
-+extern uint32_t nsm_xmit_getport(const int sock,
-+ const struct sockaddr_in *sin,
-+ const unsigned long program,
-+ const unsigned long version);
-+extern uint32_t nsm_xmit_getaddr(const int sock,
-+ const struct sockaddr_in6 *sin6,
-+ const rpcprog_t program, const rpcvers_t version);
-+extern uint32_t nsm_xmit_rpcbind(const int sock, const struct sockaddr *sap,
-+ const rpcprog_t program, const rpcvers_t version);
-+extern uint32_t nsm_xmit_notify(const int sock, const struct sockaddr *sap,
-+ const socklen_t salen, const rpcprog_t program,
-+ const char *mon_name, const int state);
-+extern uint32_t nsm_xmit_nlmcall(const int sock, const struct sockaddr *sap,
-+ const socklen_t salen, const struct mon *m,
-+ const int state);
-+extern uint32_t nsm_parse_reply(XDR *xdrs);
-+extern unsigned long
-+ nsm_recv_getport(XDR *xdrs);
-+extern uint16_t nsm_recv_getaddr(XDR *xdrs);
-+extern uint16_t nsm_recv_rpcbind(const sa_family_t family, XDR *xdrs);
-+
-+#endif /* !NFS_UTILS_SUPPORT_NSM_H */
-diff --git a/support/include/rpcmisc.h b/support/include/rpcmisc.h
-index f551a85..1b8f411 100644
---- a/support/include/rpcmisc.h
-+++ b/support/include/rpcmisc.h
-@@ -41,7 +41,12 @@ struct rpc_dtable {
- (xdrproc_t)xdr_##res_type, sizeof(res_type), \
- }
-
--
-+void nfs_svc_unregister(const rpcprog_t program,
-+ const rpcvers_t version);
-+unsigned int nfs_svc_create(char *name, const rpcprog_t program,
-+ const rpcvers_t version,
-+ void (*dispatch)(struct svc_req *, SVCXPRT *),
-+ const uint16_t port);
- void rpc_init(char *name, int prog, int vers,
- void (*dispatch)(struct svc_req *, SVCXPRT *),
- int defport);
-diff --git a/support/include/sockaddr.h b/support/include/sockaddr.h
-new file mode 100644
-index 0000000..732514b
---- /dev/null
-+++ b/support/include/sockaddr.h
-@@ -0,0 +1,237 @@
-+/*
-+ * Copyright 2009 Oracle. All rights reserved.
-+ *
-+ * This file is part of nfs-utils.
-+ *
-+ * nfs-utils is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * nfs-utils is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with nfs-utils. If not, see .
-+ */
-+
-+#ifndef NFS_UTILS_SOCKADDR_H
-+#define NFS_UTILS_SOCKADDR_H
-+
-+#include
-+#include
-+#include
-+
-+/*
-+ * 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->sa_family == sa2->sa_family)
-+ switch (sa1->sa_family) {
-+ case AF_INET:
-+ return compare_sockaddr4(sa1, sa2);
-+ case AF_INET6:
-+ return compare_sockaddr6(sa1, sa2);
-+ }
-+
-+ return false;
-+}
-+
-+#endif /* !NFS_UTILS_SOCKADDR_H */
-diff --git a/support/include/v4root.h b/support/include/v4root.h
-new file mode 100644
-index 0000000..706c15c
---- /dev/null
-+++ b/support/include/v4root.h
-@@ -0,0 +1,15 @@
-+/*
-+ * Copyright (C) 2009 Red Hat
-+ * support/include/v4root.h
-+ *
-+ * Support routines for dynamic pseudo roots.
-+ *
-+ */
-+
-+#ifndef V4ROOT_H
-+#define V4ROOT_H
-+
-+extern int v4root_needed;
-+extern void v4root_set(void);
-+
-+#endif /* V4ROOT_H */
-diff --git a/support/nfs/Makefile.am b/support/nfs/Makefile.am
-index e9462fc..60400b2 100644
---- a/support/nfs/Makefile.am
-+++ b/support/nfs/Makefile.am
-@@ -4,7 +4,8 @@ noinst_LIBRARIES = libnfs.a
- libnfs_a_SOURCES = exports.c rmtab.c xio.c rpcmisc.c rpcdispatch.c \
- xlog.c xcommon.c wildmat.c nfsclient.c \
- nfsexport.c getfh.c nfsctl.c rpc_socket.c getport.c \
-- svc_socket.c cacheio.c closeall.c nfs_mntent.c conffile.c
-+ svc_socket.c cacheio.c closeall.c nfs_mntent.c conffile.c \
-+ svc_create.c
-
- MAINTAINERCLEANFILES = Makefile.in
-
-diff --git a/support/nfs/exports.c b/support/nfs/exports.c
-index 1aaebf4..a93941c 100644
---- a/support/nfs/exports.c
-+++ b/support/nfs/exports.c
-@@ -84,6 +84,31 @@ setexportent(char *fname, char *type)
- first = 1;
- }
-
-+static void init_exportent (struct exportent *ee, int fromkernel)
-+{
-+ ee->e_flags = EXPORT_DEFAULT_FLAGS;
-+ /* some kernels assume the default is sync rather than
-+ * async. More recent kernels always report one or other,
-+ * but this test makes sure we assume same as kernel
-+ * Ditto for wgather
-+ */
-+ if (fromkernel) {
-+ ee->e_flags &= ~NFSEXP_ASYNC;
-+ ee->e_flags &= ~NFSEXP_GATHERED_WRITES;
-+ }
-+ ee->e_anonuid = 65534;
-+ ee->e_anongid = 65534;
-+ ee->e_squids = NULL;
-+ ee->e_sqgids = NULL;
-+ ee->e_mountpoint = NULL;
-+ ee->e_fslocmethod = FSLOC_NONE;
-+ ee->e_fslocdata = NULL;
-+ ee->e_secinfo[0].flav = NULL;
-+ ee->e_nsquids = 0;
-+ ee->e_nsqgids = 0;
-+ ee->e_uuid = NULL;
-+}
-+
- struct exportent *
- getexportent(int fromkernel, int fromexports)
- {
-@@ -102,26 +127,7 @@ getexportent(int fromkernel, int fromexports)
- has_default_opts = 0;
- has_default_subtree_opts = 0;
-
-- def_ee.e_flags = EXPORT_DEFAULT_FLAGS;
-- /* some kernels assume the default is sync rather than
-- * async. More recent kernels always report one or other,
-- * but this test makes sure we assume same as kernel
-- * Ditto for wgather
-- */
-- if (fromkernel) {
-- def_ee.e_flags &= ~NFSEXP_ASYNC;
-- def_ee.e_flags &= ~NFSEXP_GATHERED_WRITES;
-- }
-- def_ee.e_anonuid = 65534;
-- def_ee.e_anongid = 65534;
-- def_ee.e_squids = NULL;
-- def_ee.e_sqgids = NULL;
-- def_ee.e_mountpoint = NULL;
-- def_ee.e_fslocmethod = FSLOC_NONE;
-- def_ee.e_fslocdata = NULL;
-- def_ee.e_secinfo[0].flav = NULL;
-- def_ee.e_nsquids = 0;
-- def_ee.e_nsqgids = 0;
-+ init_exportent(&def_ee, fromkernel);
-
- ok = getpath(def_ee.e_path, sizeof(def_ee.e_path));
- if (ok <= 0)
-@@ -334,18 +340,7 @@ mkexportent(char *hname, char *path, char *options)
- {
- static struct exportent ee;
-
-- ee.e_flags = EXPORT_DEFAULT_FLAGS;
-- ee.e_anonuid = 65534;
-- ee.e_anongid = 65534;
-- ee.e_squids = NULL;
-- ee.e_sqgids = NULL;
-- ee.e_mountpoint = NULL;
-- ee.e_fslocmethod = FSLOC_NONE;
-- ee.e_fslocdata = NULL;
-- ee.e_secinfo[0].flav = NULL;
-- ee.e_nsquids = 0;
-- ee.e_nsqgids = 0;
-- ee.e_uuid = NULL;
-+ init_exportent(&ee, 0);
-
- xfree(ee.e_hostname);
- ee.e_hostname = xstrdup(hname);
-@@ -385,7 +380,7 @@ static int valid_uuid(char *uuid)
- * do nothing if it's already there. Returns the index of flavor
- * in the resulting array in any case.
- */
--static int secinfo_addflavor(struct flav_info *flav, struct exportent *ep)
-+int secinfo_addflavor(struct flav_info *flav, struct exportent *ep)
- {
- struct sec_entry *p;
-
-@@ -467,9 +462,20 @@ static void clearflags(int mask, unsigned int active, struct exportent *ep)
- }
- }
-
--/* options that can vary per flavor: */
--#define NFSEXP_SECINFO_FLAGS (NFSEXP_READONLY | NFSEXP_ROOTSQUASH \
-- | NFSEXP_ALLSQUASH)
-+/*
-+ * For those flags which are not allowed to vary by pseudoflavor,
-+ * ensure that the export flags agree with the flags on each
-+ * pseudoflavor:
-+ */
-+static void fix_pseudoflavor_flags(struct exportent *ep)
-+{
-+ struct export_features *ef;
-+ struct sec_entry *p;
-+
-+ ef = get_export_features();
-+ for (p = ep->e_secinfo; p->flav; p++)
-+ p->flags |= ep->e_flags & ~ef->secinfo_flags;
-+}
-
- /*
- * Parse option string pointed to by cp and set mount options accordingly.
-@@ -477,7 +483,6 @@ static void clearflags(int mask, unsigned int active, struct exportent *ep)
- static int
- parseopts(char *cp, struct exportent *ep, int warn, int *had_subtree_opt_ptr)
- {
-- struct sec_entry *p;
- int had_subtree_opt = 0;
- char *flname = efname?efname:"command line";
- int flline = efp?efp->x_line:0;
-@@ -507,25 +512,25 @@ parseopts(char *cp, struct exportent *ep, int warn, int *had_subtree_opt_ptr)
- else if (strcmp(opt, "rw") == 0)
- clearflags(NFSEXP_READONLY, active, ep);
- else if (!strcmp(opt, "secure"))
-- ep->e_flags &= ~NFSEXP_INSECURE_PORT;
-+ clearflags(NFSEXP_INSECURE_PORT, active, ep);
- else if (!strcmp(opt, "insecure"))
-- ep->e_flags |= NFSEXP_INSECURE_PORT;
-+ setflags(NFSEXP_INSECURE_PORT, active, ep);
- else if (!strcmp(opt, "sync"))
-- ep->e_flags &= ~NFSEXP_ASYNC;
-+ clearflags(NFSEXP_ASYNC, active, ep);
- else if (!strcmp(opt, "async"))
-- ep->e_flags |= NFSEXP_ASYNC;
-+ setflags(NFSEXP_ASYNC, active, ep);
- else if (!strcmp(opt, "nohide"))
-- ep->e_flags |= NFSEXP_NOHIDE;
-+ setflags(NFSEXP_NOHIDE, active, ep);
- else if (!strcmp(opt, "hide"))
-- ep->e_flags &= ~NFSEXP_NOHIDE;
-+ clearflags(NFSEXP_NOHIDE, active, ep);
- else if (!strcmp(opt, "crossmnt"))
-- ep->e_flags |= NFSEXP_CROSSMOUNT;
-+ setflags(NFSEXP_CROSSMOUNT, active, ep);
- else if (!strcmp(opt, "nocrossmnt"))
-- ep->e_flags &= ~NFSEXP_CROSSMOUNT;
-+ clearflags(NFSEXP_CROSSMOUNT, active, ep);
- else if (!strcmp(opt, "wdelay"))
-- ep->e_flags |= NFSEXP_GATHERED_WRITES;
-+ setflags(NFSEXP_GATHERED_WRITES, active, ep);
- else if (!strcmp(opt, "no_wdelay"))
-- ep->e_flags &= ~NFSEXP_GATHERED_WRITES;
-+ clearflags(NFSEXP_GATHERED_WRITES, active, ep);
- else if (strcmp(opt, "root_squash") == 0)
- setflags(NFSEXP_ROOTSQUASH, active, ep);
- else if (!strcmp(opt, "no_root_squash"))
-@@ -536,22 +541,22 @@ parseopts(char *cp, struct exportent *ep, int warn, int *had_subtree_opt_ptr)
- clearflags(NFSEXP_ALLSQUASH, active, ep);
- else if (strcmp(opt, "subtree_check") == 0) {
- had_subtree_opt = 1;
-- ep->e_flags &= ~NFSEXP_NOSUBTREECHECK;
-+ clearflags(NFSEXP_NOSUBTREECHECK, active, ep);
- } else if (strcmp(opt, "no_subtree_check") == 0) {
- had_subtree_opt = 1;
-- ep->e_flags |= NFSEXP_NOSUBTREECHECK;
-+ setflags(NFSEXP_NOSUBTREECHECK, active, ep);
- } else if (strcmp(opt, "auth_nlm") == 0)
-- ep->e_flags &= ~NFSEXP_NOAUTHNLM;
-+ clearflags(NFSEXP_NOAUTHNLM, active, ep);
- else if (strcmp(opt, "no_auth_nlm") == 0)
-- ep->e_flags |= NFSEXP_NOAUTHNLM;
-+ setflags(NFSEXP_NOAUTHNLM, active, ep);
- else if (strcmp(opt, "secure_locks") == 0)
-- ep->e_flags &= ~NFSEXP_NOAUTHNLM;
-+ clearflags(NFSEXP_NOAUTHNLM, active, ep);
- else if (strcmp(opt, "insecure_locks") == 0)
-- ep->e_flags |= NFSEXP_NOAUTHNLM;
-+ setflags(NFSEXP_NOAUTHNLM, active, ep);
- else if (strcmp(opt, "acl") == 0)
-- ep->e_flags &= ~NFSEXP_NOACL;
-+ clearflags(NFSEXP_NOACL, active, ep);
- else if (strcmp(opt, "no_acl") == 0)
-- ep->e_flags |= NFSEXP_NOACL;
-+ setflags(NFSEXP_NOACL, active, ep);
- else if (strncmp(opt, "anonuid=", 8) == 0) {
- char *oe;
- ep->e_anonuid = strtol(opt+8, &oe, 10);
-@@ -583,11 +588,11 @@ bad_option:
- char *oe;
- if (strcmp(opt+5, "root") == 0) {
- ep->e_fsid = 0;
-- ep->e_flags |= NFSEXP_FSID;
-+ setflags(NFSEXP_FSID, active, ep);
- } else {
- ep->e_fsid = strtoul(opt+5, &oe, 0);
- if (opt[5]!='\0' && *oe == '\0')
-- ep->e_flags |= NFSEXP_FSID;
-+ setflags(NFSEXP_FSID, active, ep);
- else if (valid_uuid(opt+5))
- ep->e_uuid = strdup(opt+5);
- else {
-@@ -628,22 +633,15 @@ bad_option:
- } else {
- xlog(L_ERROR, "%s:%d: unknown keyword \"%s\"\n",
- flname, flline, opt);
-- ep->e_flags |= NFSEXP_ALLSQUASH | NFSEXP_READONLY;
-+ setflags(NFSEXP_ALLSQUASH | NFSEXP_READONLY, active, ep);
- goto bad_option;
- }
- free(opt);
- while (isblank(*cp))
- cp++;
- }
-- /*
-- * Turn on nohide which will allow this export to cross over
-- * the 'mount --bind' mount point.
-- */
-- if (ep->e_fslocdata)
-- ep->e_flags |= NFSEXP_NOHIDE;
-
-- for (p = ep->e_secinfo; p->flav; p++)
-- p->flags |= ep->e_flags & ~NFSEXP_SECINFO_FLAGS;
-+ fix_pseudoflavor_flags(ep);
- ep->e_squids = squids;
- ep->e_sqgids = sqgids;
- ep->e_nsquids = nsquids;
-@@ -760,4 +758,34 @@ syntaxerr(char *msg)
- xlog(L_ERROR, "%s:%d: syntax error: %s",
- efname, efp?efp->x_line:0, msg);
- }
--
-+struct export_features *get_export_features(void)
-+{
-+ static char *path = "/proc/fs/nfsd/export_features";
-+ static struct export_features ef;
-+ static int cached = 0;
-+ char buf[50];
-+ int c;
-+ int fd;
-+
-+ if (cached)
-+ return &ef;
-+
-+ ef.flags = NFSEXP_OLDFLAGS;
-+ ef.secinfo_flags = NFSEXP_OLD_SECINFO_FLAGS;
-+
-+ fd = open(path, O_RDONLY);
-+ if (fd == -1)
-+ goto good;
-+ fd = read(fd, buf, 50);
-+ if (fd == -1)
-+ goto err;
-+ c = sscanf(buf, "%x %x", &ef.flags, &ef.secinfo_flags);
-+ if (c != 2)
-+ goto err;
-+good:
-+ cached = 1;
-+ return &ef;
-+err:
-+ xlog(L_WARNING, "unexpected error reading %s", path);
-+ return &ef;
-+}
-diff --git a/support/nfs/getport.c b/support/nfs/getport.c
-index 4bdf556..c930539 100644
---- a/support/nfs/getport.c
-+++ b/support/nfs/getport.c
-@@ -45,6 +45,7 @@
- #include
- #endif
-
-+#include "sockaddr.h"
- #include "nfsrpc.h"
-
- /*
-@@ -199,7 +200,63 @@ static CLIENT *nfs_gp_get_rpcbclient(struct sockaddr *sap,
- return clnt;
- }
-
--/*
-+/**
-+ * nfs_get_proto - Convert a netid to an address family and protocol number
-+ * @netid: C string containing a netid
-+ * @family: OUT: address family
-+ * @protocol: OUT: protocol number
-+ *
-+ * Returns 1 and fills in @protocol if the netid was recognized;
-+ * otherwise zero is returned.
-+ */
-+#ifdef HAVE_LIBTIRPC
-+int
-+nfs_get_proto(const char *netid, sa_family_t *family, unsigned long *protocol)
-+{
-+ struct netconfig *nconf;
-+ struct protoent *proto;
-+
-+ nconf = getnetconfigent(netid);
-+ if (nconf == NULL)
-+ return 0;
-+
-+ proto = getprotobyname(nconf->nc_proto);
-+ if (proto == NULL) {
-+ freenetconfigent(nconf);
-+ return 0;
-+ }
-+
-+ *family = AF_UNSPEC;
-+ if (strcmp(nconf->nc_protofmly, NC_INET) == 0)
-+ *family = AF_INET;
-+ if (strcmp(nconf->nc_protofmly, NC_INET6) == 0)
-+ *family = AF_INET6;
-+ freenetconfigent(nconf);
-+
-+ *protocol = (unsigned long)proto->p_proto;
-+ return 1;
-+}
-+#else /* !HAVE_LIBTIRPC */
-+int
-+nfs_get_proto(const char *netid, sa_family_t *family, unsigned long *protocol)
-+{
-+ struct protoent *proto;
-+
-+ proto = getprotobyname(netid);
-+ if (proto == NULL)
-+ return 0;
-+
-+ *family = AF_INET;
-+ *protocol = (unsigned long)proto->p_proto;
-+ return 1;
-+}
-+#endif /* !HAVE_LIBTIRPC */
-+
-+/**
-+ * nfs_get_netid - Convert a protocol family and protocol name to a netid
-+ * @family: protocol family
-+ * @protocol: protocol number
-+ *
- * One of the arguments passed when querying remote rpcbind services
- * via rpcbind v3 or v4 is a netid string. This replaces the pm_prot
- * field used in legacy PMAP_GETPORT calls.
-@@ -213,13 +270,12 @@ static CLIENT *nfs_gp_get_rpcbclient(struct sockaddr *sap,
- * first entry that matches @family and @protocol and whose netid string
- * fits in the provided buffer.
- *
-- * Returns a '\0'-terminated string if successful; otherwise NULL.
-+ * Returns a '\0'-terminated string if successful. Caller must
-+ * free the returned string. Otherwise NULL is returned, and
- * rpc_createerr.cf_stat is set to reflect the error.
- */
- #ifdef HAVE_LIBTIRPC
--
--static char *nfs_gp_get_netid(const sa_family_t family,
-- const unsigned short protocol)
-+char *nfs_get_netid(const sa_family_t family, const unsigned long protocol)
- {
- char *nc_protofmly, *nc_proto, *nc_netid;
- struct netconfig *nconf;
-@@ -255,6 +311,9 @@ static char *nfs_gp_get_netid(const sa_family_t family,
-
- nc_netid = strdup(nconf->nc_netid);
- endnetconfig(handle);
-+
-+ if (nc_netid == NULL)
-+ rpc_createerr.cf_stat = RPC_SYSTEMERROR;
- return nc_netid;
- }
- endnetconfig(handle);
-@@ -263,8 +322,28 @@ out:
- rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
- return NULL;
- }
-+#else /* !HAVE_LIBTIRPC */
-+char *nfs_get_netid(const sa_family_t family, const unsigned long protocol)
-+{
-+ struct protoent *proto;
-+ char *netid;
-
--#endif /* HAVE_LIBTIRPC */
-+ if (family != AF_INET)
-+ goto out;
-+ proto = getprotobynumber((int)protocol);
-+ if (proto == NULL)
-+ goto out;
-+
-+ netid = strdup(proto->p_name);
-+ if (netid == NULL)
-+ rpc_createerr.cf_stat = RPC_SYSTEMERROR;
-+ return netid;
-+
-+out:
-+ rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
-+ return NULL;
-+}
-+#endif /* !HAVE_LIBTIRPC */
-
- /*
- * Extract a port number from a universal address, and terminate the
-@@ -421,7 +500,7 @@ static int nfs_gp_init_rpcb_parms(const struct sockaddr *sap,
- {
- char *netid, *addr;
-
-- netid = nfs_gp_get_netid(sap->sa_family, protocol);
-+ netid = nfs_get_netid(sap->sa_family, protocol);
- if (netid == NULL)
- return 0;
-
-@@ -627,8 +706,8 @@ int nfs_rpc_ping(const struct sockaddr *sap, const socklen_t salen,
- const rpcprog_t program, const rpcvers_t version,
- const unsigned short protocol, const struct timeval *timeout)
- {
-- struct sockaddr_storage address;
-- struct sockaddr *saddr = (struct sockaddr *)&address;
-+ union nfs_sockaddr address;
-+ struct sockaddr *saddr = &address.sa;
- CLIENT *client;
- struct timeval tout = { -1, 0 };
- int result = 0;
-@@ -696,8 +775,8 @@ unsigned short nfs_getport(const struct sockaddr *sap,
- const rpcvers_t version,
- const unsigned short protocol)
- {
-- struct sockaddr_storage address;
-- struct sockaddr *saddr = (struct sockaddr *)&address;
-+ union nfs_sockaddr address;
-+ struct sockaddr *saddr = &address.sa;
- struct timeval timeout = { -1, 0 };
- unsigned short port = 0;
- CLIENT *client;
-@@ -755,8 +834,8 @@ int nfs_getport_ping(struct sockaddr *sap, const socklen_t salen,
- }
-
- if (port != 0) {
-- struct sockaddr_storage address;
-- struct sockaddr *saddr = (struct sockaddr *)&address;
-+ union nfs_sockaddr address;
-+ struct sockaddr *saddr = &address.sa;
-
- memcpy(saddr, sap, (size_t)salen);
- nfs_set_port(saddr, port);
-@@ -807,8 +886,8 @@ unsigned short nfs_getlocalport(const rpcprot_t program,
- const rpcvers_t version,
- const unsigned short protocol)
- {
-- struct sockaddr_storage address;
-- struct sockaddr *lb_addr = (struct sockaddr *)&address;
-+ union nfs_sockaddr address;
-+ struct sockaddr *lb_addr = &address.sa;
- socklen_t lb_len = sizeof(*lb_addr);
- unsigned short port = 0;
-
-@@ -891,8 +970,8 @@ unsigned short nfs_rpcb_getaddr(const struct sockaddr *sap,
- const unsigned short protocol,
- const struct timeval *timeout)
- {
-- struct sockaddr_storage address;
-- struct sockaddr *saddr = (struct sockaddr *)&address;
-+ union nfs_sockaddr address;
-+ struct sockaddr *saddr = &address.sa;
- CLIENT *client;
- struct rpcb parms;
- struct timeval tout = { -1, 0 };
-diff --git a/support/nfs/rpc_socket.c b/support/nfs/rpc_socket.c
-index 9c20f61..0e20824 100644
---- a/support/nfs/rpc_socket.c
-+++ b/support/nfs/rpc_socket.c
-@@ -26,6 +26,8 @@
-
- #include
- #include
-+
-+#include
- #include
- #include
- #include
-@@ -38,6 +40,7 @@
- #include
- #include
-
-+#include "sockaddr.h"
- #include "nfsrpc.h"
-
- #ifdef HAVE_LIBTIRPC
-@@ -51,6 +54,7 @@
- #define NFSRPC_TIMEOUT_UDP (3)
- #define NFSRPC_TIMEOUT_TCP (10)
-
-+
- /*
- * Set up an RPC client for communicating via a AF_LOCAL socket.
- *
-@@ -121,10 +125,10 @@ static int nfs_bind(const int sock, const sa_family_t family)
-
- switch (family) {
- case AF_INET:
-- return bind(sock, (struct sockaddr *)&sin,
-+ return bind(sock, (struct sockaddr *)(char *)&sin,
- (socklen_t)sizeof(sin));
- case AF_INET6:
-- return bind(sock, (struct sockaddr *)&sin6,
-+ return bind(sock, (struct sockaddr *)(char *)&sin6,
- (socklen_t)sizeof(sin6));
- }
-
-@@ -153,9 +157,9 @@ static int nfs_bindresvport(const int sock, const sa_family_t family)
-
- switch (family) {
- case AF_INET:
-- return bindresvport_sa(sock, (struct sockaddr *)&sin);
-+ return bindresvport_sa(sock, (struct sockaddr *)(char *)&sin);
- case AF_INET6:
-- return bindresvport_sa(sock, (struct sockaddr *)&sin6);
-+ return bindresvport_sa(sock, (struct sockaddr *)(char *)&sin6);
- }
-
- errno = EAFNOSUPPORT;
-@@ -416,49 +420,6 @@ static CLIENT *nfs_get_tcpclient(const struct sockaddr *sap,
- }
-
- /**
-- * 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
- * @salen: length of socket address
-diff --git a/support/nfs/svc_create.c b/support/nfs/svc_create.c
-new file mode 100644
-index 0000000..59ba505
---- /dev/null
-+++ b/support/nfs/svc_create.c
-@@ -0,0 +1,246 @@
-+/*
-+ * Copyright 2009 Oracle. All rights reserved.
-+ *
-+ * This file is part of nfs-utils.
-+ *
-+ * nfs-utils is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * nfs-utils is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with nfs-utils. If not, see .
-+ */
-+
-+#ifdef HAVE_CONFIG_H
-+#include
-+#endif
-+
-+#include
-+#include
-+#include
-+#include
-+#include
-+#include
-+#include
-+
-+#include
-+
-+#include
-+#include
-+
-+#include
-+#include
-+
-+#ifdef HAVE_TCP_WRAPPER
-+#include "tcpwrapper.h"
-+#endif
-+
-+#include "rpcmisc.h"
-+#include "xlog.h"
-+
-+#ifdef HAVE_LIBTIRPC
-+
-+/*
-+ * Set up an appropriate bind address, given @port and @nconf.
-+ *
-+ * Returns getaddrinfo(3) results if successful. Caller must
-+ * invoke freeaddrinfo(3) on these results.
-+ *
-+ * Otherwise NULL is returned if an error occurs.
-+ */
-+__attribute_malloc__
-+static struct addrinfo *
-+svc_create_bindaddr(struct netconfig *nconf, const uint16_t port)
-+{
-+ struct addrinfo *ai = NULL;
-+ struct addrinfo hint = {
-+ .ai_flags = AI_PASSIVE | AI_NUMERICSERV,
-+ };
-+ char buf[8];
-+ int error;
-+
-+ if (strcmp(nconf->nc_protofmly, NC_INET) == 0)
-+ hint.ai_family = AF_INET;
-+#ifdef IPV6_SUPPORTED
-+ else if (strcmp(nconf->nc_protofmly, NC_INET6) == 0)
-+ hint.ai_family = AF_INET6;
-+#endif /* IPV6_SUPPORTED */
-+ else {
-+ xlog(D_GENERAL, "Unrecognized bind address family: %s",
-+ nconf->nc_protofmly);
-+ return NULL;
-+ }
-+
-+ if (strcmp(nconf->nc_proto, NC_UDP) == 0)
-+ hint.ai_protocol = (int)IPPROTO_UDP;
-+ else if (strcmp(nconf->nc_proto, NC_TCP) == 0)
-+ hint.ai_protocol = (int)IPPROTO_TCP;
-+ else {
-+ xlog(D_GENERAL, "Unrecognized bind address protocol: %s",
-+ nconf->nc_proto);
-+ return NULL;
-+ }
-+
-+ (void)snprintf(buf, sizeof(buf), "%u", port);
-+ error = getaddrinfo(NULL, buf, &hint, &ai);
-+ if (error != 0) {
-+ xlog(L_ERROR, "Failed to construct bind address: %s",
-+ gai_strerror(error));
-+ return NULL;
-+ }
-+
-+ return ai;
-+}
-+
-+static unsigned int
-+svc_create_nconf(const char *name, const rpcprog_t program,
-+ const rpcvers_t version,
-+ void (*dispatch)(struct svc_req *, SVCXPRT *),
-+ const uint16_t port, struct netconfig *nconf)
-+{
-+ struct t_bind bindaddr;
-+ struct addrinfo *ai;
-+ SVCXPRT *xprt;
-+
-+ ai = svc_create_bindaddr(nconf, port);
-+ if (ai == NULL)
-+ return 0;
-+
-+ bindaddr.addr.buf = ai->ai_addr;
-+ bindaddr.qlen = SOMAXCONN;
-+
-+ xprt = svc_tli_create(RPC_ANYFD, nconf, &bindaddr, 0, 0);
-+ freeaddrinfo(ai);
-+ if (xprt == NULL) {
-+ xlog(D_GENERAL, "Failed to create listener xprt "
-+ "(%s, %u, %s)", name, version, nconf->nc_netid);
-+ return 0;
-+ }
-+
-+ if (!svc_reg(xprt, program, version, dispatch, nconf)) {
-+ /* svc_reg(3) destroys @xprt in this case */
-+ xlog(D_GENERAL, "Failed to register (%s, %u, %s)",
-+ name, version, nconf->nc_netid);
-+ return 0;
-+ }
-+
-+ return 1;
-+}
-+
-+/**
-+ * nfs_svc_create - start up RPC svc listeners
-+ * @name: C string containing name of new service
-+ * @program: RPC program number to register
-+ * @version: RPC version number to register
-+ * @dispatch: address of function that handles incoming RPC requests
-+ * @port: if not zero, transport listens on this port
-+ *
-+ * Sets up network transports for receiving RPC requests, and starts
-+ * the RPC dispatcher. Returns the number of started network transports.
-+ */
-+unsigned int
-+nfs_svc_create(__attribute__((unused)) char *name,
-+ const rpcprog_t program, const rpcvers_t version,
-+ void (*dispatch)(struct svc_req *, SVCXPRT *),
-+ const uint16_t port)
-+{
-+ const struct sigaction create_sigaction = {
-+ .sa_handler = SIG_IGN,
-+ };
-+ unsigned int visible, up;
-+ struct netconfig *nconf;
-+ void *handlep;
-+
-+ /*
-+ * Ignore SIGPIPE to avoid exiting sideways when peers
-+ * close their TCP connection while we're trying to reply
-+ * to them.
-+ */
-+ (void)sigaction(SIGPIPE, &create_sigaction, NULL);
-+
-+ handlep = setnetconfig();
-+ if (handlep == NULL) {
-+ xlog(L_ERROR, "Failed to access local netconfig database: %s",
-+ nc_sperror());
-+ return 0;
-+ }
-+
-+ visible = 0;
-+ up = 0;
-+ while ((nconf = getnetconfig(handlep)) != NULL) {
-+ if (!(nconf->nc_flag & NC_VISIBLE))
-+ continue;
-+ visible++;
-+ up += svc_create_nconf(name, program, version, dispatch,
-+ port, nconf);
-+ }
-+
-+ if (visible == 0)
-+ xlog(L_ERROR, "Failed to find any visible netconfig entries");
-+
-+ if (endnetconfig(handlep) == -1)
-+ xlog(L_ERROR, "Failed to close local netconfig database: %s",
-+ nc_sperror());
-+
-+ return up;
-+}
-+
-+/**
-+ * nfs_svc_unregister - remove service registrations from local rpcbind database
-+ * @program: RPC program number to unregister
-+ * @version: RPC version number to unregister
-+ *
-+ * Removes all registrations for [ @program, @version ] .
-+ */
-+void
-+nfs_svc_unregister(const rpcprog_t program, const rpcvers_t version)
-+{
-+ if (rpcb_unset(program, version, NULL) == FALSE)
-+ xlog(D_GENERAL, "Failed to unregister program %lu, version %lu",
-+ (unsigned long)program, (unsigned long)version);
-+}
-+
-+#else /* !HAVE_LIBTIRPC */
-+
-+/**
-+ * nfs_svc_create - start up RPC svc listeners
-+ * @name: C string containing name of new service
-+ * @program: RPC program number to register
-+ * @version: RPC version number to register
-+ * @dispatch: address of function that handles incoming RPC requests
-+ * @port: if not zero, transport listens on this port
-+ *
-+ * Sets up network transports for receiving RPC requests, and starts
-+ * the RPC dispatcher. Returns the number of started network transports.
-+ */
-+unsigned int
-+nfs_svc_create(char *name, const rpcprog_t program, const rpcvers_t version,
-+ void (*dispatch)(struct svc_req *, SVCXPRT *),
-+ const uint16_t port)
-+{
-+ rpc_init(name, (int)program, (int)version, dispatch, (int)port);
-+ return 1;
-+}
-+
-+/**
-+ * nfs_svc_unregister - remove service registrations from local rpcbind database
-+ * @program: RPC program number to unregister
-+ * @version: RPC version number to unregister
-+ *
-+ * Removes all registrations for [ @program, @version ] .
-+ */
-+void
-+nfs_svc_unregister(const rpcprog_t program, const rpcvers_t version)
-+{
-+ if (pmap_unset((unsigned long)program, (unsigned long)version) == FALSE)
-+ xlog(D_GENERAL, "Failed to unregister program %lu, version %lu",
-+ (unsigned long)program, (unsigned long)version);
-+}
-+
-+#endif /* !HAVE_LIBTIRPC */
-diff --git a/support/nsm/Makefile.am b/support/nsm/Makefile.am
-new file mode 100644
-index 0000000..2038e68
---- /dev/null
-+++ b/support/nsm/Makefile.am
-@@ -0,0 +1,45 @@
-+## Process this file with automake to produce Makefile.in
-+
-+GENFILES_CLNT = sm_inter_clnt.c
-+GENFILES_SVC = sm_inter_svc.c
-+GENFILES_XDR = sm_inter_xdr.c
-+GENFILES_H = sm_inter.h
-+
-+GENFILES = $(GENFILES_CLNT) $(GENFILES_SVC) $(GENFILES_XDR) $(GENFILES_H)
-+
-+EXTRA_DIST = sm_inter.x
-+
-+noinst_LIBRARIES = libnsm.a
-+libnsm_a_SOURCES = $(GENFILES) file.c rpc.c
-+
-+BUILT_SOURCES = $(GENFILES)
-+
-+if CONFIG_RPCGEN
-+RPCGEN = $(top_builddir)/tools/rpcgen/rpcgen
-+$(RPCGEN):
-+ make -C ../../tools/rpcgen all
-+else
-+RPCGEN = @RPCGEN_PATH@
-+endif
-+
-+$(GENFILES_CLNT): %_clnt.c: %.x $(RPCGEN)
-+ test -f $@ && rm -rf $@ || true
-+ $(RPCGEN) -l -o $@ $<
-+
-+$(GENFILES_SVC): %_svc.c: %.x $(RPCGEN)
-+ test -f $@ && rm -rf $@ || true
-+ $(RPCGEN) -m -o $@ $<
-+
-+$(GENFILES_XDR): %_xdr.c: %.x $(RPCGEN)
-+ test -f $@ && rm -rf $@ || true
-+ $(RPCGEN) -c -o $@ $<
-+
-+$(GENFILES_H): %.h: %.x $(RPCGEN)
-+ test -f $@ && rm -rf $@ || true
-+ $(RPCGEN) -h -o $@ $<
-+ rm -f $(top_builddir)/support/include/sm_inter.h
-+ $(LN_S) ../nsm/sm_inter.h $(top_builddir)/support/include/sm_inter.h
-+
-+MAINTAINERCLEANFILES = Makefile.in
-+
-+CLEANFILES = $(GENFILES) $(top_builddir)/support/include/sm_inter.h
-diff --git a/support/nsm/file.c b/support/nsm/file.c
-new file mode 100644
-index 0000000..d469219
---- /dev/null
-+++ b/support/nsm/file.c
-@@ -0,0 +1,1067 @@
-+/*
-+ * Copyright 2009 Oracle. All rights reserved.
-+ *
-+ * This file is part of nfs-utils.
-+ *
-+ * nfs-utils is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * nfs-utils is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with nfs-utils. If not, see .
-+ */
-+
-+/*
-+ * NSM for Linux.
-+ *
-+ * Callback information and NSM state is stored in files, usually
-+ * under /var/lib/nfs. A database of information contained in local
-+ * files stores NLM callback data and what remote peers to notify of
-+ * reboots.
-+ *
-+ * For each monitored remote peer, a text file is created under the
-+ * directory specified by NSM_MONITOR_DIR. The name of the file
-+ * is a valid DNS hostname. The hostname string must be a valid
-+ * ASCII DNS name, and must not contain slash characters, white space,
-+ * or '\0' (ie. anything that might have some special meaning in a
-+ * path name).
-+ *
-+ * The contents of each file include seven blank-separated fields of
-+ * text, finished with '\n'. The first field contains the network
-+ * address of the NLM service to call back. The current implementation
-+ * supports using only IPv4 addresses, so the only contents of this
-+ * field are a network order IPv4 address expressed in 8 hexadecimal
-+ * characters.
-+ *
-+ * The next four fields are text strings of hexadecimal characters,
-+ * representing:
-+ *
-+ * 2. A 4 byte RPC program number of the NLM service to call back
-+ * 3. A 4 byte RPC version number of the NLM service to call back
-+ * 4. A 4 byte RPC procedure number of the NLM service to call back
-+ * 5. A 16 byte opaque cookie that the NLM service uses to identify
-+ * the monitored host
-+ *
-+ * The sixth field is the monitored host's mon_name, passed to statd
-+ * via an SM_MON request.
-+ *
-+ * The seventh field is the my_name for this peer, which is the
-+ * hostname of the local NLM (currently on Linux, the result of
-+ * `uname -n`). This can be used as the source address/hostname
-+ * when sending SM_NOTIFY requests.
-+ *
-+ * The NSM protocol does not limit the contents of these strings
-+ * in any way except that they must fit into 1024 bytes. Our
-+ * implementation requires that these strings not contain
-+ * white space or '\0'.
-+ */
-+
-+#ifdef HAVE_CONFIG_H
-+#include
-+#endif
-+
-+#include
-+#include
-+#include
-+#include
-+
-+#include
-+#include
-+#include
-+#ifndef S_SPLINT_S
-+#include
-+#endif
-+#include
-+#include
-+#include
-+#include
-+#include
-+#include
-+
-+#include "xlog.h"
-+#include "nsm.h"
-+
-+#define RPCARGSLEN (4 * (8 + 1))
-+#define LINELEN (RPCARGSLEN + SM_PRIV_SIZE * 2 + 1)
-+
-+#define NSM_KERNEL_STATE_FILE "/proc/sys/fs/nfs/nsm_local_state"
-+
-+/*
-+ * Some distributions place statd's files in a subdirectory
-+ */
-+#define NSM_PATH_EXTENSION
-+/* #define NSM_PATH_EXTENSION "/statd" */
-+
-+#define NSM_DEFAULT_STATEDIR NFS_STATEDIR NSM_PATH_EXTENSION
-+
-+static char nsm_base_dirname[PATH_MAX] = NSM_DEFAULT_STATEDIR;
-+
-+#define NSM_MONITOR_DIR "sm"
-+#define NSM_NOTIFY_DIR "sm.bak"
-+#define NSM_STATE_FILE "state"
-+
-+
-+static _Bool
-+error_check(const int len, const size_t buflen)
-+{
-+ return (len < 0) || ((size_t)len >= buflen);
-+}
-+
-+static _Bool
-+exact_error_check(const ssize_t len, const size_t buflen)
-+{
-+ return (len < 0) || ((size_t)len != buflen);
-+}
-+
-+/*
-+ * Returns a dynamically allocated, '\0'-terminated buffer
-+ * containing an appropriate pathname, or NULL if an error
-+ * occurs. Caller must free the returned result with free(3).
-+ */
-+__attribute_malloc__
-+static char *
-+nsm_make_record_pathname(const char *directory, const char *hostname)
-+{
-+ const char *c;
-+ size_t size;
-+ char *path;
-+ int len;
-+
-+ /*
-+ * Block hostnames that contain characters that have
-+ * meaning to the file system (like '/'), or that can
-+ * be confusing on visual inspection (like ' ').
-+ */
-+ for (c = hostname; *c != '\0'; c++)
-+ if (*c == '/' || isspace((int)*c) != 0) {
-+ xlog(D_GENERAL, "Hostname contains invalid characters");
-+ return NULL;
-+ }
-+
-+ size = strlen(nsm_base_dirname) + strlen(directory) + strlen(hostname) + 3;
-+ if (size > PATH_MAX) {
-+ xlog(D_GENERAL, "Hostname results in pathname that is too long");
-+ return NULL;
-+ }
-+
-+ path = malloc(size);
-+ if (path == NULL) {
-+ xlog(D_GENERAL, "Failed to allocate memory for pathname");
-+ return NULL;
-+ }
-+
-+ len = snprintf(path, size, "%s/%s/%s",
-+ nsm_base_dirname, directory, hostname);
-+ if (error_check(len, size)) {
-+ xlog(D_GENERAL, "Pathname did not fit in specified buffer");
-+ free(path);
-+ return NULL;
-+ }
-+
-+ return path;
-+}
-+
-+/*
-+ * Returns a dynamically allocated, '\0'-terminated buffer
-+ * containing an appropriate pathname, or NULL if an error
-+ * occurs. Caller must free the returned result with free(3).
-+ */
-+__attribute_malloc__
-+static char *
-+nsm_make_pathname(const char *directory)
-+{
-+ size_t size;
-+ char *path;
-+ int len;
-+
-+ size = strlen(nsm_base_dirname) + strlen(directory) + 2;
-+ if (size > PATH_MAX)
-+ return NULL;
-+
-+ path = malloc(size);
-+ if (path == NULL)
-+ return NULL;
-+
-+ len = snprintf(path, size, "%s/%s", nsm_base_dirname, directory);
-+ if (error_check(len, size)) {
-+ free(path);
-+ return NULL;
-+ }
-+
-+ return path;
-+}
-+
-+/*
-+ * Returns a dynamically allocated, '\0'-terminated buffer
-+ * containing an appropriate pathname, or NULL if an error
-+ * occurs. Caller must free the returned result with free(3).
-+ */
-+__attribute_malloc__
-+static char *
-+nsm_make_temp_pathname(const char *pathname)
-+{
-+ size_t size;
-+ char *path;
-+ int len;
-+
-+ size = strlen(pathname) + sizeof(".new") + 2;
-+ if (size > PATH_MAX)
-+ return NULL;
-+
-+ path = malloc(size);
-+ if (path == NULL)
-+ return NULL;
-+
-+ len = snprintf(path, size, "%s.new", pathname);
-+ if (error_check(len, size)) {
-+ free(path);
-+ return NULL;
-+ }
-+
-+ return path;
-+}
-+
-+/*
-+ * Use "mktemp, write, rename" to update the contents of a file atomically.
-+ *
-+ * Returns true if completely successful, or false if some error occurred.
-+ */
-+static _Bool
-+nsm_atomic_write(const char *path, const void *buf, const size_t buflen)
-+{
-+ _Bool result = false;
-+ ssize_t len;
-+ char *temp;
-+ int fd;
-+
-+ temp = nsm_make_temp_pathname(path);
-+ if (temp == NULL) {
-+ xlog(L_ERROR, "Failed to create new path for %s", path);
-+ goto out;
-+ }
-+
-+ fd = open(temp, O_CREAT | O_TRUNC | O_SYNC | O_WRONLY, 0644);
-+ if (fd == -1) {
-+ xlog(L_ERROR, "Failed to create %s: %m", temp);
-+ goto out;
-+ }
-+
-+ len = write(fd, buf, buflen);
-+ if (exact_error_check(len, buflen)) {
-+ xlog(L_ERROR, "Failed to write %s: %m", temp);
-+ (void)close(fd);
-+ (void)unlink(temp);
-+ goto out;
-+ }
-+
-+ if (close(fd) == -1) {
-+ xlog(L_ERROR, "Failed to close %s: %m", temp);
-+ (void)unlink(temp);
-+ goto out;
-+ }
-+
-+ if (rename(temp, path) == -1) {
-+ xlog(L_ERROR, "Failed to rename %s -> %s: %m",
-+ temp, path);
-+ (void)unlink(temp);
-+ goto out;
-+ }
-+
-+ /* Ostensibly, a sync(2) is not needed here because
-+ * open(O_CREAT), write(O_SYNC), and rename(2) are
-+ * already synchronous with persistent storage, for
-+ * any file system we care about. */
-+
-+ result = true;
-+
-+out:
-+ free(temp);
-+ return result;
-+}
-+
-+/**
-+ * nsm_setup_pathnames - set up pathname
-+ * @progname: C string containing name of program, for error messages
-+ * @parentdir: C string containing pathname to on-disk state, or NULL
-+ *
-+ * This runs before logging is set up, so error messages are directed
-+ * to stderr.
-+ *
-+ * Returns true and sets up our pathnames, if @parentdir was valid
-+ * and usable; otherwise false is returned.
-+ */
-+_Bool
-+nsm_setup_pathnames(const char *progname, const char *parentdir)
-+{
-+ static char buf[PATH_MAX];
-+ struct stat st;
-+ char *path;
-+
-+ /* First: test length of name and whether it exists */
-+ if (lstat(parentdir, &st) == -1) {
-+ (void)fprintf(stderr, "%s: Failed to stat %s: %s",
-+ progname, parentdir, strerror(errno));
-+ return false;
-+ }
-+
-+ /* Ensure we have a clean directory pathname */
-+ strncpy(buf, parentdir, sizeof(buf));
-+ path = dirname(buf);
-+ if (*path == '.') {
-+ (void)fprintf(stderr, "%s: Unusable directory %s",
-+ progname, parentdir);
-+ return false;
-+ }
-+
-+ xlog(D_CALL, "Using %s as the state directory", parentdir);
-+ strncpy(nsm_base_dirname, parentdir, sizeof(nsm_base_dirname));
-+ return true;
-+}
-+
-+/**
-+ * nsm_is_default_parentdir - check if parent directory is default
-+ *
-+ * Returns true if the active statd parent directory, set by
-+ * nsm_change_pathname(), is the same as the built-in default
-+ * parent directory; otherwise false is returned.
-+ */
-+_Bool
-+nsm_is_default_parentdir(void)
-+{
-+ return strcmp(nsm_base_dirname, NSM_DEFAULT_STATEDIR) == 0;
-+}
-+
-+/*
-+ * Clear all capabilities but CAP_NET_BIND_SERVICE. This permits
-+ * callers to acquire privileged source ports, but all other root
-+ * capabilities are disallowed.
-+ *
-+ * Returns true if successful, or false if some error occurred.
-+ */
-+static _Bool
-+nsm_clear_capabilities(void)
-+{
-+ cap_t caps;
-+
-+ caps = cap_from_text("cap_net_bind_service=ep");
-+ if (caps == NULL) {
-+ xlog(L_ERROR, "Failed to allocate capability: %m");
-+ return false;
-+ }
-+
-+ if (cap_set_proc(caps) == -1) {
-+ xlog(L_ERROR, "Failed to set capability flags: %m");
-+ (void)cap_free(caps);
-+ return false;
-+ }
-+
-+ (void)cap_free(caps);
-+ return true;
-+}
-+
-+/**
-+ * nsm_drop_privileges - drop root privileges
-+ * @pidfd: file descriptor of a pid file
-+ *
-+ * Returns true if successful, or false if some error occurred.
-+ *
-+ * Set our effective UID and GID to that of our on-disk database.
-+ */
-+_Bool
-+nsm_drop_privileges(const int pidfd)
-+{
-+ struct stat st;
-+
-+ (void)umask(S_IRWXO);
-+
-+ /*
-+ * XXX: If we can't stat dirname, or if dirname is owned by
-+ * root, we should use "statduser" instead, which is set up
-+ * by configure.ac. Nothing in nfs-utils seems to use
-+ * "statduser," though.
-+ */
-+ if (lstat(nsm_base_dirname, &st) == -1) {
-+ xlog(L_ERROR, "Failed to stat %s: %m", nsm_base_dirname);
-+ return false;
-+ }
-+
-+ if (st.st_uid == 0) {
-+ xlog_warn("Running as root. "
-+ "chown %s to choose different user", nsm_base_dirname);
-+ return true;
-+ }
-+
-+ if (chdir(nsm_base_dirname) == -1) {
-+ xlog(L_ERROR, "Failed to change working directory to %s: %m",
-+ nsm_base_dirname);
-+ return false;
-+ }
-+
-+ /*
-+ * If the pidfile happens to reside on NFS, dropping privileges
-+ * will probably cause us to lose access, even though we are
-+ * holding it open. Chown it to prevent this.
-+ */
-+ if (pidfd >= 0)
-+ if (fchown(pidfd, st.st_uid, st.st_gid) == -1)
-+ xlog_warn("Failed to change owner of pidfile: %m");
-+
-+ /*
-+ * Don't clear capabilities when dropping root.
-+ */
-+ if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == -1) {
-+ xlog(L_ERROR, "prctl(PR_SET_KEEPCAPS) failed: %m");
-+ return 0;
-+ }
-+
-+ if (setgroups(0, NULL) == -1) {
-+ xlog(L_ERROR, "Failed to drop supplementary groups: %m");
-+ return false;
-+ }
-+
-+ /*
-+ * ORDER
-+ *
-+ * setgid(2) first, as setuid(2) may remove privileges needed
-+ * to set the group id.
-+ */
-+ if (setgid(st.st_gid) == -1 || setuid(st.st_uid) == -1) {
-+ xlog(L_ERROR, "Failed to drop privileges: %m");
-+ return false;
-+ }
-+
-+ xlog(D_CALL, "Effective UID, GID: %u, %u", st.st_uid, st.st_gid);
-+
-+ return nsm_clear_capabilities();
-+}
-+
-+/**
-+ * nsm_get_state - retrieve on-disk NSM state number
-+ *
-+ * Returns an odd NSM state number read from disk, or an initial
-+ * state number. Zero is returned if some error occurs.
-+ */
-+int
-+nsm_get_state(_Bool update)
-+{
-+ int fd, state = 0;
-+ ssize_t result;
-+ char *path = NULL;
-+
-+ path = nsm_make_pathname(NSM_STATE_FILE);
-+ if (path == NULL) {
-+ xlog(L_ERROR, "Failed to allocate path for " NSM_STATE_FILE);
-+ goto out;
-+ }
-+
-+ fd = open(path, O_RDONLY);
-+ if (fd == -1) {
-+ if (errno != ENOENT) {
-+ xlog(L_ERROR, "Failed to open %s: %m", path);
-+ goto out;
-+ }
-+
-+ xlog(L_NOTICE, "Initializing NSM state");
-+ state = 1;
-+ update = true;
-+ goto update;
-+ }
-+
-+ result = read(fd, &state, sizeof(state));
-+ if (exact_error_check(result, sizeof(state))) {
-+ xlog_warn("Failed to read %s: %m", path);
-+
-+ xlog(L_NOTICE, "Initializing NSM state");
-+ state = 1;
-+ update = true;
-+ goto update;
-+ }
-+
-+ if ((state & 1) == 0)
-+ state++;
-+
-+update:
-+ (void)close(fd);
-+
-+ if (update) {
-+ state += 2;
-+ if (!nsm_atomic_write(path, &state, sizeof(state)))
-+ state = 0;
-+ }
-+
-+out:
-+ free(path);
-+ return state;
-+}
-+
-+/**
-+ * nsm_update_kernel_state - attempt to post new NSM state to kernel
-+ * @state: NSM state number
-+ *
-+ */
-+void
-+nsm_update_kernel_state(const int state)
-+{
-+ ssize_t result;
-+ char buf[20];
-+ int fd, len;
-+
-+ fd = open(NSM_KERNEL_STATE_FILE, O_WRONLY);
-+ if (fd == -1) {
-+ xlog(D_GENERAL, "Failed to open " NSM_KERNEL_STATE_FILE ": %m");
-+ return;
-+ }
-+
-+ len = snprintf(buf, sizeof(buf), "%d", state);
-+ if (error_check(len, sizeof(buf))) {
-+ xlog_warn("Failed to form NSM state number string");
-+ return;
-+ }
-+
-+ result = write(fd, buf, strlen(buf));
-+ if (exact_error_check(result, strlen(buf)))
-+ xlog_warn("Failed to write NSM state number: %m");
-+
-+ if (close(fd) == -1)
-+ xlog(L_ERROR, "Failed to close NSM state file "
-+ NSM_KERNEL_STATE_FILE ": %m");
-+}
-+
-+/**
-+ * nsm_retire_monitored_hosts - back up all hosts from "sm/" to "sm.bak/"
-+ *
-+ * Returns the count of host records that were moved.
-+ *
-+ * Note that if any error occurs during this process, some monitor
-+ * records may be left in the "sm" directory.
-+ */
-+unsigned int
-+nsm_retire_monitored_hosts(void)
-+{
-+ unsigned int count = 0;
-+ struct dirent *de;
-+ char *path;
-+ DIR *dir;
-+
-+ path = nsm_make_pathname(NSM_MONITOR_DIR);
-+ if (path == NULL) {
-+ xlog(L_ERROR, "Failed to allocate path for " NSM_MONITOR_DIR);
-+ return count;
-+ }
-+
-+ dir = opendir(path);
-+ free(path);
-+ if (dir == NULL) {
-+ xlog_warn("Failed to open " NSM_MONITOR_DIR ": %m");
-+ return count;
-+ }
-+
-+ while ((de = readdir(dir)) != NULL) {
-+ char *src, *dst;
-+
-+ if (de->d_type != (unsigned char)DT_REG)
-+ continue;
-+ if (de->d_name[0] == '.')
-+ continue;
-+
-+ src = nsm_make_record_pathname(NSM_MONITOR_DIR, de->d_name);
-+ if (src == NULL) {
-+ xlog_warn("Bad monitor file name, skipping");
-+ continue;
-+ }
-+
-+ dst = nsm_make_record_pathname(NSM_NOTIFY_DIR, de->d_name);
-+ if (dst == NULL) {
-+ free(src);
-+ xlog_warn("Bad notify file name, skipping");
-+ continue;
-+ }
-+
-+ if (rename(src, dst) == -1)
-+ xlog_warn("Failed to rename %s -> %s: %m",
-+ src, dst);
-+ else {
-+ xlog(D_GENERAL, "Retired record for mon_name %s",
-+ de->d_name);
-+ count++;
-+ }
-+
-+ free(dst);
-+ free(src);
-+ }
-+
-+ (void)closedir(dir);
-+ return count;
-+}
-+
-+/*
-+ * nsm_priv_to_hex - convert a NSM private cookie to a hex string.
-+ *
-+ * @priv: buffer holding the binary NSM private cookie
-+ * @buf: output buffer for NULL terminated hex string
-+ * @buflen: size of output buffer
-+ *
-+ * Returns the length of the resulting string or 0 on error
-+ */
-+size_t
-+nsm_priv_to_hex(const char *priv, char *buf, const size_t buflen)
-+{
-+ int i, len;
-+ size_t remaining = buflen;
-+
-+ for (i = 0; i < SM_PRIV_SIZE; i++) {
-+ len = snprintf(buf, remaining, "%02x",
-+ (unsigned int)(0xff & priv[i]));
-+ if (error_check(len, remaining))
-+ return 0;
-+ buf += len;
-+ remaining -= (size_t)len;
-+ }
-+
-+ return buflen - remaining;
-+}
-+
-+/*
-+ * Returns the length in bytes of the created record.
-+ */
-+__attribute_noinline__
-+static size_t
-+nsm_create_monitor_record(char *buf, const size_t buflen,
-+ const struct sockaddr *sap, const struct mon *m)
-+{
-+ const struct sockaddr_in *sin = (const struct sockaddr_in *)sap;
-+ size_t hexlen, remaining = buflen;
-+ int len;
-+
-+ len = snprintf(buf, remaining, "%08x %08x %08x %08x ",
-+ (unsigned int)sin->sin_addr.s_addr,
-+ (unsigned int)m->mon_id.my_id.my_prog,
-+ (unsigned int)m->mon_id.my_id.my_vers,
-+ (unsigned int)m->mon_id.my_id.my_proc);
-+ if (error_check(len, remaining))
-+ return 0;
-+ buf += len;
-+ remaining -= (size_t)len;
-+
-+ hexlen = nsm_priv_to_hex(m->priv, buf, remaining);
-+ if (hexlen == 0)
-+ return 0;
-+ buf += hexlen;
-+ remaining -= hexlen;
-+
-+ len = snprintf(buf, remaining, " %s %s\n",
-+ m->mon_id.mon_name, m->mon_id.my_id.my_name);
-+ if (error_check(len, remaining))
-+ return 0;
-+ remaining -= (size_t)len;
-+
-+ return buflen - remaining;
-+}
-+
-+static _Bool
-+nsm_append_monitored_host(const char *path, const char *line)
-+{
-+ _Bool result = false;
-+ char *buf = NULL;
-+ struct stat stb;
-+ size_t buflen;
-+ ssize_t len;
-+ int fd;
-+
-+ if (stat(path, &stb) == -1) {
-+ xlog(L_ERROR, "Failed to insert: "
-+ "could not stat original file %s: %m", path);
-+ goto out;
-+ }
-+ buflen = (size_t)stb.st_size + strlen(line);
-+
-+ buf = malloc(buflen + 1);
-+ if (buf == NULL) {
-+ xlog(L_ERROR, "Failed to insert: no memory");
-+ goto out;
-+ }
-+ memset(buf, 0, buflen + 1);
-+
-+ fd = open(path, O_RDONLY);
-+ if (fd == -1) {
-+ xlog(L_ERROR, "Failed to insert: "
-+ "could not open original file %s: %m", path);
-+ goto out;
-+ }
-+
-+ len = read(fd, buf, (size_t)stb.st_size);
-+ if (exact_error_check(len, (size_t)stb.st_size)) {
-+ xlog(L_ERROR, "Failed to insert: "
-+ "could not read original file %s: %m", path);
-+ (void)close(fd);
-+ goto out;
-+ }
-+ (void)close(fd);
-+
-+ strcat(buf, line);
-+
-+ if (nsm_atomic_write(path, buf, buflen))
-+ result = true;
-+
-+out:
-+ free(buf);
-+ return result;
-+}
-+
-+/**
-+ * nsm_insert_monitored_host - write callback data for one host to disk
-+ * @hostname: C string containing a hostname
-+ * @sap: sockaddr containing NLM callback address
-+ * @mon: SM_MON arguments to save
-+ *
-+ * Returns true if successful, otherwise false if some error occurs.
-+ */
-+_Bool
-+nsm_insert_monitored_host(const char *hostname, const struct sockaddr *sap,
-+ const struct mon *m)
-+{
-+ static char buf[LINELEN + 1 + SM_MAXSTRLEN + 2];
-+ char *path;
-+ _Bool result = false;
-+ ssize_t len;
-+ size_t size;
-+ int fd;
-+
-+ path = nsm_make_record_pathname(NSM_MONITOR_DIR, hostname);
-+ if (path == NULL) {
-+ xlog(L_ERROR, "Failed to insert: bad monitor hostname '%s'",
-+ hostname);
-+ return false;
-+ }
-+
-+ size = nsm_create_monitor_record(buf, sizeof(buf), sap, m);
-+ if (size == 0) {
-+ xlog(L_ERROR, "Failed to insert: record too long");
-+ goto out;
-+ }
-+
-+ /*
-+ * If exclusive create fails, we're adding a new line to an
-+ * existing file.
-+ */
-+ fd = open(path, O_WRONLY | O_CREAT | O_EXCL | O_SYNC, S_IRUSR | S_IWUSR);
-+ if (fd == -1) {
-+ if (errno != EEXIST) {
-+ xlog(L_ERROR, "Failed to insert: creating %s: %m", path);
-+ goto out;
-+ }
-+
-+ result = nsm_append_monitored_host(path, buf);
-+ goto out;
-+ }
-+ result = true;
-+
-+ len = write(fd, buf, size);
-+ if (exact_error_check(len, size)) {
-+ xlog_warn("Failed to insert: writing %s: %m", path);
-+ (void)unlink(path);
-+ result = false;
-+ }
-+
-+ if (close(fd) == -1) {
-+ xlog(L_ERROR, "Failed to insert: closing %s: %m", path);
-+ (void)unlink(path);
-+ result = false;
-+ }
-+
-+out:
-+ free(path);
-+ return result;
-+}
-+
-+__attribute_noinline__
-+static _Bool
-+nsm_parse_line(char *line, struct sockaddr_in *sin, struct mon *m)
-+{
-+ unsigned int i, tmp;
-+ int count;
-+ char *c;
-+
-+ c = strchr(line, '\n');
-+ if (c != NULL)
-+ *c = '\0';
-+
-+ count = sscanf(line, "%8x %8x %8x %8x ",
-+ (unsigned int *)&sin->sin_addr.s_addr,
-+ (unsigned int *)&m->mon_id.my_id.my_prog,
-+ (unsigned int *)&m->mon_id.my_id.my_vers,
-+ (unsigned int *)&m->mon_id.my_id.my_proc);
-+ if (count != 4)
-+ return false;
-+
-+ c = line + RPCARGSLEN;
-+ for (i = 0; i < SM_PRIV_SIZE; i++) {
-+ if (sscanf(c, "%2x", &tmp) != 1)
-+ return false;
-+ m->priv[i] = (char)tmp;
-+ c += 2;
-+ }
-+
-+ c++;
-+ m->mon_id.mon_name = c;
-+ while (*c != '\0' && *c != ' ')
-+ c++;
-+ if (*c != '\0')
-+ *c++ = '\0';
-+ while (*c == ' ')
-+ c++;
-+ m->mon_id.my_id.my_name = c;
-+
-+ return true;
-+}
-+
-+/*
-+ * Stuff a 'struct mon' with callback data, and call @func.
-+ *
-+ * Returns the count of in-core records created.
-+ */
-+static unsigned int
-+nsm_read_line(const char *hostname, const time_t timestamp, char *line,
-+ nsm_populate_t func)
-+{
-+ struct sockaddr_in sin = {
-+ .sin_family = AF_INET,
-+ };
-+ struct mon m;
-+
-+ if (!nsm_parse_line(line, &sin, &m))
-+ return 0;
-+
-+ return func(hostname, (struct sockaddr *)(char *)&sin, &m, timestamp);
-+}
-+
-+/*
-+ * Given a filename, reads data from a file under NSM_MONITOR_DIR
-+ * and invokes @func so caller can populate their in-core
-+ * database with this data.
-+ */
-+static unsigned int
-+nsm_load_host(const char *directory, const char *filename, nsm_populate_t func)
-+{
-+ char buf[LINELEN + 1 + SM_MAXSTRLEN + 2];
-+ unsigned int result = 0;
-+ struct stat stb;
-+ char *path;
-+ FILE *f;
-+
-+ path = nsm_make_record_pathname(directory, filename);
-+ if (path == NULL)
-+ goto out_err;
-+
-+ if (stat(path, &stb) == -1) {
-+ xlog(L_ERROR, "Failed to stat %s: %m", path);
-+ goto out_freepath;
-+ }
-+
-+ f = fopen(path, "r");
-+ if (f == NULL) {
-+ xlog(L_ERROR, "Failed to open %s: %m", path);
-+ goto out_freepath;
-+ }
-+
-+ while (fgets(buf, (int)sizeof(buf), f) != NULL) {
-+ buf[sizeof(buf) - 1] = '\0';
-+ result += nsm_read_line(filename, stb.st_mtime, buf, func);
-+ }
-+ if (result == 0)
-+ xlog(L_ERROR, "Failed to read monitor data from %s", path);
-+
-+ (void)fclose(f);
-+
-+out_freepath:
-+ free(path);
-+out_err:
-+ return result;
-+}
-+
-+static unsigned int
-+nsm_load_dir(const char *directory, nsm_populate_t func)
-+{
-+ unsigned int count = 0;
-+ struct dirent *de;
-+ char *path;
-+ DIR *dir;
-+
-+ path = nsm_make_pathname(directory);
-+ if (path == NULL) {
-+ xlog(L_ERROR, "Failed to allocate path for directory %s",
-+ directory);
-+ return 0;
-+ }
-+
-+ dir = opendir(path);
-+ free(path);
-+ if (dir == NULL) {
-+ xlog(L_ERROR, "Failed to open directory %s: %m",
-+ directory);
-+ return 0;
-+ }
-+
-+ while ((de = readdir(dir)) != NULL) {
-+ if (de->d_type != (unsigned char)DT_REG)
-+ continue;
-+ if (de->d_name[0] == '.')
-+ continue;
-+
-+ count += nsm_load_host(directory, de->d_name, func);
-+ }
-+
-+ (void)closedir(dir);
-+ return count;
-+}
-+
-+/**
-+ * nsm_load_monitor_list - load list of hosts to monitor
-+ * @func: callback function to create entry for one host
-+ *
-+ * Returns the count of hosts that were found in the directory.
-+ */
-+unsigned int
-+nsm_load_monitor_list(nsm_populate_t func)
-+{
-+ return nsm_load_dir(NSM_MONITOR_DIR, func);
-+}
-+
-+/**
-+ * nsm_load_notify_list - load list of hosts to notify
-+ * @func: callback function to create entry for one host
-+ *
-+ * Returns the count of hosts that were found in the directory.
-+ */
-+unsigned int
-+nsm_load_notify_list(nsm_populate_t func)
-+{
-+ return nsm_load_dir(NSM_NOTIFY_DIR, func);
-+}
-+
-+static void
-+nsm_delete_host(const char *directory, const char *hostname,
-+ const char *mon_name, const char *my_name)
-+{
-+ char line[LINELEN + 1 + SM_MAXSTRLEN + 2];
-+ char *outbuf = NULL;
-+ struct stat stb;
-+ char *path, *next;
-+ size_t remaining;
-+ FILE *f;
-+
-+ path = nsm_make_record_pathname(directory, hostname);
-+ if (path == NULL) {
-+ xlog(L_ERROR, "Bad filename, not deleting");
-+ return;
-+ }
-+
-+ if (stat(path, &stb) == -1) {
-+ xlog(L_ERROR, "Failed to delete: "
-+ "could not stat original file %s: %m", path);
-+ goto out;
-+ }
-+ remaining = (size_t)stb.st_size + 1;
-+
-+ outbuf = malloc(remaining);
-+ if (outbuf == NULL) {
-+ xlog(L_ERROR, "Failed to delete: no memory");
-+ goto out;
-+ }
-+
-+ f = fopen(path, "r");
-+ if (f == NULL) {
-+ xlog(L_ERROR, "Failed to delete: "
-+ "could not open original file %s: %m", path);
-+ goto out;
-+ }
-+
-+ /*
-+ * Walk the records in the file, and copy the non-matching
-+ * ones to our output buffer.
-+ */
-+ next = outbuf;
-+ while (fgets(line, (int)sizeof(line), f) != NULL) {
-+ struct sockaddr_in sin;
-+ struct mon m;
-+ size_t len;
-+
-+ if (!nsm_parse_line(line, &sin, &m)) {
-+ xlog(L_ERROR, "Failed to delete: "
-+ "could not parse original file %s", path);
-+ (void)fclose(f);
-+ goto out;
-+ }
-+
-+ if (strcmp(mon_name, m.mon_id.mon_name) == 0 &&
-+ strcmp(my_name, m.mon_id.my_id.my_name) == 0)
-+ continue;
-+
-+ /* nsm_parse_line destroys the contents of line[], so
-+ * reconstruct the copy in our output buffer. */
-+ len = nsm_create_monitor_record(next, remaining,
-+ (struct sockaddr *)(char *)&sin, &m);
-+ if (len == 0) {
-+ xlog(L_ERROR, "Failed to delete: "
-+ "could not construct output record");
-+ (void)fclose(f);
-+ goto out;
-+ }
-+ next += len;
-+ remaining -= len;
-+ }
-+
-+ (void)fclose(f);
-+
-+ /*
-+ * If nothing was copied when we're done, then unlink the file.
-+ * Otherwise, atomically update the contents of the file.
-+ */
-+ if (next != outbuf) {
-+ if (!nsm_atomic_write(path, outbuf, strlen(outbuf)))
-+ xlog(L_ERROR, "Failed to delete: "
-+ "could not write new file %s: %m", path);
-+ } else {
-+ if (unlink(path) == -1)
-+ xlog(L_ERROR, "Failed to delete: "
-+ "could not unlink file %s: %m", path);
-+ }
-+
-+out:
-+ free(outbuf);
-+ free(path);
-+}
-+
-+/**
-+ * nsm_delete_monitored_host - delete on-disk record for monitored host
-+ * @hostname: '\0'-terminated C string containing hostname of record to delete
-+ * @mon_name: '\0'-terminated C string containing monname of record to delete
-+ * @my_name: '\0'-terminated C string containing myname of record to delete
-+ *
-+ */
-+void
-+nsm_delete_monitored_host(const char *hostname, const char *mon_name,
-+ const char *my_name)
-+{
-+ nsm_delete_host(NSM_MONITOR_DIR, hostname, mon_name, my_name);
-+}
-+
-+/**
-+ * nsm_delete_notified_host - delete on-disk host record after notification
-+ * @hostname: '\0'-terminated C string containing hostname of record to delete
-+ * @mon_name: '\0'-terminated C string containing monname of record to delete
-+ * @my_name: '\0'-terminated C string containing myname of record to delete
-+ *
-+ */
-+void
-+nsm_delete_notified_host(const char *hostname, const char *mon_name,
-+ const char *my_name)
-+{
-+ nsm_delete_host(NSM_NOTIFY_DIR, hostname, mon_name, my_name);
-+}
-diff --git a/support/nsm/rpc.c b/support/nsm/rpc.c
-new file mode 100644
-index 0000000..4e5f40e
---- /dev/null
-+++ b/support/nsm/rpc.c
-@@ -0,0 +1,534 @@
-+/*
-+ * Copyright 2009 Oracle. All rights reserved.
-+ *
-+ * This file is part of nfs-utils.
-+ *
-+ * nfs-utils is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * nfs-utils is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with nfs-utils. If not, see .
-+ */
-+
-+/*
-+ * NSM for Linux.
-+ *
-+ * Instead of using ONC or TI RPC library calls, statd constructs
-+ * RPC calls directly in socket buffers. This allows a single
-+ * socket to be concurrently shared among several different RPC
-+ * programs and versions using a simple RPC request dispatcher.
-+ *
-+ * This file contains the details of RPC header and call
-+ * construction and reply parsing, and a method for creating a
-+ * socket for use with these functions.
-+ */
-+
-+#ifdef HAVE_CONFIG_H
-+#include
-+#endif /* HAVE_CONFIG_H */
-+
-+#include
-+#include
-+#include
-+
-+#include
-+#include
-+#include
-+#include
-+#include
-+
-+#include
-+#include
-+#include
-+#include
-+
-+#include
-+#include
-+#include
-+
-+#ifdef HAVE_LIBTIRPC
-+#include
-+#include
-+#endif /* HAVE_LIBTIRPC */
-+
-+#include "xlog.h"
-+#include "nfsrpc.h"
-+#include "nsm.h"
-+#include "sm_inter.h"
-+
-+/*
-+ * Returns a fresh XID appropriate for RPC over UDP -- never zero.
-+ */
-+static uint32_t
-+nsm_next_xid(void)
-+{
-+ static uint32_t nsm_xid = 0;
-+ struct timeval now;
-+
-+ if (nsm_xid == 0) {
-+ (void)gettimeofday(&now, NULL);
-+ nsm_xid = (uint32_t)getpid() ^
-+ (uint32_t)now.tv_sec ^ (uint32_t)now.tv_usec;
-+ }
-+
-+ return nsm_xid++;
-+}
-+
-+/*
-+ * Select a fresh XID and construct an RPC header in @mesg.
-+ * Always use AUTH_NULL credentials and verifiers.
-+ *
-+ * Returns the new XID.
-+ */
-+static uint32_t
-+nsm_init_rpc_header(const rpcprog_t program, const rpcvers_t version,
-+ const rpcproc_t procedure, struct rpc_msg *mesg)
-+{
-+ struct call_body *cb = &mesg->rm_call;
-+ uint32_t xid = nsm_next_xid();
-+
-+ memset(mesg, 0, sizeof(*mesg));
-+
-+ mesg->rm_xid = (unsigned long)xid;
-+ mesg->rm_direction = CALL;
-+
-+ cb->cb_rpcvers = RPC_MSG_VERSION;
-+ cb->cb_prog = program;
-+ cb->cb_vers = version;
-+ cb->cb_proc = procedure;
-+
-+ cb->cb_cred.oa_flavor = AUTH_NULL;
-+ cb->cb_cred.oa_base = (caddr_t) NULL;
-+ cb->cb_cred.oa_length = 0;
-+ cb->cb_verf.oa_flavor = AUTH_NULL;
-+ cb->cb_verf.oa_base = (caddr_t) NULL;
-+ cb->cb_verf.oa_length = 0;
-+
-+ return xid;
-+}
-+
-+/*
-+ * Initialize the network send buffer and XDR memory for encoding.
-+ */
-+static void
-+nsm_init_xdrmem(char *msgbuf, const unsigned int msgbuflen,
-+ XDR *xdrp)
-+{
-+ memset(msgbuf, 0, (size_t)msgbuflen);
-+ memset(xdrp, 0, sizeof(*xdrp));
-+ xdrmem_create(xdrp, msgbuf, msgbuflen, XDR_ENCODE);
-+}
-+
-+/*
-+ * Send a completed RPC call on a socket.
-+ *
-+ * Returns true if all the bytes were sent successfully; otherwise
-+ * false if any error occurred.
-+ */
-+static _Bool
-+nsm_rpc_sendto(const int sock, const struct sockaddr *sap,
-+ const socklen_t salen, XDR *xdrs, void *buf)
-+{
-+ const size_t buflen = (size_t)xdr_getpos(xdrs);
-+ ssize_t err;
-+
-+ err = sendto(sock, buf, buflen, 0, sap, salen);
-+ if ((err < 0) || ((size_t)err != buflen)) {
-+ xlog(L_ERROR, "%s: sendto failed: %m", __func__);
-+ return false;
-+ }
-+ return true;
-+}
-+
-+/**
-+ * nsm_xmit_getport - post a PMAP_GETPORT call on a socket descriptor
-+ * @sock: datagram socket descriptor
-+ * @sin: pointer to AF_INET socket address of server
-+ * @program: RPC program number to query
-+ * @version: RPC version number to query
-+ *
-+ * Send a PMAP_GETPORT call to the portmap daemon at @sin using
-+ * socket descriptor @sock. This request queries the RPC program
-+ * [program, version, IPPROTO_UDP].
-+ *
-+ * NB: PMAP_GETPORT works only for IPv4 hosts. This implementation
-+ * works only over UDP, and queries only UDP registrations.
-+ *
-+ * Returns the XID of the call, or zero if an error occurred.
-+ */
-+uint32_t
-+nsm_xmit_getport(const int sock, const struct sockaddr_in *sin,
-+ const unsigned long program,
-+ const unsigned long version)
-+{
-+ char msgbuf[NSM_MAXMSGSIZE];
-+ struct sockaddr_in addr;
-+ struct rpc_msg mesg;
-+ _Bool sent = false;
-+ struct pmap parms = {
-+ .pm_prog = program,
-+ .pm_vers = version,
-+ .pm_prot = (unsigned long)IPPROTO_UDP,
-+ };
-+ uint32_t xid;
-+ XDR xdr;
-+
-+ xlog(D_CALL, "Sending PMAP_GETPORT for %u, %u, udp", program, version);
-+
-+ nsm_init_xdrmem(msgbuf, NSM_MAXMSGSIZE, &xdr);
-+ xid = nsm_init_rpc_header(PMAPPROG, PMAPVERS,
-+ (rpcproc_t)PMAPPROC_GETPORT, &mesg);
-+
-+ addr = *sin;
-+ addr.sin_port = htons(PMAPPORT);
-+
-+ if (xdr_callmsg(&xdr, &mesg) == TRUE &&
-+ xdr_pmap(&xdr, &parms) == TRUE)
-+ sent = nsm_rpc_sendto(sock, (struct sockaddr *)(char *)&addr,
-+ (socklen_t)sizeof(addr), &xdr, msgbuf);
-+ else
-+ xlog(L_ERROR, "%s: can't encode PMAP_GETPORT call", __func__);
-+
-+ xdr_destroy(&xdr);
-+
-+ if (sent == false)
-+ return 0;
-+ return xid;
-+}
-+
-+/**
-+ * nsm_xmit_getaddr - post an RPCB_GETADDR call on a socket descriptor
-+ * @sock: datagram socket descriptor
-+ * @sin: pointer to AF_INET6 socket address of server
-+ * @program: RPC program number to query
-+ * @version: RPC version number to query
-+ *
-+ * Send an RPCB_GETADDR call to the rpcbind daemon at @sap using
-+ * socket descriptor @sock. This request queries the RPC program
-+ * [program, version, "udp6"].
-+ *
-+ * NB: RPCB_GETADDR works for both IPv4 and IPv6 hosts. This
-+ * implementation works only over UDP and AF_INET6, and queries
-+ * only "udp6" registrations.
-+ *
-+ * Returns the XID of the call, or zero if an error occurred.
-+ */
-+#ifdef HAVE_LIBTIRPC
-+uint32_t
-+nsm_xmit_getaddr(const int sock, const struct sockaddr_in6 *sin6,
-+ const rpcprog_t program, const rpcvers_t version)
-+{
-+ char msgbuf[NSM_MAXMSGSIZE];
-+ struct sockaddr_in6 addr;
-+ struct rpc_msg mesg;
-+ _Bool sent = false;
-+ struct rpcb parms = {
-+ .r_prog = program,
-+ .r_vers = version,
-+ .r_netid = "udp6",
-+ .r_owner = "",
-+ };
-+ uint32_t xid;
-+ XDR xdr;
-+
-+ xlog(D_CALL, "Sending RPCB_GETADDR for %u, %u, udp6", program, version);
-+
-+ nsm_init_xdrmem(msgbuf, NSM_MAXMSGSIZE, &xdr);
-+ xid = nsm_init_rpc_header(RPCBPROG, RPCBVERS,
-+ (rpcproc_t)RPCBPROC_GETADDR, &mesg);
-+
-+ addr = *sin6;
-+ addr.sin6_port = htons(PMAPPORT);
-+ parms.r_addr = nfs_sockaddr2universal((struct sockaddr *)(char *)&addr);
-+ if (parms.r_addr == NULL) {
-+ xlog(L_ERROR, "%s: can't encode socket address", __func__);
-+ return 0;
-+ }
-+
-+ if (xdr_callmsg(&xdr, &mesg) == TRUE &&
-+ xdr_rpcb(&xdr, &parms) == TRUE)
-+ sent = nsm_rpc_sendto(sock, (struct sockaddr *)(char *)&addr,
-+ (socklen_t)sizeof(addr), &xdr, msgbuf);
-+ else
-+ xlog(L_ERROR, "%s: can't encode RPCB_GETADDR call", __func__);
-+
-+ xdr_destroy(&xdr);
-+ free(parms.r_addr);
-+
-+ if (sent == false)
-+ return 0;
-+ return xid;
-+}
-+#else /* !HAVE_LIBTIRPC */
-+uint32_t
-+nsm_xmit_getaddr(const int sock __attribute__((unused)),
-+ const struct sockaddr_in6 *sin6 __attribute__((unused)),
-+ const rpcprog_t program __attribute__((unused)),
-+ const rpcvers_t version __attribute__((unused)))
-+{
-+ return 0;
-+}
-+#endif /* !HAVE_LIBTIRPC */
-+
-+/**
-+ * nsm_xmit_rpcbind - post an rpcbind request
-+ * @sock: datagram socket descriptor
-+ * @sap: pointer to socket address of server
-+ * @program: RPC program number to query
-+ * @version: RPC version number to query
-+ *
-+ * Send an rpcbind query to the rpcbind daemon at @sap using
-+ * socket descriptor @sock.
-+ *
-+ * NB: This implementation works only over UDP, but can query IPv4 or IPv6
-+ * hosts. It queries only UDP registrations.
-+ *
-+ * Returns the XID of the call, or zero if an error occurred.
-+ */
-+uint32_t
-+nsm_xmit_rpcbind(const int sock, const struct sockaddr *sap,
-+ const rpcprog_t program, const rpcvers_t version)
-+{
-+ switch (sap->sa_family) {
-+ case AF_INET:
-+ return nsm_xmit_getport(sock, (const struct sockaddr_in *)sap,
-+ program, version);
-+ case AF_INET6:
-+ return nsm_xmit_getaddr(sock, (const struct sockaddr_in6 *)sap,
-+ program, version);
-+ }
-+ return 0;
-+}
-+
-+/**
-+ * nsm_xmit_notify - post an NSMPROC_NOTIFY call on a socket descriptor
-+ * @sock: datagram socket descriptor
-+ * @sap: pointer to socket address of peer to notify (port already filled in)
-+ * @salen: length of socket address
-+ * @program: RPC program number to use
-+ * @mon_name: mon_name of local peer (ie the rebooting system)
-+ * @state: state of local peer
-+ *
-+ * Send an NSMPROC_NOTIFY call to the peer at @sap using socket descriptor @sock.
-+ * This request notifies the peer that we have rebooted.
-+ *
-+ * NB: This implementation works only over UDP, but supports both AF_INET
-+ * and AF_INET6.
-+ *
-+ * Returns the XID of the call, or zero if an error occurred.
-+ */
-+uint32_t
-+nsm_xmit_notify(const int sock, const struct sockaddr *sap,
-+ const socklen_t salen, const rpcprog_t program,
-+ const char *mon_name, const int state)
-+{
-+ char msgbuf[NSM_MAXMSGSIZE];
-+ struct stat_chge state_change;
-+ struct rpc_msg mesg;
-+ _Bool sent = false;
-+ uint32_t xid;
-+ XDR xdr;
-+
-+ state_change.mon_name = strdup(mon_name);
-+ if (state_change.mon_name == NULL) {
-+ xlog(L_ERROR, "%s: no memory", __func__);
-+ return 0;
-+ }
-+ state_change.state = state;
-+
-+ xlog(D_CALL, "Sending SM_NOTIFY for %s", mon_name);
-+
-+ nsm_init_xdrmem(msgbuf, NSM_MAXMSGSIZE, &xdr);
-+ xid = nsm_init_rpc_header(program, SM_VERS, SM_NOTIFY, &mesg);
-+
-+ if (xdr_callmsg(&xdr, &mesg) == TRUE &&
-+ xdr_stat_chge(&xdr, &state_change) == TRUE)
-+ sent = nsm_rpc_sendto(sock, sap, salen, &xdr, msgbuf);
-+ else
-+ xlog(L_ERROR, "%s: can't encode NSMPROC_NOTIFY call",
-+ __func__);
-+
-+ xdr_destroy(&xdr);
-+ free(state_change.mon_name);
-+
-+ if (sent == false)
-+ return 0;
-+ return xid;
-+}
-+
-+/**
-+ * nsm_xmit_nlmcall - post an unnamed call to local NLM on a socket descriptor
-+ * @sock: datagram socket descriptor
-+ * @sap: address/port of NLM service to contact
-+ * @salen: size of @sap
-+ * @m: callback data defining RPC call to make
-+ * @state: state of rebooting host
-+ *
-+ * Send an unnamed call (previously requested via NSMPROC_MON) to the
-+ * specified local UDP-based RPC service using socket descriptor @sock.
-+ *
-+ * NB: This implementation works only over UDP, but supports both AF_INET
-+ * and AF_INET6.
-+ *
-+ * Returns the XID of the call, or zero if an error occurred.
-+ */
-+uint32_t
-+nsm_xmit_nlmcall(const int sock, const struct sockaddr *sap,
-+ const socklen_t salen, const struct mon *m,
-+ const int state)
-+{
-+ const struct my_id *id = &m->mon_id.my_id;
-+ char msgbuf[NSM_MAXMSGSIZE];
-+ struct status new_status;
-+ struct rpc_msg mesg;
-+ _Bool sent = false;
-+ uint32_t xid;
-+ XDR xdr;
-+
-+ xlog(D_CALL, "Sending NLM downcall for %s", m->mon_id.mon_name);
-+
-+ nsm_init_xdrmem(msgbuf, NSM_MAXMSGSIZE, &xdr);
-+ xid = nsm_init_rpc_header((rpcprog_t)id->my_prog,
-+ (rpcvers_t)id->my_vers,
-+ (rpcproc_t)id->my_proc, &mesg);
-+
-+ new_status.mon_name = m->mon_id.mon_name;
-+ new_status.state = state;
-+ memcpy(&new_status.priv, &m->priv, sizeof(new_status.priv));
-+
-+ if (xdr_callmsg(&xdr, &mesg) == TRUE &&
-+ xdr_status(&xdr, &new_status) == TRUE)
-+ sent = nsm_rpc_sendto(sock, sap, salen, &xdr, msgbuf);
-+ else
-+ xlog(L_ERROR, "%s: can't encode NLM downcall", __func__);
-+
-+ xdr_destroy(&xdr);
-+
-+ if (sent == false)
-+ return 0;
-+ return xid;
-+}
-+
-+/**
-+ * nsm_parse_reply - parse and validate the header in an RPC reply
-+ * @xdrs: pointer to XDR
-+ *
-+ * Returns the XID of the reply, or zero if an error occurred.
-+ */
-+uint32_t
-+nsm_parse_reply(XDR *xdrs)
-+{
-+ struct rpc_msg mesg = {
-+ .rm_reply.rp_acpt.ar_results.proc = (xdrproc_t)xdr_void,
-+ };
-+ uint32_t xid;
-+
-+ if (xdr_replymsg(xdrs, &mesg) == FALSE) {
-+ xlog(L_ERROR, "%s: can't decode RPC reply", __func__);
-+ return 0;
-+ }
-+ xid = (uint32_t)mesg.rm_xid;
-+
-+ if (mesg.rm_reply.rp_stat != MSG_ACCEPTED) {
-+ xlog(L_ERROR, "%s: [0x%x] RPC status %d",
-+ __func__, xid, mesg.rm_reply.rp_stat);
-+ return 0;
-+ }
-+
-+ if (mesg.rm_reply.rp_acpt.ar_stat != SUCCESS) {
-+ xlog(L_ERROR, "%s: [0x%x] RPC accept status %d",
-+ __func__, xid, mesg.rm_reply.rp_acpt.ar_stat);
-+ return 0;
-+ }
-+
-+ return xid;
-+}
-+
-+/**
-+ * nsm_recv_getport - parse PMAP_GETPORT reply
-+ * @xdrs: pointer to XDR
-+ *
-+ * Returns the port number from the RPC reply, or zero
-+ * if an error occurred.
-+ */
-+unsigned long
-+nsm_recv_getport(XDR *xdrs)
-+{
-+ unsigned long port = 0;
-+
-+ if (xdr_u_long(xdrs, &port) == FALSE)
-+ xlog(L_ERROR, "%s: can't decode pmap reply",
-+ __func__);
-+ if (port > UINT16_MAX) {
-+ xlog(L_ERROR, "%s: bad port number",
-+ __func__);
-+ port = 0;
-+ }
-+
-+ xlog(D_CALL, "Received PMAP_GETPORT result: %lu", port);
-+ return port;
-+}
-+
-+/**
-+ * nsm_recv_getaddr - parse RPCB_GETADDR reply
-+ * @xdrs: pointer to XDR
-+ *
-+ * Returns the port number from the RPC reply, or zero
-+ * if an error occurred.
-+ */
-+uint16_t
-+nsm_recv_getaddr(XDR *xdrs)
-+{
-+ char *uaddr = NULL;
-+ int port;
-+
-+ if (xdr_wrapstring(xdrs, &uaddr) == FALSE)
-+ xlog(L_ERROR, "%s: can't decode rpcb reply",
-+ __func__);
-+
-+ if ((uaddr == NULL) || (uaddr[0] == '\0')) {
-+ xlog(D_CALL, "Received RPCB_GETADDR result: "
-+ "program not registered");
-+ return 0;
-+ }
-+
-+ port = nfs_universal2port(uaddr);
-+
-+ xdr_free((xdrproc_t)xdr_wrapstring, (char *)&uaddr);
-+
-+ if (port < 0 || port > UINT16_MAX) {
-+ xlog(L_ERROR, "%s: bad port number",
-+ __func__);
-+ return 0;
-+ }
-+
-+ xlog(D_CALL, "Received RPCB_GETADDR result: %d", port);
-+ return (uint16_t)port;
-+}
-+
-+/**
-+ * nsm_recv_rpcbind - parse rpcbind reply
-+ * @af: address family of reply
-+ * @xdrs: pointer to XDR
-+ *
-+ * Returns the port number from the RPC reply, or zero
-+ * if an error occurred.
-+ */
-+uint16_t
-+nsm_recv_rpcbind(const sa_family_t family, XDR *xdrs)
-+{
-+ switch (family) {
-+ case AF_INET:
-+ return (uint16_t)nsm_recv_getport(xdrs);
-+ case AF_INET6:
-+ return nsm_recv_getaddr(xdrs);
-+ }
-+ return 0;
-+}
-diff --git a/support/nsm/sm_inter.x b/support/nsm/sm_inter.x
-new file mode 100644
-index 0000000..d8e0ad7
---- /dev/null
-+++ b/support/nsm/sm_inter.x
-@@ -0,0 +1,131 @@
-+/*
-+ * Copyright (C) 1986 Sun Microsystems, Inc.
-+ * Modified by Jeffrey A. Uphoff, 1995, 1997-1999.
-+ * Modified by Olaf Kirch, 1996.
-+ * Modified by H.J. Lu, 1998.
-+ *
-+ * NSM for Linux.
-+ */
-+
-+/*
-+ * Copyright (c) 2009, Sun Microsystems, Inc.
-+ * All rights reserved.
-+ *
-+ * Redistribution and use in source and binary forms, with or without
-+ * modification, are permitted provided that the following conditions are met:
-+ * - Redistributions of source code must retain the above copyright notice,
-+ * this list of conditions and the following disclaimer.
-+ * - Redistributions in binary form must reproduce the above copyright notice,
-+ * this list of conditions and the following disclaimer in the documentation
-+ * and/or other materials provided with the distribution.
-+ * - Neither the name of Sun Microsystems, Inc. nor the names of its
-+ * contributors may be used to endorse or promote products derived
-+ * from this software without specific prior written permission.
-+ *
-+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-+ * POSSIBILITY OF SUCH DAMAGE.
-+ */
-+
-+/*
-+ * Status monitor protocol specification
-+ */
-+
-+#ifdef RPC_CLNT
-+%#include
-+#endif
-+
-+program SM_PROG {
-+ version SM_VERS {
-+ /* res_stat = stat_succ if status monitor agrees to monitor */
-+ /* res_stat = stat_fail if status monitor cannot monitor */
-+ /* if res_stat == stat_succ, state = state number of site sm_name */
-+ struct sm_stat_res SM_STAT(struct sm_name) = 1;
-+
-+ /* res_stat = stat_succ if status monitor agrees to monitor */
-+ /* res_stat = stat_fail if status monitor cannot monitor */
-+ /* stat consists of state number of local site */
-+ struct sm_stat_res SM_MON(struct mon) = 2;
-+
-+ /* stat consists of state number of local site */
-+ struct sm_stat SM_UNMON(struct mon_id) = 3;
-+
-+ /* stat consists of state number of local site */
-+ struct sm_stat SM_UNMON_ALL(struct my_id) = 4;
-+
-+ void SM_SIMU_CRASH(void) = 5;
-+
-+ void SM_NOTIFY(struct stat_chge) = 6;
-+
-+ } = 1;
-+} = 100024;
-+
-+const SM_MAXSTRLEN = 1024;
-+const SM_PRIV_SIZE = 16;
-+
-+struct sm_name {
-+ string mon_name;
-+};
-+
-+struct my_id {
-+ string my_name; /* name of the site iniates the monitoring request*/
-+ int my_prog; /* rpc program # of the requesting process */
-+ int my_vers; /* rpc version # of the requesting process */
-+ int my_proc; /* rpc procedure # of the requesting process */
-+};
-+
-+struct mon_id {
-+ string mon_name; /* name of the site to be monitored */
-+ struct my_id my_id;
-+};
-+
-+
-+struct mon {
-+ struct mon_id mon_id;
-+ opaque priv[SM_PRIV_SIZE]; /* private information to store at monitor for requesting process */
-+};
-+
-+struct stat_chge {
-+ string mon_name; /* name of the site that had the state change */
-+ int state;
-+};
-+
-+/*
-+ * state # of status monitor monitonically increases each time
-+ * status of the site changes:
-+ * an even number (>= 0) indicates the site is down and
-+ * an odd number (> 0) indicates the site is up;
-+ */
-+struct sm_stat {
-+ int state; /* state # of status monitor */
-+};
-+
-+enum res {
-+ stat_succ = 0, /* status monitor agrees to monitor */
-+ stat_fail = 1 /* status monitor cannot monitor */
-+};
-+
-+struct sm_stat_res {
-+ res res_stat;
-+ int state;
-+};
-+
-+/*
-+ * structure of the status message sent back by the status monitor
-+ * when monitor site status changes
-+ */
-+struct status {
-+ string mon_name;
-+ int state;
-+ opaque priv[SM_PRIV_SIZE]; /* stored private information */
-+};
-+
-+%#define SM_INTER_X
-diff --git a/tests/Makefile.am b/tests/Makefile.am
-new file mode 100644
-index 0000000..faa8197
---- /dev/null
-+++ b/tests/Makefile.am
-@@ -0,0 +1,13 @@
-+## Process this file with automake to produce Makefile.in
-+
-+check_PROGRAMS = statdb_dump
-+statdb_dump_SOURCES = statdb_dump.c
-+
-+statdb_dump_LDADD = ../support/nfs/libnfs.a \
-+ ../support/nsm/libnsm.a $(LIBCAP)
-+
-+SUBDIRS = nsm_client
-+
-+MAINTAINERCLEANFILES = Makefile.in
-+
-+TESTS = t0001-statd-basic-mon-unmon.sh
-diff --git a/tests/nsm_client/Makefile.am b/tests/nsm_client/Makefile.am
-new file mode 100644
-index 0000000..4bf0a45
---- /dev/null
-+++ b/tests/nsm_client/Makefile.am
-@@ -0,0 +1,45 @@
-+## Process this file with automake to produce Makefile.in
-+
-+GENFILES_CLNT = nlm_sm_inter_clnt.c
-+GENFILES_SVC = nlm_sm_inter_svc.c
-+GENFILES_XDR = nlm_sm_inter_xdr.c
-+GENFILES_H = nlm_sm_inter.h
-+
-+GENFILES = $(GENFILES_CLNT) $(GENFILES_SVC) $(GENFILES_XDR) $(GENFILES_H)
-+
-+
-+check_PROGRAMS = nsm_client
-+nsm_client_SOURCES = $(GENFILES) nsm_client.c
-+
-+BUILT_SOURCES = $(GENFILES)
-+nsm_client_LDADD = ../../support/nfs/libnfs.a \
-+ ../../support/nsm/libnsm.a $(LIBCAP)
-+
-+if CONFIG_RPCGEN
-+RPCGEN = $(top_builddir)/tools/rpcgen/rpcgen
-+$(RPCGEN):
-+ make -C ../../tools/rpcgen all
-+else
-+RPCGEN = @RPCGEN_PATH@
-+endif
-+
-+$(GENFILES_CLNT): %_clnt.c: %.x $(RPCGEN)
-+ test -f $@ && rm -rf $@ || true
-+ $(RPCGEN) -l -o $@ $<
-+
-+$(GENFILES_SVC): %_svc.c: %.x $(RPCGEN)
-+ test -f $@ && rm -rf $@ || true
-+ $(RPCGEN) -m -o $@ $<
-+
-+$(GENFILES_XDR): %_xdr.c: %.x $(RPCGEN)
-+ test -f $@ && rm -rf $@ || true
-+ $(RPCGEN) -c -o $@ $<
-+
-+$(GENFILES_H): %.h: %.x $(RPCGEN)
-+ test -f $@ && rm -rf $@ || true
-+ $(RPCGEN) -h -o $@ $<
-+
-+MAINTAINERCLEANFILES = Makefile.in
-+
-+CLEANFILES = $(GENFILES)
-+
-diff --git a/tests/nsm_client/README b/tests/nsm_client/README
-new file mode 100644
-index 0000000..85379dd
---- /dev/null
-+++ b/tests/nsm_client/README
-@@ -0,0 +1,12 @@
-+The nsm_client program is intended for testing statd. It has the ability
-+to act as a synthetic NSM client for sending artificial NSM calls to any
-+host you choose.
-+
-+It also has an NLM simulator that implements the call that statd uses to
-+communicate with lockd. The daemon simulator will start itself up,
-+register as an NLM service and listen for "downcalls" from statd. When
-+it gets one, it will log a message.
-+
-+Note that lockd will need to be down when using the daemon simulator. It
-+also does not implement the entire NLM protocol and is only really
-+useful for testing statd's downcall.
-diff --git a/tests/nsm_client/nlm_sm_inter.x b/tests/nsm_client/nlm_sm_inter.x
-new file mode 100644
-index 0000000..95fa326
---- /dev/null
-+++ b/tests/nsm_client/nlm_sm_inter.x
-@@ -0,0 +1,43 @@
-+/*
-+ * Copyright (C) 1995, 1997-1999 Jeffrey A. Uphoff
-+ * Modified by Olaf Kirch, 1996.
-+ * Modified by H.J. Lu, 1998.
-+ * Modified by Jeff Layton, 2010.
-+ *
-+ * NLM similator for Linux
-+ */
-+
-+#ifdef RPC_CLNT
-+%#include
-+#endif
-+
-+/*
-+ * statd rejects monitor registrations for any non-lockd services, so pretend
-+ * to be lockd when testing. Furthermore, the only call we care about from
-+ * statd is #16, which is the downcall to notify the kernel of a host's status
-+ * change.
-+ */
-+program NLM_SM_PROG {
-+ /* version 3 of the NLM protocol */
-+ version NLM_SM_VERS3 {
-+ void NLM_SM_NOTIFY(struct nlm_sm_notify) = 16;
-+ } = 3;
-+
-+ /* version 2 of NLM protocol */
-+ version NLM_SM_VERS4 {
-+ void NLM_SM_NOTIFY(struct nlm_sm_notify) = 16;
-+ } = 4;
-+} = 100021;
-+
-+const SM_MAXSTRLEN = 1024;
-+const SM_PRIV_SIZE = 16;
-+
-+/*
-+ * structure of the status message sent back by the status monitor
-+ * when monitor site status changes
-+ */
-+struct nlm_sm_notify {
-+ string mon_name;
-+ int state;
-+ opaque priv[SM_PRIV_SIZE]; /* stored private information */
-+};
-diff --git a/tests/nsm_client/nsm_client.c b/tests/nsm_client/nsm_client.c
-new file mode 100644
-index 0000000..0d1159a
---- /dev/null
-+++ b/tests/nsm_client/nsm_client.c
-@@ -0,0 +1,465 @@
-+/*
-+ * nsm_client.c -- synthetic client and lockd simulator for testing statd
-+ *
-+ * Copyright (C) 2010 Red Hat, Jeff Layton
-+ *
-+ * This program is free software; you can redistribute it and/or
-+ * modify it under the terms of the GNU General Public License
-+ * as published by the Free Software Foundation; either version 2
-+ * of the License, or (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin Street, Fifth Floor,
-+ * Boston, MA 02110-1301, USA.
-+ *
-+ * Very loosely based on "simulator.c" in the statd directory. Original
-+ * copyright for that program follows:
-+ *
-+ * Copyright (C) 1995-1997, 1999 Jeffrey A. Uphoff
-+ */
-+
-+#ifdef HAVE_CONFIG_H
-+#include "config.h"
-+#endif
-+
-+#include
-+#include
-+#include
-+#include
-+#include
-+#include
-+#include
-+#include
-+#include
-+#include
-+#include
-+
-+#include "nfslib.h"
-+#include "nfsrpc.h"
-+#include "nsm.h"
-+#include "sm_inter.h"
-+#include "nlm_sm_inter.h"
-+#include "sockaddr.h"
-+#include "xcommon.h"
-+
-+static void daemon_simulator(void);
-+static void sim_killer(int sig);
-+static int nsm_client_crash(char *);
-+static int nsm_client_mon(char *, char *, char *, char *, int, int);
-+static int nsm_client_stat(char *, char *);
-+static int nsm_client_notify(char *, char *, char *);
-+static int nsm_client_unmon(char *, char *, char *, int, int);
-+static int nsm_client_unmon_all(char *, char *, int, int);
-+
-+extern void nlm_sm_prog_4(struct svc_req *rqstp, register SVCXPRT *transp);
-+extern void svc_exit(void);
-+
-+/*
-+ * default to 15 retransmit interval, which seems to be the default for
-+ * UDP clients w/ legacy glibc RPC
-+ */
-+static struct timeval retrans_interval =
-+{
-+ .tv_sec = 15,
-+};
-+
-+static struct option longopts[] =
-+{
-+ { "help", 0, 0, 'h' },
-+ { "host", 0, 0, 'H' },
-+ { "name", 1, 0, 'n' },
-+ { "program", 1, 0, 'P' },
-+ { "version", 1, 0, 'v' },
-+ { NULL, 0, 0, 0 },
-+};
-+
-+static int
-+usage(char *program)
-+{
-+ printf("Usage:\n");
-+ printf("%s [options] [arg]...\n", program);
-+ printf("where command is one of these with the specified args:\n");
-+ printf("crash\t\t\t\ttell host to simulate crash\n");
-+ printf("daemon\t\t\t\t\tstart up lockd daemon simulator\n");
-+ printf("notify \tsend a reboot notification to host\n");
-+ printf("stat \t\t\tget status of on host\n");
-+ printf("unmon_all\t\t\ttell host to unmon everything\n");
-+ printf("unmon \t\t\ttell host to unmon \n");
-+ printf("mon \t\ttell host to monitor with private \n");
-+ return 1;
-+}
-+
-+static int
-+hex2bin(char *dst, size_t dstlen, char *src)
-+{
-+ int i;
-+ unsigned int tmp;
-+
-+ for (i = 0; *src && i < dstlen; i++) {
-+ if (sscanf(src, "%2x", &tmp) != 1)
-+ return 0;
-+ dst[i] = tmp;
-+ src++;
-+ if (!*src)
-+ break;
-+ src++;
-+ }
-+
-+ return 1;
-+}
-+
-+static void
-+bin2hex(char *dst, char *src, size_t srclen)
-+{
-+ int i;
-+
-+ for (i = 0; i < srclen; i++)
-+ dst += sprintf(dst, "%02x", 0xff & src[i]);
-+}
-+
-+int
-+main(int argc, char **argv)
-+{
-+ int arg, err = 0;
-+ int remaining_args;
-+ char my_name[NI_MAXHOST], host[NI_MAXHOST];
-+ char cookie[SM_PRIV_SIZE];
-+ int my_prog = NLM_SM_PROG;
-+ int my_vers = NLM_SM_VERS4;
-+
-+ my_name[0] = '\0';
-+ host[0] = '\0';
-+
-+ while ((arg = getopt_long(argc, argv, "hHn:P:v:", longopts,
-+ NULL)) != EOF) {
-+ switch (arg) {
-+ case 'H':
-+ strncpy(host, optarg, sizeof(host));
-+ case 'n':
-+ strncpy(my_name, optarg, sizeof(my_name));
-+ case 'P':
-+ my_prog = atoi(optarg);
-+ case 'v':
-+ my_vers = atoi(optarg);
-+ }
-+ }
-+
-+ remaining_args = argc - optind;
-+ if (remaining_args <= 0)
-+ usage(argv[0]);
-+
-+ if (!my_name[0])
-+ gethostname(my_name, sizeof(my_name));
-+ if (!host[0])
-+ strncpy(host, "127.0.0.1", sizeof(host));
-+
-+ if (!strcasecmp(argv[optind], "daemon")) {
-+ daemon_simulator();
-+ } else if (!strcasecmp(argv[optind], "crash")) {
-+ err = nsm_client_crash(host);
-+ } else if (!strcasecmp(argv[optind], "stat")) {
-+ if (remaining_args < 2)
-+ usage(argv[0]);
-+ err = nsm_client_stat(host, argv[optind + 2]);
-+ } else if (!strcasecmp(argv[optind], "unmon_all")) {
-+ err = nsm_client_unmon_all(host, my_name, my_prog, my_vers);
-+ } else if (!strcasecmp(argv[optind], "unmon")) {
-+ if (remaining_args < 2)
-+ usage(argv[0]);
-+ err = nsm_client_unmon(host, argv[optind + 1], my_name, my_prog,
-+ my_vers);
-+ } else if (!strcasecmp(argv[optind], "notify")) {
-+ if (remaining_args < 2)
-+ usage(argv[0]);
-+ err = nsm_client_notify(host, argv[optind + 1],
-+ argv[optind + 2]);
-+ } else if (!strcasecmp(argv[optind], "mon")) {
-+ if (remaining_args < 2)
-+ usage(argv[0]);
-+
-+ memset(cookie, '\0', SM_PRIV_SIZE);
-+ if (!hex2bin(cookie, sizeof(cookie), argv[optind + 2])) {
-+ fprintf(stderr, "SYS:%d\n", EINVAL);
-+ printf("Unable to convert hex cookie %s to binary.\n",
-+ argv[optind + 2]);
-+ return 1;
-+ }
-+
-+ err = nsm_client_mon(host, argv[optind + 1], cookie, my_name,
-+ my_prog, my_vers);
-+ } else {
-+ err = usage(argv[0]);
-+ }
-+
-+ return err;
-+}
-+
-+static CLIENT *
-+nsm_client_get_rpcclient(const char *node)
-+{
-+ unsigned short port;
-+ struct addrinfo *ai;
-+ struct addrinfo hints = { .ai_flags = AI_ADDRCONFIG };
-+ int err;
-+ CLIENT *client = NULL;
-+
-+#ifndef IPV6_ENABLED
-+ hints.ai_family = AF_INET;
-+#endif /* IPV6_ENABLED */
-+
-+ /* FIXME: allow support for providing port? */
-+ err = getaddrinfo(node, NULL, &hints, &ai);
-+ if (err) {
-+ fprintf(stderr, "EAI:%d\n", err);
-+ if (err == EAI_SYSTEM)
-+ fprintf(stderr, "SYS:%d\n", errno);
-+ printf("Unable to translate host to address: %s\n",
-+ err == EAI_SYSTEM ? strerror(errno) :
-+ gai_strerror(err));
-+ return client;
-+ }
-+
-+ /* FIXME: allow for TCP too? */
-+ port = nfs_getport(ai->ai_addr, ai->ai_addrlen, SM_PROG,
-+ SM_VERS, IPPROTO_UDP);
-+ if (!port) {
-+ fprintf(stderr, "RPC:%d\n", rpc_createerr.cf_stat);
-+ printf("Unable to determine port for service\n");
-+ goto out;
-+ }
-+
-+ nfs_set_port(ai->ai_addr, port);
-+
-+ client = nfs_get_rpcclient(ai->ai_addr, ai->ai_addrlen, IPPROTO_UDP,
-+ SM_PROG, SM_VERS, &retrans_interval);
-+ if (!client) {
-+ fprintf(stderr, "RPC:%d\n", rpc_createerr.cf_stat);
-+ printf("RPC client creation failed\n");
-+ }
-+out:
-+ freeaddrinfo(ai);
-+ return client;
-+}
-+
-+static int
-+nsm_client_mon(char *calling, char *monitoring, char *cookie, char *my_name,
-+ int my_prog, int my_vers)
-+{
-+ CLIENT *client;
-+ sm_stat_res *result;
-+ mon mon;
-+ int err = 0;
-+
-+ printf("Calling %s (as %s) to monitor %s\n", calling, my_name,
-+ monitoring);
-+
-+ if ((client = nsm_client_get_rpcclient(calling)) == NULL)
-+ return 1;
-+
-+ memcpy(mon.priv, cookie, SM_PRIV_SIZE);
-+ mon.mon_id.my_id.my_name = my_name;
-+ mon.mon_id.my_id.my_prog = my_prog;
-+ mon.mon_id.my_id.my_vers = my_vers;
-+ mon.mon_id.my_id.my_proc = NLM_SM_NOTIFY;
-+ mon.mon_id.mon_name = monitoring;
-+
-+ if (!(result = sm_mon_1(&mon, client))) {
-+ fprintf(stderr, "RPC:%d\n", rpc_createerr.cf_stat);
-+ printf("%s\n", clnt_sperror(client, "sm_mon_1"));
-+ err = 1;
-+ goto mon_out;
-+ }
-+
-+ printf("SM_MON request %s, state: %d\n",
-+ result->res_stat == stat_succ ? "successful" : "failed",
-+ result->state);
-+
-+ if (result->res_stat != stat_succ) {
-+ fprintf(stderr, "RPC:%d\n", rpc_createerr.cf_stat);
-+ err = 1;
-+ }
-+
-+mon_out:
-+ clnt_destroy(client);
-+ return err;
-+}
-+
-+static int
-+nsm_client_unmon(char *calling, char *unmonitoring, char *my_name, int my_prog,
-+ int my_vers)
-+{
-+ CLIENT *client;
-+ sm_stat *result;
-+ mon_id mon_id;
-+ int err = 0;
-+
-+ printf("Calling %s (as %s) to unmonitor %s\n", calling, my_name,
-+ unmonitoring);
-+
-+ if ((client = nsm_client_get_rpcclient(calling)) == NULL)
-+ return 1;
-+
-+ mon_id.my_id.my_name = my_name;
-+ mon_id.my_id.my_prog = my_prog;
-+ mon_id.my_id.my_vers = my_vers;
-+ mon_id.my_id.my_proc = NLM_SM_NOTIFY;
-+ mon_id.mon_name = unmonitoring;
-+
-+ if (!(result = sm_unmon_1(&mon_id, client))) {
-+ fprintf(stderr, "RPC:%d\n", rpc_createerr.cf_stat);
-+ printf("%s\n", clnt_sperror(client, "sm_unmon_1"));
-+ err = 1;
-+ goto unmon_out;
-+ }
-+
-+ printf("SM_UNMON state: %d\n", result->state);
-+
-+unmon_out:
-+ clnt_destroy(client);
-+ return err;
-+}
-+
-+static int
-+nsm_client_unmon_all(char *calling, char *my_name, int my_prog, int my_vers)
-+{
-+ CLIENT *client;
-+ sm_stat *result;
-+ my_id my_id;
-+ int err = 0;
-+
-+ printf("Calling %s (as %s) to unmonitor all hosts\n", calling, my_name);
-+
-+ if ((client = nsm_client_get_rpcclient(calling)) == NULL) {
-+ printf("RPC client creation failed\n");
-+ return 1;
-+ }
-+
-+ my_id.my_name = my_name;
-+ my_id.my_prog = my_prog;
-+ my_id.my_vers = my_vers;
-+ my_id.my_proc = NLM_SM_NOTIFY;
-+
-+ if (!(result = sm_unmon_all_1(&my_id, client))) {
-+ fprintf(stderr, "RPC:%d\n", rpc_createerr.cf_stat);
-+ printf("%s\n", clnt_sperror(client, "sm_unmon_all_1"));
-+ err = 1;
-+ goto unmon_all_out;
-+ }
-+
-+ printf("SM_UNMON_ALL state: %d\n", result->state);
-+
-+unmon_all_out:
-+ return err;
-+}
-+
-+static int
-+nsm_client_crash(char *host)
-+{
-+ CLIENT *client;
-+
-+ if ((client = nsm_client_get_rpcclient(host)) == NULL)
-+ return 1;
-+
-+ if (!sm_simu_crash_1(NULL, client)) {
-+ fprintf(stderr, "RPC:%d\n", rpc_createerr.cf_stat);
-+ printf("%s\n", clnt_sperror(client, "sm_simu_crash_1"));
-+ return 1;
-+ }
-+
-+ return 0;
-+}
-+
-+static int
-+nsm_client_stat(char *calling, char *monitoring)
-+{
-+ CLIENT *client;
-+ sm_name checking;
-+ sm_stat_res *result;
-+
-+ if ((client = nsm_client_get_rpcclient(calling)) == NULL)
-+ return 1;
-+
-+ checking.mon_name = monitoring;
-+
-+ if (!(result = sm_stat_1(&checking, client))) {
-+ fprintf(stderr, "RPC:%d\n", rpc_createerr.cf_stat);
-+ printf("%s\n", clnt_sperror(client, "sm_stat_1"));
-+ return 1;
-+ }
-+
-+ if (result->res_stat != stat_succ) {
-+ fprintf(stderr, "RPC:%d\n", rpc_createerr.cf_stat);
-+ printf("stat_fail from %s for %s, state: %d\n", calling,
-+ monitoring, result->state);
-+ return 1;
-+ }
-+
-+ printf("stat_succ from %s for %s, state: %d\n", calling,
-+ monitoring, result->state);
-+
-+ return 0;
-+}
-+
-+static int
-+nsm_client_notify(char *calling, char *mon_name, char *statestr)
-+{
-+ CLIENT *client;
-+
-+ stat_chge stat_chge = { .mon_name = mon_name };
-+
-+ stat_chge.state = atoi(statestr);
-+
-+ if ((client = nsm_client_get_rpcclient(calling)) == NULL)
-+ return 1;
-+
-+ if (!sm_notify_1(&stat_chge, client)) {
-+ fprintf(stderr, "RPC:%d\n", rpc_createerr.cf_stat);
-+ printf("%s\n", clnt_sperror(client, "sm_notify_1"));
-+ return 1;
-+ }
-+
-+ return 0;
-+}
-+
-+static void sim_killer(int sig)
-+{
-+#ifdef HAVE_LIBTIRPC
-+ (void) rpcb_unset(NLM_SM_PROG, NLM_SM_VERS4, NULL);
-+#else
-+ (void) pmap_unset(NLM_SM_PROG, NLM_SM_VERS4);
-+#endif
-+ exit(0);
-+}
-+
-+static void daemon_simulator(void)
-+{
-+ signal(SIGHUP, sim_killer);
-+ signal(SIGINT, sim_killer);
-+ signal(SIGTERM, sim_killer);
-+ /* FIXME: allow for different versions? */
-+ nfs_svc_create("nlmsim", NLM_SM_PROG, NLM_SM_VERS4, nlm_sm_prog_4, 0);
-+ svc_run();
-+}
-+
-+void *nlm_sm_notify_4_svc(struct nlm_sm_notify *argp, struct svc_req *rqstp)
-+{
-+ static char *result;
-+ char priv[SM_PRIV_SIZE * 2 + 1];
-+
-+ bin2hex(priv, argp->priv, SM_PRIV_SIZE);
-+
-+ printf("state=%d:mon_name=%s:private=%s\n", argp->state,
-+ argp->mon_name, priv);
-+ return (void *) &result;
-+}
-+
-+void *nlm_sm_notify_3_svc(struct nlm_sm_notify *argp, struct svc_req *rqstp)
-+{
-+ return nlm_sm_notify_4_svc(argp, rqstp);
-+}
-diff --git a/tests/statdb_dump.c b/tests/statdb_dump.c
-new file mode 100644
-index 0000000..92d63f2
---- /dev/null
-+++ b/tests/statdb_dump.c
-@@ -0,0 +1,99 @@
-+/*
-+ * statdb_dump.c -- dump contents of statd's monitor DB
-+ *
-+ * Copyright (C) 2010 Red Hat, Jeff Layton
-+ *
-+ * This program is free software; you can redistribute it and/or
-+ * modify it under the terms of the GNU General Public License
-+ * as published by the Free Software Foundation; either version 2
-+ * of the License, or (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 51 Franklin Street, Fifth Floor,
-+ * Boston, MA 02110-1301, USA.
-+ */
-+
-+#ifdef HAVE_CONFIG_H
-+#include "config.h"
-+#endif
-+
-+#include
-+#include
-+#include
-+
-+#include "nsm.h"
-+#include "xlog.h"
-+
-+static char cookiebuf[(SM_PRIV_SIZE * 2) + 1];
-+static char addrbuf[INET6_ADDRSTRLEN + 1];
-+
-+static unsigned int
-+dump_host(const char *hostname, const struct sockaddr *sa, const struct mon *m,
-+ const time_t timestamp)
-+{
-+ int ret;
-+ const char *addr;
-+ const struct sockaddr_in *sin;
-+ const struct sockaddr_in6 *sin6;
-+
-+ ret = nsm_priv_to_hex(m->priv, cookiebuf, sizeof(cookiebuf));
-+ if (!ret) {
-+ xlog(L_ERROR, "Unable to convert cookie to hex string.\n");
-+ return ret;
-+ }
-+
-+ switch (sa->sa_family) {
-+ case AF_INET:
-+ sin = (struct sockaddr_in *)(char *)sa;
-+ addr = inet_ntop(sa->sa_family, &sin->sin_addr.s_addr, addrbuf,
-+ (socklen_t)sizeof(addrbuf));
-+ break;
-+ case AF_INET6:
-+ sin6 = (struct sockaddr_in6 *)(char *)sa;
-+ addr = inet_ntop(sa->sa_family, &sin6->sin6_addr, addrbuf,
-+ (socklen_t)sizeof(addrbuf));
-+ break;
-+ default:
-+ xlog(L_ERROR, "Unrecognized address family: %hu\n",
-+ sa->sa_family);
-+ return 0;
-+ }
-+
-+ if (addr == NULL) {
-+ xlog(L_ERROR, "Unable to convert sockaddr to string: %s\n",
-+ strerror(errno));
-+ return 0;
-+ }
-+
-+ /*
-+ * Callers of this program should assume that in the future, extra
-+ * fields may be added to the output. Anyone adding extra fields to
-+ * the output should add them to the end of the line.
-+ */
-+ printf("%s %s %s %s %s %d %d %d\n",
-+ hostname, addr, cookiebuf,
-+ m->mon_id.mon_name,
-+ m->mon_id.my_id.my_name,
-+ m->mon_id.my_id.my_prog,
-+ m->mon_id.my_id.my_vers,
-+ m->mon_id.my_id.my_proc);
-+
-+ return 1;
-+}
-+
-+int
-+main(int argc, char **argv)
-+{
-+ xlog_syslog(0);
-+ xlog_stderr(1);
-+ xlog_open(argv[0]);
-+
-+ nsm_load_monitor_list(dump_host);
-+ return 0;
-+}
-diff --git a/tests/t0001-statd-basic-mon-unmon.sh b/tests/t0001-statd-basic-mon-unmon.sh
-new file mode 100644
-index 0000000..00127fb
---- /dev/null
-+++ b/tests/t0001-statd-basic-mon-unmon.sh
-@@ -0,0 +1,58 @@
-+#!/bin/bash
-+#
-+# statd_basic_mon_unmon -- test basic mon/unmon functionality with statd
-+#
-+# Copyright (C) 2010 Red Hat, Jeff Layton
-+#
-+# This program is free software; you can redistribute it and/or
-+# modify it under the terms of the GNU General Public License
-+# as published by the Free Software Foundation; either version 2
-+# of the License, or (at your option) any later version.
-+#
-+# This program is distributed in the hope that it will be useful,
-+# but WITHOUT ANY WARRANTY; without even the implied warranty of
-+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-+# GNU General Public License for more details.
-+#
-+# You should have received a copy of the GNU General Public License
-+# along with this program; if not, write to the Free Software Foundation, Inc.,
-+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-+#
-+
-+. ./test-lib.sh
-+
-+# This test needs root privileges
-+check_root
-+
-+start_statd
-+if [ $? -ne 0 ]; then
-+ echo "FAIL: problem starting statd"
-+ exit 1
-+fi
-+
-+COOKIE=`echo $$ | md5sum | cut -d' ' -f1`
-+MON_NAME=`hostname`
-+
-+nsm_client mon $MON_NAME $COOKIE
-+if [ $? -ne 0 ]; then
-+ echo "FAIL: mon failed"
-+ kill_statd
-+ exit 1
-+fi
-+
-+statdb_dump | grep $MON_NAME | grep -q $COOKIE
-+if [ $? -ne 0 ]; then
-+ echo "FAIL: monitor DB doesn't seem to contain entry"
-+ kill_statd
-+ exit 1
-+fi
-+
-+nsm_client unmon $MON_NAME
-+if [ $? -ne 0 ]; then
-+ echo "FAIL: unmon failed"
-+ kill_statd
-+ exit 1
-+fi
-+
-+kill_statd
-+
-diff --git a/tests/test-lib.sh b/tests/test-lib.sh
-new file mode 100644
-index 0000000..3d47264
---- /dev/null
-+++ b/tests/test-lib.sh
-@@ -0,0 +1,60 @@
-+#!/bin/bash
-+#
-+# test-lib.sh -- library of functions for nfs-utils tests
-+#
-+# Copyright (C) 2010 Red Hat, Jeff Layton
-+#
-+# This program is free software; you can redistribute it and/or
-+# modify it under the terms of the GNU General Public License
-+# as published by the Free Software Foundation; either version 2
-+# of the License, or (at your option) any later version.
-+#
-+# This program is distributed in the hope that it will be useful,
-+# but WITHOUT ANY WARRANTY; without even the implied warranty of
-+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-+# GNU General Public License for more details.
-+#
-+# You should have received a copy of the GNU General Public License
-+# along with this program; if not, write to the Free Software Foundation, Inc.,
-+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-+#
-+
-+# make sure $srcdir is set and sanity check it
-+srcdir=${srcdir-.}
-+if [ ! -d ${srcdir} ]; then
-+ echo "***ERROR***: bad installation -- \$srcdir=${srcdir}"
-+ exit 1
-+fi
-+
-+export PATH=$PATH:${srcdir}:${srcdir}/nsm_client
-+
-+# Some tests require root privileges. Check for them and skip the test (exit 77)
-+# if the caller doesn't have them.
-+check_root() {
-+ if [ $EUID -ne 0 ]; then
-+ echo "*** Skipping this test as it requires root privs ***"
-+ exit 77
-+ fi
-+}
-+
-+# is lockd registered as a service?
-+lockd_registered() {
-+ rpcinfo -p | grep -q nlockmgr
-+ return $?
-+}
-+
-+# start up statd
-+start_statd() {
-+ rpcinfo -u 127.0.0.1 status 1 &> /dev/null
-+ if [ $? -eq 0 ]; then
-+ echo "***ERROR***: statd is already running and should "
-+ echo " be down when starting this test"
-+ return 1
-+ fi
-+ $srcdir/../utils/statd/statd --no-notify
-+}
-+
-+# shut down statd
-+kill_statd() {
-+ kill `cat /var/run/rpc.statd.pid`
-+}
-diff --git a/utils/gssd/gssd.c b/utils/gssd/gssd.c
-index 40a2b4d..bd37a5f 100644
---- a/utils/gssd/gssd.c
-+++ b/utils/gssd/gssd.c
-@@ -56,7 +56,6 @@
- #include "krb5_util.h"
-
- char pipefs_dir[PATH_MAX] = GSSD_PIPEFS_DIR;
--char pipefs_nfsdir[PATH_MAX] = GSSD_PIPEFS_DIR;
- char keytabfile[PATH_MAX] = GSSD_DEFAULT_KEYTAB_FILE;
- char ccachedir[PATH_MAX] = GSSD_DEFAULT_CRED_DIR;
- char *ccachesearch[GSSD_MAX_CCACHE_SEARCH + 1];
-@@ -159,11 +158,6 @@ main(int argc, char *argv[])
- if (preferred_realm == NULL)
- gssd_k5_get_default_realm(&preferred_realm);
-
-- snprintf(pipefs_nfsdir, sizeof(pipefs_nfsdir), "%s/%s",
-- pipefs_dir, GSSD_SERVICE_NAME);
-- if (pipefs_nfsdir[sizeof(pipefs_nfsdir)-1] != '\0')
-- errx(1, "pipefs_nfsdir path name too long");
--
- if ((progname = strrchr(argv[0], '/')))
- progname++;
- else
-diff --git a/utils/gssd/gssd.h b/utils/gssd/gssd.h
-index 3c52f46..465c305 100644
---- a/utils/gssd/gssd.h
-+++ b/utils/gssd/gssd.h
-@@ -60,7 +60,6 @@ enum {AUTHTYPE_KRB5, AUTHTYPE_SPKM3, AUTHTYPE_LIPKEY};
-
-
- extern char pipefs_dir[PATH_MAX];
--extern char pipefs_nfsdir[PATH_MAX];
- extern char keytabfile[PATH_MAX];
- extern char *ccachesearch[];
- extern int use_memcache;
-@@ -83,13 +82,24 @@ struct clnt_info {
- int krb5_poll_index;
- int spkm3_fd;
- int spkm3_poll_index;
-+ int gssd_fd;
-+ int gssd_poll_index;
- struct sockaddr_storage addr;
- };
-
-+TAILQ_HEAD(topdirs_list_head, topdirs_info) topdirs_list;
-+
-+struct topdirs_info {
-+ TAILQ_ENTRY(topdirs_info) list;
-+ char *dirname;
-+ int fd;
-+};
-+
- void init_client_list(void);
- int update_client_list(void);
- void handle_krb5_upcall(struct clnt_info *clp);
- void handle_spkm3_upcall(struct clnt_info *clp);
-+void handle_gssd_upcall(struct clnt_info *clp);
- int gssd_acquire_cred(char *server_name);
- void gssd_run(void);
-
-diff --git a/utils/gssd/gssd_main_loop.c b/utils/gssd/gssd_main_loop.c
-index 917b662..f1a68d3 100644
---- a/utils/gssd/gssd_main_loop.c
-+++ b/utils/gssd/gssd_main_loop.c
-@@ -49,6 +49,7 @@
- #include
- #include
- #include
-+#include
-
- #include "gssd.h"
- #include "err_util.h"
-@@ -73,6 +74,17 @@ scan_poll_results(int ret)
-
- for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next)
- {
-+ i = clp->gssd_poll_index;
-+ if (i >= 0 && pollarray[i].revents) {
-+ if (pollarray[i].revents & POLLHUP)
-+ dir_changed = 1;
-+ if (pollarray[i].revents & POLLIN)
-+ handle_gssd_upcall(clp);
-+ pollarray[clp->gssd_poll_index].revents = 0;
-+ ret--;
-+ if (!ret)
-+ break;
-+ }
- i = clp->krb5_poll_index;
- if (i >= 0 && pollarray[i].revents) {
- if (pollarray[i].revents & POLLHUP)
-@@ -98,12 +110,85 @@ scan_poll_results(int ret)
- }
- };
-
-+static int
-+topdirs_add_entry(struct dirent *dent)
-+{
-+ struct topdirs_info *tdi;
-+
-+ tdi = calloc(sizeof(struct topdirs_info), 1);
-+ if (tdi == NULL) {
-+ printerr(0, "ERROR: Couldn't allocate struct topdirs_info\n");
-+ return -1;
-+ }
-+ tdi->dirname = malloc(PATH_MAX);
-+ if (tdi->dirname == NULL) {
-+ printerr(0, "ERROR: Couldn't allocate directory name\n");
-+ free(tdi);
-+ return -1;
-+ }
-+ snprintf(tdi->dirname, PATH_MAX, "%s/%s", pipefs_dir, dent->d_name);
-+ tdi->fd = open(tdi->dirname, O_RDONLY);
-+ if (tdi->fd != -1) {
-+ fcntl(tdi->fd, F_SETSIG, DNOTIFY_SIGNAL);
-+ fcntl(tdi->fd, F_NOTIFY,
-+ DN_CREATE|DN_DELETE|DN_MODIFY|DN_MULTISHOT);
-+ }
-+
-+ TAILQ_INSERT_HEAD(&topdirs_list, tdi, list);
-+ return 0;
-+}
-+
-+static void
-+topdirs_free_list(void)
-+{
-+ struct topdirs_info *tdi;
-+
-+ TAILQ_FOREACH(tdi, &topdirs_list, list) {
-+ free(tdi->dirname);
-+ if (tdi->fd != -1)
-+ close(tdi->fd);
-+ TAILQ_REMOVE(&topdirs_list, tdi, list);
-+ free(tdi);
-+ }
-+}
-+
-+static int
-+topdirs_init_list(void)
-+{
-+ DIR *pipedir;
-+ struct dirent *dent;
-+ int ret;
-+
-+ TAILQ_INIT(&topdirs_list);
-+
-+ pipedir = opendir(pipefs_dir);
-+ if (pipedir == NULL) {
-+ printerr(0, "ERROR: could not open rpc_pipefs directory '%s': "
-+ "%s\n", pipefs_dir, strerror(errno));
-+ return -1;
-+ }
-+ for (dent = readdir(pipedir); dent != NULL; dent = readdir(pipedir)) {
-+ if (dent->d_type != DT_DIR ||
-+ strcmp(dent->d_name, ".") == 0 ||
-+ strcmp(dent->d_name, "..") == 0) {
-+ continue;
-+ }
-+ ret = topdirs_add_entry(dent);
-+ if (ret)
-+ goto out_err;
-+ }
-+ closedir(pipedir);
-+ return 0;
-+out_err:
-+ topdirs_free_list();
-+ return -1;
-+}
-+
- void
- gssd_run()
- {
- int ret;
- struct sigaction dn_act;
-- int fd;
- sigset_t set;
-
- /* Taken from linux/Documentation/dnotify.txt: */
-@@ -117,13 +202,8 @@ gssd_run()
- sigaddset(&set, DNOTIFY_SIGNAL);
- sigprocmask(SIG_UNBLOCK, &set, NULL);
-
-- if ((fd = open(pipefs_nfsdir, O_RDONLY)) == -1) {
-- printerr(0, "ERROR: failed to open %s: %s\n",
-- pipefs_nfsdir, strerror(errno));
-- exit(1);
-- }
-- fcntl(fd, F_SETSIG, DNOTIFY_SIGNAL);
-- fcntl(fd, F_NOTIFY, DN_CREATE|DN_DELETE|DN_MODIFY|DN_MULTISHOT);
-+ if (topdirs_init_list() != 0)
-+ return;
-
- init_client_list();
-
-@@ -132,8 +212,7 @@ gssd_run()
- while (dir_changed) {
- dir_changed = 0;
- if (update_client_list()) {
-- printerr(0, "ERROR: couldn't update "
-- "client list\n");
-+ /* Error msg is already printed */
- exit(1);
- }
- }
-@@ -151,6 +230,7 @@ gssd_run()
- scan_poll_results(ret);
- }
- }
-- close(fd);
-+ topdirs_free_list();
-+
- return;
- }
-diff --git a/utils/gssd/gssd_proc.c b/utils/gssd/gssd_proc.c
-index 37e2aa5..be4fb11 100644
---- a/utils/gssd/gssd_proc.c
-+++ b/utils/gssd/gssd_proc.c
-@@ -73,6 +73,7 @@
- #include "krb5_util.h"
- #include "context.h"
- #include "nfsrpc.h"
-+#include "nfslib.h"
-
- /*
- * pollarray:
-@@ -83,20 +84,22 @@
- * linked list of struct clnt_info which associates a clntXXX directory
- * with an index into pollarray[], and other basic data about that client.
- *
-- * Directory structure: created by the kernel nfs client
-- * {pipefs_nfsdir}/clntXX : one per rpc_clnt struct in the kernel
-- * {pipefs_nfsdir}/clntXX/krb5 : read uid for which kernel wants
-+ * Directory structure: created by the kernel
-+ * {rpc_pipefs}/{dir}/clntXX : one per rpc_clnt struct in the kernel
-+ * {rpc_pipefs}/{dir}/clntXX/krb5 : read uid for which kernel wants
- * a context, write the resulting context
-- * {pipefs_nfsdir}/clntXX/info : stores info such as server name
-+ * {rpc_pipefs}/{dir}/clntXX/info : stores info such as server name
-+ * {rpc_pipefs}/{dir}/clntXX/gssd : pipe for all gss mechanisms using
-+ * a text-based string of parameters
- *
- * Algorithm:
-- * Poll all {pipefs_nfsdir}/clntXX/krb5 files. When ready, data read
-- * is a uid; performs rpcsec_gss context initialization protocol to
-+ * Poll all {rpc_pipefs}/{dir}/clntXX/YYYY files. When data is ready,
-+ * read and process; performs rpcsec_gss context initialization protocol to
- * get a cred for that user. Writes result to corresponding krb5 file
- * in a form the kernel code will understand.
- * In addition, we make sure we are notified whenever anything is
-- * created or destroyed in {pipefs_nfsdir} or in an of the clntXX directories,
-- * and rescan the whole {pipefs_nfsdir} when this happens.
-+ * created or destroyed in {rpc_pipefs} or in any of the clntXX directories,
-+ * and rescan the whole {rpc_pipefs} when this happens.
- */
-
- struct pollfd * pollarray;
-@@ -105,7 +108,7 @@ int pollsize; /* the size of pollaray (in pollfd's) */
-
- /*
- * convert a presentation address string to a sockaddr_storage struct. Returns
-- * true on success and false on failure.
-+ * true on success or false on failure.
- *
- * Note that we do not populate the sin6_scope_id field here for IPv6 addrs.
- * gssd nececessarily relies on hostname resolution and DNS AAAA records
-@@ -117,26 +120,43 @@ int pollsize; /* the size of pollaray (in pollfd's) */
- * not really feasible at present.
- */
- static int
--addrstr_to_sockaddr(struct sockaddr *sa, const char *addr, const int port)
-+addrstr_to_sockaddr(struct sockaddr *sa, const char *node, const char *port)
- {
-- struct sockaddr_in *s4 = (struct sockaddr_in *) sa;
--#ifdef IPV6_SUPPORTED
-- struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) sa;
--#endif /* IPV6_SUPPORTED */
-+ int rc;
-+ struct addrinfo *res;
-+ struct addrinfo hints = { .ai_flags = AI_NUMERICHOST | AI_NUMERICSERV };
-
-- if (inet_pton(AF_INET, addr, &s4->sin_addr)) {
-- s4->sin_family = AF_INET;
-- s4->sin_port = htons(port);
--#ifdef IPV6_SUPPORTED
-- } else if (inet_pton(AF_INET6, addr, &s6->sin6_addr)) {
-- s6->sin6_family = AF_INET6;
-- s6->sin6_port = htons(port);
-+#ifndef IPV6_SUPPORTED
-+ hints.ai_family = AF_INET;
- #endif /* IPV6_SUPPORTED */
-- } else {
-- printerr(0, "ERROR: unable to convert %s to address\n", addr);
-+
-+ rc = getaddrinfo(node, port, &hints, &res);
-+ if (rc) {
-+ printerr(0, "ERROR: unable to convert %s|%s to sockaddr: %s\n",
-+ node, port, rc == EAI_SYSTEM ? strerror(errno) :
-+ gai_strerror(rc));
- return 0;
- }
-
-+#ifdef IPV6_SUPPORTED
-+ /*
-+ * getnameinfo ignores the scopeid. If the address turns out to have
-+ * a non-zero scopeid, we can't use it -- the resolved host might be
-+ * completely different from the one intended.
-+ */
-+ if (res->ai_addr->sa_family == AF_INET6) {
-+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)res->ai_addr;
-+ if (sin6->sin6_scope_id) {
-+ printerr(0, "ERROR: address %s has non-zero "
-+ "sin6_scope_id!\n", node);
-+ freeaddrinfo(res);
-+ return 0;
-+ }
-+ }
-+#endif /* IPV6_SUPPORTED */
-+
-+ memcpy(sa, res->ai_addr, res->ai_addrlen);
-+ freeaddrinfo(res);
- return 1;
- }
-
-@@ -194,11 +214,10 @@ read_service_info(char *info_file_name, char **servicename, char **servername,
- char program[16];
- char version[16];
- char protoname[16];
-- char cb_port[128];
-+ char port[128];
- char *p;
- int fd = -1;
- int numfields;
-- int port = 0;
-
- *servicename = *servername = *protocol = NULL;
-
-@@ -227,20 +246,22 @@ read_service_info(char *info_file_name, char **servicename, char **servername,
- goto fail;
- }
-
-- cb_port[0] = '\0';
-+ port[0] = '\0';
- if ((p = strstr(buf, "port")) != NULL)
-- sscanf(p, "port: %127s\n", cb_port);
-+ sscanf(p, "port: %127s\n", port);
-
- /* check service, program, and version */
-- if(memcmp(service, "nfs", 3)) return -1;
-+ if (memcmp(service, "nfs", 3) != 0)
-+ return -1;
- *prog = atoi(program + 1); /* skip open paren */
- *vers = atoi(version);
-- if((*prog != 100003) || ((*vers != 2) && (*vers != 3) && (*vers != 4)))
-- goto fail;
-
-- if (cb_port[0] != '\0') {
-- port = atoi(cb_port);
-- if (port < 0 || port > 65535)
-+ if (strlen(service) == 3 ) {
-+ if ((*prog != 100003) || ((*vers != 2) && (*vers != 3) &&
-+ (*vers != 4)))
-+ goto fail;
-+ } else if (memcmp(service, "nfs4_cb", 7) == 0) {
-+ if (*vers != 1)
- goto fail;
- }
-
-@@ -281,9 +302,13 @@ destroy_client(struct clnt_info *clp)
- if (clp->spkm3_poll_index != -1)
- memset(&pollarray[clp->spkm3_poll_index], 0,
- sizeof(struct pollfd));
-+ if (clp->gssd_poll_index != -1)
-+ memset(&pollarray[clp->gssd_poll_index], 0,
-+ sizeof(struct pollfd));
- if (clp->dir_fd != -1) close(clp->dir_fd);
- if (clp->krb5_fd != -1) close(clp->krb5_fd);
- if (clp->spkm3_fd != -1) close(clp->spkm3_fd);
-+ if (clp->gssd_fd != -1) close(clp->gssd_fd);
- free(clp->dirname);
- free(clp->servicename);
- free(clp->servername);
-@@ -303,8 +328,10 @@ insert_new_clnt(void)
- }
- clp->krb5_poll_index = -1;
- clp->spkm3_poll_index = -1;
-+ clp->gssd_poll_index = -1;
- clp->krb5_fd = -1;
- clp->spkm3_fd = -1;
-+ clp->gssd_fd = -1;
- clp->dir_fd = -1;
-
- TAILQ_INSERT_HEAD(&clnt_list, clp, list);
-@@ -315,19 +342,43 @@ out:
- static int
- process_clnt_dir_files(struct clnt_info * clp)
- {
-- char kname[32];
-- char sname[32];
-- char info_file_name[32];
-+ char name[PATH_MAX];
-+ char gname[PATH_MAX];
-+ char info_file_name[PATH_MAX];
-
-- if (clp->krb5_fd == -1) {
-- snprintf(kname, sizeof(kname), "%s/krb5", clp->dirname);
-- clp->krb5_fd = open(kname, O_RDWR);
-+ if (clp->gssd_fd == -1) {
-+ snprintf(gname, sizeof(gname), "%s/gssd", clp->dirname);
-+ clp->gssd_fd = open(gname, O_RDWR);
- }
-- if (clp->spkm3_fd == -1) {
-- snprintf(sname, sizeof(sname), "%s/spkm3", clp->dirname);
-- clp->spkm3_fd = open(sname, O_RDWR);
-+ if (clp->gssd_fd == -1) {
-+ if (clp->krb5_fd == -1) {
-+ snprintf(name, sizeof(name), "%s/krb5", clp->dirname);
-+ clp->krb5_fd = open(name, O_RDWR);
-+ }
-+ if (clp->spkm3_fd == -1) {
-+ snprintf(name, sizeof(name), "%s/spkm3", clp->dirname);
-+ clp->spkm3_fd = open(name, O_RDWR);
-+ }
-+
-+ /* If we opened a gss-specific pipe, let's try opening
-+ * the new upcall pipe again. If we succeed, close
-+ * gss-specific pipe(s).
-+ */
-+ if (clp->krb5_fd != -1 || clp->spkm3_fd != -1) {
-+ clp->gssd_fd = open(gname, O_RDWR);
-+ if (clp->gssd_fd != -1) {
-+ if (clp->krb5_fd != -1)
-+ close(clp->krb5_fd);
-+ clp->krb5_fd = -1;
-+ if (clp->spkm3_fd != -1)
-+ close(clp->spkm3_fd);
-+ clp->spkm3_fd = -1;
-+ }
-+ }
- }
-- if((clp->krb5_fd == -1) && (clp->spkm3_fd == -1))
-+
-+ if ((clp->krb5_fd == -1) && (clp->spkm3_fd == -1) &&
-+ (clp->gssd_fd == -1))
- return -1;
- snprintf(info_file_name, sizeof(info_file_name), "%s/info",
- clp->dirname);
-@@ -362,6 +413,15 @@ get_poll_index(int *ind)
- static int
- insert_clnt_poll(struct clnt_info *clp)
- {
-+ if ((clp->gssd_fd != -1) && (clp->gssd_poll_index == -1)) {
-+ if (get_poll_index(&clp->gssd_poll_index)) {
-+ printerr(0, "ERROR: Too many gssd clients\n");
-+ return -1;
-+ }
-+ pollarray[clp->gssd_poll_index].fd = clp->gssd_fd;
-+ pollarray[clp->gssd_poll_index].events |= POLLIN;
-+ }
-+
- if ((clp->krb5_fd != -1) && (clp->krb5_poll_index == -1)) {
- if (get_poll_index(&clp->krb5_poll_index)) {
- printerr(0, "ERROR: Too many krb5 clients\n");
-@@ -384,17 +444,18 @@ insert_clnt_poll(struct clnt_info *clp)
- }
-
- static void
--process_clnt_dir(char *dir)
-+process_clnt_dir(char *dir, char *pdir)
- {
- struct clnt_info * clp;
-
- if (!(clp = insert_new_clnt()))
- goto fail_destroy_client;
-
-- if (!(clp->dirname = calloc(strlen(dir) + 1, 1))) {
-+ /* An extra for the '/', and an extra for the null */
-+ if (!(clp->dirname = calloc(strlen(dir) + strlen(pdir) + 2, 1))) {
- goto fail_destroy_client;
- }
-- memcpy(clp->dirname, dir, strlen(dir));
-+ sprintf(clp->dirname, "%s/%s", pdir, dir);
- if ((clp->dir_fd = open(clp->dirname, O_RDONLY)) == -1) {
- printerr(0, "ERROR: can't open %s: %s\n",
- clp->dirname, strerror(errno));
-@@ -438,16 +499,24 @@ init_client_list(void)
- * directories, since the DNOTIFY could have been in there.
- */
- static void
--update_old_clients(struct dirent **namelist, int size)
-+update_old_clients(struct dirent **namelist, int size, char *pdir)
- {
- struct clnt_info *clp;
- void *saveprev;
- int i, stillhere;
-+ char fname[PATH_MAX];
-
- for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next) {
-+ /* only compare entries in the global list that are from the
-+ * same pipefs parent directory as "pdir"
-+ */
-+ if (strncmp(clp->dirname, pdir, strlen(pdir)) != 0) continue;
-+
- stillhere = 0;
- for (i=0; i < size; i++) {
-- if (!strcmp(clp->dirname, namelist[i]->d_name)) {
-+ snprintf(fname, sizeof(fname), "%s/%s",
-+ pdir, namelist[i]->d_name);
-+ if (strcmp(clp->dirname, fname) == 0) {
- stillhere = 1;
- break;
- }
-@@ -468,48 +537,69 @@ update_old_clients(struct dirent **namelist, int size)
-
- /* Search for a client by directory name, return 1 if found, 0 otherwise */
- static int
--find_client(char *dirname)
-+find_client(char *dirname, char *pdir)
- {
- struct clnt_info *clp;
-+ char fname[PATH_MAX];
-
-- for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next)
-- if (!strcmp(clp->dirname, dirname))
-+ for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next) {
-+ snprintf(fname, sizeof(fname), "%s/%s", pdir, dirname);
-+ if (strcmp(clp->dirname, fname) == 0)
- return 1;
-+ }
- return 0;
- }
-
--/* Used to read (and re-read) list of clients, set up poll array. */
--int
--update_client_list(void)
-+static int
-+process_pipedir(char *pipe_name)
- {
- struct dirent **namelist;
- int i, j;
-
-- if (chdir(pipefs_nfsdir) < 0) {
-+ if (chdir(pipe_name) < 0) {
- printerr(0, "ERROR: can't chdir to %s: %s\n",
-- pipefs_nfsdir, strerror(errno));
-+ pipe_name, strerror(errno));
- return -1;
- }
-
-- j = scandir(pipefs_nfsdir, &namelist, NULL, alphasort);
-+ j = scandir(pipe_name, &namelist, NULL, alphasort);
- if (j < 0) {
- printerr(0, "ERROR: can't scandir %s: %s\n",
-- pipefs_nfsdir, strerror(errno));
-+ pipe_name, strerror(errno));
- return -1;
- }
-- update_old_clients(namelist, j);
-+
-+ update_old_clients(namelist, j, pipe_name);
- for (i=0; i < j; i++) {
- if (i < FD_ALLOC_BLOCK
- && !strncmp(namelist[i]->d_name, "clnt", 4)
-- && !find_client(namelist[i]->d_name))
-- process_clnt_dir(namelist[i]->d_name);
-+ && !find_client(namelist[i]->d_name, pipe_name))
-+ process_clnt_dir(namelist[i]->d_name, pipe_name);
- free(namelist[i]);
- }
-
- free(namelist);
-+
- return 0;
- }
-
-+/* Used to read (and re-read) list of clients, set up poll array. */
-+int
-+update_client_list(void)
-+{
-+ int retval = -1;
-+ struct topdirs_info *tdi;
-+
-+ TAILQ_FOREACH(tdi, &topdirs_list, list) {
-+ retval = process_pipedir(tdi->dirname);
-+ if (retval)
-+ printerr(1, "WARNING: error processing %s\n",
-+ tdi->dirname);
-+
-+ }
-+ return retval;
-+}
-+
- static int
- do_downcall(int k5_fd, uid_t uid, struct authgss_private_data *pd,
- gss_buffer_desc *context_token)
-@@ -798,15 +888,14 @@ int create_auth_rpc_client(struct clnt_info *clp,
- goto out;
- }
-
--
- /*
- * this code uses the userland rpcsec gss library to create a krb5
- * context on behalf of the kernel
- */
--void
--handle_krb5_upcall(struct clnt_info *clp)
-+static void
-+process_krb5_upcall(struct clnt_info *clp, uid_t uid, int fd, char *tgtname,
-+ char *service)
- {
-- uid_t uid;
- CLIENT *rpc_clnt = NULL;
- AUTH *auth = NULL;
- struct authgss_private_data pd;
-@@ -815,23 +904,51 @@ handle_krb5_upcall(struct clnt_info *clp)
- char **ccname;
- char **dirname;
- int create_resp = -1;
-+ int err, downcall_err = -EACCES;
-
-- printerr(1, "handling krb5 upcall\n");
-+ printerr(1, "handling krb5 upcall (%s)\n", clp->dirname);
-
-+ if (tgtname) {
-+ if (clp->servicename) {
-+ free(clp->servicename);
-+ clp->servicename = strdup(tgtname);
-+ }
-+ }
- token.length = 0;
- token.value = NULL;
- memset(&pd, 0, sizeof(struct authgss_private_data));
-
-- if (read(clp->krb5_fd, &uid, sizeof(uid)) < sizeof(uid)) {
-- printerr(0, "WARNING: failed reading uid from krb5 "
-- "upcall pipe: %s\n", strerror(errno));
-- goto out;
-- }
--
-- if (uid != 0 || (uid == 0 && root_uses_machine_creds == 0)) {
-+ /*
-+ * If "service" is specified, then the kernel is indicating that
-+ * we must use machine credentials for this request. (Regardless
-+ * of the uid value or the setting of root_uses_machine_creds.)
-+ * If the service value is "*", then any service name can be used.
-+ * Otherwise, it specifies the service name that should be used.
-+ * (For now, the values of service will only be "*" or "nfs".)
-+ *
-+ * Restricting gssd to use "nfs" service name is needed for when
-+ * the NFS server is doing a callback to the NFS client. In this
-+ * case, the NFS server has to authenticate itself as "nfs" --
-+ * even if there are other service keys such as "host" or "root"
-+ * in the keytab.
-+ *
-+ * Another case when the kernel may specify the service attribute
-+ * is when gssd is being asked to create the context for a
-+ * SETCLIENT_ID operation. In this case, machine credentials
-+ * must be used for the authentication. However, the service name
-+ * used for this case is not important.
-+ *
-+ */
-+ printerr(2, "%s: service is '%s'\n", __func__,
-+ service ? service : "");
-+ if (uid != 0 || (uid == 0 && root_uses_machine_creds == 0 &&
-+ service == NULL)) {
- /* Tell krb5 gss which credentials cache to use */
- for (dirname = ccachesearch; *dirname != NULL; dirname++) {
-- if (gssd_setup_krb5_user_gss_ccache(uid, clp->servername, *dirname) == 0)
-+ err = gssd_setup_krb5_user_gss_ccache(uid, clp->servername, *dirname);
-+ if (err == -EKEYEXPIRED)
-+ downcall_err = -EKEYEXPIRED;
-+ else if (!err)
- create_resp = create_auth_rpc_client(clp, &rpc_clnt, &auth, uid,
- AUTHTYPE_KRB5);
- if (create_resp == 0)
-@@ -839,12 +956,13 @@ handle_krb5_upcall(struct clnt_info *clp)
- }
- }
- if (create_resp != 0) {
-- if (uid == 0 && root_uses_machine_creds == 1) {
-+ if (uid == 0 && (root_uses_machine_creds == 1 ||
-+ service != NULL)) {
- int nocache = 0;
- int success = 0;
- do {
- gssd_refresh_krb5_machine_credential(clp->servername,
-- NULL, nocache);
-+ NULL, service);
- /*
- * Get a list of credential cache names and try each
- * of them until one works or we've tried them all
-@@ -904,7 +1022,7 @@ handle_krb5_upcall(struct clnt_info *clp)
- goto out_return_error;
- }
-
-- do_downcall(clp->krb5_fd, uid, &pd, &token);
-+ do_downcall(fd, uid, &pd, &token);
-
- out:
- if (token.value)
-@@ -920,7 +1038,7 @@ out:
- return;
-
- out_return_error:
-- do_error_downcall(clp->krb5_fd, uid, -1);
-+ do_error_downcall(fd, uid, downcall_err);
- goto out;
- }
-
-@@ -928,26 +1046,19 @@ out_return_error:
- * this code uses the userland rpcsec gss library to create an spkm3
- * context on behalf of the kernel
- */
--void
--handle_spkm3_upcall(struct clnt_info *clp)
-+static void
-+process_spkm3_upcall(struct clnt_info *clp, uid_t uid, int fd)
- {
-- uid_t uid;
- CLIENT *rpc_clnt = NULL;
- AUTH *auth = NULL;
- struct authgss_private_data pd;
- gss_buffer_desc token;
-
-- printerr(2, "handling spkm3 upcall\n");
-+ printerr(2, "handling spkm3 upcall (%s)\n", clp->dirname);
-
- token.length = 0;
- token.value = NULL;
-
-- if (read(clp->spkm3_fd, &uid, sizeof(uid)) < sizeof(uid)) {
-- printerr(0, "WARNING: failed reading uid from spkm3 "
-- "upcall pipe: %s\n", strerror(errno));
-- goto out;
-- }
--
- if (create_auth_rpc_client(clp, &rpc_clnt, &auth, uid, AUTHTYPE_SPKM3)) {
- printerr(0, "WARNING: Failed to create spkm3 context for "
- "user with uid %d\n", uid);
-@@ -968,7 +1079,7 @@ handle_spkm3_upcall(struct clnt_info *clp)
- goto out_return_error;
- }
-
-- do_downcall(clp->spkm3_fd, uid, &pd, &token);
-+ do_downcall(fd, uid, &pd, &token);
-
- out:
- if (token.value)
-@@ -980,6 +1091,139 @@ out:
- return;
-
- out_return_error:
-- do_error_downcall(clp->spkm3_fd, uid, -1);
-+ do_error_downcall(fd, uid, -1);
- goto out;
- }
-+
-+void
-+handle_krb5_upcall(struct clnt_info *clp)
-+{
-+ uid_t uid;
-+
-+ if (read(clp->krb5_fd, &uid, sizeof(uid)) < sizeof(uid)) {
-+ printerr(0, "WARNING: failed reading uid from krb5 "
-+ "upcall pipe: %s\n", strerror(errno));
-+ return;
-+ }
-+
-+ return process_krb5_upcall(clp, uid, clp->krb5_fd, NULL, NULL);
-+}
-+
-+void
-+handle_spkm3_upcall(struct clnt_info *clp)
-+{
-+ uid_t uid;
-+
-+ if (read(clp->spkm3_fd, &uid, sizeof(uid)) < sizeof(uid)) {
-+ printerr(0, "WARNING: failed reading uid from spkm3 "
-+ "upcall pipe: %s\n", strerror(errno));
-+ return;
-+ }
-+
-+ return process_spkm3_upcall(clp, uid, clp->spkm3_fd);
-+}
-+
-+void
-+handle_gssd_upcall(struct clnt_info *clp)
-+{
-+ uid_t uid;
-+ char *lbuf = NULL;
-+ int lbuflen = 0;
-+ char *p;
-+ char *mech = NULL;
-+ char *target = NULL;
-+ char *service = NULL;
-+
-+ printerr(1, "handling gssd upcall (%s)\n", clp->dirname);
-+
-+ if (readline(clp->gssd_fd, &lbuf, &lbuflen) != 1) {
-+ printerr(0, "WARNING: handle_gssd_upcall: "
-+ "failed reading request\n");
-+ return;
-+ }
-+ printerr(2, "%s: '%s'\n", __func__, lbuf);
-+
-+ /* find the mechanism name */
-+ if ((p = strstr(lbuf, "mech=")) != NULL) {
-+ mech = malloc(lbuflen);
-+ if (!mech)
-+ goto out;
-+ if (sscanf(p, "mech=%s", mech) != 1) {
-+ printerr(0, "WARNING: handle_gssd_upcall: "
-+ "failed to parse gss mechanism name "
-+ "in upcall string '%s'\n", lbuf);
-+ goto out;
-+ }
-+ } else {
-+ printerr(0, "WARNING: handle_gssd_upcall: "
-+ "failed to find gss mechanism name "
-+ "in upcall string '%s'\n", lbuf);
-+ goto out;
-+ }
-+
-+ /* read uid */
-+ if ((p = strstr(lbuf, "uid=")) != NULL) {
-+ if (sscanf(p, "uid=%d", &uid) != 1) {
-+ printerr(0, "WARNING: handle_gssd_upcall: "
-+ "failed to parse uid "
-+ "in upcall string '%s'\n", lbuf);
-+ goto out;
-+ }
-+ } else {
-+ printerr(0, "WARNING: handle_gssd_upcall: "
-+ "failed to find uid "
-+ "in upcall string '%s'\n", lbuf);
-+ goto out;
-+ }
-+
-+ /* read target name */
-+ if ((p = strstr(lbuf, "target=")) != NULL) {
-+ target = malloc(lbuflen);
-+ if (!target)
-+ goto out;
-+ if (sscanf(p, "target=%s", target) != 1) {
-+ printerr(0, "WARNING: handle_gssd_upcall: "
-+ "failed to parse target name "
-+ "in upcall string '%s'\n", lbuf);
-+ goto out;
-+ }
-+ }
-+
-+ /*
-+ * read the service name
-+ *
-+ * The presence of attribute "service=" indicates that machine
-+ * credentials should be used for this request. If the value
-+ * is "*", then any machine credentials available can be used.
-+ * If the value is anything else, then machine credentials for
-+ * the specified service name (always "nfs" for now) should be
-+ * used.
-+ */
-+ if ((p = strstr(lbuf, "service=")) != NULL) {
-+ service = malloc(lbuflen);
-+ if (!service)
-+ goto out;
-+ if (sscanf(p, "service=%s", service) != 1) {
-+ printerr(0, "WARNING: handle_gssd_upcall: "
-+ "failed to parse service type "
-+ "in upcall string '%s'\n", lbuf);
-+ goto out;
-+ }
-+ }
-+
-+ if (strcmp(mech, "krb5") == 0)
-+ process_krb5_upcall(clp, uid, clp->gssd_fd, target, service);
-+ else if (strcmp(mech, "spkm3") == 0)
-+ process_spkm3_upcall(clp, uid, clp->gssd_fd);
-+ else
-+ printerr(0, "WARNING: handle_gssd_upcall: "
-+ "received unknown gss mech '%s'\n", mech);
-+
-+out:
-+ free(lbuf);
-+ free(mech);
-+ free(target);
-+ free(service);
-+ return;
-+}
-+
-diff --git a/utils/gssd/krb5_util.c b/utils/gssd/krb5_util.c
-index 78e9775..1295f57 100644
---- a/utils/gssd/krb5_util.c
-+++ b/utils/gssd/krb5_util.c
-@@ -170,9 +170,8 @@ select_krb5_ccache(const struct dirent *d)
- * what we want. Otherwise, return zero and no dirent pointer.
- * The caller is responsible for freeing the dirent if one is returned.
- *
-- * Returns:
-- * 0 => could not find an existing entry
-- * 1 => found an existing entry
-+ * Returns 0 if a valid-looking entry was found and a non-zero error
-+ * code otherwise.
- */
- static int
- gssd_find_existing_krb5_ccache(uid_t uid, char *dirname, struct dirent **d)
-@@ -186,7 +185,7 @@ gssd_find_existing_krb5_ccache(uid_t uid, char *dirname, struct dirent **d)
- char buf[1030];
- char *princname = NULL;
- char *realm = NULL;
-- int score, best_match_score = 0;
-+ int score, best_match_score = 0, err = -EACCES;
-
- memset(&best_match_stat, 0, sizeof(best_match_stat));
- *d = NULL;
-@@ -229,6 +228,7 @@ gssd_find_existing_krb5_ccache(uid_t uid, char *dirname, struct dirent **d)
- printerr(3, "CC file '%s' is expired or corrupt\n",
- statname);
- free(namelist[i]);
-+ err = -EKEYEXPIRED;
- continue;
- }
-
-@@ -284,11 +284,12 @@ gssd_find_existing_krb5_ccache(uid_t uid, char *dirname, struct dirent **d)
- }
- free(namelist);
- }
-- if (found)
-- {
-+ if (found) {
- *d = best_match_dir;
-+ return 0;
- }
-- return found;
-+
-+ return err;
- }
-
-
-@@ -797,10 +798,9 @@ gssd_search_krb5_keytab(krb5_context context, krb5_keytab kt,
- */
- static int
- find_keytab_entry(krb5_context context, krb5_keytab kt, const char *hostname,
-- krb5_keytab_entry *kte)
-+ krb5_keytab_entry *kte, const char **svcnames)
- {
- krb5_error_code code;
-- const char *svcnames[] = { "root", "nfs", "host", NULL };
- char **realmnames = NULL;
- char myhostname[NI_MAXHOST], targethostname[NI_MAXHOST];
- int i, j, retval;
-@@ -1025,29 +1025,29 @@ err_cache:
- * given only a UID. We really need more information, but we
- * do the best we can.
- *
-- * Returns:
-- * 0 => a ccache was found
-- * 1 => no ccache was found
-+ * Returns 0 if a ccache was found, and a non-zero error code otherwise.
- */
- int
- gssd_setup_krb5_user_gss_ccache(uid_t uid, char *servername, char *dirname)
- {
- char buf[MAX_NETOBJ_SZ];
- struct dirent *d;
-+ int err;
-
- printerr(2, "getting credentials for client with uid %u for "
- "server %s\n", uid, servername);
- memset(buf, 0, sizeof(buf));
-- if (gssd_find_existing_krb5_ccache(uid, dirname, &d)) {
-- snprintf(buf, sizeof(buf), "FILE:%s/%s", dirname, d->d_name);
-- free(d);
-- }
-- else
-- return 1;
-+ err = gssd_find_existing_krb5_ccache(uid, dirname, &d);
-+ if (err)
-+ return err;
-+
-+ snprintf(buf, sizeof(buf), "FILE:%s/%s", dirname, d->d_name);
-+ free(d);
-+
- printerr(2, "using %s as credentials cache for client with "
- "uid %u for server %s\n", buf, uid, servername);
- gssd_set_krb5_ccache_name(buf);
-- return 0;
-+ return err;
- }
-
- /*
-@@ -1096,7 +1096,8 @@ gssd_get_krb5_machine_cred_list(char ***list)
- for (ple = gssd_k5_kt_princ_list; ple; ple = ple->next) {
- if (ple->ccname) {
- /* Make sure cred is up-to-date before returning it */
-- retval = gssd_refresh_krb5_machine_credential(NULL, ple, 0);
-+ retval = gssd_refresh_krb5_machine_credential(NULL, ple,
-+ NULL);
- if (retval)
- continue;
- if (i + 1 > listsize) {
-@@ -1186,14 +1187,24 @@ gssd_destroy_krb5_machine_creds(void)
- */
- int
- gssd_refresh_krb5_machine_credential(char *hostname,
-- struct gssd_k5_kt_princ *ple, int nocache)
-+ struct gssd_k5_kt_princ *ple,
-+ char *service)
- {
- krb5_error_code code = 0;
- krb5_context context;
- krb5_keytab kt = NULL;;
- int retval = 0;
- char *k5err = NULL;
-+ const char *svcnames[4] = { "root", "nfs", "host", NULL };
-
-+ /*
-+ * If a specific service name was specified, use it.
-+ * Otherwise, use the default list.
-+ */
-+ if (service != NULL && strcmp(service, "*") != 0) {
-+ svcnames[0] = service;
-+ svcnames[1] = NULL;
-+ }
- if (hostname == NULL && ple == NULL)
- return EINVAL;
-
-@@ -1216,7 +1227,7 @@ gssd_refresh_krb5_machine_credential(char *hostname,
- if (ple == NULL) {
- krb5_keytab_entry kte;
-
-- code = find_keytab_entry(context, kt, hostname, &kte);
-+ code = find_keytab_entry(context, kt, hostname, &kte, svcnames);
- if (code) {
- printerr(0, "ERROR: %s: no usable keytab entry found "
- "in keytab %s for connection with host %s\n",
-@@ -1241,7 +1252,7 @@ gssd_refresh_krb5_machine_credential(char *hostname,
- goto out;
- }
- }
-- retval = gssd_get_single_krb5_cred(context, kt, ple, nocache);
-+ retval = gssd_get_single_krb5_cred(context, kt, ple, 0);
- out:
- if (kt)
- krb5_kt_close(context, kt);
-diff --git a/utils/gssd/krb5_util.h b/utils/gssd/krb5_util.h
-index 4b6b281..4602cc3 100644
---- a/utils/gssd/krb5_util.h
-+++ b/utils/gssd/krb5_util.h
-@@ -30,7 +30,8 @@ void gssd_free_krb5_machine_cred_list(char **list);
- void gssd_setup_krb5_machine_gss_ccache(char *servername);
- void gssd_destroy_krb5_machine_creds(void);
- int gssd_refresh_krb5_machine_credential(char *hostname,
-- struct gssd_k5_kt_princ *ple, int nocache);
-+ struct gssd_k5_kt_princ *ple,
-+ char *service);
- char *gssd_k5_err_msg(krb5_context context, krb5_error_code code);
- void gssd_k5_get_default_realm(char **def_realm);
-
-diff --git a/utils/gssd/svcgssd_proc.c b/utils/gssd/svcgssd_proc.c
-index 6f2ba61..f1bfbef 100644
---- a/utils/gssd/svcgssd_proc.c
-+++ b/utils/gssd/svcgssd_proc.c
-@@ -56,6 +56,7 @@
- #include "gss_util.h"
- #include "err_util.h"
- #include "context.h"
-+#include "gss_oids.h"
-
- extern char * mech2file(gss_OID mech);
- #define SVCGSSD_CONTEXT_CHANNEL "/proc/net/rpc/auth.rpcsec.context/channel"
-@@ -73,7 +74,7 @@ struct svc_cred {
- static int
- do_svc_downcall(gss_buffer_desc *out_handle, struct svc_cred *cred,
- gss_OID mech, gss_buffer_desc *context_token,
-- int32_t endtime)
-+ int32_t endtime, char *client_name)
- {
- FILE *f;
- int i;
-@@ -98,9 +99,10 @@ do_svc_downcall(gss_buffer_desc *out_handle, struct svc_cred *cred,
- qword_printint(f, cred->cr_gid);
- qword_printint(f, cred->cr_ngroups);
- printerr(2, "mech: %s, hndl len: %d, ctx len %d, timeout: %d (%d from now), "
-- "uid: %d, gid: %d, num aux grps: %d:\n",
-+ "clnt: %s, uid: %d, gid: %d, num aux grps: %d:\n",
- fname, out_handle->length, context_token->length,
- endtime, endtime - time(0),
-+ client_name ? client_name : "",
- cred->cr_uid, cred->cr_gid, cred->cr_ngroups);
- for (i=0; i < cred->cr_ngroups; i++) {
- qword_printint(f, cred->cr_groups[i]);
-@@ -108,6 +110,8 @@ do_svc_downcall(gss_buffer_desc *out_handle, struct svc_cred *cred,
- }
- qword_print(f, fname);
- qword_printhex(f, context_token->value, context_token->length);
-+ if (client_name)
-+ qword_print(f, client_name);
- err = qword_eol(f);
- if (err) {
- printerr(1, "WARNING: error writing to downcall channel "
-@@ -307,6 +311,75 @@ print_hexl(const char *description, unsigned char *cp, int length)
- }
- #endif
-
-+static int
-+get_krb5_hostbased_name (gss_buffer_desc *name, char **hostbased_name)
-+{
-+ char *p, *sname = NULL;
-+ if (strchr(name->value, '@') && strchr(name->value, '/')) {
-+ if ((sname = calloc(name->length, 1)) == NULL) {
-+ printerr(0, "ERROR: get_krb5_hostbased_name failed "
-+ "to allocate %d bytes\n", name->length);
-+ return -1;
-+ }
-+ /* read in name and instance and replace '/' with '@' */
-+ sscanf(name->value, "%[^@]", sname);
-+ p = strrchr(sname, '/');
-+ if (p == NULL) { /* The '@' preceeded the '/' */
-+ free(sname);
-+ return -1;
-+ }
-+ *p = '@';
-+ }
-+ *hostbased_name = sname;
-+ return 0;
-+}
-+
-+static int
-+get_hostbased_client_name(gss_name_t client_name, gss_OID mech,
-+ char **hostbased_name)
-+{
-+ u_int32_t maj_stat, min_stat;
-+ gss_buffer_desc name;
-+ gss_OID name_type = GSS_C_NO_OID;
-+ char *cname;
-+ int res = -1;
-+
-+ *hostbased_name = NULL; /* preset in case we fail */
-+
-+ /* Get the client's gss authenticated name */
-+ maj_stat = gss_display_name(&min_stat, client_name, &name, &name_type);
-+ if (maj_stat != GSS_S_COMPLETE) {
-+ pgsserr("get_hostbased_client_name: gss_display_name",
-+ maj_stat, min_stat, mech);
-+ goto out_err;
-+ }
-+ if (name.length >= 0xffff) { /* don't overflow */
-+ printerr(0, "ERROR: get_hostbased_client_name: "
-+ "received gss_name is too long (%d bytes)\n",
-+ name.length);
-+ goto out_rel_buf;
-+ }
-+
-+ /* For Kerberos, transform the NT_KRB5_PRINCIPAL name to
-+ * an NT_HOSTBASED_SERVICE name */
-+ if (g_OID_equal(&krb5oid, mech)) {
-+ if (get_krb5_hostbased_name(&name, &cname) == 0)
-+ *hostbased_name = cname;
-+ }
-+
-+ /* No support for SPKM3, just print a warning (for now) */
-+ if (g_OID_equal(&spkm3oid, mech)) {
-+ printerr(1, "WARNING: get_hostbased_client_name: "
-+ "no hostbased_name support for SPKM3\n");
-+ }
-+
-+ res = 0;
-+out_rel_buf:
-+ gss_release_buffer(&min_stat, &name);
-+out_err:
-+ return res;
-+}
-+
- void
- handle_nullreq(FILE *f) {
- /* XXX initialize to a random integer to reduce chances of unnecessary
-@@ -325,7 +398,7 @@ handle_nullreq(FILE *f) {
- null_token = {.value = NULL};
- u_int32_t ret_flags;
- gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
-- gss_name_t client_name;
-+ gss_name_t client_name = NULL;
- gss_OID mech = GSS_C_NO_OID;
- u_int32_t maj_stat = GSS_S_FAILURE, min_stat = 0;
- u_int32_t ignore_min_stat;
-@@ -334,6 +407,7 @@ handle_nullreq(FILE *f) {
- static int lbuflen = 0;
- static char *cp;
- int32_t ctx_endtime;
-+ char *hostbased_name = NULL;
-
- printerr(1, "handling null request\n");
-
-@@ -396,11 +470,13 @@ handle_nullreq(FILE *f) {
- if (get_ids(client_name, mech, &cred)) {
- /* get_ids() prints error msg */
- maj_stat = GSS_S_BAD_NAME; /* XXX ? */
-- gss_release_name(&ignore_min_stat, &client_name);
- goto out_err;
- }
-- gss_release_name(&ignore_min_stat, &client_name);
--
-+ if (get_hostbased_client_name(client_name, mech, &hostbased_name)) {
-+ /* get_hostbased_client_name() prints error msg */
-+ maj_stat = GSS_S_BAD_NAME; /* XXX ? */
-+ goto out_err;
-+ }
-
- /* Context complete. Pass handle_seq in out_handle to use
- * for context lookup in the kernel. */
-@@ -419,7 +495,8 @@ handle_nullreq(FILE *f) {
- /* We no longer need the gss context */
- gss_delete_sec_context(&ignore_min_stat, &ctx, &ignore_out_tok);
-
-- do_svc_downcall(&out_handle, &cred, mech, &ctx_token, ctx_endtime);
-+ do_svc_downcall(&out_handle, &cred, mech, &ctx_token, ctx_endtime,
-+ hostbased_name);
- continue_needed:
- send_response(f, &in_handle, &in_tok, maj_stat, min_stat,
- &out_handle, &out_tok);
-@@ -428,6 +505,9 @@ out:
- free(ctx_token.value);
- if (out_tok.value != NULL)
- gss_release_buffer(&ignore_min_stat, &out_tok);
-+ if (client_name)
-+ gss_release_name(&ignore_min_stat, &client_name);
-+ free(hostbased_name);
- printerr(1, "finished handling null request\n");
- return;
-
-diff --git a/utils/mount/mount.c b/utils/mount/mount.c
-index 355df79..6b9e164 100644
---- a/utils/mount/mount.c
-+++ b/utils/mount/mount.c
-@@ -593,6 +593,9 @@ int main(int argc, char *argv[])
- if (mnt_err == EX_BG) {
- printf(_("%s: backgrounding \"%s\"\n"),
- progname, spec);
-+ printf(_("%s: mount options: \"%s\"\n"),
-+ progname, extra_opts);
-+
- fflush(stdout);
-
- /*
-diff --git a/utils/mount/network.c b/utils/mount/network.c
-index 7b1152a..92bba2d 100644
---- a/utils/mount/network.c
-+++ b/utils/mount/network.c
-@@ -42,6 +42,7 @@
- #include
- #include
-
-+#include "sockaddr.h"
- #include "xcommon.h"
- #include "mount.h"
- #include "nls.h"
-@@ -56,10 +57,6 @@
- #define CONNECT_TIMEOUT (20)
- #define MOUNT_TIMEOUT (30)
-
--#if SIZEOF_SOCKLEN_T - 0 == 0
--#define socklen_t unsigned int
--#endif
--
- extern int nfs_mount_data_version;
- extern char *progname;
- extern int verbose;
-@@ -193,8 +190,18 @@ static const unsigned int *nfs_default_proto()
- }
- #endif /* MOUNT_CONFIG */
-
--static int nfs_lookup(const char *hostname, const sa_family_t family,
-- struct sockaddr *sap, socklen_t *salen)
-+/**
-+ * nfs_lookup - resolve hostname to an IPv4 or IPv6 socket address
-+ * @hostname: pointer to C string containing DNS hostname to resolve
-+ * @family: address family hint
-+ * @sap: pointer to buffer to fill with socket address
-+ * @len: IN: size of buffer to fill; OUT: size of socket address
-+ *
-+ * Returns 1 and places a socket address at @sap if successful;
-+ * otherwise zero.
-+ */
-+int nfs_lookup(const char *hostname, const sa_family_t family,
-+ struct sockaddr *sap, socklen_t *salen)
- {
- struct addrinfo *gai_results;
- struct addrinfo gai_hint = {
-@@ -243,25 +250,6 @@ static int nfs_lookup(const char *hostname, const sa_family_t family,
- }
-
- /**
-- * 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
- * @sin: returns an IPv4 address
-@@ -283,8 +271,8 @@ int nfs_gethostbyname(const char *hostname, struct sockaddr_in *sin)
- * OUT: length of converted socket address
- *
- * Convert a presentation format address string to a socket address.
-- * Similar to nfs_name_to_address(), but the DNS query is squelched,
-- * and won't make any noise if the getaddrinfo() call fails.
-+ * Similar to nfs_lookup(), but the DNS query is squelched, and it
-+ * won't make any noise if the getaddrinfo() call fails.
- *
- * Returns 1 and fills in @sap and @salen if successful; otherwise zero.
- *
-@@ -549,8 +537,8 @@ static int nfs_probe_port(const struct sockaddr *sap, const socklen_t salen,
- struct pmap *pmap, const unsigned long *versions,
- const unsigned int *protos)
- {
-- struct sockaddr_storage address;
-- struct sockaddr *saddr = (struct sockaddr *)&address;
-+ union nfs_sockaddr address;
-+ struct sockaddr *saddr = &address.sa;
- const unsigned long prog = pmap->pm_prog, *p_vers;
- const unsigned int prot = (u_int)pmap->pm_prot, *p_prot;
- const u_short port = (u_short) pmap->pm_port;
-@@ -840,8 +828,8 @@ int start_statd(void)
- int nfs_advise_umount(const struct sockaddr *sap, const socklen_t salen,
- const struct pmap *pmap, const dirpath *argp)
- {
-- struct sockaddr_storage address;
-- struct sockaddr *saddr = (struct sockaddr *)&address;
-+ union nfs_sockaddr address;
-+ struct sockaddr *saddr = &address.sa;
- struct pmap mnt_pmap = *pmap;
- struct timeval timeout = {
- .tv_sec = MOUNT_TIMEOUT >> 3,
-@@ -1289,6 +1277,7 @@ nfs_nfs_version(struct mount_options *options, unsigned long *version)
- int
- nfs_nfs_protocol(struct mount_options *options, unsigned long *protocol)
- {
-+ sa_family_t family;
- char *option;
-
- switch (po_rightmost(options, nfs_transport_opttbl)) {
-@@ -1300,17 +1289,8 @@ nfs_nfs_protocol(struct mount_options *options, unsigned long *protocol)
- return 1;
- case 2: /* proto */
- option = po_get(options, "proto");
-- if (option) {
-- if (strcmp(option, "tcp") == 0) {
-- *protocol = IPPROTO_TCP;
-- return 1;
-- }
-- if (strcmp(option, "udp") == 0) {
-- *protocol = IPPROTO_UDP;
-- return 1;
-- }
-- return 0;
-- }
-+ if (option != NULL)
-+ return nfs_get_proto(option, &family, protocol);
- }
-
- /*
-@@ -1352,6 +1332,40 @@ nfs_nfs_port(struct mount_options *options, unsigned long *port)
- }
-
- /*
-+ * 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.
-+ */
-+int nfs_nfs_proto_family(struct mount_options *options,
-+ sa_family_t *family)
-+{
-+ unsigned long protocol;
-+ char *option;
-+
-+#ifdef IPV6_SUPPORTED
-+ *family = AF_UNSPEC;
-+#else
-+ *family = AF_INET;
-+#endif
-+
-+ switch (po_rightmost(options, nfs_transport_opttbl)) {
-+ case 0: /* udp */
-+ return 1;
-+ case 1: /* tcp */
-+ return 1;
-+ case 2: /* proto */
-+ option = po_get(options, "proto");
-+ if (option != NULL)
-+ return nfs_get_proto(option, family, &protocol);
-+ }
-+
-+ /*
-+ * NFS transport protocol wasn't specified. Return the
-+ * default address family.
-+ */
-+ return 1;
-+}
-+
-+/*
- * "mountprog" is supported only by the legacy mount command. The
- * kernel mount client does not support this option.
- *
-@@ -1419,20 +1433,12 @@ nfs_mount_version(struct mount_options *options, unsigned long *version)
- 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;
-- }
-- return 0;
-- }
-+ if (option != NULL)
-+ return nfs_get_proto(option, &family, protocol);
-
- /*
- * MNT transport protocol wasn't specified. If the NFS
-@@ -1472,6 +1478,35 @@ nfs_mount_port(struct mount_options *options, unsigned long *port)
- return 1;
- }
-
-+/*
-+ * Returns TRUE and fills in @family if a valid MNT protocol option
-+ * is found, or FALSE if the option was specified with an invalid value.
-+ */
-+int nfs_mount_proto_family(struct mount_options *options,
-+ sa_family_t *family)
-+{
-+ unsigned long protocol;
-+ char *option;
-+
-+#ifdef HAVE_LIBTIRPC
-+ *family = AF_UNSPEC;
-+#else
-+ *family = AF_INET;
-+#endif
-+
-+ option = po_get(options, "mountproto");
-+ if (option != NULL)
-+ return nfs_get_proto(option, family, &protocol);
-+
-+ /*
-+ * 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);
-+}
-+
- /**
- * nfs_options2pmap - set up pmap structs based on mount options
- * @options: pointer to mount options
-diff --git a/utils/mount/network.h b/utils/mount/network.h
-index 7eb89b0..2a3a110 100644
---- a/utils/mount/network.h
-+++ b/utils/mount/network.h
-@@ -44,7 +44,8 @@ int nfs_probe_bothports(const struct sockaddr *, const socklen_t,
- struct pmap *, const struct sockaddr *,
- const socklen_t, struct pmap *);
- int nfs_gethostbyname(const char *, struct sockaddr_in *);
--int nfs_name_to_address(const char *, struct sockaddr *, socklen_t *);
-+int nfs_lookup(const char *hostname, const sa_family_t family,
-+ struct sockaddr *sap, socklen_t *salen);
- int nfs_string_to_sockaddr(const char *, struct sockaddr *, socklen_t *);
- int nfs_present_sockaddr(const struct sockaddr *,
- const socklen_t, char *, const size_t);
-@@ -56,6 +57,8 @@ int clnt_ping(struct sockaddr_in *, const unsigned long,
-
- struct mount_options;
-
-+int nfs_nfs_proto_family(struct mount_options *options, sa_family_t *family);
-+int nfs_mount_proto_family(struct mount_options *options, sa_family_t *family);
- int nfs_nfs_version(struct mount_options *options, unsigned long *version);
- int nfs_nfs_protocol(struct mount_options *options, unsigned long *protocol);
-
-diff --git a/utils/mount/nfs.man b/utils/mount/nfs.man
-index 2299637..93bd642 100644
---- a/utils/mount/nfs.man
-+++ b/utils/mount/nfs.man
-@@ -58,9 +58,17 @@ The server's hostname and export pathname
- are separated by a colon, while
- the mount options are separated by commas. The remaining fields
- are separated by blanks or tabs.
-+.P
- The server's hostname can be an unqualified hostname,
- a fully qualified domain name,
--or a dotted quad IPv4 address.
-+a dotted quad IPv4 address, or
-+an IPv6 address enclosed in square brackets.
-+Link-local and site-local IPv6 addresses must be accompanied by an
-+interface identifier.
-+See
-+.BR ipv6 (7)
-+for details on specifying raw IPv6 addresses.
-+.P
- The
- .I fstype
- field contains either "nfs" (for version 2 or version 3 NFS mounts)
-@@ -470,32 +478,38 @@ for mounting the
- .B nfs
- file system type.
- .TP 1.5i
--.BI proto= transport
--The transport the NFS client uses
-+.BI proto= netid
-+The transport protocol name and protocol family the NFS client uses
- to transmit requests to the NFS server for this mount point.
--.I transport
--can be either
--.B udp
--or
--.BR tcp .
--Each transport uses different default
-+If an NFS server has both an IPv4 and an IPv6 address, using a specific
-+netid will force the use of IPv4 or IPv6 networking to communicate
-+with that server.
-+.IP
-+If support for TI-RPC is built into the
-+.B mount.nfs
-+command,
-+.I netid
-+is a valid netid listed in
-+.IR /etc/netconfig .
-+Otherwise,
-+.I netid
-+is one of "tcp," "udp," or "rdma," and only IPv4 may be used.
-+.IP
-+Each transport protocol uses different default
- .B retrans
- and
- .B timeo
--settings; refer to the description of these two mount options for details.
-+settings.
-+Refer to the description of these two mount options for details.
- .IP
- In addition to controlling how the NFS client transmits requests to
- the server, this mount option also controls how the
- .BR mount (8)
- command communicates with the server's rpcbind and mountd services.
--Specifying
--.B proto=tcp
--forces all traffic from the
-+Specifying a netid that uses TCP forces all traffic from the
- .BR mount (8)
- command and the NFS client to use TCP.
--Specifying
--.B proto=udp
--forces all traffic types to use UDP.
-+Specifying a netid that uses UDP forces all traffic types to use UDP.
- .IP
- If the
- .B proto
-@@ -548,15 +562,20 @@ or the server's mountd service is not available on the advertised port.
- This option can be used when mounting an NFS server
- through a firewall that blocks the rpcbind protocol.
- .TP 1.5i
--.BI mountproto= transport
--The transport the NFS client uses
-+.BI mountproto= netid
-+The transport protocol name and protocol family the NFS client uses
- to transmit requests to the NFS server's mountd service when performing
- this mount request, and when later unmounting this mount point.
--.I transport
--can be either
--.B udp
--or
--.BR tcp .
-+.IP
-+If support for TI-RPC is built into the
-+.B mount.nfs
-+command,
-+.I netid
-+is a valid netid listed in
-+.IR /etc/netconfig .
-+Otherwise,
-+.I netid
-+is one of "tcp" or "udp," and only IPv4 may be used.
- .IP
- This option can be used when mounting an NFS server
- through a firewall that blocks a particular transport.
-@@ -566,6 +585,7 @@ option, different transports for mountd requests and NFS requests
- can be specified.
- If the server's mountd service is not available via the specified
- transport, the mount request fails.
-+.IP
- Refer to the TRANSPORT METHODS section for more on how the
- .B mountproto
- mount option interacts with the
-@@ -709,17 +729,26 @@ for mounting the
- .B nfs4
- file system type.
- .TP 1.5i
--.BI proto= transport
--The transport the NFS client uses
-+.BI proto= netid
-+The transport protocol name and protocol family the NFS client uses
- to transmit requests to the NFS server for this mount point.
--.I transport
--can be either
--.B udp
--or
--.BR tcp .
-+If an NFS server has both an IPv4 and an IPv6 address, using a specific
-+netid will force the use of IPv4 or IPv6 networking to communicate
-+with that server.
-+.IP
-+If support for TI-RPC is built into the
-+.B mount.nfs
-+command,
-+.I netid
-+is a valid netid listed in
-+.IR /etc/netconfig .
-+Otherwise,
-+.I netid
-+is one of "tcp" or "udp," and only IPv4 may be used.
-+.IP
- All NFS version 4 servers are required to support TCP,
- so if this mount option is not specified, the NFS version 4 client
--uses the TCP transport protocol.
-+uses the TCP protocol.
- Refer to the TRANSPORT METHODS section for more details.
- .TP 1.5i
- .BI port= n
-@@ -779,7 +808,8 @@ The DATA AND METADATA COHERENCE section discusses
- the behavior of this option in more detail.
- .TP 1.5i
- .BI clientaddr= n.n.n.n
--Specifies a single IPv4 address (in dotted-quad form)
-+Specifies a single IPv4 address (in dotted-quad form),
-+or a non-link-local IPv6 address,
- that the NFS client advertises to allow servers
- to perform NFS version 4 callback requests against
- files on this mount point. If the server is unable to
-@@ -855,6 +885,14 @@ This example can be used to mount /usr over NFS.
- .TA 2.5i +0.7i +0.7i +.7i
- server:/export /usr nfs ro,nolock,nocto,actimeo=3600 0 0
- .FI
-+.P
-+This example shows how to mount an NFS server
-+using a raw IPv6 link-local address.
-+.P
-+.NF
-+.TA 2.5i +0.7i +0.7i +.7i
-+ [fe80::215:c5ff:fb3e:e2b1%eth0]:/export /mnt nfs defaults 0 0
-+.FI
- .SH "TRANSPORT METHODS"
- NFS clients send requests to NFS servers via
- Remote Procedure Calls, or
-@@ -1498,6 +1536,8 @@ such as security negotiation, server referrals, and named attributes.
- .BR mount.nfs (5),
- .BR umount.nfs (5),
- .BR exports (5),
-+.BR netconfig (5),
-+.BR ipv6 (7),
- .BR nfsd (8),
- .BR sm-notify (8),
- .BR rpc.statd (8),
-diff --git a/utils/mount/nfs4mount.c b/utils/mount/nfs4mount.c
-index a2f318f..4a2fab7 100644
---- a/utils/mount/nfs4mount.c
-+++ b/utils/mount/nfs4mount.c
-@@ -217,8 +217,11 @@ int nfs4mount(const char *spec, const char *node, int flags,
- progname);
- goto fail;
- }
-- snprintf(new_opts, sizeof(new_opts), "%s%saddr=%s",
-- old_opts, *old_opts ? "," : "", s);
-+ if (running_bg)
-+ strncpy(new_opts, old_opts, sizeof(new_opts));
-+ else
-+ snprintf(new_opts, sizeof(new_opts), "%s%saddr=%s",
-+ old_opts, *old_opts ? "," : "", s);
- *extra_opts = xstrdup(new_opts);
-
- /* Set default options.
-@@ -434,15 +437,17 @@ int nfs4mount(const char *spec, const char *node, int flags,
- break;
- }
-
-- switch(rpc_createerr.cf_stat){
-- case RPC_TIMEDOUT:
-- break;
-- case RPC_SYSTEMERROR:
-- if (errno == ETIMEDOUT)
-+ if (!bg) {
-+ switch(rpc_createerr.cf_stat) {
-+ case RPC_TIMEDOUT:
- break;
-- default:
-- rpc_mount_errors(hostname, 0, bg);
-- goto fail;
-+ case RPC_SYSTEMERROR:
-+ if (errno == ETIMEDOUT)
-+ break;
-+ default:
-+ rpc_mount_errors(hostname, 0, bg);
-+ goto fail;
-+ }
- }
-
- if (bg && !running_bg) {
-diff --git a/utils/mount/nfsmount.c b/utils/mount/nfsmount.c
-index 6355681..6b3356c 100644
---- a/utils/mount/nfsmount.c
-+++ b/utils/mount/nfsmount.c
-@@ -170,7 +170,7 @@ parse_options(char *old_opts, struct nfs_mount_data *data,
- struct pmap *mnt_pmap = &mnt_server->pmap;
- struct pmap *nfs_pmap = &nfs_server->pmap;
- int len;
-- char *opt, *opteq, *p, *opt_b;
-+ char *opt, *opteq, *p, *opt_b, *tmp_opts;
- char *mounthost = NULL;
- char cbuf[128];
- int open_quote = 0;
-@@ -179,7 +179,8 @@ parse_options(char *old_opts, struct nfs_mount_data *data,
- *bg = 0;
-
- len = strlen(new_opts);
-- for (p=old_opts, opt_b=NULL; p && *p; p++) {
-+ tmp_opts = xstrdup(old_opts);
-+ for (p=tmp_opts, opt_b=NULL; p && *p; p++) {
- if (!opt_b)
- opt_b = p; /* begin of the option item */
- if (*p == '"')
-@@ -457,10 +458,12 @@ parse_options(char *old_opts, struct nfs_mount_data *data,
- goto out_bad;
- *mnt_server->hostname = mounthost;
- }
-+ free(tmp_opts);
- return 1;
- bad_parameter:
- nfs_error(_("%s: Bad nfs mount parameter: %s\n"), progname, opt);
- out_bad:
-+ free(tmp_opts);
- return 0;
- }
-
-diff --git a/utils/mount/nfsumount.c b/utils/mount/nfsumount.c
-index c5505b1..9d798a2 100644
---- a/utils/mount/nfsumount.c
-+++ b/utils/mount/nfsumount.c
-@@ -169,10 +169,15 @@ out:
- static int nfs_umount_do_umnt(struct mount_options *options,
- char **hostname, char **dirname)
- {
-- struct sockaddr_storage address;
-- struct sockaddr *sap = (struct sockaddr *)&address;
-+ union {
-+ struct sockaddr sa;
-+ struct sockaddr_in s4;
-+ struct sockaddr_in6 s6;
-+ } address;
-+ struct sockaddr *sap = &address.sa;
- socklen_t salen = sizeof(address);
- struct pmap nfs_pmap, mnt_pmap;
-+ sa_family_t family;
-
- if (!nfs_options2pmap(options, &nfs_pmap, &mnt_pmap)) {
- nfs_error(_("%s: bad mount options"), progname);
-@@ -189,8 +194,10 @@ static int nfs_umount_do_umnt(struct mount_options *options,
- return EX_FAIL;
- }
-
-- if (nfs_name_to_address(*hostname, sap, &salen) == 0)
-- /* nfs_name_to_address reports any errors */
-+ if (!nfs_mount_proto_family(options, &family))
-+ return 0;
-+ if (!nfs_lookup(*hostname, family, sap, &salen))
-+ /* nfs_lookup reports any errors */
- return EX_FAIL;
-
- if (nfs_advise_umount(sap, salen, &mnt_pmap, dirname) == 0)
-diff --git a/utils/mount/stropts.c b/utils/mount/stropts.c
-index b595649..57a4b32 100644
---- a/utils/mount/stropts.c
-+++ b/utils/mount/stropts.c
-@@ -35,9 +35,11 @@
- #include
- #include
-
-+#include "sockaddr.h"
- #include "xcommon.h"
- #include "mount.h"
- #include "nls.h"
-+#include "nfsrpc.h"
- #include "mount_constants.h"
- #include "stropts.h"
- #include "error.h"
-@@ -81,7 +83,7 @@ struct nfsmount_info {
- *node, /* mounted-on dir */
- *type; /* "nfs" or "nfs4" */
- char *hostname; /* server's hostname */
-- struct sockaddr_storage address; /* server's address */
-+ union nfs_sockaddr address;
- socklen_t salen; /* size of server's address */
-
- struct mount_options *options; /* parsed mount options */
-@@ -204,9 +206,9 @@ static int nfs_append_clientaddr_option(const struct sockaddr *sap,
- socklen_t salen,
- struct mount_options *options)
- {
-- struct sockaddr_storage dummy;
-- struct sockaddr *my_addr = (struct sockaddr *)&dummy;
-- socklen_t my_len = sizeof(dummy);
-+ union nfs_sockaddr address;
-+ struct sockaddr *my_addr = &address.sa;
-+ socklen_t my_len = sizeof(address);
-
- if (po_contains(options, "clientaddr") == PO_FOUND)
- return 1;
-@@ -218,21 +220,33 @@ static int nfs_append_clientaddr_option(const struct sockaddr *sap,
- }
-
- /*
-- * Resolve the 'mounthost=' hostname and append a new option using
-- * the resulting address.
-+ * Determine whether to append a 'mountaddr=' option. The option is needed if:
-+ *
-+ * 1. "mounthost=" was specified, or
-+ * 2. The address families for proto= and mountproto= are different.
- */
--static int nfs_fix_mounthost_option(struct mount_options *options)
-+static int nfs_fix_mounthost_option(struct mount_options *options,
-+ const char *nfs_hostname)
- {
-- struct sockaddr_storage dummy;
-- struct sockaddr *sap = (struct sockaddr *)&dummy;
-- socklen_t salen = sizeof(dummy);
-+ union nfs_sockaddr address;
-+ struct sockaddr *sap = &address.sa;
-+ socklen_t salen = sizeof(address);
-+ sa_family_t nfs_family, mnt_family;
- char *mounthost;
-
-+ if (!nfs_nfs_proto_family(options, &nfs_family))
-+ return 0;
-+ if (!nfs_mount_proto_family(options, &mnt_family))
-+ return 0;
-+
- mounthost = po_get(options, "mounthost");
-- if (!mounthost)
-- return 1;
-+ if (mounthost == NULL) {
-+ if (nfs_family == mnt_family)
-+ return 1;
-+ mounthost = (char *)nfs_hostname;
-+ }
-
-- if (!nfs_name_to_address(mounthost, sap, &salen)) {
-+ if (!nfs_lookup(mounthost, mnt_family, sap, &salen)) {
- nfs_error(_("%s: unable to determine mount server's address"),
- progname);
- return 0;
-@@ -319,13 +333,16 @@ static int nfs_set_version(struct nfsmount_info *mi)
- */
- static int nfs_validate_options(struct nfsmount_info *mi)
- {
-- struct sockaddr *sap = (struct sockaddr *)&mi->address;
-+ struct sockaddr *sap = &mi->address.sa;
-+ sa_family_t family;
-
- if (!nfs_parse_devname(mi->spec, &mi->hostname, NULL))
- return 0;
-
-+ if (!nfs_nfs_proto_family(mi->options, &family))
-+ return 0;
- mi->salen = sizeof(mi->address);
-- if (!nfs_name_to_address(mi->hostname, sap, &mi->salen))
-+ if (!nfs_lookup(mi->hostname, family, sap, &mi->salen))
- return 0;
-
- if (!nfs_set_version(mi))
-@@ -371,10 +388,13 @@ static int nfs_extract_server_addresses(struct mount_options *options,
- }
-
- static int nfs_construct_new_options(struct mount_options *options,
-+ struct sockaddr *nfs_saddr,
- struct pmap *nfs_pmap,
-+ struct sockaddr *mnt_saddr,
- struct pmap *mnt_pmap)
- {
- char new_option[64];
-+ char *netid;
-
- po_remove_all(options, "nfsprog");
- po_remove_all(options, "mountprog");
-@@ -391,20 +411,14 @@ static int nfs_construct_new_options(struct mount_options *options,
- po_remove_all(options, "proto");
- po_remove_all(options, "udp");
- po_remove_all(options, "tcp");
-- switch (nfs_pmap->pm_prot) {
-- case IPPROTO_TCP:
-- snprintf(new_option, sizeof(new_option) - 1,
-- "proto=tcp");
-- if (po_append(options, new_option) == PO_FAILED)
-- return 0;
-- break;
-- case IPPROTO_UDP:
-- snprintf(new_option, sizeof(new_option) - 1,
-- "proto=udp");
-- if (po_append(options, new_option) == PO_FAILED)
-- return 0;
-- break;
-- }
-+ netid = nfs_get_netid(nfs_saddr->sa_family, nfs_pmap->pm_prot);
-+ if (netid == NULL)
-+ return 0;
-+ snprintf(new_option, sizeof(new_option) - 1,
-+ "proto=%s", netid);
-+ free(netid);
-+ if (po_append(options, new_option) == PO_FAILED)
-+ return 0;
-
- po_remove_all(options, "port");
- if (nfs_pmap->pm_port != NFS_PORT) {
-@@ -421,20 +435,14 @@ static int nfs_construct_new_options(struct mount_options *options,
- return 0;
-
- po_remove_all(options, "mountproto");
-- switch (mnt_pmap->pm_prot) {
-- case IPPROTO_TCP:
-- snprintf(new_option, sizeof(new_option) - 1,
-- "mountproto=tcp");
-- if (po_append(options, new_option) == PO_FAILED)
-- return 0;
-- break;
-- case IPPROTO_UDP:
-- snprintf(new_option, sizeof(new_option) - 1,
-- "mountproto=udp");
-- if (po_append(options, new_option) == PO_FAILED)
-- return 0;
-- break;
-- }
-+ netid = nfs_get_netid(mnt_saddr->sa_family, mnt_pmap->pm_prot);
-+ if (netid == NULL)
-+ return 0;
-+ snprintf(new_option, sizeof(new_option) - 1,
-+ "mountproto=%s", netid);
-+ free(netid);
-+ if (po_append(options, new_option) == PO_FAILED)
-+ return 0;
-
- po_remove_all(options, "mountport");
- snprintf(new_option, sizeof(new_option) - 1,
-@@ -461,12 +469,12 @@ static int nfs_construct_new_options(struct mount_options *options,
- static int
- nfs_rewrite_pmap_mount_options(struct mount_options *options)
- {
-- struct sockaddr_storage nfs_address;
-- struct sockaddr *nfs_saddr = (struct sockaddr *)&nfs_address;
-+ union nfs_sockaddr nfs_address;
-+ struct sockaddr *nfs_saddr = &nfs_address.sa;
- socklen_t nfs_salen = sizeof(nfs_address);
- struct pmap nfs_pmap;
-- struct sockaddr_storage mnt_address;
-- struct sockaddr *mnt_saddr = (struct sockaddr *)&mnt_address;
-+ union nfs_sockaddr mnt_address;
-+ struct sockaddr *mnt_saddr = &mnt_address.sa;
- socklen_t mnt_salen = sizeof(mnt_address);
- struct pmap mnt_pmap;
- char *option;
-@@ -510,7 +518,8 @@ nfs_rewrite_pmap_mount_options(struct mount_options *options)
- 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;
- }
-@@ -566,7 +575,7 @@ static int nfs_try_mount_v3v2(struct nfsmount_info *mi)
- return result;
- }
-
-- if (!nfs_fix_mounthost_option(options)) {
-+ if (!nfs_fix_mounthost_option(options, mi->hostname)) {
- errno = EINVAL;
- goto out_fail;
- }
-@@ -601,7 +610,7 @@ out_fail:
- */
- static int nfs_try_mount_v4(struct nfsmount_info *mi)
- {
-- struct sockaddr *sap = (struct sockaddr *)&mi->address;
-+ struct sockaddr *sap = &mi->address.sa;
- struct mount_options *options = po_dup(mi->options);
- int result = 0;
-
-@@ -611,6 +620,18 @@ static int nfs_try_mount_v4(struct nfsmount_info *mi)
- }
-
- if (mi->version == 0) {
-+ if (po_contains(options, "mounthost") ||
-+ po_contains(options, "mountaddr") ||
-+ po_contains(options, "mountvers") ||
-+ po_contains(options, "mountproto")) {
-+ /*
-+ * Since these mountd options are set assume version 3
-+ * is wanted so error out with EPROTONOSUPPORT so the
-+ * protocol negation starts with v3.
-+ */
-+ errno = EPROTONOSUPPORT;
-+ goto out_fail;
-+ }
- if (po_append(options, "vers=4") == PO_FAILED) {
- errno = EINVAL;
- goto out_fail;
-@@ -656,9 +677,10 @@ static int nfs_try_mount(struct nfsmount_info *mi)
- /*
- * To deal with legacy Linux servers that don't
- * automatically export a pseudo root, retry
-- * ENOENT errors using version 3
-+ * ENOENT errors using version 3. And for
-+ * Linux servers prior to 2.6.25, retry EPERM
- */
-- if (errno != ENOENT)
-+ if (errno != ENOENT && errno != EPERM)
- break;
- }
- }
-diff --git a/utils/mountd/Makefile.am b/utils/mountd/Makefile.am
-index 1e76cf8..eba81fc 100644
---- a/utils/mountd/Makefile.am
-+++ b/utils/mountd/Makefile.am
-@@ -8,7 +8,7 @@ KPREFIX = @kprefix@
- sbin_PROGRAMS = mountd
-
- mountd_SOURCES = mountd.c mount_dispatch.c auth.c rmtab.c cache.c \
-- svc_run.c fsloc.c mountd.h
-+ svc_run.c fsloc.c v4root.c mountd.h
- mountd_LDADD = ../../support/export/libexport.a \
- ../../support/nfs/libnfs.a \
- ../../support/misc/libmisc.a \
-diff --git a/utils/mountd/auth.c b/utils/mountd/auth.c
-index 575f207..13eba70 100644
---- a/utils/mountd/auth.c
-+++ b/utils/mountd/auth.c
-@@ -20,6 +20,7 @@
- #include "exportfs.h"
- #include "mountd.h"
- #include "xmalloc.h"
-+#include "v4root.h"
-
- enum auth_error
- {
-@@ -102,75 +103,91 @@ auth_reload()
- memset(&my_client, 0, sizeof(my_client));
- xtab_export_read();
- check_useipaddr();
-+ v4root_set();
-+
- ++counter;
-
- return counter;
- }
-
-+static char *get_client_hostname(struct sockaddr_in *caller, struct hostent *hp, enum auth_error *error)
-+{
-+ char *n;
-+
-+ if (use_ipaddr)
-+ return strdup(inet_ntoa(caller->sin_addr));
-+ n = client_compose(hp);
-+ *error = unknown_host;
-+ if (!n)
-+ return NULL;
-+ if (*n)
-+ return n;
-+ free(n);
-+ return strdup("DEFAULT");
-+}
-+
-+/* return static nfs_export with details filled in */
- static nfs_export *
--auth_authenticate_internal(char *what, struct sockaddr_in *caller,
-+auth_authenticate_newcache(char *what, struct sockaddr_in *caller,
- char *path, struct hostent *hp,
- enum auth_error *error)
- {
-- nfs_export *exp;
-+ nfs_export *exp;
-+ int i;
-
-- if (new_cache) {
-- int i;
-- /* return static nfs_export with details filled in */
-- char *n;
-- free(my_client.m_hostname);
-- if (use_ipaddr) {
-- my_client.m_hostname =
-- strdup(inet_ntoa(caller->sin_addr));
-- } else {
-- n = client_compose(hp);
-- *error = unknown_host;
-- if (!n)
-- my_client.m_hostname = NULL;
-- else if (*n)
-- my_client.m_hostname = n;
-- else {
-- free(n);
-- my_client.m_hostname = strdup("DEFAULT");
-- }
-+ free(my_client.m_hostname);
-+
-+ my_client.m_hostname = get_client_hostname(caller, hp, error);
-+ if (my_client.m_hostname == NULL)
-+ return NULL;
-+
-+ my_client.m_naddr = 1;
-+ my_client.m_addrlist[0] = caller->sin_addr;
-+ my_exp.m_client = &my_client;
-+
-+ exp = NULL;
-+ for (i = 0; !exp && i < MCL_MAXTYPES; i++)
-+ for (exp = exportlist[i].p_head; exp; exp = exp->m_next) {
-+ if (strcmp(path, exp->m_export.e_path))
-+ continue;
-+ if (!use_ipaddr && !client_member(my_client.m_hostname, exp->m_client->m_hostname))
-+ continue;
-+ if (use_ipaddr && !client_check(exp->m_client, hp))
-+ continue;
-+ break;
- }
-- if (my_client.m_hostname == NULL)
-- return NULL;
-- my_client.m_naddr = 1;
-- my_client.m_addrlist[0] = caller->sin_addr;
-- my_exp.m_client = &my_client;
--
-- exp = NULL;
-- for (i = 0; !exp && i < MCL_MAXTYPES; i++)
-- for (exp = exportlist[i].p_head; exp; exp = exp->m_next) {
-- if (strcmp(path, exp->m_export.e_path))
-- continue;
-- if (!use_ipaddr && !client_member(my_client.m_hostname, exp->m_client->m_hostname))
-- continue;
-- if (use_ipaddr && !client_check(exp->m_client, hp))
-- continue;
-- break;
-- }
-- *error = not_exported;
-- if (!exp)
-- return exp;
-+ *error = not_exported;
-+ if (!exp)
-+ return NULL;
-
-- my_exp.m_export = exp->m_export;
-- exp = &my_exp;
-+ my_exp.m_export = exp->m_export;
-+ exp = &my_exp;
-+ return exp;
-+}
-+
-+static nfs_export *
-+auth_authenticate_internal(char *what, struct sockaddr_in *caller,
-+ char *path, struct hostent *hp,
-+ enum auth_error *error)
-+{
-+ nfs_export *exp;
-
-+ if (new_cache) {
-+ exp = auth_authenticate_newcache(what, caller, path, hp, error);
-+ if (!exp)
-+ return NULL;
- } else {
- if (!(exp = export_find(hp, path))) {
- *error = no_entry;
- return NULL;
- }
-- if (!exp->m_mayexport) {
-- *error = not_exported;
-- return NULL;
-- }
-+ }
-+ if (exp->m_export.e_flags & NFSEXP_V4ROOT) {
-+ *error = no_entry;
-+ return NULL;
- }
- if (!(exp->m_export.e_flags & NFSEXP_INSECURE_PORT) &&
-- (ntohs(caller->sin_port) < IPPORT_RESERVED/2 ||
-- ntohs(caller->sin_port) >= IPPORT_RESERVED)) {
-+ ntohs(caller->sin_port) >= IPPORT_RESERVED) {
- *error = illegal_port;
- return NULL;
- }
-diff --git a/utils/mountd/cache.c b/utils/mountd/cache.c
-index e4e2f22..d63e10a 100644
---- a/utils/mountd/cache.c
-+++ b/utils/mountd/cache.c
-@@ -614,73 +614,54 @@ static int dump_to_cache(FILE *f, char *domain, char *path, struct exportent *ex
- return qword_eol(f);
- }
-
--void nfsd_export(FILE *f)
-+static int is_subdirectory(char *subpath, char *path)
- {
-- /* requests are:
-- * domain path
-- * determine export options and return:
-- * domain path expiry flags anonuid anongid fsid
-- */
--
-- char *cp;
-- int i;
-- char *dom, *path;
-- nfs_export *exp, *found = NULL;
-- int found_type = 0;
-- struct in_addr addr;
-- struct hostent *he = NULL;
--
--
-- if (readline(fileno(f), &lbuf, &lbuflen) != 1)
-- return;
-+ int l = strlen(path);
-
-- xlog(D_CALL, "nfsd_export: inbuf '%s'", lbuf);
-+ return strcmp(subpath, path) == 0
-+ || (strncmp(subpath, path, l) == 0 && path[l] == '/');
-+}
-
-- cp = lbuf;
-- dom = malloc(strlen(cp));
-- path = malloc(strlen(cp));
-+static int path_matches(nfs_export *exp, char *path)
-+{
-+ if (exp->m_export.e_flags & NFSEXP_CROSSMOUNT)
-+ return is_subdirectory(path, exp->m_export.e_path);
-+ return strcmp(path, exp->m_export.e_path) == 0;
-+}
-
-- if (!dom || !path)
-- goto out;
-+static int client_matches(nfs_export *exp, char *dom, struct hostent *he)
-+{
-+ if (use_ipaddr)
-+ return client_check(exp->m_client, he);
-+ return client_member(dom, exp->m_client->m_hostname);
-+}
-
-- if (qword_get(&cp, dom, strlen(lbuf)) <= 0)
-- goto out;
-- if (qword_get(&cp, path, strlen(lbuf)) <= 0)
-- goto out;
-+static int export_matches(nfs_export *exp, char *dom, char *path, struct hostent *he)
-+{
-+ return path_matches(exp, path) && client_matches(exp, dom, he);
-+}
-
-- auth_reload();
-+static nfs_export *lookup_export(char *dom, char *path, struct hostent *he)
-+{
-+ nfs_export *exp;
-+ nfs_export *found = NULL;
-+ int found_type = 0;
-+ int i;
-
-- /* now find flags for this export point in this domain */
- for (i=0 ; i < MCL_MAXTYPES; i++) {
- for (exp = exportlist[i].p_head; exp; exp = exp->m_next) {
-- if (!use_ipaddr && !client_member(dom, exp->m_client->m_hostname))
-- continue;
-- if (exp->m_export.e_flags & NFSEXP_CROSSMOUNT) {
-- /* if path is a mountpoint below e_path, then OK */
-- int l = strlen(exp->m_export.e_path);
-- if (strcmp(path, exp->m_export.e_path) == 0 ||
-- (strncmp(path, exp->m_export.e_path, l) == 0 &&
-- path[l] == '/' &&
-- is_mountpoint(path)))
-- /* ok */;
-- else
-- continue;
-- } else if (strcmp(path, exp->m_export.e_path) != 0)
-+ if (!export_matches(exp, dom, path, he))
- continue;
-- if (use_ipaddr) {
-- if (he == NULL) {
-- if (!inet_aton(dom, &addr))
-- goto out;
-- he = client_resolve(addr);
-- }
-- if (!client_check(exp->m_client, he))
-- continue;
-- }
- if (!found) {
- found = exp;
- found_type = i;
- continue;
- }
-+
-+ /* Always prefer non-V4ROOT mounts */
-+ if (found->m_export.e_flags & NFSEXP_V4ROOT)
-+ continue;
-+
- /* If one is a CROSSMOUNT, then prefer the longest path */
- if (((found->m_export.e_flags & NFSEXP_CROSSMOUNT) ||
- (exp->m_export.e_flags & NFSEXP_CROSSMOUNT)) &&
-@@ -703,6 +684,50 @@ void nfsd_export(FILE *f)
- }
- }
- }
-+ return found;
-+}
-+
-+void nfsd_export(FILE *f)
-+{
-+ /* requests are:
-+ * domain path
-+ * determine export options and return:
-+ * domain path expiry flags anonuid anongid fsid
-+ */
-+
-+ char *cp;
-+ char *dom, *path;
-+ nfs_export *found = NULL;
-+ struct in_addr addr;
-+ struct hostent *he = NULL;
-+
-+
-+ if (readline(fileno(f), &lbuf, &lbuflen) != 1)
-+ return;
-+
-+ xlog(D_CALL, "nfsd_export: inbuf '%s'", lbuf);
-+
-+ cp = lbuf;
-+ dom = malloc(strlen(cp));
-+ path = malloc(strlen(cp));
-+
-+ if (!dom || !path)
-+ goto out;
-+
-+ if (qword_get(&cp, dom, strlen(lbuf)) <= 0)
-+ goto out;
-+ if (qword_get(&cp, path, strlen(lbuf)) <= 0)
-+ goto out;
-+
-+ auth_reload();
-+
-+ if (use_ipaddr) {
-+ if (!inet_aton(dom, &addr))
-+ goto out;
-+ he = client_resolve(addr);
-+ }
-+
-+ found = lookup_export(dom, path, he);
-
- if (found) {
- if (dump_to_cache(f, dom, path, &found->m_export) < 0) {
-diff --git a/utils/mountd/mountd.c b/utils/mountd/mountd.c
-index 888fd8c..a0a1f2d 100644
---- a/utils/mountd/mountd.c
-+++ b/utils/mountd/mountd.c
-@@ -509,12 +509,89 @@ get_rootfh(struct svc_req *rqstp, dirpath *path, nfs_export **expret,
- return fh;
- }
-
-+static void remove_all_clients(exportnode *e)
-+{
-+ struct groupnode *g, *ng;
-+
-+ for (g = e->ex_groups; g; g = ng) {
-+ ng = g->gr_next;
-+ xfree(g->gr_name);
-+ xfree(g);
-+ }
-+ e->ex_groups = NULL;
-+}
-+
-+static void free_exportlist(exports *elist)
-+{
-+ struct exportnode *e, *ne;
-+
-+ for (e = *elist; e != NULL; e = ne) {
-+ ne = e->ex_next;
-+ remove_all_clients(e);
-+ xfree(e->ex_dir);
-+ xfree(e);
-+ }
-+ *elist = NULL;
-+}
-+
-+static void prune_clients(nfs_export *exp, struct exportnode *e)
-+{
-+ struct hostent *hp;
-+ struct groupnode *c, **cp;
-+
-+ cp = &e->ex_groups;
-+ while ((c = *cp) != NULL) {
-+ if (client_gettype(c->gr_name) == MCL_FQDN
-+ && (hp = gethostbyname(c->gr_name))) {
-+ hp = hostent_dup(hp);
-+ if (client_check(exp->m_client, hp)) {
-+ *cp = c->gr_next;
-+ xfree(c->gr_name);
-+ xfree(c);
-+ xfree (hp);
-+ continue;
-+ }
-+ xfree (hp);
-+ }
-+ cp = &(c->gr_next);
-+ }
-+}
-+
-+static exportnode *lookup_or_create_elist_entry(exports *elist, nfs_export *exp)
-+{
-+ exportnode *e;
-+
-+ for (e = *elist; e != NULL; e = e->ex_next) {
-+ if (!strcmp(exp->m_export.e_path, e->ex_dir))
-+ return e;
-+ }
-+ e = xmalloc(sizeof(*e));
-+ e->ex_next = *elist;
-+ e->ex_groups = NULL;
-+ e->ex_dir = xstrdup(exp->m_export.e_path);
-+ *elist = e;
-+ return e;
-+}
-+
-+static void insert_group(struct exportnode *e, char *newname)
-+{
-+ struct groupnode *g;
-+
-+ for (g = e->ex_groups; g; g = g->gr_next)
-+ if (strcmp(g->gr_name, newname))
-+ return;
-+
-+ g = xmalloc(sizeof(*g));
-+ g->gr_name = xstrdup(newname);
-+ g->gr_next = e->ex_groups;
-+ e->ex_groups = g;
-+}
-+
- static exports
- get_exportlist(void)
- {
- static exports elist = NULL;
-- struct exportnode *e, *ne;
-- struct groupnode *g, *ng, *c, **cp;
-+ struct exportnode *e;
- nfs_export *exp;
- int i;
- static unsigned int ecounter;
-@@ -526,77 +603,26 @@ get_exportlist(void)
-
- ecounter = acounter;
-
-- for (e = elist; e != NULL; e = ne) {
-- ne = e->ex_next;
-- for (g = e->ex_groups; g != NULL; g = ng) {
-- ng = g->gr_next;
-- xfree(g->gr_name);
-- xfree(g);
-- }
-- xfree(e->ex_dir);
-- xfree(e);
-- }
-- elist = NULL;
-+ free_exportlist(&elist);
-
- for (i = 0; i < MCL_MAXTYPES; i++) {
- for (exp = exportlist[i].p_head; exp; exp = exp->m_next) {
-- for (e = elist; e != NULL; e = e->ex_next) {
-- if (!strcmp(exp->m_export.e_path, e->ex_dir))
-- break;
-- }
-- if (!e) {
-- e = (struct exportnode *) xmalloc(sizeof(*e));
-- e->ex_next = elist;
-- e->ex_groups = NULL;
-- e->ex_dir = xstrdup(exp->m_export.e_path);
-- elist = e;
-- }
--
-- /* We need to check if we should remove
-- previous ones. */
-- if (i == MCL_ANONYMOUS && e->ex_groups) {
-- for (g = e->ex_groups; g; g = ng) {
-- ng = g->gr_next;
-- xfree(g->gr_name);
-- xfree(g);
-- }
-- e->ex_groups = NULL;
-+ /* Don't show pseudo exports */
-+ if (exp->m_export.e_flags & NFSEXP_V4ROOT)
- continue;
-- }
--
-- if (i != MCL_FQDN && e->ex_groups) {
-- struct hostent *hp;
-+ e = lookup_or_create_elist_entry(&elist, exp);
-
-- cp = &e->ex_groups;
-- while ((c = *cp) != NULL) {
-- if (client_gettype (c->gr_name) == MCL_FQDN
-- && (hp = gethostbyname(c->gr_name))) {
-- hp = hostent_dup (hp);
-- if (client_check(exp->m_client, hp)) {
-- *cp = c->gr_next;
-- xfree(c->gr_name);
-- xfree(c);
-- xfree (hp);
-+ /* exports to "*" absorb any others */
-+ if (i == MCL_ANONYMOUS && e->ex_groups) {
-+ remove_all_clients(e);
- continue;
-- }
-- xfree (hp);
-- }
-- cp = &(c->gr_next);
-- }
- }
-+ /* non-FQDN's absorb FQDN's they contain: */
-+ if (i != MCL_FQDN && e->ex_groups)
-+ prune_clients(exp, e);
-
-- if (exp->m_export.e_hostname [0] != '\0') {
-- for (g = e->ex_groups; g; g = g->gr_next)
-- if (strcmp (exp->m_export.e_hostname,
-- g->gr_name) == 0)
-- break;
-- if (g)
-- continue;
-- g = (struct groupnode *) xmalloc(sizeof(*g));
-- g->gr_name = xstrdup(exp->m_export.e_hostname);
-- g->gr_next = e->ex_groups;
-- e->ex_groups = g;
-- }
-+ if (exp->m_export.e_hostname[0] != '\0')
-+ insert_group(e, exp->m_export.e_hostname);
- }
- }
-
-diff --git a/utils/mountd/rmtab.c b/utils/mountd/rmtab.c
-index c371f8d..b028529 100644
---- a/utils/mountd/rmtab.c
-+++ b/utils/mountd/rmtab.c
-@@ -143,23 +143,16 @@ mountlist_del_all(struct sockaddr_in *sin)
- return;
- if (!(hp = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET))) {
- xlog(L_ERROR, "can't get hostname of %s", inet_ntoa(addr));
-- xfunlock(lockid);
-- return;
-+ goto out_unlock;
- }
-- else
-- hp = hostent_dup (hp);
-+ hp = hostent_dup (hp);
-+
-+ if (!setrmtabent("r"))
-+ goto out_free;
-+
-+ if (!(fp = fsetrmtabent(_PATH_RMTABTMP, "w")))
-+ goto out_close;
-
-- if (!setrmtabent("r")) {
-- xfunlock(lockid);
-- free (hp);
-- return;
-- }
-- if (!(fp = fsetrmtabent(_PATH_RMTABTMP, "w"))) {
-- endrmtabent();
-- xfunlock(lockid);
-- free (hp);
-- return;
-- }
- while ((rep = getrmtabent(1, NULL)) != NULL) {
- if (strcmp(rep->r_client, hp->h_name) == 0 &&
- (exp = auth_authenticate("umountall", sin, rep->r_path)))
-@@ -170,10 +163,13 @@ mountlist_del_all(struct sockaddr_in *sin)
- xlog(L_ERROR, "couldn't rename %s to %s",
- _PATH_RMTABTMP, _PATH_RMTAB);
- }
-- endrmtabent(); /* close & unlink */
- fendrmtabent(fp);
-- xfunlock(lockid);
-+out_close:
-+ endrmtabent(); /* close & unlink */
-+out_free:
- free (hp);
-+out_unlock:
-+ xfunlock(lockid);
- }
-
- mountlist
-diff --git a/utils/mountd/v4root.c b/utils/mountd/v4root.c
-new file mode 100644
-index 0000000..7fd6af3
---- /dev/null
-+++ b/utils/mountd/v4root.c
-@@ -0,0 +1,196 @@
-+/*
-+ * Copyright (C) 2009 Red Hat
-+ *
-+ * support/export/v4root.c
-+ *
-+ * Routines used to support NFSv4 pseudo roots
-+ *
-+ */
-+
-+#ifdef HAVE_CONFIG_H
-+#include
-+#endif
-+
-+#include
-+#include
-+#include
-+#include
-+#include
-+#include
-+
-+#include
-+#include
-+
-+#include "xlog.h"
-+#include "exportfs.h"
-+#include "nfslib.h"
-+#include "misc.h"
-+#include "v4root.h"
-+
-+int v4root_needed;
-+
-+static nfs_export pseudo_root = {
-+ .m_next = NULL,
-+ .m_client = NULL,
-+ .m_export = {
-+ .e_hostname = "*",
-+ .e_path = "/",
-+ .e_flags = NFSEXP_READONLY | NFSEXP_ROOTSQUASH
-+ | NFSEXP_NOSUBTREECHECK | NFSEXP_FSID
-+ | NFSEXP_V4ROOT,
-+ .e_anonuid = 65534,
-+ .e_anongid = 65534,
-+ .e_squids = NULL,
-+ .e_nsquids = 0,
-+ .e_sqgids = NULL,
-+ .e_nsqgids = 0,
-+ .e_fsid = 0,
-+ .e_mountpoint = NULL,
-+ },
-+ .m_exported = 0,
-+ .m_xtabent = 1,
-+ .m_mayexport = 1,
-+ .m_changed = 0,
-+ .m_warned = 0,
-+};
-+
-+void set_pseudofs_security(struct exportent *pseudo, struct exportent *source)
-+{
-+ struct sec_entry *se;
-+ int i;
-+
-+ if (source->e_flags & NFSEXP_INSECURE_PORT)
-+ pseudo->e_flags |= NFSEXP_INSECURE_PORT;
-+ for (se = source->e_secinfo; se->flav; se++) {
-+ struct sec_entry *new;
-+
-+ i = secinfo_addflavor(se->flav, pseudo);
-+ new = &pseudo->e_secinfo[i];
-+
-+ if (se->flags & NFSEXP_INSECURE_PORT)
-+ new->flags |= NFSEXP_INSECURE_PORT;
-+ }
-+}
-+
-+/*
-+ * Create a pseudo export
-+ */
-+static struct exportent *
-+v4root_create(char *path, nfs_export *export)
-+{
-+ nfs_export *exp;
-+ struct exportent eep;
-+ struct exportent *curexp = &export->m_export;
-+
-+ dupexportent(&eep, &pseudo_root.m_export);
-+ eep.e_hostname = strdup(curexp->e_hostname);
-+ strncpy(eep.e_path, path, sizeof(eep.e_path));
-+ if (strcmp(path, "/") != 0)
-+ eep.e_flags &= ~NFSEXP_FSID;
-+ set_pseudofs_security(&eep, curexp);
-+ exp = export_create(&eep, 0);
-+ if (exp == NULL)
-+ return NULL;
-+ xlog(D_CALL, "v4root_create: path '%s'", exp->m_export.e_path);
-+ return &exp->m_export;
-+}
-+
-+/*
-+ * Make sure the kernel has pseudo root support.
-+ */
-+static int
-+v4root_support(void)
-+{
-+ struct export_features *ef;
-+ static int warned = 0;
-+
-+ ef = get_export_features();
-+
-+ if (ef->flags & NFSEXP_V4ROOT)
-+ return 1;
-+ if (!warned) {
-+ xlog(L_WARNING, "Kernel does not have pseudo root support.");
-+ xlog(L_WARNING, "NFS v4 mounts will be disabled unless fsid=0");
-+ xlog(L_WARNING, "is specfied in /etc/exports file.");
-+ warned++;
-+ }
-+ return 0;
-+}
-+
-+int pseudofs_update(char *hostname, char *path, nfs_export *source)
-+{
-+ nfs_export *exp;
-+
-+ exp = export_lookup(hostname, path, 0);
-+ if (exp && !(exp->m_export.e_flags & NFSEXP_V4ROOT))
-+ return 0;
-+ if (!exp) {
-+ if (v4root_create(path, source) == NULL) {
-+ xlog(L_WARNING, "v4root_set: Unable to create "
-+ "pseudo export for '%s'", path);
-+ return -ENOMEM;
-+ }
-+ return 0;
-+ }
-+ /* Update an existing V4ROOT export: */
-+ set_pseudofs_security(&exp->m_export, &source->m_export);
-+ return 0;
-+}
-+
-+static int v4root_add_parents(nfs_export *exp)
-+{
-+ char *hostname = exp->m_export.e_hostname;
-+ char *path;
-+ char *ptr;
-+
-+ path = strdup(exp->m_export.e_path);
-+ if (!path)
-+ return -ENOMEM;
-+ for (ptr = path + 1; ptr; ptr = strchr(ptr, '/')) {
-+ int ret;
-+ char saved;
-+
-+ saved = *ptr;
-+ *ptr = '\0';
-+ ret = pseudofs_update(hostname, path, exp);
-+ if (ret)
-+ return ret;
-+ *ptr = saved;
-+ ptr++;
-+ }
-+ free(path);
-+ return 0;
-+}
-+
-+/*
-+ * Create pseudo exports by running through the real export
-+ * looking at the components of the path that make up the export.
-+ * Those path components, if not exported, will become pseudo
-+ * exports allowing them to be found when the kernel does an upcall
-+ * looking for components of the v4 mount.
-+ */
-+void
-+v4root_set()
-+{
-+ nfs_export *exp;
-+ int i, ret;
-+
-+ if (!v4root_needed)
-+ return;
-+ if (!v4root_support())
-+ return;
-+
-+ for (i = 0; i < MCL_MAXTYPES; i++) {
-+ for (exp = exportlist[i].p_head; exp; exp = exp->m_next) {
-+ if (exp->m_export.e_flags & NFSEXP_V4ROOT)
-+ /*
-+ * We just added this one, so its
-+ * parents are already dealt with!
-+ */
-+ continue;
-+
-+ ret = v4root_add_parents(exp);
-+ /* XXX: error handling! */
-+ }
-+ }
-+}
-diff --git a/utils/nfsd/nfssvc.c b/utils/nfsd/nfssvc.c
-index 12d3253..b8028bb 100644
---- a/utils/nfsd/nfssvc.c
-+++ b/utils/nfsd/nfssvc.c
-@@ -212,7 +212,7 @@ int
- nfssvc_set_sockets(const int family, const unsigned int protobits,
- const char *host, const char *port)
- {
-- struct addrinfo hints = { .ai_flags = AI_PASSIVE | AI_ADDRCONFIG };
-+ struct addrinfo hints = { .ai_flags = AI_PASSIVE };
-
- hints.ai_family = family;
-
-diff --git a/utils/showmount/showmount.c b/utils/showmount/showmount.c
-index 418e8b9..f567093 100644
---- a/utils/showmount/showmount.c
-+++ b/utils/showmount/showmount.c
-@@ -78,29 +78,36 @@ static void usage(FILE *fp, int n)
- exit(n);
- }
-
--static const char *nfs_sm_pgmtbl[] = {
-+static const char *mount_pgm_tbl[] = {
- "showmount",
- "mount",
- "mountd",
- NULL,
- };
-
-+static const rpcvers_t mount_vers_tbl[] = {
-+ MOUNTVERS_NFSV3,
-+ MOUNTVERS_POSIX,
-+ MOUNTVERS,
-+};
-+static const unsigned int max_vers_tblsz =
-+ (sizeof(mount_vers_tbl)/sizeof(mount_vers_tbl[0]));
-+
- /*
- * Generate an RPC client handle connected to the mountd service
- * at @hostname, or die trying.
- *
- * Supports both AF_INET and AF_INET6 server addresses.
- */
--static CLIENT *nfs_get_mount_client(const char *hostname)
-+static CLIENT *nfs_get_mount_client(const char *hostname, rpcvers_t vers)
- {
-- rpcprog_t program = nfs_getrpcbyname(MOUNTPROG, nfs_sm_pgmtbl);
-+ rpcprog_t program = nfs_getrpcbyname(MOUNTPROG, mount_pgm_tbl);
- CLIENT *client;
-
-- client = clnt_create(hostname, program, MOUNTVERS, "tcp");
-+ client = clnt_create(hostname, program, vers, "tcp");
- if (client)
- return client;
--
-- client = clnt_create(hostname, program, MOUNTVERS, "udp");
-+ client = clnt_create(hostname, program, vers, "udp");
- if (client)
- return client;
-
-@@ -123,6 +130,7 @@ int main(int argc, char **argv)
- int i;
- int n;
- int maxlen;
-+ int unsigned vers=0;
- char **dumpv;
-
- program_name = argv[0];
-@@ -185,11 +193,12 @@ int main(int argc, char **argv)
- break;
- }
-
-- mclient = nfs_get_mount_client(hostname);
-+ mclient = nfs_get_mount_client(hostname, mount_vers_tbl[vers]);
- mclient->cl_auth = authunix_create_default();
- total_timeout.tv_sec = TOTAL_TIMEOUT;
- total_timeout.tv_usec = 0;
-
-+again:
- if (eflag) {
- memset(&exportlist, '\0', sizeof(exportlist));
-
-@@ -197,6 +206,13 @@ int main(int argc, char **argv)
- (xdrproc_t) xdr_void, NULL,
- (xdrproc_t) xdr_exports, (caddr_t) &exportlist,
- total_timeout);
-+ if (clnt_stat == RPC_PROGVERSMISMATCH) {
-+ if (++vers < max_vers_tblsz) {
-+ (void)CLNT_CONTROL(mclient, CLSET_VERS,
-+ (void *)&mount_vers_tbl[vers]);
-+ goto again;
-+ }
-+ }
- if (clnt_stat != RPC_SUCCESS) {
- clnt_perror(mclient, "rpc mount export");
- clnt_destroy(mclient);
-@@ -232,6 +248,13 @@ int main(int argc, char **argv)
- (xdrproc_t) xdr_void, NULL,
- (xdrproc_t) xdr_mountlist, (caddr_t) &dumplist,
- total_timeout);
-+ if (clnt_stat == RPC_PROGVERSMISMATCH) {
-+ if (++vers < max_vers_tblsz) {
-+ (void)CLNT_CONTROL(mclient, CLSET_VERS,
-+ (void *)&mount_vers_tbl[vers]);
-+ goto again;
-+ }
-+ }
- if (clnt_stat != RPC_SUCCESS) {
- clnt_perror(mclient, "rpc mount dump");
- clnt_destroy(mclient);
-diff --git a/utils/statd/Makefile.am b/utils/statd/Makefile.am
-index 8a3ba4e..1744791 100644
---- a/utils/statd/Makefile.am
-+++ b/utils/statd/Makefile.am
-@@ -2,31 +2,25 @@
-
- man8_MANS = statd.man sm-notify.man
-
--GENFILES_CLNT = sm_inter_clnt.c
--GENFILES_SVC = sm_inter_svc.c
--GENFILES_XDR = sm_inter_xdr.c
--GENFILES_H = sm_inter.h
--
--GENFILES = $(GENFILES_CLNT) $(GENFILES_SVC) $(GENFILES_XDR) $(GENFILES_H)
--
- RPCPREFIX = rpc.
- KPREFIX = @kprefix@
- sbin_PROGRAMS = statd sm-notify
- dist_sbin_SCRIPTS = start-statd
--statd_SOURCES = callback.c notlist.c log.c misc.c monitor.c \
-+statd_SOURCES = callback.c notlist.c misc.c monitor.c hostname.c \
- simu.c stat.c statd.c svc_run.c rmtcall.c \
-- sm_inter_clnt.c sm_inter_svc.c sm_inter_xdr.c log.h \
-- notlist.h statd.h system.h version.h sm_inter.h
-+ notlist.h statd.h system.h version.h
- sm_notify_SOURCES = sm-notify.c
-
- BUILT_SOURCES = $(GENFILES)
--statd_LDADD = ../../support/export/libexport.a \
-+statd_LDADD = ../../support/nsm/libnsm.a \
- ../../support/nfs/libnfs.a \
- ../../support/misc/libmisc.a \
-- $(LIBWRAP) $(LIBNSL)
--sm_notify_LDADD = $(LIBNSL)
-+ $(LIBWRAP) $(LIBNSL) $(LIBCAP)
-+sm_notify_LDADD = ../../support/nsm/libnsm.a \
-+ ../../support/nfs/libnfs.a \
-+ $(LIBNSL) $(LIBCAP)
-
--EXTRA_DIST = sim_sm_inter.x sm_inter.x $(man8_MANS) COPYRIGHT simulate.c
-+EXTRA_DIST = sim_sm_inter.x $(man8_MANS) COPYRIGHT simulate.c
-
- if CONFIG_RPCGEN
- RPCGEN = $(top_builddir)/tools/rpcgen/rpcgen
-diff --git a/utils/statd/callback.c b/utils/statd/callback.c
-index 8885238..d1cc139 100644
---- a/utils/statd/callback.c
-+++ b/utils/statd/callback.c
-@@ -10,10 +10,9 @@
- #include
- #endif
-
--#include
-+#include
-
- #include "rpcmisc.h"
--#include "misc.h"
- #include "statd.h"
- #include "notlist.h"
-
-@@ -21,30 +20,85 @@
- /* notify_list *cbnl = NULL; ... never used */
-
-
--/*
-+/*
- * Services SM_NOTIFY requests.
-- * Any clients that have asked us to monitor that host are put on
-- * the global callback list, which is processed as soon as statd
-- * returns to svc_run.
-+ *
-+ * When NLM uses an SM_MON request to tell statd to monitor a remote,
-+ * the request contains a "mon_name" argument. This is usually the
-+ * "caller_name" argument of an NLMPROC_LOCK request. On Linux, the
-+ * NLM can send statd the remote's IP address instead of its
-+ * caller_name. The NSM protocol does not allow both the remote's
-+ * caller_name and it's IP address to be sent in the same SM_MON
-+ * request.
-+ *
-+ * The remote's caller_name is useful because it makes it simple
-+ * to identify rebooting remotes by matching the "mon_name" argument
-+ * they sent via an SM_NOTIFY request.
-+ *
-+ * The caller_name string may not be a fully qualified domain name,
-+ * or even registered in the DNS database, however. Having the
-+ * remote's IP address is useful because then there is no ambiguity
-+ * about where to send an SM_NOTIFY after the local system reboots.
-+ *
-+ * Without the actual caller_name, however, statd must use an
-+ * heuristic to match an incoming SM_NOTIFY request to one of the
-+ * hosts it is currently monitoring. The incoming mon_name in an
-+ * SM_NOTIFY address is converted to a list of IP addresses using
-+ * DNS. Each mon_name on statd's monitor list is also converted to
-+ * an address list, and the two lists are checked to see if there is
-+ * a matching address.
-+ *
-+ * There are some risks to this strategy:
-+ *
-+ * 1. The external DNS database is not reliable. It can change
-+ * over time, or the forward and reverse mappings could be
-+ * inconsistent.
-+ *
-+ * 2. If statd's monitor list becomes substantial, finding a match
-+ * can generate a not inconsequential amount of DNS traffic.
-+ *
-+ * 3. statd is a single-threaded service. When DNS becomes slow or
-+ * unresponsive, statd also becomes slow or unresponsive.
-+ *
-+ * 4. If the remote does not have a DNS entry at all (or if the
-+ * remote can resolve itself, but the local host can't resolve
-+ * the remote's hostname), the remote cannot be monitored, and
-+ * therefore NLM locking cannot be provided for that host.
-+ *
-+ * 5. Local DNS resolution can produce different results for the
-+ * mon_name than the results the remote might see for the same
-+ * query, especially if the remote did not send a caller_name
-+ * or mon_name that is a fully qualified domain name.
-+ *
-+ * Note that a caller_name is passed from NFS client to server,
-+ * but the client never knows what mon_name the server might use
-+ * to notify it of a reboot. On Linux, the client extracts the
-+ * server's name from the devname it was passed by the mount
-+ * command. This is often not a fully-qualified domain name.
- */
- void *
- sm_notify_1_svc(struct stat_chge *argp, struct svc_req *rqstp)
- {
- notify_list *lp, *call;
- static char *result = NULL;
-- struct sockaddr_in *sin = nfs_getrpccaller_in(rqstp->rq_xprt);
-- char *ip_addr = xstrdup(inet_ntoa(sin->sin_addr));
-+ struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt);
-+ char ip_addr[INET6_ADDRSTRLEN];
-
-- dprintf(N_DEBUG, "Received SM_NOTIFY from %s, state: %d",
-+ xlog(D_CALL, "Received SM_NOTIFY from %s, state: %d",
- argp->mon_name, argp->state);
-
- /* quick check - don't bother if we're not monitoring anyone */
- if (rtnl == NULL) {
-- note(N_WARNING, "SM_NOTIFY from %s while not monitoring any hosts.",
-+ xlog_warn("SM_NOTIFY from %s while not monitoring any hosts",
- argp->mon_name);
- return ((void *) &result);
- }
-
-+ if (!statd_present_address(sap, ip_addr, sizeof(ip_addr))) {
-+ xlog_warn("Unrecognized sender address");
-+ return ((void *) &result);
-+ }
-+
- /* okir change: statd doesn't remove the remote host from its
- * internal monitor list when receiving an SM_NOTIFY call from
- * it. Lockd will want to continue monitoring the remote host
-@@ -52,8 +106,8 @@ sm_notify_1_svc(struct stat_chge *argp, struct svc_req *rqstp)
- */
- for (lp = rtnl ; lp ; lp = lp->next)
- if (NL_STATE(lp) != argp->state &&
-- (matchhostname(argp->mon_name, lp->dns_name) ||
-- matchhostname(ip_addr, lp->dns_name))) {
-+ (statd_matchhostname(argp->mon_name, lp->dns_name) ||
-+ statd_matchhostname(ip_addr, lp->dns_name))) {
- NL_STATE(lp) = argp->state;
- call = nlist_clone(lp);
- nlist_insert(¬ify, call);
-diff --git a/utils/statd/hostname.c b/utils/statd/hostname.c
-new file mode 100644
-index 0000000..7d704cc
---- /dev/null
-+++ b/utils/statd/hostname.c
-@@ -0,0 +1,284 @@
-+/*
-+ * Copyright 2009 Oracle. All rights reserved.
-+ *
-+ * This file is part of nfs-utils.
-+ *
-+ * nfs-utils is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * nfs-utils is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with nfs-utils. If not, see .
-+ */
-+
-+/*
-+ * NSM for Linux.
-+ */
-+
-+#ifdef HAVE_CONFIG_H
-+#include
-+#endif
-+
-+#include
-+#include
-+
-+#include
-+#include
-+#include
-+#include
-+#include
-+#include
-+
-+#include "sockaddr.h"
-+#include "statd.h"
-+#include "xlog.h"
-+
-+#ifndef HAVE_DECL_AI_ADDRCONFIG
-+#define AI_ADDRCONFIG 0
-+#endif
-+
-+/**
-+ * statd_present_address - convert sockaddr to presentation address
-+ * @sap: pointer to socket address to convert
-+ * @buf: pointer to buffer to fill in
-+ * @buflen: length of buffer
-+ *
-+ * Convert the passed-in sockaddr-style address to presentation format.
-+ * The presentation format address is placed in @buf and is
-+ * '\0'-terminated.
-+ *
-+ * Returns true if successful; otherwise false.
-+ *
-+ * getnameinfo(3) is preferred, since it can parse IPv6 scope IDs.
-+ * An alternate version of statd_present_address() is available to
-+ * handle older glibcs that do not have getnameinfo(3).
-+ */
-+#ifdef HAVE_GETNAMEINFO
-+_Bool
-+statd_present_address(const struct sockaddr *sap, char *buf, const size_t buflen)
-+{
-+ socklen_t salen;
-+ int error;
-+
-+ salen = nfs_sockaddr_length(sap);
-+ if (salen == 0) {
-+ xlog(D_GENERAL, "%s: unsupported address family",
-+ __func__);
-+ return false;
-+ }
-+
-+ error = getnameinfo(sap, salen, buf, (socklen_t)buflen,
-+ NULL, 0, NI_NUMERICHOST);
-+ if (error != 0) {
-+ xlog(D_GENERAL, "%s: getnameinfo(3): %s",
-+ __func__, gai_strerror(error));
-+ return false;
-+ }
-+ return true;
-+}
-+#else /* !HAVE_GETNAMEINFO */
-+_Bool
-+statd_present_address(const struct sockaddr *sap, char *buf, const size_t buflen)
-+{
-+ const struct sockaddr_in *sin = (const struct sockaddr_in *)sap;
-+
-+ if (sin->sin_family != AF_INET) {
-+ xlog(D_GENERAL, "%s: unsupported address family", __func__);
-+ return false;
-+ }
-+
-+ /* ensure '\0' termination */
-+ memset(buf, 0, buflen);
-+
-+ if (inet_ntop(AF_INET, (char *)&sin->sin_addr,
-+ buf, (socklen_t)buflen) == NULL) {
-+ xlog(D_GENERAL, "%s: inet_ntop(3): %m", __func__);
-+ return false;
-+ }
-+ return true;
-+}
-+#endif /* !HAVE_GETNAMEINFO */
-+
-+/*
-+ * Look up the hostname; report exceptional errors. Caller must
-+ * call freeaddrinfo(3) if a valid addrinfo is returned.
-+ */
-+__attribute_malloc__
-+static struct addrinfo *
-+get_addrinfo(const char *hostname, const struct addrinfo *hint)
-+{
-+ struct addrinfo *ai = NULL;
-+ int error;
-+
-+ error = getaddrinfo(hostname, NULL, hint, &ai);
-+ switch (error) {
-+ case 0:
-+ return ai;
-+ case EAI_NONAME:
-+ break;
-+ default:
-+ xlog(D_GENERAL, "%s: failed to resolve host %s: %s",
-+ __func__, hostname, gai_strerror(error));
-+ }
-+
-+ return NULL;
-+}
-+
-+#ifdef HAVE_GETNAMEINFO
-+static _Bool
-+get_nameinfo(const struct sockaddr *sap, const socklen_t salen,
-+ /*@out@*/ char *buf, const socklen_t buflen)
-+{
-+ int error;
-+
-+ error = getnameinfo(sap, salen, buf, buflen, NULL, 0, NI_NAMEREQD);
-+ if (error != 0) {
-+ xlog(D_GENERAL, "%s: failed to resolve address: %s",
-+ __func__, gai_strerror(error));
-+ return false;
-+ }
-+
-+ return true;
-+}
-+#else /* !HAVE_GETNAMEINFO */
-+static _Bool
-+get_nameinfo(const struct sockaddr *sap,
-+ __attribute__ ((unused)) const socklen_t salen,
-+ /*@out@*/ char *buf, socklen_t buflen)
-+{
-+ struct sockaddr_in *sin = (struct sockaddr_in *)(char *)sap;
-+ struct hostent *hp;
-+
-+ if (sin->sin_family != AF_INET) {
-+ xlog(D_GENERAL, "%s: unknown address family: %d",
-+ sin->sin_family);
-+ return false;
-+ }
-+
-+ hp = gethostbyaddr((const char *)&(sin->sin_addr.s_addr),
-+ sizeof(struct in_addr), AF_INET);
-+ if (hp == NULL) {
-+ xlog(D_GENERAL, "%s: failed to resolve address: %m", __func__);
-+ return false;
-+ }
-+
-+ strncpy(buf, hp->h_name, (size_t)buflen);
-+ return true;
-+}
-+#endif /* !HAVE_GETNAMEINFO */
-+
-+/**
-+ * statd_canonical_name - choose file name for monitor record files
-+ * @hostname: C string containing hostname or presentation address
-+ *
-+ * Returns a '\0'-terminated ASCII string containing a fully qualified
-+ * canonical hostname, or NULL if @hostname does not have a reverse
-+ * mapping. Caller must free the result with free(3).
-+ *
-+ * Incoming hostnames are looked up to determine the canonical hostname,
-+ * and incoming presentation addresses are converted to canonical
-+ * hostnames.
-+ *
-+ * We won't monitor peers that don't have a reverse map. The canonical
-+ * name gives us a key for our monitor list.
-+ */
-+__attribute_malloc__
-+char *
-+statd_canonical_name(const char *hostname)
-+{
-+ struct addrinfo hint = {
-+#ifdef IPV6_SUPPORTED
-+ .ai_family = AF_UNSPEC,
-+#else /* !IPV6_SUPPORTED */
-+ .ai_family = AF_INET,
-+#endif /* !IPV6_SUPPORTED */
-+ .ai_flags = AI_NUMERICHOST,
-+ .ai_protocol = (int)IPPROTO_UDP,
-+ };
-+ char buf[NI_MAXHOST];
-+ struct addrinfo *ai;
-+
-+ ai = get_addrinfo(hostname, &hint);
-+ if (ai != NULL) {
-+ /* @hostname was a presentation address */
-+ _Bool result;
-+ result = get_nameinfo(ai->ai_addr, ai->ai_addrlen,
-+ buf, (socklen_t)sizeof(buf));
-+ freeaddrinfo(ai);
-+ if (!result)
-+ return NULL;
-+ return strdup(buf);
-+ }
-+
-+ /* @hostname was a hostname */
-+ hint.ai_flags = AI_CANONNAME;
-+ ai = get_addrinfo(hostname, &hint);
-+ if (ai == NULL)
-+ return NULL;
-+ strcpy(buf, ai->ai_canonname);
-+ freeaddrinfo(ai);
-+
-+ return strdup(buf);
-+}
-+
-+/**
-+ * statd_matchhostname - check if two hostnames are equivalent
-+ * @hostname1: C string containing hostname
-+ * @hostname2: C string containing hostname
-+ *
-+ * Returns true if the hostnames are the same, the hostnames resolve
-+ * to the same canonical name, or the hostnames resolve to at least
-+ * one address that is the same. False is returned if the hostnames
-+ * do not match in any of these ways, if either hostname contains
-+ * wildcard characters, if either hostname is a netgroup name, or
-+ * if an error occurs.
-+ */
-+_Bool
-+statd_matchhostname(const char *hostname1, const char *hostname2)
-+{
-+ struct addrinfo *ai1, *ai2, *results1 = NULL, *results2 = NULL;
-+ struct addrinfo hint = {
-+ .ai_family = AF_UNSPEC,
-+ .ai_flags = AI_CANONNAME,
-+ .ai_protocol = (int)IPPROTO_UDP,
-+ };
-+ _Bool result = false;
-+
-+ if (strcasecmp(hostname1, hostname2) == 0) {
-+ result = true;
-+ goto out;
-+ }
-+
-+ results1 = get_addrinfo(hostname1, &hint);
-+ if (results1 == NULL)
-+ goto out;
-+ results2 = get_addrinfo(hostname2, &hint);
-+ if (results2 == NULL)
-+ goto out;
-+
-+ if (strcasecmp(results1->ai_canonname, results2->ai_canonname) == 0) {
-+ result = true;
-+ goto out;
-+ }
-+
-+ for (ai1 = results1; ai1 != NULL; ai1 = ai1->ai_next)
-+ for (ai2 = results2; ai2 != NULL; ai2 = ai2->ai_next)
-+ if (nfs_compare_sockaddr(ai1->ai_addr, ai2->ai_addr)) {
-+ result = true;
-+ break;
-+ }
-+
-+out:
-+ freeaddrinfo(results2);
-+ freeaddrinfo(results1);
-+
-+ xlog(D_CALL, "%s: hostnames %s", __func__,
-+ (result ? "matched" : "did not match"));
-+ return result;
-+}
-diff --git a/utils/statd/log.c b/utils/statd/log.c
-deleted file mode 100644
-index a6ca996..0000000
---- a/utils/statd/log.c
-+++ /dev/null
-@@ -1,95 +0,0 @@
--/*
-- * Copyright (C) 1995 Olaf Kirch
-- * Modified by Jeffrey A. Uphoff, 1995, 1997, 1999.
-- * Modified by H.J. Lu, 1998.
-- * Modified by Lon Hohberger, Oct. 2000
-- *
-- * NSM for Linux.
-- */
--
--/*
-- * log.c - logging functions for lockd/statd
-- * 260295 okir started with simply syslog logging.
-- */
--
--#ifdef HAVE_CONFIG_H
--#include
--#endif
--
--#include
--#include
--#include
--#include
--#include
--#include
--#include
--#include
--#include "log.h"
--#include "statd.h"
--
--static pid_t mypid;
-- /* Turns on logging to console/stderr. */
--#if 0
--static int opt_debug = 0; /* Will be command-line option, eventually */
--#endif
--
--void log_init(void)
--{
-- if (!(run_mode & MODE_LOG_STDERR))
-- openlog(name_p, LOG_PID | LOG_NDELAY, LOG_DAEMON);
--
-- mypid = getpid();
--
-- note(N_WARNING,"Version %s Starting",version_p);
--}
--
--void log_background(void)
--{
-- /* NOP */
--}
--
--void die(char *fmt, ...)
--{
-- char buffer[1024];
-- va_list ap;
--
-- va_start(ap, fmt);
-- vsnprintf (buffer, 1024, fmt, ap);
-- va_end(ap);
-- buffer[1023]=0;
--
-- note(N_FATAL, "%s", buffer);
--
--#ifndef DEBUG
-- exit (2);
--#else
-- abort(); /* make a core */
--#endif
--}
--
--void note(int level, char *fmt, ...)
--{
-- char buffer[1024];
-- va_list ap;
--
-- va_start(ap, fmt);
-- vsnprintf (buffer, 1024, fmt, ap);
-- va_end(ap);
-- buffer[1023]=0;
--
-- if ((!(run_mode & MODE_LOG_STDERR)) && (level < N_DEBUG)) {
-- syslog(level, "%s", buffer);
-- } else if (run_mode & MODE_LOG_STDERR) {
-- /* Log everything, including dprintf() stuff to stderr */
-- time_t now;
-- struct tm * tm;
--
-- time(&now);
-- tm = localtime(&now);
-- fprintf (stderr, "%02d/%02d/%04d %02d:%02d:%02d %s[%d]: %s\n",
-- tm->tm_mon + 1, tm->tm_mday, tm->tm_year + 1900,
-- tm->tm_hour, tm->tm_min, tm->tm_sec,
-- name_p, mypid,
-- buffer);
-- }
--}
-diff --git a/utils/statd/log.h b/utils/statd/log.h
-deleted file mode 100644
-index fc55d3c..0000000
---- a/utils/statd/log.h
-+++ /dev/null
-@@ -1,42 +0,0 @@
--/*
-- * Copyright (C) 1995 Olaf Kirch
-- * Modified by Jeffrey A. Uphoff, 1996, 1997, 1999.
-- * Modified by Lon Hohberger, Oct. 2000
-- *
-- * NSM for Linux.
-- */
--
--/*
-- * logging functionality
-- * 260295 okir
-- */
--
--#ifndef _LOCKD_LOG_H_
--#define _LOCKD_LOG_H_
--
--#include
--
--void log_init(void);
--void log_background(void);
--void log_enable(int facility);
--int log_enabled(int facility);
--void note(int level, char *fmt, ...);
--void die(char *fmt, ...);
--
--/*
-- * Map per-application severity to system severity. What's fatal for
-- * lockd is merely an itching spot from the universe's point of view.
-- */
--#define N_CRIT LOG_CRIT
--#define N_FATAL LOG_ERR
--#define N_ERROR LOG_WARNING
--#define N_WARNING LOG_NOTICE
--#define N_DEBUG LOG_DEBUG
--
--#ifdef DEBUG
--#define dprintf note
--#else
--#define dprintf if (run_mode & MODE_LOG_STDERR) note
--#endif
--
--#endif /* _LOCKD_LOG_H_ */
-diff --git a/utils/statd/misc.c b/utils/statd/misc.c
-index 7256291..f2a086f 100644
---- a/utils/statd/misc.c
-+++ b/utils/statd/misc.c
-@@ -29,8 +29,7 @@ xmalloc (size_t size)
- return ((void *)NULL);
-
- if (!(ptr = malloc (size)))
-- /* SHIT! SHIT! SHIT! */
-- die ("malloc failed");
-+ xlog_err ("malloc failed");
-
- return (ptr);
- }
-@@ -46,32 +45,7 @@ xstrdup (const char *string)
-
- /* Will only fail if underlying malloc() fails (ENOMEM). */
- if (!(result = strdup (string)))
-- die ("strdup failed");
-+ xlog_err ("strdup failed");
-
- return (result);
- }
--
--
--/*
-- * Unlinking a file.
-- */
--void
--xunlink (char *path, char *host)
--{
-- char *tozap;
--
-- tozap = malloc(strlen(path)+strlen(host)+2);
-- if (tozap == NULL) {
-- note(N_ERROR, "xunlink: malloc failed: errno %d (%s)",
-- errno, strerror(errno));
-- return;
-- }
-- sprintf (tozap, "%s/%s", path, host);
--
-- if (unlink (tozap) == -1)
-- note(N_ERROR, "unlink (%s): %s", tozap, strerror (errno));
-- else
-- dprintf (N_DEBUG, "Unlinked %s", tozap);
--
-- free(tozap);
--}
-diff --git a/utils/statd/monitor.c b/utils/statd/monitor.c
-index a2c9e2b..325dfd3 100644
---- a/utils/statd/monitor.c
-+++ b/utils/statd/monitor.c
-@@ -21,34 +21,38 @@
- #include
- #include
-
-+#include "sockaddr.h"
- #include "rpcmisc.h"
--#include "misc.h"
-+#include "nsm.h"
- #include "statd.h"
- #include "notlist.h"
- #include "ha-callout.h"
-
- notify_list * rtnl = NULL; /* Run-time notify list. */
-
--#define LINELEN (4*(8+1)+SM_PRIV_SIZE*2+1)
--
- /*
- * Reject requests from non-loopback addresses in order
- * to prevent attack described in CERT CA-99.05.
-+ *
-+ * Although the kernel contacts the statd service via only IPv4
-+ * transports, the statd service can receive other requests, such
-+ * as SM_NOTIFY, from remote peers via IPv6.
- */
--static int
-+static _Bool
- caller_is_localhost(struct svc_req *rqstp)
- {
-- struct sockaddr_in *sin = nfs_getrpccaller_in(rqstp->rq_xprt);
-- struct in_addr caller;
--
-- caller = sin->sin_addr;
-- if (caller.s_addr != htonl(INADDR_LOOPBACK)) {
-- note(N_WARNING,
-- "Call to statd from non-local host %s",
-- inet_ntoa(caller));
-- return 0;
-- }
-- return 1;
-+ struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt);
-+ char buf[INET6_ADDRSTRLEN];
-+
-+ if (!nfs_is_v4_loopback(sap))
-+ goto out_nonlocal;
-+ return true;
-+
-+out_nonlocal:
-+ if (!statd_present_address(sap, buf, sizeof(buf)))
-+ buf[0] = '\0';
-+ xlog_warn("SM_MON/SM_UNMON call from non-local host %s", buf);
-+ return false;
- }
-
- /*
-@@ -61,13 +65,15 @@ sm_mon_1_svc(struct mon *argp, struct svc_req *rqstp)
- char *mon_name = argp->mon_id.mon_name,
- *my_name = argp->mon_id.my_id.my_name;
- struct my_id *id = &argp->mon_id.my_id;
-- char *path;
- char *cp;
-- int fd;
- notify_list *clnt;
-- struct in_addr my_addr;
-- char *dnsname;
-- struct hostent *hostinfo = NULL;
-+ struct sockaddr_in my_addr = {
-+ .sin_family = AF_INET,
-+ .sin_addr.s_addr = htonl(INADDR_LOOPBACK),
-+ };
-+ char *dnsname = NULL;
-+
-+ xlog(D_CALL, "Received SM_MON for %s from %s", mon_name, my_name);
-
- /* Assume that we'll fail. */
- result.res_stat = STAT_FAIL;
-@@ -79,7 +85,6 @@ sm_mon_1_svc(struct mon *argp, struct svc_req *rqstp)
- */
- if (!caller_is_localhost(rqstp))
- goto failure;
-- my_addr.s_addr = htonl(INADDR_LOOPBACK);
-
- /* 2. Reject any registrations for non-lockd services.
- *
-@@ -92,8 +97,7 @@ sm_mon_1_svc(struct mon *argp, struct svc_req *rqstp)
- if (id->my_prog != 100021 ||
- (id->my_proc != 16 && id->my_proc != 24))
- {
-- note(N_WARNING,
-- "Attempt to register callback to %d/%d",
-+ xlog_warn("Attempt to register callback to %d/%d",
- id->my_prog, id->my_proc);
- goto failure;
- }
-@@ -105,12 +109,9 @@ sm_mon_1_svc(struct mon *argp, struct svc_req *rqstp)
-
- /* must check for /'s in hostname! See CERT's CA-96.09 for details. */
- if (strchr(mon_name, '/') || mon_name[0] == '.') {
-- note(N_CRIT, "SM_MON request for hostname containing '/' "
-+ xlog(L_ERROR, "SM_MON request for hostname containing '/' "
- "or starting '.': %s", mon_name);
-- note(N_CRIT, "POSSIBLE SPOOF/ATTACK ATTEMPT!");
-- goto failure;
-- } else if ((hostinfo = gethostbyname(mon_name)) == NULL) {
-- note(N_WARNING, "gethostbyname error for %s", mon_name);
-+ xlog(L_ERROR, "POSSIBLE SPOOF/ATTACK ATTEMPT!");
- goto failure;
- }
-
-@@ -124,15 +125,13 @@ sm_mon_1_svc(struct mon *argp, struct svc_req *rqstp)
- * Now choose a hostname to use for matching. We cannot
- * really trust much in the incoming NOTIFY, so to make
- * sure that multi-homed hosts work nicely, we get an
-- * FQDN now, and use that for matching
-+ * FQDN now, and use that for matching.
- */
-- hostinfo = gethostbyaddr(hostinfo->h_addr,
-- hostinfo->h_length,
-- hostinfo->h_addrtype);
-- if (hostinfo)
-- dnsname = xstrdup(hostinfo->h_name);
-- else
-- dnsname = xstrdup(my_name);
-+ dnsname = statd_canonical_name(mon_name);
-+ if (dnsname == NULL) {
-+ xlog(L_WARNING, "No canonical hostname found for %s", mon_name);
-+ goto failure;
-+ }
-
- /* Now check to see if this is a duplicate, and warn if so.
- * I will also return STAT_FAIL. (I *think* this is how I should
-@@ -146,18 +145,19 @@ sm_mon_1_svc(struct mon *argp, struct svc_req *rqstp)
- clnt = rtnl;
-
- while ((clnt = nlist_gethost(clnt, mon_name, 0))) {
-- if (matchhostname(NL_MY_NAME(clnt), my_name) &&
-+ if (statd_matchhostname(NL_MY_NAME(clnt), my_name) &&
- NL_MY_PROC(clnt) == id->my_proc &&
- NL_MY_PROG(clnt) == id->my_prog &&
- NL_MY_VERS(clnt) == id->my_vers &&
- memcmp(NL_PRIV(clnt), argp->priv, SM_PRIV_SIZE) == 0) {
- /* Hey! We already know you guys! */
-- dprintf(N_DEBUG,
-+ xlog(D_GENERAL,
- "Duplicate SM_MON request for %s "
- "from procedure on %s",
- mon_name, my_name);
-
- /* But we'll let you pass anyway. */
-+ free(dnsname);
- goto success;
- }
- clnt = NL_NEXT(clnt);
-@@ -168,11 +168,11 @@ sm_mon_1_svc(struct mon *argp, struct svc_req *rqstp)
- * doesn't fail. (I should probably fix this assumption.)
- */
- if (!(clnt = nlist_new(my_name, mon_name, 0))) {
-- note(N_WARNING, "out of memory");
-+ free(dnsname);
-+ xlog_warn("out of memory");
- goto failure;
- }
-
-- NL_ADDR(clnt) = my_addr;
- NL_MY_PROG(clnt) = id->my_prog;
- NL_MY_VERS(clnt) = id->my_vers;
- NL_MY_PROC(clnt) = id->my_proc;
-@@ -182,40 +182,16 @@ sm_mon_1_svc(struct mon *argp, struct svc_req *rqstp)
- /*
- * Now, Create file on stable storage for host.
- */
--
-- path=xmalloc(strlen(SM_DIR)+strlen(dnsname)+2);
-- sprintf(path, "%s/%s", SM_DIR, dnsname);
-- if ((fd = open(path, O_WRONLY|O_SYNC|O_CREAT|O_APPEND,
-- S_IRUSR|S_IWUSR)) < 0) {
-- /* Didn't fly. We won't monitor. */
-- note(N_ERROR, "creat(%s) failed: %s", path, strerror (errno));
-+ if (!nsm_insert_monitored_host(dnsname,
-+ (struct sockaddr *)(char *)&my_addr, argp)) {
- nlist_free(NULL, clnt);
-- free(path);
- goto failure;
- }
-- {
-- char buf[LINELEN + 1 + SM_MAXSTRLEN*2 + 4];
-- char *e;
-- int i;
-- e = buf + sprintf(buf, "%08x %08x %08x %08x ",
-- my_addr.s_addr, id->my_prog,
-- id->my_vers, id->my_proc);
-- for (i=0; ipriv[i]));
-- if (e+1-buf != LINELEN) abort();
-- e += sprintf(e, " %s %s\n", mon_name, my_name);
-- if (write(fd, buf, e-buf) != (e-buf)) {
-- note(N_WARNING, "writing to %s failed: errno %d (%s)",
-- path, errno, strerror(errno));
-- }
-- }
-
-- free(path);
- /* PRC: do the HA callout: */
- ha_callout("add-client", mon_name, my_name, -1);
- nlist_insert(&rtnl, clnt);
-- close(fd);
-- dprintf(N_DEBUG, "MONITORING %s for %s", mon_name, my_name);
-+ xlog(D_GENERAL, "MONITORING %s for %s", mon_name, my_name);
- success:
- result.res_stat = STAT_SUCC;
- /* SUN's sm_inter.x says this should be "state number of local site".
-@@ -232,75 +208,49 @@ sm_mon_1_svc(struct mon *argp, struct svc_req *rqstp)
- return (&result);
-
- failure:
-- note(N_WARNING, "STAT_FAIL to %s for SM_MON of %s", my_name, mon_name);
-+ xlog_warn("STAT_FAIL to %s for SM_MON of %s", my_name, mon_name);
- return (&result);
- }
-
--void load_state(void)
-+static unsigned int
-+load_one_host(const char *hostname,
-+ __attribute__ ((unused)) const struct sockaddr *sap,
-+ const struct mon *m,
-+ __attribute__ ((unused)) const time_t timestamp)
- {
-- DIR *d;
-- struct dirent *de;
-- char buf[LINELEN + 1 + SM_MAXSTRLEN + 2];
--
-- d = opendir(SM_DIR);
-- if (!d)
-- return;
-- while ((de = readdir(d))) {
-- char *path;
-- FILE *f;
-- int p;
--
-- if (de->d_name[0] == '.')
-- continue;
-- path = xmalloc(strlen(SM_DIR)+strlen(de->d_name)+2);
-- sprintf(path, "%s/%s", SM_DIR, de->d_name);
-- f = fopen(path, "r");
-- free(path);
-- if (f == NULL)
-- continue;
-- while (fgets(buf, sizeof(buf), f) != NULL) {
-- int addr, proc, prog, vers;
-- char priv[SM_PRIV_SIZE];
-- char *monname, *myname;
-- char *b;
-- int i;
-- notify_list *clnt;
--
-- buf[sizeof(buf)-1] = 0;
-- b = strchr(buf, '\n');
-- if (b) *b = 0;
-- sscanf(buf, "%x %x %x %x ",
-- &addr, &prog, &vers, &proc);
-- b = buf+36;
-- for (i=0; idns_name = xstrdup(de->d_name);
-- memcpy(NL_PRIV(clnt), priv, SM_PRIV_SIZE);
-- nlist_insert(&rtnl, clnt);
-- }
-- fclose(f);
-+ notify_list *clnt;
-+
-+ clnt = nlist_new(m->mon_id.my_id.my_name,
-+ m->mon_id.mon_name, 0);
-+ if (clnt == NULL)
-+ return 0;
-+
-+ clnt->dns_name = strdup(hostname);
-+ if (clnt->dns_name == NULL) {
-+ nlist_free(NULL, clnt);
-+ return 0;
- }
-- closedir(d);
--}
-
-+ xlog(D_GENERAL, "Adding record for %s to the monitor list...",
-+ hostname);
-
-+ NL_MY_PROG(clnt) = m->mon_id.my_id.my_prog;
-+ NL_MY_VERS(clnt) = m->mon_id.my_id.my_vers;
-+ NL_MY_PROC(clnt) = m->mon_id.my_id.my_proc;
-+ memcpy(NL_PRIV(clnt), m->priv, SM_PRIV_SIZE);
-
-+ nlist_insert(&rtnl, clnt);
-+ return 1;
-+}
-+
-+void load_state(void)
-+{
-+ unsigned int count;
-+
-+ count = nsm_load_monitor_list(load_one_host);
-+ if (count)
-+ xlog(D_GENERAL, "Loaded %u previously monitored hosts");
-+}
-
- /*
- * Services SM_UNMON requests.
-@@ -320,6 +270,8 @@ sm_unmon_1_svc(struct mon_id *argp, struct svc_req *rqstp)
- struct my_id *id = &argp->my_id;
- char *cp;
-
-+ xlog(D_CALL, "Received SM_UNMON for %s from %s", mon_name, my_name);
-+
- result.state = MY_STATE;
-
- if (!caller_is_localhost(rqstp))
-@@ -333,9 +285,8 @@ sm_unmon_1_svc(struct mon_id *argp, struct svc_req *rqstp)
-
- /* Check if we're monitoring anyone. */
- if (rtnl == NULL) {
-- note(N_WARNING,
-- "Received SM_UNMON request from %s for %s while not "
-- "monitoring any hosts.", my_name, argp->mon_name);
-+ xlog_warn("Received SM_UNMON request from %s for %s while not "
-+ "monitoring any hosts", my_name, argp->mon_name);
- return (&result);
- }
- clnt = rtnl;
-@@ -347,18 +298,19 @@ sm_unmon_1_svc(struct mon_id *argp, struct svc_req *rqstp)
- * entry winds up in the list the way I'm currently handling them.)
- */
- while ((clnt = nlist_gethost(clnt, mon_name, 0))) {
-- if (matchhostname(NL_MY_NAME(clnt), my_name) &&
-+ if (statd_matchhostname(NL_MY_NAME(clnt), my_name) &&
- NL_MY_PROC(clnt) == id->my_proc &&
- NL_MY_PROG(clnt) == id->my_prog &&
- NL_MY_VERS(clnt) == id->my_vers) {
- /* Match! */
-- dprintf(N_DEBUG, "UNMONITORING %s for %s",
-+ xlog(D_GENERAL, "UNMONITORING %s for %s",
- mon_name, my_name);
-
- /* PRC: do the HA callout: */
- ha_callout("del-client", mon_name, my_name, -1);
-
-- xunlink(SM_DIR, clnt->dns_name);
-+ nsm_delete_monitored_host(clnt->dns_name,
-+ mon_name, my_name);
- nlist_free(&rtnl, clnt);
-
- return (&result);
-@@ -367,7 +319,7 @@ sm_unmon_1_svc(struct mon_id *argp, struct svc_req *rqstp)
- }
-
- failure:
-- note(N_WARNING, "Received erroneous SM_UNMON request from %s for %s",
-+ xlog_warn("Received erroneous SM_UNMON request from %s for %s",
- my_name, mon_name);
- return (&result);
- }
-@@ -381,13 +333,15 @@ sm_unmon_all_1_svc(struct my_id *argp, struct svc_req *rqstp)
- notify_list *clnt;
- char *my_name = argp->my_name;
-
-+ xlog(D_CALL, "Received SM_UNMON_ALL for %s", my_name);
-+
- if (!caller_is_localhost(rqstp))
- goto failure;
-
- result.state = MY_STATE;
-
- if (rtnl == NULL) {
-- note(N_WARNING, "Received SM_UNMON_ALL request from %s "
-+ xlog_warn("Received SM_UNMON_ALL request from %s "
- "while not monitoring any hosts", my_name);
- return (&result);
- }
-@@ -401,7 +355,7 @@ sm_unmon_all_1_svc(struct my_id *argp, struct svc_req *rqstp)
- char mon_name[SM_MAXSTRLEN + 1];
- notify_list *temp;
-
-- dprintf(N_DEBUG,
-+ xlog(D_GENERAL,
- "UNMONITORING (SM_UNMON_ALL) %s for %s",
- NL_MON_NAME(clnt), NL_MY_NAME(clnt));
- strncpy(mon_name, NL_MON_NAME(clnt),
-@@ -410,7 +364,8 @@ sm_unmon_all_1_svc(struct my_id *argp, struct svc_req *rqstp)
- temp = NL_NEXT(clnt);
- /* PRC: do the HA callout: */
- ha_callout("del-client", mon_name, my_name, -1);
-- xunlink(SM_DIR, clnt->dns_name);
-+ nsm_delete_monitored_host(clnt->dns_name,
-+ mon_name, my_name);
- nlist_free(&rtnl, clnt);
- ++count;
- clnt = temp;
-@@ -419,8 +374,8 @@ sm_unmon_all_1_svc(struct my_id *argp, struct svc_req *rqstp)
- }
-
- if (!count) {
-- dprintf(N_DEBUG, "SM_UNMON_ALL request from %s with no "
-- "SM_MON requests from it.", my_name);
-+ xlog(D_GENERAL, "SM_UNMON_ALL request from %s with no "
-+ "SM_MON requests from it", my_name);
- }
-
- failure:
-diff --git a/utils/statd/notlist.c b/utils/statd/notlist.c
-index 1698c26..0341c15 100644
---- a/utils/statd/notlist.c
-+++ b/utils/statd/notlist.c
-@@ -17,7 +17,6 @@
- #endif
-
- #include
--#include "misc.h"
- #include "statd.h"
- #include "notlist.h"
-
-@@ -190,7 +189,6 @@ nlist_clone(notify_list *entry)
- NL_MY_PROG(new) = NL_MY_PROG(entry);
- NL_MY_VERS(new) = NL_MY_VERS(entry);
- NL_MY_PROC(new) = NL_MY_PROC(entry);
-- NL_ADDR(new) = NL_ADDR(entry);
- memcpy(NL_PRIV(new), NL_PRIV(entry), SM_PRIV_SIZE);
-
- return new;
-@@ -234,7 +232,8 @@ nlist_gethost(notify_list *list, char *host, int myname)
- notify_list *lp;
-
- for (lp = list; lp; lp = lp->next) {
-- if (matchhostname(host, myname? NL_MY_NAME(lp) : NL_MON_NAME(lp)))
-+ if (statd_matchhostname(host,
-+ myname? NL_MY_NAME(lp) : NL_MON_NAME(lp)))
- return lp;
- }
-
-diff --git a/utils/statd/notlist.h b/utils/statd/notlist.h
-index 664c9d8..6ed0da8 100644
---- a/utils/statd/notlist.h
-+++ b/utils/statd/notlist.h
-@@ -12,15 +12,14 @@
- */
- struct notify_list {
- mon mon; /* Big honkin' NSM structure. */
-- struct in_addr addr; /* IP address for callback. */
-- unsigned short port; /* port number for callback */
-+ in_port_t port; /* port number for callback */
- short int times; /* Counter used for various things. */
- int state; /* For storing notified state for callbacks. */
- char *dns_name; /* used for matching incoming
- * NOTIFY requests */
- struct notify_list *next; /* Linked list forward pointer. */
- struct notify_list *prev; /* Linked list backward pointer. */
-- u_int32_t xid; /* XID of MS_NOTIFY RPC call */
-+ uint32_t xid; /* XID of MS_NOTIFY RPC call */
- time_t when; /* notify: timeout for re-xmit */
- };
-
-@@ -53,7 +52,6 @@ extern notify_list * nlist_gethost(notify_list *, char *, int);
- #define NL_FIRST NL_NEXT
- #define NL_PREV(L) ((L)->prev)
- #define NL_DATA(L) ((L)->mon)
--#define NL_ADDR(L) ((L)->addr)
- #define NL_STATE(L) ((L)->state)
- #define NL_TIMES(L) ((L)->times)
- #define NL_MON_ID(L) (NL_DATA((L)).mon_id)
-diff --git a/utils/statd/rmtcall.c b/utils/statd/rmtcall.c
-index cc1a4a4..0e52fe2 100644
---- a/utils/statd/rmtcall.c
-+++ b/utils/statd/rmtcall.c
-@@ -37,22 +37,19 @@
- #include
- #include
- #include
--#ifdef HAVE_IFADDRS_H
--#include
--#endif /* HAVE_IFADDRS_H */
-+
- #include "sm_inter.h"
- #include "statd.h"
- #include "notlist.h"
--#include "log.h"
- #include "ha-callout.h"
-
-+#include "nsm.h"
-+#include "nfsrpc.h"
-+
- #if SIZEOF_SOCKLEN_T - 0 == 0
- #define socklen_t int
- #endif
-
--#define MAXMSGSIZE (2048 / sizeof(unsigned int))
--
--static unsigned long xid = 0; /* RPC XID counter */
- static int sockfd = -1; /* notify socket */
-
- /*
-@@ -81,7 +78,7 @@ statd_get_socket(void)
- if (sockfd >= 0) close(sockfd);
-
- if ((sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
-- note(N_CRIT, "%s: Can't create socket: %m", __func__);
-+ xlog(L_ERROR, "%s: Can't create socket: %m", __func__);
- return -1;
- }
-
-@@ -91,7 +88,7 @@ statd_get_socket(void)
- sin.sin_addr.s_addr = INADDR_ANY;
-
- if (bindresvport(sockfd, &sin) < 0) {
-- dprintf(N_WARNING, "%s: can't bind to reserved port",
-+ xlog(D_GENERAL, "%s: can't bind to reserved port",
- __func__);
- break;
- }
-@@ -104,112 +101,37 @@ statd_get_socket(void)
- return sockfd;
- }
-
--static unsigned long
--xmit_call(struct sockaddr_in *sin,
-- u_int32_t prog, u_int32_t vers, u_int32_t proc,
-- xdrproc_t func, void *obj)
--/* __u32 prog, __u32 vers, __u32 proc, xdrproc_t func, void *obj) */
--{
-- unsigned int msgbuf[MAXMSGSIZE], msglen;
-- struct rpc_msg mesg;
-- struct pmap pmap;
-- XDR xdr, *xdrs = &xdr;
-- int err;
--
-- if (!xid)
-- xid = getpid() + time(NULL);
--
-- mesg.rm_xid = ++xid;
-- mesg.rm_direction = CALL;
-- mesg.rm_call.cb_rpcvers = 2;
-- if (sin->sin_port == 0) {
-- sin->sin_port = htons(PMAPPORT);
-- mesg.rm_call.cb_prog = PMAPPROG;
-- mesg.rm_call.cb_vers = PMAPVERS;
-- mesg.rm_call.cb_proc = PMAPPROC_GETPORT;
-- pmap.pm_prog = prog;
-- pmap.pm_vers = vers;
-- pmap.pm_prot = IPPROTO_UDP;
-- pmap.pm_port = 0;
-- func = (xdrproc_t) xdr_pmap;
-- obj = &pmap;
-- } else {
-- mesg.rm_call.cb_prog = prog;
-- mesg.rm_call.cb_vers = vers;
-- mesg.rm_call.cb_proc = proc;
-- }
-- mesg.rm_call.cb_cred.oa_flavor = AUTH_NULL;
-- mesg.rm_call.cb_cred.oa_base = (caddr_t) NULL;
-- mesg.rm_call.cb_cred.oa_length = 0;
-- mesg.rm_call.cb_verf.oa_flavor = AUTH_NULL;
-- mesg.rm_call.cb_verf.oa_base = (caddr_t) NULL;
-- mesg.rm_call.cb_verf.oa_length = 0;
--
-- /* Create XDR memory object for encoding */
-- xdrmem_create(xdrs, (caddr_t) msgbuf, sizeof(msgbuf), XDR_ENCODE);
--
-- /* Encode the RPC header part and payload */
-- if (!xdr_callmsg(xdrs, &mesg) || !func(xdrs, obj)) {
-- dprintf(N_WARNING, "%s: can't encode RPC message!", __func__);
-- xdr_destroy(xdrs);
-- return 0;
-- }
--
-- /* Get overall length of datagram */
-- msglen = xdr_getpos(xdrs);
--
-- if ((err = sendto(sockfd, msgbuf, msglen, 0,
-- (struct sockaddr *) sin, sizeof(*sin))) < 0) {
-- dprintf(N_WARNING, "%s: sendto failed: %m", __func__);
-- } else if (err != msglen) {
-- dprintf(N_WARNING, "%s: short write: %m", __func__);
-- }
--
-- xdr_destroy(xdrs);
--
-- return err == msglen? xid : 0;
--}
--
- static notify_list *
--recv_rply(struct sockaddr_in *sin, u_long *portp)
-+recv_rply(u_long *portp)
- {
-- unsigned int msgbuf[MAXMSGSIZE], msglen;
-- struct rpc_msg mesg;
-+ char msgbuf[NSM_MAXMSGSIZE];
-+ ssize_t msglen;
- notify_list *lp = NULL;
-- XDR xdr, *xdrs = &xdr;
-- socklen_t alen = sizeof(*sin);
--
-- /* Receive message */
-- if ((msglen = recvfrom(sockfd, msgbuf, sizeof(msgbuf), 0,
-- (struct sockaddr *) sin, &alen)) < 0) {
-- dprintf(N_WARNING, "%s: recvfrom failed: %m", __func__);
-+ XDR xdr;
-+ struct sockaddr_in sin;
-+ socklen_t alen = (socklen_t)sizeof(sin);
-+ uint32_t xid;
-+
-+ memset(msgbuf, 0, sizeof(msgbuf));
-+ msglen = recvfrom(sockfd, msgbuf, sizeof(msgbuf), 0,
-+ (struct sockaddr *)(char *)&sin, &alen);
-+ if (msglen == (ssize_t)-1) {
-+ xlog_warn("%s: recvfrom failed: %m", __func__);
- return NULL;
- }
-
-- /* Create XDR object for decoding buffer */
-- xdrmem_create(xdrs, (caddr_t) msgbuf, msglen, XDR_DECODE);
--
-- memset(&mesg, 0, sizeof(mesg));
-- mesg.rm_reply.rp_acpt.ar_results.where = NULL;
-- mesg.rm_reply.rp_acpt.ar_results.proc = (xdrproc_t) xdr_void;
--
-- if (!xdr_replymsg(xdrs, &mesg)) {
-- note(N_WARNING, "%s: can't decode RPC message!", __func__);
-+ memset(&xdr, 0, sizeof(xdr));
-+ xdrmem_create(&xdr, msgbuf, (unsigned int)msglen, XDR_DECODE);
-+ xid = nsm_parse_reply(&xdr);
-+ if (xid == 0)
- goto done;
-- }
-+ if (sin.sin_addr.s_addr != htonl(INADDR_LOOPBACK)) {
-+ struct in_addr addr = sin.sin_addr;
-+ char buf[INET_ADDRSTRLEN];
-
-- if (mesg.rm_reply.rp_stat != 0) {
-- note(N_WARNING, "%s: [%s] RPC status %d",
-- __func__,
-- inet_ntoa(sin->sin_addr),
-- mesg.rm_reply.rp_stat);
-- goto done;
-- }
-- if (mesg.rm_reply.rp_acpt.ar_stat != 0) {
-- note(N_WARNING, "%s: [%s] RPC status %d",
-- __func__,
-- inet_ntoa(sin->sin_addr),
-- mesg.rm_reply.rp_acpt.ar_stat);
-+ xlog_warn("%s: Unrecognized reply from %s", __func__,
-+ inet_ntop(AF_INET, &addr, buf,
-+ (socklen_t)sizeof(buf)));
- goto done;
- }
-
-@@ -217,32 +139,15 @@ recv_rply(struct sockaddr_in *sin, u_long *portp)
- /* LH - this was a bug... it should have been checking
- * the xid from the response message from the client,
- * not the static, internal xid */
-- if (lp->xid != mesg.rm_xid)
-+ if (lp->xid != xid)
- continue;
-- if (lp->addr.s_addr != sin->sin_addr.s_addr) {
-- char addr [18];
-- strncpy (addr, inet_ntoa(lp->addr),
-- sizeof (addr) - 1);
-- addr [sizeof (addr) - 1] = '\0';
-- dprintf(N_WARNING, "%s: address mismatch: "
-- "expected %s, got %s", __func__,
-- addr, inet_ntoa(sin->sin_addr));
-- }
-- if (lp->port == 0) {
-- if (!xdr_u_long(xdrs, portp)) {
-- note(N_WARNING,
-- "%s: [%s] can't decode reply body!",
-- __func__,
-- inet_ntoa(sin->sin_addr));
-- lp = NULL;
-- goto done;
-- }
-- }
-+ if (lp->port == 0)
-+ *portp = nsm_recv_getport(&xdr);
- break;
- }
-
- done:
-- xdr_destroy(xdrs);
-+ xdr_destroy(&xdr);
- return lp;
- }
-
-@@ -253,15 +158,10 @@ static int
- process_entry(notify_list *lp)
- {
- struct sockaddr_in sin;
-- struct status new_status;
-- xdrproc_t func;
-- void *objp;
-- u_int32_t proc, vers, prog;
--/* __u32 proc, vers, prog; */
-
- if (NL_TIMES(lp) == 0) {
-- note(N_DEBUG, "%s: Cannot notify %s, giving up.",
-- __func__, inet_ntoa(NL_ADDR(lp)));
-+ xlog(D_GENERAL, "%s: Cannot notify localhost, giving up",
-+ __func__);
- return 0;
- }
-
-@@ -270,23 +170,31 @@ process_entry(notify_list *lp)
- sin.sin_port = lp->port;
- /* LH - moved address into switch */
-
-- prog = NL_MY_PROG(lp);
-- vers = NL_MY_VERS(lp);
-- proc = NL_MY_PROC(lp);
--
- /* __FORCE__ loopback for callbacks to lockd ... */
- /* Just in case we somehow ignored it thus far */
- sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
-
-- func = (xdrproc_t) xdr_status;
-- objp = &new_status;
-- new_status.mon_name = NL_MON_NAME(lp);
-- new_status.state = NL_STATE(lp);
-- memcpy(new_status.priv, NL_PRIV(lp), SM_PRIV_SIZE);
-+ if (sin.sin_port == 0)
-+ lp->xid = nsm_xmit_getport(sockfd, &sin,
-+ (rpcprog_t)NL_MY_PROG(lp),
-+ (rpcvers_t)NL_MY_VERS(lp));
-+ else {
-+ struct mon m;
-+
-+ memcpy(m.priv, NL_PRIV(lp), SM_PRIV_SIZE);
-
-- lp->xid = xmit_call(&sin, prog, vers, proc, func, objp);
-- if (!lp->xid) {
-- note(N_WARNING, "%s: failed to notify port %d",
-+ m.mon_id.mon_name = NL_MON_NAME(lp);
-+ m.mon_id.my_id.my_name = NULL;
-+ m.mon_id.my_id.my_prog = NL_MY_PROG(lp);
-+ m.mon_id.my_id.my_vers = NL_MY_VERS(lp);
-+ m.mon_id.my_id.my_proc = NL_MY_PROC(lp);
-+
-+ lp->xid = nsm_xmit_nlmcall(sockfd,
-+ (struct sockaddr *)(char *)&sin,
-+ (socklen_t)sizeof(sin), &m, NL_STATE(lp));
-+ }
-+ if (lp->xid == 0) {
-+ xlog_warn("%s: failed to notify port %d",
- __func__, ntohs(lp->port));
- }
- NL_TIMES(lp) -= 1;
-@@ -300,14 +208,13 @@ process_entry(notify_list *lp)
- int
- process_reply(FD_SET_TYPE *rfds)
- {
-- struct sockaddr_in sin;
- notify_list *lp;
- u_long port;
-
- if (sockfd == -1 || !FD_ISSET(sockfd, rfds))
- return 0;
-
-- if (!(lp = recv_rply(&sin, &port)))
-+ if (!(lp = recv_rply(&port)))
- return 1;
-
- if (lp->port == 0) {
-@@ -319,10 +226,10 @@ process_reply(FD_SET_TYPE *rfds)
- nlist_insert_timer(¬ify, lp);
- return 1;
- }
-- note(N_WARNING, "%s: [%s] service %d not registered",
-- __func__, inet_ntoa(lp->addr), NL_MY_PROG(lp));
-+ xlog_warn("%s: service %d not registered on localhost",
-+ __func__, NL_MY_PROG(lp));
- } else {
-- dprintf(N_DEBUG, "%s: Callback to %s (for %d) succeeded.",
-+ xlog(D_GENERAL, "%s: Callback to %s (for %d) succeeded",
- __func__, NL_MY_NAME(lp), NL_MON_NAME(lp));
- }
- nlist_free(¬ify, lp);
-@@ -346,8 +253,8 @@ process_notify_list(void)
- nlist_remove(¬ify, entry);
- nlist_insert_timer(¬ify, entry);
- } else {
-- note(N_ERROR,
-- "%s: Can't callback %s (%d,%d), giving up.",
-+ xlog(L_ERROR,
-+ "%s: Can't callback %s (%d,%d), giving up",
- __func__,
- NL_MY_NAME(entry),
- NL_MY_PROG(entry),
-diff --git a/utils/statd/simu.c b/utils/statd/simu.c
-index a7ecb85..825e428 100644
---- a/utils/statd/simu.c
-+++ b/utils/statd/simu.c
-@@ -8,8 +8,10 @@
- #include
- #endif
-
-+#include
- #include
-
-+#include "sockaddr.h"
- #include "rpcmisc.h"
- #include "statd.h"
- #include "notlist.h"
-@@ -19,32 +21,29 @@ extern void my_svc_exit (void);
-
- /*
- * Services SM_SIMU_CRASH requests.
-+ *
-+ * Although the kernel contacts the statd service via only IPv4
-+ * transports, the statd service can receive other requests, such
-+ * as SM_NOTIFY, from remote peers via IPv6.
- */
- void *
--sm_simu_crash_1_svc (void *argp, struct svc_req *rqstp)
-+sm_simu_crash_1_svc (__attribute__ ((unused)) void *argp, struct svc_req *rqstp)
- {
-- struct sockaddr_in *sin = nfs_getrpccaller_in(rqstp->rq_xprt);
-+ struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt);
-+ char buf[INET6_ADDRSTRLEN];
- static char *result = NULL;
-- struct in_addr caller;
-
-- if (sin->sin_family != AF_INET) {
-- note(N_WARNING, "Call to statd from non-AF_INET address");
-- goto failure;
-- }
-+ xlog(D_CALL, "Received SM_SIMU_CRASH");
-
-- caller = sin->sin_addr;
-- if (caller.s_addr != htonl(INADDR_LOOPBACK)) {
-- note(N_WARNING, "Call to statd from non-local host %s",
-- inet_ntoa(caller));
-- goto failure;
-- }
-+ if (!nfs_is_v4_loopback(sap))
-+ goto out_nonlocal;
-
-- if (ntohs(sin->sin_port) >= 1024) {
-- note(N_WARNING, "Call to statd-simu-crash from unprivileged port");
-+ if ((int)nfs_get_port(sap) >= IPPORT_RESERVED) {
-+ xlog_warn("SM_SIMU_CRASH call from unprivileged port");
- goto failure;
- }
-
-- note (N_WARNING, "*** SIMULATING CRASH! ***");
-+ xlog_warn("*** SIMULATING CRASH! ***");
- my_svc_exit ();
-
- if (rtnl)
-@@ -52,4 +51,10 @@ sm_simu_crash_1_svc (void *argp, struct svc_req *rqstp)
-
- failure:
- return ((void *)&result);
-+
-+ out_nonlocal:
-+ if (!statd_present_address(sap, buf, sizeof(buf)))
-+ buf[0] = '\0';
-+ xlog_warn("SM_SIMU_CRASH call from non-local host %s", buf);
-+ goto failure;
- }
-diff --git a/utils/statd/simulate.c b/utils/statd/simulate.c
-index de8f1c9..4ed1468 100644
---- a/utils/statd/simulate.c
-+++ b/utils/statd/simulate.c
-@@ -38,7 +38,9 @@ extern void svc_exit (void);
- void
- simulator (int argc, char **argv)
- {
-- log_enable (1);
-+ xlog_stderr (1);
-+ xlog_syslog (0);
-+ xlog_open ("statd simulator");
-
- if (argc == 2)
- if (!strcasecmp (*argv, "crash"))
-@@ -61,7 +63,7 @@ simulator (int argc, char **argv)
- simulate_mon (*(&argv[1]), *(&argv[2]), *(&argv[3]), *(&argv[4]),
- *(&argv[5]));
- }
-- die ("WTF? Give me something I can use!");
-+ xlog_err ("WTF? Give me something I can use!");
- }
-
- static void
-@@ -72,11 +74,11 @@ simulate_mon (char *calling, char *monitoring, char *as, char *proggy,
- sm_stat_res *result;
- mon mon;
-
-- dprintf (N_DEBUG, "Calling %s (as %s) to monitor %s", calling, as,
-+ xlog (D_GENERAL, "Calling %s (as %s) to monitor %s", calling, as,
- monitoring);
-
- if ((client = clnt_create (calling, SM_PROG, SM_VERS, "udp")) == NULL)
-- die ("%s", clnt_spcreateerror ("clnt_create"));
-+ xlog_err ("%s", clnt_spcreateerror ("clnt_create"));
-
- memcpy (mon.priv, fool, SM_PRIV_SIZE);
- mon.mon_id.my_id.my_name = xstrdup (as);
-@@ -87,16 +89,15 @@ simulate_mon (char *calling, char *monitoring, char *as, char *proggy,
- mon.mon_id.mon_name = monitoring;
-
- if (!(result = sm_mon_1 (&mon, client)))
-- die ("%s", clnt_sperror (client, "sm_mon_1"));
-+ xlog_err ("%s", clnt_sperror (client, "sm_mon_1"));
-
- free (mon.mon_id.my_id.my_name);
-
- if (result->res_stat != STAT_SUCC) {
-- note (N_FATAL, "SM_MON request failed, state: %d", result->state);
-- exit (0);
-+ xlog_err ("SM_MON request failed, state: %d", result->state);
- } else {
-- dprintf (N_DEBUG, "SM_MON result successful, state: %d\n", result->state);
-- dprintf (N_DEBUG, "Waiting for callback.");
-+ xlog (D_GENERAL, "SM_MON result successful, state: %d\n", result->state);
-+ xlog (D_GENERAL, "Waiting for callback");
- daemon_simulator ();
- exit (0);
- }
-@@ -109,11 +110,11 @@ simulate_unmon (char *calling, char *unmonitoring, char *as, char *proggy)
- sm_stat *result;
- mon_id mon_id;
-
-- dprintf (N_DEBUG, "Calling %s (as %s) to unmonitor %s", calling, as,
-+ xlog (D_GENERAL, "Calling %s (as %s) to unmonitor %s", calling, as,
- unmonitoring);
-
- if ((client = clnt_create (calling, SM_PROG, SM_VERS, "udp")) == NULL)
-- die ("%s", clnt_spcreateerror ("clnt_create"));
-+ xlog_err ("%s", clnt_spcreateerror ("clnt_create"));
-
- mon_id.my_id.my_name = xstrdup (as);
- mon_id.my_id.my_prog = atoi (proggy) * SIM_SM_PROG;
-@@ -122,10 +123,10 @@ simulate_unmon (char *calling, char *unmonitoring, char *as, char *proggy)
- mon_id.mon_name = unmonitoring;
-
- if (!(result = sm_unmon_1 (&mon_id, client)))
-- die ("%s", clnt_sperror (client, "sm_unmon_1"));
-+ xlog_err ("%s", clnt_sperror (client, "sm_unmon_1"));
-
- free (mon_id.my_id.my_name);
-- dprintf (N_DEBUG, "SM_UNMON request returned state: %d\n", result->state);
-+ xlog (D_GENERAL, "SM_UNMON request returned state: %d\n", result->state);
- exit (0);
- }
-
-@@ -136,10 +137,10 @@ simulate_unmon_all (char *calling, char *as, char *proggy)
- sm_stat *result;
- my_id my_id;
-
-- dprintf (N_DEBUG, "Calling %s (as %s) to unmonitor all hosts", calling, as);
-+ xlog (D_GENERAL, "Calling %s (as %s) to unmonitor all hosts", calling, as);
-
- if ((client = clnt_create (calling, SM_PROG, SM_VERS, "udp")) == NULL)
-- die ("%s", clnt_spcreateerror ("clnt_create"));
-+ xlog_err ("%s", clnt_spcreateerror ("clnt_create"));
-
- my_id.my_name = xstrdup (as);
- my_id.my_prog = atoi (proggy) * SIM_SM_PROG;
-@@ -147,10 +148,10 @@ simulate_unmon_all (char *calling, char *as, char *proggy)
- my_id.my_proc = SIM_SM_MON;
-
- if (!(result = sm_unmon_all_1 (&my_id, client)))
-- die ("%s", clnt_sperror (client, "sm_unmon_all_1"));
-+ xlog_err ("%s", clnt_sperror (client, "sm_unmon_all_1"));
-
- free (my_id.my_name);
-- dprintf (N_DEBUG, "SM_UNMON_ALL request returned state: %d\n", result->state);
-+ xlog (D_GENERAL, "SM_UNMON_ALL request returned state: %d\n", result->state);
- exit (0);
- }
-
-@@ -160,10 +161,10 @@ simulate_crash (char *host)
- CLIENT *client;
-
- if ((client = clnt_create (host, SM_PROG, SM_VERS, "udp")) == NULL)
-- die ("%s", clnt_spcreateerror ("clnt_create"));
-+ xlog_err ("%s", clnt_spcreateerror ("clnt_create"));
-
- if (!sm_simu_crash_1 (NULL, client))
-- die ("%s", clnt_sperror (client, "sm_simu_crash_1"));
-+ xlog_err ("%s", clnt_sperror (client, "sm_simu_crash_1"));
-
- exit (0);
- }
-@@ -176,18 +177,18 @@ simulate_stat (char *calling, char *monitoring)
- sm_stat_res *result;
-
- if ((client = clnt_create (calling, SM_PROG, SM_VERS, "udp")) == NULL)
-- die ("%s", clnt_spcreateerror ("clnt_create"));
-+ xlog_err ("%s", clnt_spcreateerror ("clnt_create"));
-
- checking.mon_name = monitoring;
-
- if (!(result = sm_stat_1 (&checking, client)))
-- die ("%s", clnt_sperror (client, "sm_stat_1"));
-+ xlog_err ("%s", clnt_sperror (client, "sm_stat_1"));
-
- if (result->res_stat == STAT_SUCC)
-- dprintf (N_DEBUG, "STAT_SUCC from %s for %s, state: %d", calling,
-+ xlog (D_GENERAL, "STAT_SUCC from %s for %s, state: %d", calling,
- monitoring, result->state);
- else
-- dprintf (N_DEBUG, "STAT_FAIL from %s for %s, state: %d", calling,
-+ xlog (D_GENERAL, "STAT_FAIL from %s for %s, state: %d", calling,
- monitoring, result->state);
-
- exit (0);
-@@ -196,9 +197,8 @@ simulate_stat (char *calling, char *monitoring)
- static void
- sim_killer (int sig)
- {
-- note (N_FATAL, "Simulator caught signal %d, un-registering and exiting.", sig);
- pmap_unset (sim_port, SIM_SM_VERS);
-- exit (0);
-+ xlog_err ("Simulator caught signal %d, un-registering and exiting", sig);
- }
-
- static void
-@@ -219,7 +219,7 @@ sim_sm_mon_1_svc (struct status *argp, struct svc_req *rqstp)
- {
- static char *result;
-
-- dprintf (N_DEBUG, "Recieved state %d for mon_name %s (opaque \"%s\")",
-+ xlog (D_GENERAL, "Recieved state %d for mon_name %s (opaque \"%s\")",
- argp->state, argp->mon_name, argp->priv);
- svc_exit ();
- return ((void *)&result);
-diff --git a/utils/statd/sm-notify.c b/utils/statd/sm-notify.c
-index 72dcff4..3259a3e 100644
---- a/utils/statd/sm-notify.c
-+++ b/utils/statd/sm-notify.c
-@@ -8,6 +8,7 @@
- #include
- #endif
-
-+#include
- #include
- #include
- #include
-@@ -28,129 +29,114 @@
- #include
- #include
-
--#ifndef BASEDIR
--# ifdef NFS_STATEDIR
--# define BASEDIR NFS_STATEDIR
--# else
--# define BASEDIR "/var/lib/nfs"
--# endif
--#endif
--
--#define DEFAULT_SM_STATE_PATH BASEDIR "/state"
--#define DEFAULT_SM_DIR_PATH BASEDIR "/sm"
--#define DEFAULT_SM_BAK_PATH DEFAULT_SM_DIR_PATH ".bak"
-+#include "sockaddr.h"
-+#include "xlog.h"
-+#include "nsm.h"
-+#include "nfsrpc.h"
-
--char *_SM_BASE_PATH = BASEDIR;
--char *_SM_STATE_PATH = DEFAULT_SM_STATE_PATH;
--char *_SM_DIR_PATH = DEFAULT_SM_DIR_PATH;
--char *_SM_BAK_PATH = DEFAULT_SM_BAK_PATH;
-+#ifndef HAVE_DECL_AI_ADDRCONFIG
-+#define AI_ADDRCONFIG 0
-+#endif
-
--#define NSM_PROG 100024
--#define NSM_PROGRAM 100024
--#define NSM_VERSION 1
- #define NSM_TIMEOUT 2
--#define NSM_NOTIFY 6
- #define NSM_MAX_TIMEOUT 120 /* don't make this too big */
--#define MAXMSGSIZE 256
-
- struct nsm_host {
- struct nsm_host * next;
- char * name;
-- char * path;
-- struct sockaddr_storage addr;
-+ char * mon_name;
-+ char * my_name;
- struct addrinfo *ai;
- time_t last_used;
- time_t send_next;
- unsigned int timeout;
- unsigned int retries;
-- unsigned int xid;
-+ uint32_t xid;
- };
-
- static char nsm_hostname[256];
--static uint32_t nsm_state;
-+static int nsm_state;
-+static int nsm_family = AF_INET;
- static int opt_debug = 0;
--static int opt_quiet = 0;
--static int opt_update_state = 1;
-+static _Bool opt_update_state = true;
- static unsigned int opt_max_retry = 15 * 60;
--static char * opt_srcaddr = 0;
--static uint16_t opt_srcport = 0;
--static int log_syslog = 0;
-+static char * opt_srcaddr = NULL;
-+static char * opt_srcport = NULL;
-
--static unsigned int nsm_get_state(int);
--static void notify(void);
-+static void notify(const int sock);
- static int notify_host(int, struct nsm_host *);
- static void recv_reply(int);
--static void backup_hosts(const char *, const char *);
--static void get_hosts(const char *);
- static void insert_host(struct nsm_host *);
- static struct nsm_host *find_host(uint32_t);
--static void nsm_log(int fac, const char *fmt, ...);
- static int record_pid(void);
--static void drop_privs(void);
--static void set_kernel_nsm_state(int state);
-
- static struct nsm_host * hosts = NULL;
-
--/*
-- * Address handling utilities
-- */
--
--static unsigned short smn_get_port(const struct sockaddr *sap)
-+__attribute_malloc__
-+static struct addrinfo *
-+smn_lookup(const char *name)
- {
-- switch (sap->sa_family) {
-- case AF_INET:
-- return ntohs(((struct sockaddr_in *)sap)->sin_port);
-- case AF_INET6:
-- return ntohs(((struct sockaddr_in6 *)sap)->sin6_port);
-- }
-- return 0;
--}
-+ struct addrinfo *ai = NULL;
-+ struct addrinfo hint = {
-+ .ai_flags = AI_ADDRCONFIG,
-+ .ai_family = (nsm_family == AF_INET ? AF_INET: AF_UNSPEC),
-+ .ai_protocol = (int)IPPROTO_UDP,
-+ };
-+ int error;
-
--static void smn_set_port(struct sockaddr *sap, const unsigned short port)
--{
-- switch (sap->sa_family) {
-- case AF_INET:
-- ((struct sockaddr_in *)sap)->sin_port = htons(port);
-- break;
-- case AF_INET6:
-- ((struct sockaddr_in6 *)sap)->sin6_port = htons(port);
-- break;
-+ error = getaddrinfo(name, NULL, &hint, &ai);
-+ if (error != 0) {
-+ xlog(D_GENERAL, "getaddrinfo(3): %s", gai_strerror(error));
-+ return NULL;
- }
-+
-+ return ai;
- }
-
--static struct addrinfo *smn_lookup(const char *name)
-+__attribute_malloc__
-+static struct nsm_host *
-+smn_alloc_host(const char *hostname, const char *mon_name,
-+ const char *my_name, const time_t timestamp)
- {
-- struct addrinfo *ai, hint = {
--#if HAVE_DECL_AI_ADDRCONFIG
-- .ai_flags = AI_ADDRCONFIG,
--#endif /* HAVE_DECL_AI_ADDRCONFIG */
-- .ai_family = AF_INET,
-- .ai_protocol = IPPROTO_UDP,
-- };
-- int error;
-+ struct nsm_host *host;
-
-- error = getaddrinfo(name, NULL, &hint, &ai);
-- switch (error) {
-- case 0:
-- return ai;
-- case EAI_SYSTEM:
-- if (opt_debug)
-- nsm_log(LOG_ERR, "getaddrinfo(3): %s",
-- strerror(errno));
-- break;
-- default:
-- if (opt_debug)
-- nsm_log(LOG_ERR, "getaddrinfo(3): %s",
-- gai_strerror(error));
-+ host = calloc(1, sizeof(*host));
-+ if (host == NULL)
-+ goto out_nomem;
-+
-+ host->name = strdup(hostname);
-+ host->mon_name = strdup(mon_name);
-+ host->my_name = strdup(my_name);
-+ if (host->name == NULL ||
-+ host->mon_name == NULL ||
-+ host->my_name == NULL) {
-+ free(host->my_name);
-+ free(host->mon_name);
-+ free(host->name);
-+ free(host);
-+ goto out_nomem;
- }
-
-+ host->last_used = timestamp;
-+ host->timeout = NSM_TIMEOUT;
-+ host->retries = 100; /* force address retry */
-+
-+ return host;
-+
-+out_nomem:
-+ xlog_warn("Unable to allocate memory");
- return NULL;
- }
-
- static void smn_forget_host(struct nsm_host *host)
- {
-- unlink(host->path);
-- free(host->path);
-+ xlog(D_CALL, "Removing %s (%s, %s) from notify list",
-+ host->name, host->mon_name, host->my_name);
-+
-+ nsm_delete_notified_host(host->name, host->mon_name, host->my_name);
-+
-+ free(host->my_name);
-+ free(host->mon_name);
- free(host->name);
- if (host->ai)
- freeaddrinfo(host->ai);
-@@ -158,13 +144,219 @@ static void smn_forget_host(struct nsm_host *host)
- free(host);
- }
-
-+static unsigned int
-+smn_get_host(const char *hostname,
-+ __attribute__ ((unused)) const struct sockaddr *sap,
-+ const struct mon *m, const time_t timestamp)
-+{
-+ struct nsm_host *host;
-+
-+ host = smn_alloc_host(hostname,
-+ m->mon_id.mon_name, m->mon_id.my_id.my_name, timestamp);
-+ if (host == NULL)
-+ return 0;
-+
-+ insert_host(host);
-+ xlog(D_GENERAL, "Added host %s to notify list", hostname);
-+ return 1;
-+}
-+
-+#ifdef IPV6_SUPPORTED
-+static int smn_socket(void)
-+{
-+ int sock;
-+
-+ /*
-+ * Use an AF_INET socket if IPv6 is disabled on the
-+ * local system.
-+ */
-+ sock = socket(AF_INET6, SOCK_DGRAM, 0);
-+ if (sock == -1) {
-+ if (errno != EAFNOSUPPORT) {
-+ xlog(L_ERROR, "Failed to create RPC socket: %m");
-+ return -1;
-+ }
-+ sock = socket(AF_INET, SOCK_DGRAM, 0);
-+ if (sock < 0) {
-+ xlog(L_ERROR, "Failed to create RPC socket: %m");
-+ return -1;
-+ }
-+ } else
-+ nsm_family = AF_INET6;
-+
-+ if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) {
-+ xlog(L_ERROR, "fcntl(3) on RPC socket failed: %m");
-+ goto out_close;
-+ }
-+
-+ /*
-+ * TI-RPC over IPv6 (udp6/tcp6) does not handle IPv4. However,
-+ * since sm-notify open-codes all of its RPC support, it can
-+ * use a single socket and let the local network stack provide
-+ * the correct mapping between address families automatically.
-+ * This is the same thing that is done in the kernel.
-+ */
-+ if (nsm_family == AF_INET6) {
-+ const int zero = 0;
-+ socklen_t zerolen = (socklen_t)sizeof(zero);
-+
-+ if (setsockopt(sock, SOL_IPV6, IPV6_V6ONLY,
-+ (char *)&zero, zerolen) == -1) {
-+ xlog(L_ERROR, "setsockopt(3) on RPC socket failed: %m");
-+ goto out_close;
-+ }
-+ }
-+
-+ return sock;
-+
-+out_close:
-+ (void)close(sock);
-+ return -1;
-+}
-+#else /* !IPV6_SUPPORTED */
-+static int smn_socket(void)
-+{
-+ int sock;
-+
-+ sock = socket(AF_INET, SOCK_DGRAM, 0);
-+ if (sock == -1) {
-+ xlog(L_ERROR, "Failed to create RPC socket: %m");
-+ return -1;
-+ }
-+
-+ if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) {
-+ xlog(L_ERROR, "fcntl(3) on RPC socket failed: %m");
-+ (void)close(sock);
-+ return -1;
-+ }
-+
-+ return sock;
-+}
-+#endif /* !IPV6_SUPPORTED */
-+
-+/*
-+ * If admin specified a source address or srcport, then convert those
-+ * to a sockaddr and return it. Otherwise, return an ANYADDR address.
-+ */
-+__attribute_malloc__
-+static struct addrinfo *
-+smn_bind_address(const char *srcaddr, const char *srcport)
-+{
-+ struct addrinfo *ai = NULL;
-+ struct addrinfo hint = {
-+ .ai_flags = AI_NUMERICSERV,
-+ .ai_family = nsm_family,
-+ .ai_protocol = (int)IPPROTO_UDP,
-+ };
-+ int error;
-+
-+ if (srcaddr == NULL)
-+ hint.ai_flags |= AI_PASSIVE;
-+
-+ if (srcport == NULL)
-+ error = getaddrinfo(srcaddr, "", &hint, &ai);
-+ else
-+ error = getaddrinfo(srcaddr, srcport, &hint, &ai);
-+ if (error != 0) {
-+ xlog(L_ERROR,
-+ "Invalid bind address or port for RPC socket: %s",
-+ gai_strerror(error));
-+ return NULL;
-+ }
-+
-+ return ai;
-+}
-+
-+#ifdef HAVE_LIBTIRPC
-+static int
-+smn_bindresvport(int sock, struct sockaddr *sap)
-+{
-+ return bindresvport_sa(sock, sap);
-+}
-+
-+#else /* !HAVE_LIBTIRPC */
-+static int
-+smn_bindresvport(int sock, struct sockaddr *sap)
-+{
-+ if (sap->sa_family != AF_INET) {
-+ errno = EAFNOSUPPORT;
-+ return -1;
-+ }
-+
-+ return bindresvport(sock, (struct sockaddr_in *)(char *)sap);
-+}
-+#endif /* !HAVE_LIBTIRPC */
-+
-+/*
-+ * Prepare a socket for sending RPC requests
-+ *
-+ * Returns a bound datagram socket file descriptor, or -1 if
-+ * an error occurs.
-+ */
-+static int
-+smn_create_socket(const char *srcaddr, const char *srcport)
-+{
-+ int sock, retry_cnt = 0;
-+ struct addrinfo *ai;
-+
-+retry:
-+ sock = smn_socket();
-+ if (sock == -1)
-+ return -1;
-+
-+ ai = smn_bind_address(srcaddr, srcport);
-+ if (ai == NULL) {
-+ (void)close(sock);
-+ return -1;
-+ }
-+
-+ /* Use source port if provided on the command line,
-+ * otherwise use bindresvport */
-+ if (srcport) {
-+ if (bind(sock, ai->ai_addr, ai->ai_addrlen) == -1) {
-+ xlog(L_ERROR, "Failed to bind RPC socket: %m");
-+ freeaddrinfo(ai);
-+ (void)close(sock);
-+ return -1;
-+ }
-+ } else {
-+ struct servent *se;
-+
-+ if (smn_bindresvport(sock, ai->ai_addr) == -1) {
-+ xlog(L_ERROR,
-+ "bindresvport on RPC socket failed: %m");
-+ freeaddrinfo(ai);
-+ (void)close(sock);
-+ return -1;
-+ }
-+
-+ /* try to avoid known ports */
-+ se = getservbyport((int)nfs_get_port(ai->ai_addr), "udp");
-+ if (se != NULL && retry_cnt < 100) {
-+ retry_cnt++;
-+ freeaddrinfo(ai);
-+ (void)close(sock);
-+ goto retry;
-+ }
-+ }
-+
-+ freeaddrinfo(ai);
-+ return sock;
-+}
-+
- int
- main(int argc, char **argv)
- {
-- int c;
-- int force = 0;
-+ int c, sock, force = 0;
-+ char * progname;
-
-- while ((c = getopt(argc, argv, "dm:np:v:qP:f")) != -1) {
-+ progname = strrchr(argv[0], '/');
-+ if (progname != NULL)
-+ progname++;
-+ else
-+ progname = argv[0];
-+
-+ while ((c = getopt(argc, argv, "dm:np:v:P:f")) != -1) {
- switch (c) {
- case 'f':
- force = 1;
-@@ -176,32 +368,17 @@ main(int argc, char **argv)
- opt_max_retry = atoi(optarg) * 60;
- break;
- case 'n':
-- opt_update_state = 0;
-+ opt_update_state = false;
- break;
- case 'p':
-- opt_srcport = atoi(optarg);
-+ opt_srcport = optarg;
- break;
- case 'v':
- opt_srcaddr = optarg;
- break;
-- case 'q':
-- opt_quiet = 1;
-- break;
- case 'P':
-- _SM_BASE_PATH = strdup(optarg);
-- _SM_STATE_PATH = malloc(strlen(optarg)+1+sizeof("state"));
-- _SM_DIR_PATH = malloc(strlen(optarg)+1+sizeof("sm"));
-- _SM_BAK_PATH = malloc(strlen(optarg)+1+sizeof("sm.bak"));
-- if (_SM_BASE_PATH == NULL ||
-- _SM_STATE_PATH == NULL ||
-- _SM_DIR_PATH == NULL ||
-- _SM_BAK_PATH == NULL) {
-- nsm_log(LOG_ERR, "unable to allocate memory");
-+ if (!nsm_setup_pathnames(argv[0], optarg))
- exit(1);
-- }
-- strcat(strcpy(_SM_STATE_PATH, _SM_BASE_PATH), "/state");
-- strcat(strcpy(_SM_DIR_PATH, _SM_BASE_PATH), "/sm");
-- strcat(strcpy(_SM_BAK_PATH, _SM_BASE_PATH), "/sm.bak");
- break;
-
- default:
-@@ -211,18 +388,26 @@ main(int argc, char **argv)
-
- if (optind < argc) {
- usage: fprintf(stderr,
-- "Usage: sm-notify [-dfq] [-m max-retry-minutes] [-p srcport]\n"
-- " [-P /path/to/state/directory] [-v my_host_name]\n");
-+ "Usage: %s -notify [-dfq] [-m max-retry-minutes] [-p srcport]\n"
-+ " [-P /path/to/state/directory] [-v my_host_name]\n",
-+ progname);
- exit(1);
- }
-
-- log_syslog = 1;
-- openlog("sm-notify", LOG_PID, LOG_DAEMON);
-+ xlog_syslog(1);
-+ if (opt_debug) {
-+ xlog_stderr(1);
-+ xlog_config(D_ALL, 1);
-+ } else
-+ xlog_stderr(0);
-+
-+ xlog_open(progname);
-+ xlog(L_NOTICE, "Version " VERSION " starting");
-
-- if (strcmp(_SM_BASE_PATH, BASEDIR) == 0) {
-- if (record_pid() == 0 && force == 0 && opt_update_state == 1) {
-+ if (nsm_is_default_parentdir()) {
-+ if (record_pid() == 0 && force == 0 && opt_update_state) {
- /* already run, don't try again */
-- nsm_log(LOG_NOTICE, "Already notifying clients; Exiting!");
-+ xlog(L_NOTICE, "Already notifying clients; Exiting!");
- exit(0);
- }
- }
-@@ -231,31 +416,26 @@ usage: fprintf(stderr,
- strncpy(nsm_hostname, opt_srcaddr, sizeof(nsm_hostname)-1);
- } else
- if (gethostname(nsm_hostname, sizeof(nsm_hostname)) < 0) {
-- nsm_log(LOG_ERR, "Failed to obtain name of local host: %s",
-- strerror(errno));
-+ xlog(L_ERROR, "Failed to obtain name of local host: %m");
- exit(1);
- }
-
-- backup_hosts(_SM_DIR_PATH, _SM_BAK_PATH);
-- get_hosts(_SM_BAK_PATH);
--
-- /* If there are not hosts to notify, just exit */
-- if (!hosts) {
-- nsm_log(LOG_DEBUG, "No hosts to notify; exiting");
-+ (void)nsm_retire_monitored_hosts();
-+ if (nsm_load_notify_list(smn_get_host) == 0) {
-+ xlog(D_GENERAL, "No hosts to notify; exiting");
- return 0;
- }
-
-- /* Get and update the NSM state. This will call sync() */
- nsm_state = nsm_get_state(opt_update_state);
-- set_kernel_nsm_state(nsm_state);
-+ if (nsm_state == 0)
-+ exit(1);
-+ nsm_update_kernel_state(nsm_state);
-
- if (!opt_debug) {
-- if (!opt_quiet)
-- printf("Backgrounding to notify hosts...\n");
-+ xlog(L_NOTICE, "Backgrounding to notify hosts...\n");
-
- if (daemon(0, 0) < 0) {
-- nsm_log(LOG_ERR, "unable to background: %s",
-- strerror(errno));
-+ xlog(L_ERROR, "unable to background: %m");
- exit(1);
- }
-
-@@ -264,15 +444,21 @@ usage: fprintf(stderr,
- close(2);
- }
-
-- notify();
-+ sock = smn_create_socket(opt_srcaddr, opt_srcport);
-+ if (sock == -1)
-+ exit(1);
-+
-+ if (!nsm_drop_privileges(-1))
-+ exit(1);
-+
-+ notify(sock);
-
- if (hosts) {
- struct nsm_host *hp;
-
- while ((hp = hosts) != 0) {
- hosts = hp->next;
-- nsm_log(LOG_NOTICE,
-- "Unable to notify %s, giving up",
-+ xlog(L_NOTICE, "Unable to notify %s, giving up",
- hp->name);
- }
- exit(1);
-@@ -285,69 +471,13 @@ usage: fprintf(stderr,
- * Notify hosts
- */
- static void
--notify(void)
-+notify(const int sock)
- {
-- struct sockaddr_storage address;
-- struct sockaddr *local_addr = (struct sockaddr *)&address;
- time_t failtime = 0;
-- int sock = -1;
-- int retry_cnt = 0;
--
-- retry:
-- sock = socket(AF_INET, SOCK_DGRAM, 0);
-- if (sock < 0) {
-- nsm_log(LOG_ERR, "Failed to create RPC socket: %s",
-- strerror(errno));
-- exit(1);
-- }
-- fcntl(sock, F_SETFL, O_NONBLOCK);
--
-- memset(&address, 0, sizeof(address));
-- local_addr->sa_family = AF_INET; /* Default to IPv4 */
--
-- /* Bind source IP if provided on command line */
-- if (opt_srcaddr) {
-- struct addrinfo *ai = smn_lookup(opt_srcaddr);
-- if (!ai) {
-- nsm_log(LOG_ERR,
-- "Not a valid hostname or address: \"%s\"",
-- opt_srcaddr);
-- exit(1);
-- }
--
-- /* We know it's IPv4 at this point */
-- memcpy(local_addr, ai->ai_addr, ai->ai_addrlen);
--
-- freeaddrinfo(ai);
-- }
--
-- /* Use source port if provided on the command line,
-- * otherwise use bindresvport */
-- if (opt_srcport) {
-- smn_set_port(local_addr, opt_srcport);
-- if (bind(sock, local_addr, sizeof(struct sockaddr_in)) < 0) {
-- nsm_log(LOG_ERR, "Failed to bind RPC socket: %s",
-- strerror(errno));
-- exit(1);
-- }
-- } else {
-- struct servent *se;
-- struct sockaddr_in *sin = (struct sockaddr_in *)local_addr;
-- (void) bindresvport(sock, sin);
-- /* try to avoid known ports */
-- se = getservbyport(sin->sin_port, "udp");
-- if (se && retry_cnt < 100) {
-- retry_cnt++;
-- close(sock);
-- goto retry;
-- }
-- }
-
- if (opt_max_retry)
- failtime = time(NULL) + opt_max_retry;
-
-- drop_privs();
--
- while (hosts) {
- struct pollfd pfd;
- time_t now = time(NULL);
-@@ -383,7 +513,7 @@ notify(void)
- if (hosts == NULL)
- return;
-
-- nsm_log(LOG_DEBUG, "Host %s due in %ld seconds",
-+ xlog(D_GENERAL, "Host %s due in %ld seconds",
- hosts->name, wait);
-
- pfd.fd = sock;
-@@ -405,34 +535,18 @@ notify(void)
- static int
- notify_host(int sock, struct nsm_host *host)
- {
-- struct sockaddr_storage address;
-- struct sockaddr *dest = (struct sockaddr *)&address;
-- socklen_t destlen = sizeof(address);
-- static unsigned int xid = 0;
-- uint32_t msgbuf[MAXMSGSIZE], *p;
-- unsigned int len;
--
-- if (!xid)
-- xid = getpid() + time(NULL);
-- if (!host->xid)
-- host->xid = xid++;
-+ struct sockaddr *sap;
-+ socklen_t salen;
-
- if (host->ai == NULL) {
- host->ai = smn_lookup(host->name);
- if (host->ai == NULL) {
-- nsm_log(LOG_WARNING,
-- "DNS resolution of %s failed; "
-+ xlog_warn("DNS resolution of %s failed; "
- "retrying later", host->name);
- return 0;
- }
- }
-
-- memset(msgbuf, 0, sizeof(msgbuf));
-- p = msgbuf;
-- *p++ = htonl(host->xid);
-- *p++ = 0;
-- *p++ = htonl(2);
--
- /* If we retransmitted 4 times, reset the port to force
- * a new portmap lookup (in case statd was restarted).
- * We also rotate through multiple IP addresses at this
-@@ -440,10 +554,7 @@ notify_host(int sock, struct nsm_host *host)
- */
- if (host->retries >= 4) {
- /* don't rotate if there is only one addrinfo */
-- if (host->ai->ai_next == NULL)
-- memcpy(&host->addr, host->ai->ai_addr,
-- host->ai->ai_addrlen);
-- else {
-+ if (host->ai->ai_next != NULL) {
- struct addrinfo *first = host->ai;
- struct addrinfo **next = &host->ai;
-
-@@ -456,213 +567,100 @@ notify_host(int sock, struct nsm_host *host)
- next = & (*next)->ai_next;
- /* put first entry at end */
- *next = first;
-- memcpy(&host->addr, first->ai_addr,
-- first->ai_addrlen);
- }
-
-- smn_set_port((struct sockaddr *)&host->addr, 0);
-+ nfs_set_port(host->ai->ai_addr, 0);
- host->retries = 0;
- }
-
-- memcpy(dest, &host->addr, destlen);
-- if (smn_get_port(dest) == 0) {
-- /* Build a PMAP packet */
-- nsm_log(LOG_DEBUG, "Sending portmap query to %s", host->name);
-+ sap = host->ai->ai_addr;
-+ salen = host->ai->ai_addrlen;
-
-- smn_set_port(dest, 111);
-- *p++ = htonl(100000);
-- *p++ = htonl(2);
-- *p++ = htonl(3);
--
-- /* Auth and verf */
-- *p++ = 0; *p++ = 0;
-- *p++ = 0; *p++ = 0;
--
-- *p++ = htonl(NSM_PROGRAM);
-- *p++ = htonl(NSM_VERSION);
-- *p++ = htonl(IPPROTO_UDP);
-- *p++ = 0;
-- } else {
-- /* Build an SM_NOTIFY packet */
-- nsm_log(LOG_DEBUG, "Sending SM_NOTIFY to %s", host->name);
--
-- *p++ = htonl(NSM_PROGRAM);
-- *p++ = htonl(NSM_VERSION);
-- *p++ = htonl(NSM_NOTIFY);
--
-- /* Auth and verf */
-- *p++ = 0; *p++ = 0;
-- *p++ = 0; *p++ = 0;
--
-- /* state change */
-- len = strlen(nsm_hostname);
-- *p++ = htonl(len);
-- memcpy(p, nsm_hostname, len);
-- p += (len + 3) >> 2;
-- *p++ = htonl(nsm_state);
-- }
-- len = (p - msgbuf) << 2;
--
-- if (sendto(sock, msgbuf, len, 0, dest, destlen) < 0)
-- nsm_log(LOG_WARNING, "Sending Reboot Notification to "
-- "'%s' failed: errno %d (%s)", host->name, errno, strerror(errno));
-+ if (nfs_get_port(sap) == 0)
-+ host->xid = nsm_xmit_rpcbind(sock, sap, SM_PROG, SM_VERS);
-+ else
-+ host->xid = nsm_xmit_notify(sock, sap, salen,
-+ SM_PROG, nsm_hostname, nsm_state);
-
- return 0;
- }
-
- /*
-- * Receive reply from remote host
-+ * Extract the returned port number and set up the SM_NOTIFY call.
- */
- static void
--recv_reply(int sock)
-+recv_rpcbind_reply(struct sockaddr *sap, struct nsm_host *host, XDR *xdr)
- {
-- struct nsm_host *hp;
-- struct sockaddr *sap;
-- uint32_t msgbuf[MAXMSGSIZE], *p, *end;
-- uint32_t xid;
-- int res;
--
-- res = recv(sock, msgbuf, sizeof(msgbuf), 0);
-- if (res < 0)
-- return;
--
-- nsm_log(LOG_DEBUG, "Received packet...");
-+ uint16_t port = nsm_recv_rpcbind(sap->sa_family, xdr);
-
-- p = msgbuf;
-- end = p + (res >> 2);
--
-- xid = ntohl(*p++);
-- if (*p++ != htonl(1) /* must be REPLY */
-- || *p++ != htonl(0) /* must be ACCEPTED */
-- || *p++ != htonl(0) /* must be NULL verifier */
-- || *p++ != htonl(0)
-- || *p++ != htonl(0)) /* must be SUCCESS */
-- return;
-+ host->send_next = time(NULL);
-+ host->xid = 0;
-
-- /* Before we look at the data, find the host struct for
-- this reply */
-- if ((hp = find_host(xid)) == NULL)
-- return;
-- sap = (struct sockaddr *)&hp->addr;
--
-- if (smn_get_port(sap) == 0) {
-- /* This was a portmap request */
-- unsigned int port;
--
-- port = ntohl(*p++);
-- if (p > end)
-- goto fail;
--
-- hp->send_next = time(NULL);
-- if (port == 0) {
-- /* No binding for statd. Delay the next
-- * portmap query for max timeout */
-- nsm_log(LOG_DEBUG, "No statd on %s", hp->name);
-- hp->timeout = NSM_MAX_TIMEOUT;
-- hp->send_next += NSM_MAX_TIMEOUT;
-- } else {
-- smn_set_port(sap, port);
-- if (hp->timeout >= NSM_MAX_TIMEOUT / 4)
-- hp->timeout = NSM_MAX_TIMEOUT / 4;
-- }
-- hp->xid = 0;
-+ if (port == 0) {
-+ /* No binding for statd... */
-+ xlog(D_GENERAL, "No statd on host %s", host->name);
-+ host->timeout = NSM_MAX_TIMEOUT;
-+ host->send_next += NSM_MAX_TIMEOUT;
- } else {
-- /* Successful NOTIFY call. Server returns void,
-- * so nothing we need to do here (except
-- * check that we didn't read past the end of the
-- * packet)
-- */
-- if (p <= end) {
-- nsm_log(LOG_DEBUG, "Host %s notified successfully",
-- hp->name);
-- smn_forget_host(hp);
-- return;
-- }
-+ nfs_set_port(sap, port);
-+ if (host->timeout >= NSM_MAX_TIMEOUT / 4)
-+ host->timeout = NSM_MAX_TIMEOUT / 4;
- }
-
--fail: /* Re-insert the host */
-- insert_host(hp);
-+ insert_host(host);
- }
-
- /*
-- * Back up all hosts from the sm directory to sm.bak
-+ * Successful NOTIFY call. Server returns void, so nothing
-+ * we need to do here.
- */
- static void
--backup_hosts(const char *dirname, const char *bakname)
-+recv_notify_reply(struct nsm_host *host)
- {
-- struct dirent *de;
-- DIR *dir;
--
-- if (!(dir = opendir(dirname))) {
-- nsm_log(LOG_WARNING,
-- "Failed to open %s: %s", dirname, strerror(errno));
-- return;
-- }
-+ xlog(D_GENERAL, "Host %s notified successfully", host->name);
-
-- while ((de = readdir(dir)) != NULL) {
-- char src[1024], dst[1024];
--
-- if (de->d_name[0] == '.')
-- continue;
--
-- snprintf(src, sizeof(src), "%s/%s", dirname, de->d_name);
-- snprintf(dst, sizeof(dst), "%s/%s", bakname, de->d_name);
-- if (rename(src, dst) < 0) {
-- nsm_log(LOG_WARNING,
-- "Failed to rename %s -> %s: %m",
-- src, dst);
-- }
-- }
-- closedir(dir);
-+ smn_forget_host(host);
- }
-
- /*
-- * Get all entries from sm.bak and convert them to host entries
-+ * Receive reply from remote host
- */
- static void
--get_hosts(const char *dirname)
-+recv_reply(int sock)
- {
-- struct nsm_host *host;
-- struct dirent *de;
-- DIR *dir;
-+ struct nsm_host *hp;
-+ struct sockaddr *sap;
-+ char msgbuf[NSM_MAXMSGSIZE];
-+ uint32_t xid;
-+ ssize_t msglen;
-+ XDR xdr;
-
-- if (!(dir = opendir(dirname))) {
-- nsm_log(LOG_WARNING,
-- "Failed to open %s: %s", dirname, strerror(errno));
-+ memset(msgbuf, 0 , sizeof(msgbuf));
-+ msglen = recv(sock, msgbuf, sizeof(msgbuf), 0);
-+ if (msglen < 0)
- return;
-- }
--
-- host = NULL;
-- while ((de = readdir(dir)) != NULL) {
-- struct stat stb;
-- char path[1024];
-
-- if (de->d_name[0] == '.')
-- continue;
-- if (host == NULL)
-- host = calloc(1, sizeof(*host));
-- if (host == NULL) {
-- nsm_log(LOG_WARNING, "Unable to allocate memory");
-- return;
-- }
-+ xlog(D_GENERAL, "Received packet...");
-
-- snprintf(path, sizeof(path), "%s/%s", dirname, de->d_name);
-- if (stat(path, &stb) < 0)
-- continue;
-+ memset(&xdr, 0, sizeof(xdr));
-+ xdrmem_create(&xdr, msgbuf, (unsigned int)msglen, XDR_DECODE);
-+ xid = nsm_parse_reply(&xdr);
-+ if (xid == 0)
-+ goto out;
-
-- host->last_used = stb.st_mtime;
-- host->timeout = NSM_TIMEOUT;
-- host->path = strdup(path);
-- host->name = strdup(de->d_name);
-- host->retries = 100; /* force address retry */
-+ /* Before we look at the data, find the host struct for
-+ this reply */
-+ if ((hp = find_host(xid)) == NULL)
-+ goto out;
-
-- insert_host(host);
-- host = NULL;
-- }
-- closedir(dir);
-+ sap = hp->ai->ai_addr;
-+ if (nfs_get_port(sap) == 0)
-+ recv_rpcbind_reply(sap, hp, &xdr);
-+ else
-+ recv_notify_reply(hp);
-
-- if (host)
-- free(host);
-+out:
-+ xdr_destroy(&xdr);
- }
-
- /*
-@@ -712,84 +710,6 @@ find_host(uint32_t xid)
- return NULL;
- }
-
--
--/*
-- * Retrieve the current NSM state
-- */
--static unsigned int
--nsm_get_state(int update)
--{
-- char newfile[PATH_MAX];
-- int fd, state;
--
-- if ((fd = open(_SM_STATE_PATH, O_RDONLY)) < 0) {
-- if (!opt_quiet) {
-- nsm_log(LOG_WARNING, "%s: %m", _SM_STATE_PATH);
-- nsm_log(LOG_WARNING, "Creating %s, set initial state 1",
-- _SM_STATE_PATH);
-- }
-- state = 1;
-- update = 1;
-- } else {
-- if (read(fd, &state, sizeof(state)) != sizeof(state)) {
-- nsm_log(LOG_WARNING,
-- "%s: bad file size, setting state = 1",
-- _SM_STATE_PATH);
-- state = 1;
-- update = 1;
-- } else {
-- if (!(state & 1))
-- state += 1;
-- }
-- close(fd);
-- }
--
-- if (update) {
-- state += 2;
-- snprintf(newfile, sizeof(newfile),
-- "%s.new", _SM_STATE_PATH);
-- if ((fd = open(newfile, O_CREAT|O_WRONLY, 0644)) < 0) {
-- nsm_log(LOG_ERR, "Cannot create %s: %m", newfile);
-- exit(1);
-- }
-- if (write(fd, &state, sizeof(state)) != sizeof(state)) {
-- nsm_log(LOG_ERR,
-- "Failed to write state to %s", newfile);
-- exit(1);
-- }
-- close(fd);
-- if (rename(newfile, _SM_STATE_PATH) < 0) {
-- nsm_log(LOG_ERR,
-- "Cannot create %s: %m", _SM_STATE_PATH);
-- exit(1);
-- }
-- sync();
-- }
--
-- return state;
--}
--
--/*
-- * Log a message
-- */
--static void
--nsm_log(int fac, const char *fmt, ...)
--{
-- va_list ap;
--
-- if (fac == LOG_DEBUG && !opt_debug)
-- return;
--
-- va_start(ap, fmt);
-- if (log_syslog)
-- vsyslog(fac, fmt, ap);
-- else {
-- vfprintf(stderr, fmt, ap);
-- fputs("\n", stderr);
-- }
-- va_end(ap);
--}
--
- /*
- * Record pid in /var/run/sm-notify.pid
- * This file should remain until a reboot, even if the
-@@ -799,61 +719,20 @@ nsm_log(int fac, const char *fmt, ...)
- static int record_pid(void)
- {
- char pid[20];
-+ ssize_t len;
- int fd;
-
-- snprintf(pid, 20, "%d\n", getpid());
-+ (void)snprintf(pid, sizeof(pid), "%d\n", (int)getpid());
- fd = open("/var/run/sm-notify.pid", O_CREAT|O_EXCL|O_WRONLY, 0600);
- if (fd < 0)
- return 0;
-- if (write(fd, pid, strlen(pid)) != strlen(pid)) {
-- nsm_log(LOG_WARNING, "Writing to pid file failed: errno %d(%s)",
-- errno, strerror(errno));
-- }
-- close(fd);
-- return 1;
--}
-
--/* Drop privileges to match owner of state-directory
-- * (in case a reply triggers some unknown bug).
-- */
--static void drop_privs(void)
--{
-- struct stat st;
--
-- if (stat(_SM_DIR_PATH, &st) == -1 &&
-- stat(_SM_BASE_PATH, &st) == -1) {
-- st.st_uid = 0;
-- st.st_gid = 0;
-- }
--
-- if (st.st_uid == 0) {
-- nsm_log(LOG_WARNING,
-- "sm-notify running as root. chown %s to choose different user",
-- _SM_DIR_PATH);
-- return;
-- }
--
-- setgroups(0, NULL);
-- if (setgid(st.st_gid) == -1
-- || setuid(st.st_uid) == -1) {
-- nsm_log(LOG_ERR, "Fail to drop privileges");
-- exit(1);
-+ len = write(fd, pid, strlen(pid));
-+ if ((len < 0) || ((size_t)len != strlen(pid))) {
-+ xlog_warn("Writing to pid file failed: errno %d (%m)",
-+ errno);
- }
--}
-
--static void set_kernel_nsm_state(int state)
--{
-- int fd;
-- const char *file = "/proc/sys/fs/nfs/nsm_local_state";
--
-- fd = open(file ,O_WRONLY);
-- if (fd >= 0) {
-- char buf[20];
-- snprintf(buf, sizeof(buf), "%d", state);
-- if (write(fd, buf, strlen(buf)) != strlen(buf)) {
-- nsm_log(LOG_WARNING, "Writing to '%s' failed: errno %d (%s)",
-- file, errno, strerror(errno));
-- }
-- close(fd);
-- }
-+ (void)close(fd);
-+ return 1;
- }
-diff --git a/utils/statd/sm-notify.man b/utils/statd/sm-notify.man
-index dd03b8d..163713e 100644
---- a/utils/statd/sm-notify.man
-+++ b/utils/statd/sm-notify.man
-@@ -1,166 +1,315 @@
--.\"
--.\" sm-notify(8)
-+.\"@(#)sm-notify.8"
- .\"
- .\" Copyright (C) 2004 Olaf Kirch
--.TH sm-notify 8 "19 Mar 2007
-+.\"
-+.\" Rewritten by Chuck Lever , 2009.
-+.\" Copyright 2009 Oracle. All rights reserved.
-+.\"
-+.TH SM-NOTIFY 8 "1 November 2009
- .SH NAME
--sm-notify \- Send out NSM reboot notifications
-+sm-notify \- send reboot notifications to NFS peers
- .SH SYNOPSIS
--.BI "/sbin/sm-notify [-dfq] [-m " time "] [-p " port "] [-P " path "] [-v " my_name " ]
-+.BI "/usr/sbin/sm-notify [-dfn] [-m " minutes "] [-v " name "] [-p " notify-port "] [-P " path "]
- .SH DESCRIPTION
--File locking over NFS (v2 and v3) requires a facility to notify peers in
--case of a reboot, so that clients can reclaim locks after
--a server crash, and/or
--servers can release locks held by the rebooted client.
-+File locks are not part of persistent file system state.
-+Lock state is thus lost when a host reboots.
-+.PP
-+Network file systems must also detect when lock state is lost
-+because a remote host has rebooted.
-+After an NFS client reboots, an NFS server must release all file locks
-+held by applications that were running on that client.
-+After a server reboots, a client must remind the
-+server of file locks held by applications running on that client.
- .PP
--This is a two-step process: during normal
--operations, a mechanism is required to keep track of which
--hosts need to be informed of a reboot. And of course,
--notifications need to be sent out during reboot.
--The protocol used for this is called NSM, for
--.IR "Network Status Monitor" .
-+For NFS version 2 and version 3, the
-+.I Network Status Monitor
-+protocol (or NSM for short)
-+is used to notify NFS peers of reboots.
-+On Linux, two separate user-space components constitute the NSM service:
-+.TP
-+.B sm-notify
-+A helper program that notifies NFS peers after the local system reboots
-+.TP
-+.B rpc.statd
-+A daemon that listens for reboot notifications from other hosts, and
-+manages the list of hosts to be notified when the local system reboots
- .PP
--This implementation separates these into separate program.
-+The local NFS lock manager alerts its local
- .B rpc.statd
--tracks hosts which need to be notified and this
-+of each remote peer that should be monitored.
-+When the local system reboots, the
- .B sm-notify
--performs the notification. When
-+command notifies the NSM service on monitored peers of the reboot.
-+When a remote reboots, that peer notifies the local
-+.BR rpc.statd ,
-+which in turn passes the reboot notification
-+back to the local NFS lock manager.
-+.SH NSM OPERATION IN DETAIL
-+The first file locking interaction between an NFS client and server causes
-+the NFS lock managers on both peers to contact their local NSM service to
-+store information about the opposite peer.
-+On Linux, the local lock manager contacts
-+.BR rpc.statd .
-+.PP
-+.B rpc.statd
-+records information about each monitored NFS peer on persistent storage.
-+This information describes how to contact a remote peer
-+in case the local system reboots,
-+how to recognize which monitored peer is reporting a reboot,
-+and how to notify the local lock manager when a monitored peer
-+indicates it has rebooted.
-+.PP
-+An NFS client sends a hostname, known as the client's
-+.IR caller_name ,
-+in each file lock request.
-+An NFS server can use this hostname to send asynchronous GRANT
-+calls to a client, or to notify the client it has rebooted.
-+.PP
-+The Linux NFS server can provide the client's
-+.I caller_name
-+or the client's network address to
-+.BR rpc.statd .
-+For the purposes of the NSM protocol,
-+this name or address is known as the monitored peer's
-+.IR mon_name .
-+In addition, the local lock manager tells
- .B rpc.statd
--is started it will typically started
-+what it thinks its own hostname is.
-+For the purposes of the NSM protocol,
-+this hostname is known as
-+.IR my_name .
-+.PP
-+There is no equivalent interaction between an NFS server and a client
-+to inform the client of the server's
-+.IR caller_name .
-+Therefore NFS clients do not actually know what
-+.I mon_name
-+an NFS server might use in an SM_NOTIFY request.
-+The Linux NFS client records the server's hostname used on the mount command
-+to identify rebooting NFS servers.
-+.SS Reboot notification
-+When the local system reboots, the
-+.B sm-notify
-+command reads the list of monitored peers from persistent storage and
-+sends an SM_NOTIFY request to the NSM service on each listed remote peer.
-+It uses the
-+.I mon_name
-+string as the destination.
-+To identify which host has rebooted, the
- .B sm-notify
--but this is configurable.
--.SS Operation
--For each NFS client or server machine to be monitored,
-+command normally sends the results of
-+.BR gethostname (3)
-+as the
-+.I my_name
-+string.
-+The remote
- .B rpc.statd
--creates a file in
--.BR /var/lib/nfs/sm ", "
--and removes the file if monitoring is no longer required.
-+matches incoming SM_NOTIFY requests using this string,
-+or the caller's network address,
-+to one or more peers on its own monitor list.
- .PP
--When the machine is rebooted,
-+If
-+.B rpc.statd
-+does not find a peer on its monitor list that matches
-+an incoming SM_NOTIFY request,
-+the notification is not forwarded to the local lock manager.
-+In addition, each peer has its own
-+.IR "NSM state number" ,
-+a 32-bit integer that is bumped after each reboot by the
- .B sm-notify
--iterates through these files and notifies the peer
--.B statd
--server on those machines.
-+command.
-+.B rpc.statd
-+uses this number to distinguish between actual reboots
-+and replayed notifications.
- .PP
--Each machine has an
--.I "NSM state" ,
--which is basically an integer counter that is incremented
--each time the machine reboots. This counter is stored
--in
--.BR /var/lib/nfs/state ,
--and updated by
--.BR sm-notify .
--.SS Security
--.B sm-notify
--has little need for root privileges and so drops them as soon as
--possible.
--It continues to need to make changes to the
--.B sm
--and
--.B sm.bak
--directories so to be able to drop privileges, these must be writable
--by a non-privileged user. If these directories are owned by a
--non-root user,
--.B sm-notify
--will drop privilege to match that user once it has created sockets for
--sending out request (for which it needs privileged) but before it
--processes any reply (which is the most likely source of possible
--privilege abuse).
-+Part of NFS lock recovery is rediscovering
-+which peers need to be monitored again.
-+The
-+.B sm-notify
-+command clears the monitor list on persistent storage after each reboot.
- .SH OPTIONS
- .TP
--.BI -m " failtime
--When notifying hosts,
-+.B -d
-+Keeps
-+.B sm-notify
-+attached to its controlling terminal and running in the foreground
-+so that notification progress may be monitored directly.
-+.TP
-+.B -f
-+Send notifications even if
-+.B sm-notify
-+has already run since the last system reboot.
-+.TP
-+.BI -m " retry-time
-+Specifies the length of time, in minutes, to continue retrying
-+notifications to unresponsive hosts.
-+If this option is not specified,
- .B sm-notify
--will try to contact each host for up to 15 minutes,
--and will give up if unable to reach it within this time
--frame.
-+attempts to send notifications for 15 minutes.
-+Specifying a value of 0 causes
-+.B sm-notify
-+to continue sending notifications to unresponsive peers
-+until it is manually killed.
-+.IP
-+Notifications are retried if sending fails,
-+the remote does not respond,
-+the remote's NSM service is not registered,
-+or if there is a DNS failure
-+which prevents the remote's
-+.I mon_name
-+from being resolved to an address.
- .IP
--Using the
--.B -m
--option, you can override this. A value of 0 tells
--sm-notify to retry indefinitely; any other value is
--interpreted as the maximum retry time in minutes.
-+Hosts are not removed from the notification list until a valid
-+reply has been received.
-+However, the SM_NOTIFY procedure has a void result.
-+There is no way for
-+.B sm-notify
-+to tell if the remote recognized the sender and has started
-+appropriate lock recovery.
- .TP
--.BI -v " ipaddr-or-hostname
--This option tells
--.B sm-notify
--to bind to the specified
--.IR ipaddr ,
--(or the ipaddr of the given
--.IR hostname )
--so that all notification packets originate from this address.
--This is useful for NFS failover. The given name is also used as the
--.I name
--of this host in the NSM request.
-+.B -n
-+Prevents
-+.B sm-notify
-+from updating the local system's NSM state number.
- .TP
- .BI -p " port
--instructs
-+Specifies the source port number
- .B sm-notify
--to bind to the indicated IP
--.IR port
--number. If this option is not given, it will try to bind to
--a randomly chosen privileged port below 1024.
--.TP
--.B -q
--Be quiet. This suppresses all messages except error
--messages while collecting the list of hosts.
-+should use when sending reboot notifications.
-+If this option is not specified, a randomly chosen ephemeral port is used.
-+.IP
-+This option can be used to traverse a firewall between client and server.
- .TP
--.BI -P " /path/to/state/directory
--If
-+.BI "\-P, " "" \-\-state\-directory\-path " pathname
-+Specifies the pathname of the parent directory
-+where NSM state information resides.
-+If this option is not specified,
-+.B sm-notify
-+uses
-+.I /var/lib/nfs
-+by default.
-+.IP
-+After starting,
- .B sm-notify
--should look in a no-standard place of state file, the path can be
--given here. The directories
--.B sm
--and
--.B sm.bak
--and the file
--.B state
--must exist in that directory with the standard names.
-+attempts to set its effective UID and GID to the owner
-+and group of this directory.
- .TP
--.B -f
--If the state path has not been reset with
--.BR -P ,
-+.BI -v " ipaddr " | " hostname
-+Specifies the network address from which to send reboot notifications,
-+and the
-+.I mon_name
-+argument to use when sending SM_NOTIFY requests.
-+If this option is not specified,
- .B sm-notify
--will normally create a file in
--.B /var/run
--to indicate that it has been
--run. If this file is found when
-+uses a wildcard address as the transport bind address,
-+and uses the results of
-+.BR gethostname (3)
-+as the
-+.I mon_name
-+argument.
-+.IP
-+The
-+.I ipaddr
-+form can be expressed as either an IPv4 or an IPv6 presentation address.
-+.IP
-+This option can be useful in multi-homed configurations where
-+the remote requires notification from a specific network address.
-+.SH SECURITY
-+The
- .B sm-notify
--starts, it will not run again (as it is normally only needed once per
--reboot).
--If
--.B -f
--(for
--.BR force )
--is given,
-+command must be started as root to acquire privileges needed
-+to access the state information database.
-+It drops root privileges
-+as soon as it starts up to reduce the risk of a privilege escalation attack.
-+.PP
-+During normal operation,
-+the effective user ID it chooses is the owner of the state directory.
-+This allows it to continue to access files in that directory after it
-+has dropped its root privileges.
-+To control which user ID
-+.B rpc.statd
-+chooses, simply use
-+.BR chown (1)
-+to set the owner of
-+the state directory.
-+.SH ADDITIONAL NOTES
-+Lock recovery after a reboot is critical to maintaining data integrity
-+and preventing unnecessary application hangs.
-+.PP
-+To help
-+.B rpc.statd
-+match SM_NOTIFY requests to NLM requests, a number of best practices
-+should be observed, including:
-+.IP
-+The UTS nodename of your systems should match the DNS names that NFS
-+peers use to contact them
-+.IP
-+The UTS nodenames of your systems should always be fully qualified domain names
-+.IP
-+The forward and reverse DNS mapping of the UTS nodenames should be
-+consistent
-+.IP
-+The hostname the client uses to mount the server should match the server's
-+.I mon_name
-+in SM_NOTIFY requests it sends
-+.IP
-+The use of network addresses as a
-+.I mon_name
-+or a
-+.I my_name
-+string should be avoided when
-+interoperating with non-Linux NFS implementations.
-+.PP
-+Unmounting an NFS file system does not necessarily stop
-+either the NFS client or server from monitoring each other.
-+Both may continue monitoring each other for a time in case subsequent
-+NFS traffic between the two results in fresh mounts and additional
-+file locking.
-+.PP
-+On Linux, if the
-+.B lockd
-+kernel module is unloaded during normal operation,
-+all remote NFS peers are unmonitored.
-+This can happen on an NFS client, for example,
-+if an automounter removes all NFS mount
-+points due to inactivity.
-+.SS IPv6 and TI-RPC support
-+TI-RPC is a pre-requisite for supporting NFS on IPv6.
-+If TI-RPC support is built into the
- .B sm-notify
--will run even if the file in
--.B /var/run
--is present.
--.TP
--.B -n
--Do not update the NSM state. This is for testing only. Setting this
--flag implies
--.BR -f .
--.TP
--.B -d
--Enables debugging.
--By default,
-+command ,it will choose an appropriate IPv4 or IPv6 transport
-+based on the network address returned by DNS for each remote peer.
-+It should be fully compatible with remote systems
-+that do not support TI-RPC or IPv6.
-+.PP
-+Currently, the
- .B sm-notify
--forks and puts itself in the background after obtaining the
--list of hosts from
--.BR /var/lib/nfs/sm .
-+command supports sending notification only via datagram transport protocols.
- .SH FILES
--.BR /var/lib/nfs/state
--.br
--.BR /var/lib/nfs/sm/*
-+.TP 2.5i
-+.I /var/lib/nfs/sm
-+directory containing monitor list
-+.TP 2.5i
-+.I /var/lib/nfs/sm.bak
-+directory containing notify list
-+.TP 2.5i
-+.I /var/lib/nfs/state
-+NSM state number for this host
-+.TP 2.5i
-+.I /proc/sys/fs/nfs/nsm_local_state
-+kernel's copy of the NSM state number
-+.SH SEE ALSO
-+.BR rpc.statd (8),
-+.BR nfs (5),
-+.BR uname (2),
-+.BR hostname (7)
-+.PP
-+RFC 1094 - "NFS: Network File System Protocol Specification"
- .br
--.BR /var/lib/nfs/sm.bak/*
-+RFC 1813 - "NFS Version 3 Protocol Specification"
- .br
--.BR /var/run/sm-notify.pid
--.SH SEE ALSO
--.BR rpc.nfsd(8),
--.BR portmap(8)
-+OpenGroup Protocols for Interworking: XNFS, Version 3W - Chapter 11
- .SH AUTHORS
--.br
- Olaf Kirch
-+.br
-+Chuck Lever
-diff --git a/utils/statd/sm_inter.x b/utils/statd/sm_inter.x
-deleted file mode 100644
-index d8e0ad7..0000000
---- a/utils/statd/sm_inter.x
-+++ /dev/null
-@@ -1,131 +0,0 @@
--/*
-- * Copyright (C) 1986 Sun Microsystems, Inc.
-- * Modified by Jeffrey A. Uphoff, 1995, 1997-1999.
-- * Modified by Olaf Kirch, 1996.
-- * Modified by H.J. Lu, 1998.
-- *
-- * NSM for Linux.
-- */
--
--/*
-- * Copyright (c) 2009, Sun Microsystems, Inc.
-- * All rights reserved.
-- *
-- * Redistribution and use in source and binary forms, with or without
-- * modification, are permitted provided that the following conditions are met:
-- * - Redistributions of source code must retain the above copyright notice,
-- * this list of conditions and the following disclaimer.
-- * - Redistributions in binary form must reproduce the above copyright notice,
-- * this list of conditions and the following disclaimer in the documentation
-- * and/or other materials provided with the distribution.
-- * - Neither the name of Sun Microsystems, Inc. nor the names of its
-- * contributors may be used to endorse or promote products derived
-- * from this software without specific prior written permission.
-- *
-- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-- * POSSIBILITY OF SUCH DAMAGE.
-- */
--
--/*
-- * Status monitor protocol specification
-- */
--
--#ifdef RPC_CLNT
--%#include
--#endif
--
--program SM_PROG {
-- version SM_VERS {
-- /* res_stat = stat_succ if status monitor agrees to monitor */
-- /* res_stat = stat_fail if status monitor cannot monitor */
-- /* if res_stat == stat_succ, state = state number of site sm_name */
-- struct sm_stat_res SM_STAT(struct sm_name) = 1;
--
-- /* res_stat = stat_succ if status monitor agrees to monitor */
-- /* res_stat = stat_fail if status monitor cannot monitor */
-- /* stat consists of state number of local site */
-- struct sm_stat_res SM_MON(struct mon) = 2;
--
-- /* stat consists of state number of local site */
-- struct sm_stat SM_UNMON(struct mon_id) = 3;
--
-- /* stat consists of state number of local site */
-- struct sm_stat SM_UNMON_ALL(struct my_id) = 4;
--
-- void SM_SIMU_CRASH(void) = 5;
--
-- void SM_NOTIFY(struct stat_chge) = 6;
--
-- } = 1;
--} = 100024;
--
--const SM_MAXSTRLEN = 1024;
--const SM_PRIV_SIZE = 16;
--
--struct sm_name {
-- string mon_name;
--};
--
--struct my_id {
-- string my_name; /* name of the site iniates the monitoring request*/
-- int my_prog; /* rpc program # of the requesting process */
-- int my_vers; /* rpc version # of the requesting process */
-- int my_proc; /* rpc procedure # of the requesting process */
--};
--
--struct mon_id {
-- string mon_name; /* name of the site to be monitored */
-- struct my_id my_id;
--};
--
--
--struct mon {
-- struct mon_id mon_id;
-- opaque priv[SM_PRIV_SIZE]; /* private information to store at monitor for requesting process */
--};
--
--struct stat_chge {
-- string mon_name; /* name of the site that had the state change */
-- int state;
--};
--
--/*
-- * state # of status monitor monitonically increases each time
-- * status of the site changes:
-- * an even number (>= 0) indicates the site is down and
-- * an odd number (> 0) indicates the site is up;
-- */
--struct sm_stat {
-- int state; /* state # of status monitor */
--};
--
--enum res {
-- stat_succ = 0, /* status monitor agrees to monitor */
-- stat_fail = 1 /* status monitor cannot monitor */
--};
--
--struct sm_stat_res {
-- res res_stat;
-- int state;
--};
--
--/*
-- * structure of the status message sent back by the status monitor
-- * when monitor site status changes
-- */
--struct status {
-- string mon_name;
-- int state;
-- opaque priv[SM_PRIV_SIZE]; /* stored private information */
--};
--
--%#define SM_INTER_X
-diff --git a/utils/statd/stat.c b/utils/statd/stat.c
-index 799239f..8d8b65e 100644
---- a/utils/statd/stat.c
-+++ b/utils/statd/stat.c
-@@ -12,7 +12,7 @@
- #include
- #include "statd.h"
-
--/*
-+/*
- * Services SM_STAT requests.
- *
- * According the the X/Open spec's on this procedure: "Implementations
-@@ -37,18 +37,23 @@
- * other way to get it.
- * 3/ That's what we always did in the past.
- */
--struct sm_stat_res *
--sm_stat_1_svc (struct sm_name *argp, struct svc_req *rqstp)
-+struct sm_stat_res *
-+sm_stat_1_svc(struct sm_name *argp,
-+ __attribute__ ((unused)) struct svc_req *rqstp)
- {
- static sm_stat_res result;
-+ char *name;
-+
-+ xlog(D_CALL, "Received SM_STAT from %s", argp->mon_name);
-
-- if (gethostbyname (argp->mon_name) == NULL) {
-- note (N_WARNING, "gethostbyname error for %s", argp->mon_name);
-+ name = statd_canonical_name(argp->mon_name);
-+ if (name == NULL) {
- result.res_stat = STAT_FAIL;
-- dprintf (N_DEBUG, "STAT_FAIL for %s", argp->mon_name);
-+ xlog (D_GENERAL, "STAT_FAIL for %s", argp->mon_name);
- } else {
- result.res_stat = STAT_SUCC;
-- dprintf (N_DEBUG, "STAT_SUCC for %s", argp->mon_name);
-+ xlog (D_GENERAL, "STAT_SUCC for %s", argp->mon_name);
-+ free(name);
- }
- result.state = MY_STATE;
- return(&result);
-diff --git a/utils/statd/statd.c b/utils/statd/statd.c
-index 1c5247e..7be6454 100644
---- a/utils/statd/statd.c
-+++ b/utils/statd/statd.c
-@@ -25,33 +25,21 @@
- #include
- #include
- #include
-+
- #include "statd.h"
--#include "version.h"
- #include "nfslib.h"
-+#include "nsm.h"
-
- /* Socket operations */
- #include
- #include
-
--/* Added to enable specification of state directory path at run-time
-- * j_carlos_gomez@yahoo.com
-- */
--
--char * DIR_BASE = DEFAULT_DIR_BASE;
--
--char * SM_DIR = DEFAULT_SM_DIR;
--char * SM_BAK_DIR = DEFAULT_SM_BAK_DIR;
--char * SM_STAT_PATH = DEFAULT_SM_STAT_PATH;
--
--/* ----- end of state directory path stuff ------- */
--
- int run_mode = 0; /* foreground logging mode */
-
- /* LH - I had these local to main, but it seemed silly to have
- * two copies of each - one in main(), one static in log.c...
- * It also eliminates the 256-char static in log.c */
--char *name_p = NULL;
--const char *version_p = NULL;
-+static char *name_p = NULL;
-
- /* PRC: a high-availability callout program can be specified with -H
- * When this is done, the program will receive callouts whenever clients
-@@ -75,7 +63,6 @@ static struct option longopts[] =
- };
-
- extern void sm_prog_1 (struct svc_req *, register SVCXPRT *);
--static void load_state_number(void);
-
- #ifdef SIMULATIONS
- extern void simulator (int, char **);
-@@ -103,23 +90,26 @@ sm_prog_1_wrapper (struct svc_req *rqstp, register SVCXPRT *transp)
- #define sm_prog_1 sm_prog_1_wrapper
- #endif
-
-+static void
-+statd_unregister(void) {
-+ nfs_svc_unregister(SM_PROG, SM_VERS);
-+}
-+
- /*
- * Signal handler.
- */
- static void
- killer (int sig)
- {
-- note (N_FATAL, "Caught signal %d, un-registering and exiting.", sig);
-- pmap_unset (SM_PROG, SM_VERS);
--
-- exit (0);
-+ statd_unregister ();
-+ xlog_err ("Caught signal %d, un-registering and exiting", sig);
- }
-
- static void
- sigusr (int sig)
- {
- extern void my_svc_exit (void);
-- dprintf (N_DEBUG, "Caught signal %d, re-notifying (state %d).", sig,
-+ xlog(D_GENERAL, "Caught signal %d, re-notifying (state %d)", sig,
- MY_STATE);
- my_svc_exit();
- }
-@@ -140,8 +130,11 @@ static void log_modes(void)
- strcat(buf,"No-Daemon ");
- if (run_mode & MODE_LOG_STDERR)
- strcat(buf,"Log-STDERR ");
-+#ifdef HAVE_LIBTIRPC
-+ strcat(buf, "TI-RPC ");
-+#endif
-
-- note(N_WARNING,buf);
-+ xlog_warn(buf);
- }
-
- /*
-@@ -175,13 +168,12 @@ static void create_pidfile(void)
- unlink(pidfile);
- fp = fopen(pidfile, "w");
- if (!fp)
-- die("Opening %s failed: %s\n",
-- pidfile, strerror(errno));
-+ xlog_err("Opening %s failed: %m\n", pidfile);
- fprintf(fp, "%d\n", getpid());
- pidfd = dup(fileno(fp));
- if (fclose(fp) < 0) {
-- note(N_WARNING, "Flushing pid file failed: errno %d (%s)\n",
-- errno, strerror(errno));
-+ xlog_warn("Flushing pid file failed: errno %d (%m)\n",
-+ errno);
- }
- }
-
-@@ -189,42 +181,10 @@ static void truncate_pidfile(void)
- {
- if (pidfd >= 0) {
- if (ftruncate(pidfd, 0) < 0) {
-- note(N_WARNING, "truncating pid file failed: errno %d (%s)\n",
-- errno, strerror(errno));
-- }
-- }
--}
--
--static void drop_privs(void)
--{
-- struct stat st;
--
-- if (stat(SM_DIR, &st) == -1 &&
-- stat(DIR_BASE, &st) == -1) {
-- st.st_uid = 0;
-- st.st_gid = 0;
-- }
--
-- if (st.st_uid == 0) {
-- note(N_WARNING, "statd running as root. chown %s to choose different user\n",
-- SM_DIR);
-- return;
-- }
-- /* better chown the pid file before dropping, as if it
-- * if over nfs we might loose access
-- */
-- if (pidfd >= 0) {
-- if (fchown(pidfd, st.st_uid, st.st_gid) < 0) {
-- note(N_ERROR, "Unable to change owner of %s: %d (%s)",
-- SM_DIR, strerror (errno));
-+ xlog_warn("truncating pid file failed: errno %d (%m)\n",
-+ errno);
- }
- }
-- setgroups(0, NULL);
-- if (setgid(st.st_gid) == -1
-- || setuid(st.st_uid) == -1) {
-- note(N_ERROR, "Fail to drop privileges");
-- exit(1);
-- }
- }
-
- static void run_sm_notify(int outport)
-@@ -266,6 +226,8 @@ int main (int argc, char **argv)
-
- /* Default: daemon mode, no other options */
- run_mode = 0;
-+ xlog_stderr(0);
-+ xlog_syslog(1);
-
- /* Set the basename */
- if ((name_p = strrchr(argv[0],'/')) != NULL) {
-@@ -274,13 +236,6 @@ int main (int argc, char **argv)
- name_p = argv[0];
- }
-
-- /* Get the version */
-- if ((version_p = strrchr(VERSION,' ')) != NULL) {
-- version_p++;
-- } else {
-- version_p = VERSION;
-- }
--
- /* Set hostname */
- MY_NAME = NULL;
-
-@@ -289,7 +244,7 @@ int main (int argc, char **argv)
- switch (arg) {
- case 'V': /* Version */
- case 'v':
-- printf("%s version %s\n",name_p,version_p);
-+ printf("%s version " VERSION "\n",name_p);
- exit(0);
- case 'F': /* Foreground/nodaemon mode */
- run_mode |= MODE_NODAEMON;
-@@ -326,34 +281,8 @@ int main (int argc, char **argv)
- MY_NAME = xstrdup(optarg);
- break;
- case 'P':
--
-- if ((DIR_BASE = xstrdup(optarg)) == NULL) {
-- fprintf(stderr, "%s: xstrdup(%s) failed!\n",
-- argv[0], optarg);
-+ if (!nsm_setup_pathnames(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]);
-- exit(1);
-- }
-- if (DIR_BASE[strlen(DIR_BASE)-1] == '/') {
-- sprintf(SM_DIR, "%ssm", DIR_BASE );
-- sprintf(SM_BAK_DIR, "%ssm.bak", DIR_BASE );
-- sprintf(SM_STAT_PATH, "%sstate", DIR_BASE );
-- } else {
-- sprintf(SM_DIR, "%s/sm", DIR_BASE );
-- sprintf(SM_BAK_DIR, "%s/sm.bak", DIR_BASE );
-- sprintf(SM_STAT_PATH, "%s/state", DIR_BASE );
-- }
- break;
- case 'H': /* PRC: specify the ha-callout program */
- if ((ha_callout_prog = xstrdup(optarg)) == NULL) {
-@@ -383,7 +312,6 @@ int main (int argc, char **argv)
- run_sm_notify(out_port);
- }
-
--
- if (!(run_mode & MODE_NODAEMON)) {
- run_mode &= ~MODE_LOG_STDERR; /* Never log to console in
- daemon mode. */
-@@ -432,10 +360,6 @@ int main (int argc, char **argv)
- /* Child. */
- close(pipefds[0]);
- setsid ();
-- if (chdir (DIR_BASE) == -1) {
-- perror("statd: Could not chdir");
-- exit(1);
-- }
-
- while (pipefds[1] <= 2) {
- pipefds[1] = dup(pipefds[1]);
-@@ -455,7 +379,13 @@ int main (int argc, char **argv)
-
- /* Child. */
-
-- log_init (/*name_p,version_p*/);
-+ if (run_mode & MODE_LOG_STDERR) {
-+ xlog_syslog(0);
-+ xlog_stderr(1);
-+ xlog_config(D_ALL, 1);
-+ }
-+ xlog_open(name_p);
-+ xlog(L_NOTICE, "Version " VERSION " starting");
-
- log_modes();
-
-@@ -495,25 +425,48 @@ int main (int argc, char **argv)
- * pass on any SM_NOTIFY that arrives
- */
- load_state();
-- load_state_number();
-- pmap_unset (SM_PROG, SM_VERS);
-
-- /* this registers both UDP and TCP services */
-- rpc_init("statd", SM_PROG, SM_VERS, sm_prog_1, port);
-+ MY_STATE = nsm_get_state(0);
-+ if (MY_STATE == 0)
-+ exit(1);
-+ xlog(D_GENERAL, "Local NSM state number: %d", MY_STATE);
-+ nsm_update_kernel_state(MY_STATE);
-+
-+ /*
-+ * ORDER
-+ * Clear old listeners while still root, to override any
-+ * permission checking done by rpcbind.
-+ */
-+ statd_unregister();
-+
-+ /*
-+ * ORDER
-+ */
-+ if (!nsm_drop_privileges(pidfd))
-+ exit(1);
-+
-+ /*
-+ * ORDER
-+ * Create RPC listeners after dropping privileges. This permits
-+ * statd to unregister its own listeners when it exits.
-+ */
-+ if (nfs_svc_create("statd", SM_PROG, SM_VERS, sm_prog_1, port) == 0) {
-+ xlog(L_ERROR, "failed to create RPC listeners, exiting");
-+ exit(1);
-+ }
-+ atexit(statd_unregister);
-
- /* If we got this far, we have successfully started, so notify parent */
- if (pipefds[1] > 0) {
- status = 0;
- if (write(pipefds[1], &status, 1) != 1) {
-- note(N_WARNING, "writing to parent pipe failed: errno %d (%s)\n",
-+ xlog_warn("writing to parent pipe failed: errno %d (%s)\n",
- errno, strerror(errno));
- }
- close(pipefds[1]);
- pipefds[1] = -1;
- }
-
-- drop_privs();
--
- for (;;) {
- /*
- * Handle incoming requests: SM_NOTIFY socket requests, as
-@@ -541,29 +494,3 @@ int main (int argc, char **argv)
- }
- return 0;
- }
--
--static void
--load_state_number(void)
--{
-- int fd;
-- const char *file = "/proc/sys/fs/nfs/nsm_local_state";
--
-- if ((fd = open(SM_STAT_PATH, O_RDONLY)) == -1)
-- return;
--
-- if (read(fd, &MY_STATE, sizeof(MY_STATE)) != sizeof(MY_STATE)) {
-- note(N_WARNING, "Unable to read state from '%s': errno %d (%s)",
-- SM_STAT_PATH, errno, strerror(errno));
-- }
-- close(fd);
-- fd = open(file, O_WRONLY);
-- if (fd >= 0) {
-- char buf[20];
-- snprintf(buf, sizeof(buf), "%d", MY_STATE);
-- if (write(fd, buf, strlen(buf)) != strlen(buf))
-- note(N_WARNING, "Writing to '%s' failed: errno %d (%s)",
-- file, errno, strerror(errno));
-- close(fd);
-- }
--
--}
-diff --git a/utils/statd/statd.h b/utils/statd/statd.h
-index 88ba208..e89e666 100644
---- a/utils/statd/statd.h
-+++ b/utils/statd/statd.h
-@@ -11,29 +11,7 @@
-
- #include "sm_inter.h"
- #include "system.h"
--#include "log.h"
--
--/*
-- * Paths and filenames.
-- */
--#if defined(NFS_STATEDIR)
--# define DEFAULT_DIR_BASE NFS_STATEDIR "/"
--#else
--# define DEFAULT_DIR_BASE "/var/lib/nfs/"
--#endif
--
--#define DEFAULT_SM_DIR DEFAULT_DIR_BASE "sm"
--#define DEFAULT_SM_BAK_DIR DEFAULT_DIR_BASE "sm.bak"
--#define DEFAULT_SM_STAT_PATH DEFAULT_DIR_BASE "state"
--
--/* Added to support run-time specification of state directory path.
-- * j_carlos_gomez@yahoo.com
-- */
--
--extern char * DIR_BASE;
--extern char * SM_DIR;
--extern char * SM_BAK_DIR;
--extern char * SM_STAT_PATH;
-+#include "xlog.h"
-
- /*
- * Status definitions.
-@@ -44,7 +22,12 @@ extern char * SM_STAT_PATH;
- /*
- * Function prototypes.
- */
--extern void change_state(void);
-+extern _Bool statd_matchhostname(const char *hostname1, const char *hostname2);
-+extern _Bool statd_present_address(const struct sockaddr *sap, char *buf,
-+ const size_t buflen);
-+__attribute_malloc__
-+extern char * statd_canonical_name(const char *hostname);
-+
- extern void my_svc_run(void);
- extern void notify_hosts(void);
- extern void shuffle_dirs(void);
-@@ -53,7 +36,6 @@ extern int process_notify_list(void);
- extern int process_reply(FD_SET_TYPE *);
- extern char * xstrdup(const char *);
- extern void * xmalloc(size_t);
--extern void xunlink (char *, char *);
- extern void load_state(void);
-
- /*
-@@ -84,10 +66,3 @@ extern int run_mode;
- * another host.... */
- #define STATIC_HOSTNAME 8 /* Always use the hostname set by -n */
- #define MODE_NO_NOTIFY 16 /* Don't notify peers of a reboot */
--/*
-- * Program name and version pointers -- See statd.c for the reasoning
-- * as to why they're global.
-- */
--extern char *name_p; /* program basename */
--extern const char *version_p; /* program version */
--
-diff --git a/utils/statd/statd.man b/utils/statd/statd.man
-index e8be9f3..4ddb634 100644
---- a/utils/statd/statd.man
-+++ b/utils/statd/statd.man
-@@ -1,191 +1,403 @@
--.\"
--.\" statd(8)
-+.\"@(#)rpc.statd.8"
- .\"
- .\" Copyright (C) 1999 Olaf Kirch
- .\" Modified by Jeffrey A. Uphoff, 1999, 2002, 2005.
- .\" Modified by Lon Hohberger, 2000.
- .\" Modified by Paul Clements, 2004.
--.TH rpc.statd 8 "31 Aug 2004"
-+.\"
-+.\" Rewritten by Chuck Lever , 2009.
-+.\" Copyright 2009 Oracle. All rights reserved.
-+.\"
-+.TH RPC.STATD 8 "1 November 2009
- .SH NAME
--rpc.statd \- NSM status monitor
-+rpc.statd \- NSM service daemon
- .SH SYNOPSIS
--.B "rpc.statd [-FNL] [-d] [-?] [-n " name "] [-o " port "] [-p " port "] [-H " prog "] [-V]"
-+.BI "rpc.statd [-dh?FLNvVw] [-H " prog "] [-n " my-name "] [-o " outgoing-port "] [-p " listener-port "] [-P " path " ]
- .SH DESCRIPTION
--The
-+File locks are not part of persistent file system state.
-+Lock state is thus lost when a host reboots.
-+.PP
-+Network file systems must also detect when lock state is lost
-+because a remote host has rebooted.
-+After an NFS client reboots, an NFS server must release all file locks
-+held by applications that were running on that client.
-+After a server reboots, a client must remind the
-+server of file locks held by applications running on that client.
-+.PP
-+For NFS version 2 [RFC1094] and NFS version 3 [RFC1813], the
-+.I Network Status Monitor
-+protocol (or NSM for short)
-+is used to notify NFS peers of reboots.
-+On Linux, two separate user-space components constitute the NSM service:
-+.TP
- .B rpc.statd
--server implements the NSM (Network Status Monitor) RPC protocol.
--This service is somewhat misnamed, since it doesn't actually provide
--active monitoring as one might suspect; instead, NSM implements a
--reboot notification service. It is used by the NFS file locking service,
--.BR rpc.lockd ,
--to implement lock recovery when the NFS server machine crashes and
--reboots.
--.SS Operation
--For each NFS client or server machine to be monitored,
--.B rpc.statd
--creates a file in
--.BR /var/lib/nfs/sm .
--When starting, it normally runs
-+A daemon that listens for reboot notifications from other hosts, and
-+manages the list of hosts to be notified when the local system reboots
-+.TP
-+.B sm-notify
-+A helper program that notifies NFS peers after the local system reboots
-+.PP
-+The local NFS lock manager alerts its local
-+.B rpc.statd
-+of each remote peer that should be monitored.
-+When the local system reboots, the
- .B sm-notify
--to iterate through these files and notify the
--peer
-+command notifies the NSM service on monitored peers of the reboot.
-+When a remote reboots, that peer notifies the local
-+.BR rpc.statd ,
-+which in turn passes the reboot notification
-+back to the local NFS lock manager.
-+.SH NSM OPERATION IN DETAIL
-+The first file locking interaction between an NFS client and server causes
-+the NFS lock managers on both peers to contact their local NSM service to
-+store information about the opposite peer.
-+On Linux, the local lock manager contacts
-+.BR rpc.statd .
-+.PP
-+.B rpc.statd
-+records information about each monitored NFS peer on persistent storage.
-+This information describes how to contact a remote peer
-+in case the local system reboots,
-+how to recognize which monitored peer is reporting a reboot,
-+and how to notify the local lock manager when a monitored peer
-+indicates it has rebooted.
-+.PP
-+An NFS client sends a hostname, known as the client's
-+.IR caller_name ,
-+in each file lock request.
-+An NFS server can use this hostname to send asynchronous GRANT
-+calls to a client, or to notify the client it has rebooted.
-+.PP
-+The Linux NFS server can provide the client's
-+.I caller_name
-+or the client's network address to
-+.BR rpc.statd .
-+For the purposes of the NSM protocol,
-+this name or address is known as the monitored peer's
-+.IR mon_name .
-+In addition, the local lock manager tells
- .B rpc.statd
--on those machines.
-+what it thinks its own hostname is.
-+For the purposes of the NSM protocol,
-+this hostname is known as
-+.IR my_name .
-+.PP
-+There is no equivalent interaction between an NFS server and a client
-+to inform the client of the server's
-+.IR caller_name .
-+Therefore NFS clients do not actually know what
-+.I mon_name
-+an NFS server might use in an SM_NOTIFY request.
-+The Linux NFS client uses the server hostname from the mount command
-+to identify rebooting NFS servers.
-+.SS Reboot notification
-+When the local system reboots, the
-+.B sm-notify
-+command reads the list of monitored peers from persistent storage and
-+sends an SM_NOTIFY request to the NSM service on each listed remote peer.
-+It uses the
-+.I mon_name
-+string as the destination.
-+To identify which host has rebooted, the
-+.B sm-notify
-+command normally sends the results of
-+.BR gethostname (3)
-+as the
-+.I my_name
-+string.
-+The remote
-+.B rpc.statd
-+matches incoming SM_NOTIFY requests using this string,
-+or the caller's network address,
-+to one or more peers on its own monitor list.
-+.PP
-+If
-+.B rpc.statd
-+does not find a peer on its monitor list that matches
-+an incoming SM_NOTIFY request,
-+the notification is not forwarded to the local lock manager.
-+In addition, each peer has its own
-+.IR "NSM state number" ,
-+a 32-bit integer that is bumped after each reboot by the
-+.B sm-notify
-+command.
-+.B rpc.statd
-+uses this number to distinguish between actual reboots
-+and replayed notifications.
-+.PP
-+Part of NFS lock recovery is rediscovering
-+which peers need to be monitored again.
-+The
-+.B sm-notify
-+command clears the monitor list on persistent storage after each reboot.
- .SH OPTIONS
- .TP
--.B -F
--By default,
-+.BR -d , " --no-syslog
-+Causes
- .B rpc.statd
--forks and puts itself in the background when started. The
--.B -F
--argument tells it to remain in the foreground. This option is
--mainly for debugging purposes.
--.TP
--.B -d
--By default,
--.B rpc.statd
--sends logging messages via
--.BR syslog (3)
--to system log. The
--.B -d
--argument forces it to log verbose output to
--.B stderr
--instead. This option is mainly for debugging purposes, and may only
--be used in conjunction with the
-+to write log messages on
-+.I stderr
-+instead of to the system log,
-+if the
- .B -F
--parameter.
-+option was also specified.
- .TP
--.BI "\-n," "" " \-\-name " name
--specify a name for
--.B rpc.statd
--to use as the local hostname. By default,
--.BR rpc.statd
--will call
--.BR gethostname (2)
--to get the local hostname. Specifying
--a local hostname may be useful for machines with more than one
--interfaces.
-+.BR -F , " --foreground
-+Keeps
-+.B rpc.statd
-+attached to its controlling terminal so that NSM
-+operation can be monitored directly or run under a debugger.
-+If this option is not specified,
-+.B rpc.statd
-+backgrounds itself soon after it starts.
- .TP
--.BI "\-o," "" " \-\-outgoing\-port " port
--specify a port for
--.B rpc.statd
--to send outgoing status requests from. By default,
--.BR rpc.statd
--will ask
--.BR portmap (8)
--to assign it a port number. As of this writing, there is not
--a standard port number that
--.BR portmap
--always or usually assigns. Specifying
--a port may be useful when implementing a firewall.
-+.BR -h , " -?" , " --help
-+Causes
-+.B rpc.statd
-+to display usage information on
-+.I stderr
-+and then exit.
- .TP
--.BI "\-p," "" " \-\-port " port
--specify a port for
--.B rpc.statd
--to listen on. By default,
--.BR rpc.statd
--will ask
--.BR portmap (8)
--to assign it a port number. As of this writing, there is not
--a standard port number that
--.BR portmap
--always or usually assigns. Specifying
--a port may be useful when implementing a firewall.
-+.BI "\-H," "" " \-\-ha-callout " prog
-+Specifies a high availability callout program.
-+If this option is not specified, no callouts are performed.
-+See the
-+.B High-availability callouts
-+section below for details.
- .TP
--.BI "\-P," "" " \-\-state\-directory\-path " directory
--specify a directory in which to place statd state information.
--If this option is not specified the default of
--.BR /var/lib/nfs
--is used.
-+.BR -L , " --no-notify
-+Prevents
-+.B rpc.statd
-+from running the
-+.B sm-notify
-+command when it starts up,
-+preserving the existing NSM state number and monitor list.
-+.IP
-+Note: the
-+.B sm-notify
-+command contains a check to ensure it runs only once after each system reboot.
-+This prevents spurious reboot notification if
-+.B rpc.statd
-+restarts without the
-+.B -L
-+option.
- .TP
--.B -N
--Causes statd to run in the notify-only mode. When started in this mode, the
--statd program will check its state directory, send notifications to any
--monitored nodes, and exit once the notifications have been sent. This mode is
--used to enable Highly Available NFS implementations (i.e. HA-NFS).
--This mode is deprecated \-
-+.BI "\-n, " "" "\-\-name " ipaddr " | " hostname
-+Specifies the bind address used for RPC listener sockets.
-+The
-+.I ipaddr
-+form can be expressed as either an IPv4 or an IPv6 presentation address.
-+If this option is not specified,
-+.B rpc.statd
-+uses a wildcard address as the transport bind address.
-+.IP
-+This string is also passed to the
- .B sm-notify
--should be used directly instead.
-+command to be used as the source address from which
-+to send reboot notification requests.
-+See
-+.BR sm-notify (8)
-+for details.
- .TP
--.BR -L , " --no-notify
--Inhibits the running of
--.BR sm-notify .
--If
-+.BR -N
-+Causes
-+.B rpc.statd
-+to run the
- .B sm-notify
--is run by some other script at boot time, there is no need for
--.B statd
--to start sm-notify itself. This can be appropriate if starting of
--statd needs to be delayed until it is actually need. In such cases
-+command, and then exit.
-+Since the
-+.B sm-notify
-+command can also be run directly, this option is deprecated.
-+.TP
-+.BI "\-o," "" " \-\-outgoing\-port " port
-+Specifies the source port number the
- .B sm-notify
--should still be run at boot time.
-+command should use when sending reboot notifications.
-+See
-+.BR sm-notify (8)
-+for details.
- .TP
--.BI "\-H, " "" " \-\-ha-callout " prog
--Specify a high availability callout program, which will receive callouts
--for all client monitor and unmonitor requests. This allows
-+.BI "\-p," "" " \-\-port " port
-+Specifies the port number used for RPC listener sockets.
-+If this option is not specified,
- .B rpc.statd
--to be used in a High Availability NFS (HA-NFS) environment. The
--program will be run with 3 arguments: The first is either
--.B add-client
--or
--.B del-client
--depending on the reason for the callout.
--The second will be the name of the client.
--The third will be the name of the server as known to the client.
-+chooses a random ephemeral port for each listener socket.
-+.IP
-+This option can be used to fix the port value of its listeners when
-+SM_NOTIFY requests must traverse a firewall between clients and servers.
- .TP
--.B -?
--Causes
-+.BI "\-P, " "" \-\-state\-directory\-path " pathname
-+Specifies the pathname of the parent directory
-+where NSM state information resides.
-+If this option is not specified,
- .B rpc.statd
--to print out command-line help and exit.
-+uses
-+.I /var/lib/nfs
-+by default.
-+.IP
-+After starting,
-+.B rpc.statd
-+attempts to set its effective UID and GID to the owner
-+and group of this directory.
- .TP
--.B -V
-+.BR -v ", " -V ", " --version
- Causes
- .B rpc.statd
--to print out version information and exit.
--
--
--
--.SH TCP_WRAPPERS SUPPORT
--This
-+to display version information on
-+.I stderr
-+and then exit.
-+.SH SECURITY
-+The
-+.B rpc.statd
-+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
--version is protected by the
-+listeners using the
-+.B tcp_wrapper
-+library or
-+.BR iptables (8).
-+Note that the
-+.B tcp_wrapper
-+library supports only IPv4 networking.
-+To use the
- .B tcp_wrapper
--library. You have to give the clients access to
--.B rpc.statd
--if they should be allowed to use it. To allow connects from clients of
--the .bar.com domain you could use the following line in /etc/hosts.allow:
--
--statd: .bar.com
--
--You have to use the daemon name
-+library, add the hostnames of peers that should be allowed access to
-+.IR /etc/hosts.allow .
-+Use the daemon name
- .B statd
--for the daemon name (even if the binary has a different name).
--
--For further information please have a look at the
-+even if the
-+.B rpc.statd
-+binary has a different filename.
-+.P
-+For further information see the
- .BR tcpd (8)
- and
- .BR hosts_access (5)
--manual pages.
--
--.SH SIGNALS
--.BR SIGUSR1
--causes
--.B rpc.statd
--to re-read the notify list from disk
--and send notifications to clients. This can be used in High Availability NFS
--(HA-NFS) environments to notify clients to reacquire file locks upon takeover
--of an NFS export from another server.
--
-+man pages.
-+.SH ADDITIONAL NOTES
-+Lock recovery after a reboot is critical to maintaining data integrity
-+and preventing unnecessary application hangs.
-+.PP
-+To help
-+.B rpc.statd
-+match SM_NOTIFY requests to NLM requests, a number of best practices
-+should be observed, including:
-+.IP
-+The UTS nodename of your systems should match the DNS names that NFS
-+peers use to contact them
-+.IP
-+The UTS nodenames of your systems should always be fully qualified domain names
-+.IP
-+The forward and reverse DNS mapping of the UTS nodenames should be
-+consistent
-+.IP
-+The hostname the client uses to mount the server should match the server's
-+.I mon_name
-+in SM_NOTIFY requests it sends
-+.IP
-+The use of network addresses as a
-+.I mon_name
-+or a
-+.I my_name
-+string should be avoided when
-+interoperating with non-Linux NFS implementations.
-+.PP
-+Unmounting an NFS file system does not necessarily stop
-+either the NFS client or server from monitoring each other.
-+Both may continue monitoring each other for a time in case subsequent
-+NFS traffic between the two results in fresh mounts and additional
-+file locking.
-+.PP
-+On Linux, if the
-+.B lockd
-+kernel module is unloaded during normal operation,
-+all remote NFS peers are unmonitored.
-+This can happen on an NFS client, for example,
-+if an automounter removes all NFS mount
-+points due to inactivity.
-+.SS High-availability callouts
-+.B rpc.statd
-+can exec a special callout program during processing of
-+successful SM_MON, SM_UNMON, and SM_UNMON_ALL requests.
-+Such a program may be used in High Availability NFS (HA-NFS)
-+environments to track lock state that may need to be migrated after
-+a system reboot.
-+.PP
-+The name of the callout program is specified with the
-+.B -H
-+option.
-+The program is run with 3 arguments:
-+The first is either
-+.B add-client
-+or
-+.B del-client
-+depending on the reason for the callout.
-+The second is the
-+.I mon_name
-+of the monitored peer.
-+The third is the
-+.I caller_name
-+of the requesting lock manager.
-+.SS IPv6 and TI-RPC support
-+TI-RPC is a pre-requisite for supporting NFS on IPv6.
-+If TI-RPC support is built into
-+.BR rpc.statd ,
-+it attempts to start listeners on network transports marked
-+'visible' in
-+.IR /etc/netconfig .
-+As long as at least one network transport listener starts successfully,
-+.B rpc.statd
-+will operate.
- .SH FILES
--.BR /var/lib/nfs/state
-+.TP 2.5i
-+.I /var/lib/nfs/sm
-+directory containing monitor list
-+.TP 2.5i
-+.I /var/lib/nfs/sm.bak
-+directory containing notify list
-+.TP 2.5i
-+.I /var/lib/nfs/state
-+NSM state number for this host
-+.TP 2.5i
-+.I /var/run/run.statd.pid
-+pid file
-+.TP 2.5i
-+.I /etc/netconfig
-+network transport capability database
-+.SH SEE ALSO
-+.BR sm-notify (8),
-+.BR nfs (5),
-+.BR rpc.nfsd (8),
-+.BR rpcbind (8),
-+.BR tcpd (8),
-+.BR hosts_access (5),
-+.BR iptables (8),
-+.BR netconfig (5)
-+.sp
-+RFC 1094 - "NFS: Network File System Protocol Specification"
- .br
--.BR /var/lib/nfs/sm/*
-+RFC 1813 - "NFS Version 3 Protocol Specification"
- .br
--.BR /var/lib/nfs/sm.bak/*
--.SH SEE ALSO
--.BR rpc.nfsd(8),
--.BR portmap(8)
-+OpenGroup Protocols for Interworking: XNFS, Version 3W - Chapter 11
- .SH AUTHORS
--.br
- Jeff Uphoff
- .br
- Olaf Kirch
-@@ -195,3 +407,5 @@ H.J. Lu
- Lon Hohberger
- .br
- Paul Clements
-+.br
-+Chuck Lever
-diff --git a/utils/statd/svc_run.c b/utils/statd/svc_run.c
-index 14ee663..d98ecee 100644
---- a/utils/statd/svc_run.c
-+++ b/utils/statd/svc_run.c
-@@ -101,12 +101,12 @@ my_svc_run(void)
-
- tv.tv_sec = NL_WHEN(notify) - now;
- tv.tv_usec = 0;
-- dprintf(N_DEBUG, "Waiting for reply... (timeo %d)",
-+ xlog(D_GENERAL, "Waiting for reply... (timeo %d)",
- tv.tv_sec);
- selret = select(FD_SETSIZE, &readfds,
- (void *) 0, (void *) 0, &tv);
- } else {
-- dprintf(N_DEBUG, "Waiting for client connections.");
-+ xlog(D_GENERAL, "Waiting for client connections");
- selret = select(FD_SETSIZE, &readfds,
- (void *) 0, (void *) 0, (struct timeval *) 0);
- }
-@@ -116,8 +116,7 @@ my_svc_run(void)
- if (errno == EINTR || errno == ECONNREFUSED
- || errno == ENETUNREACH || errno == EHOSTUNREACH)
- continue;
-- note(N_ERROR, "my_svc_run() - select: %s",
-- strerror (errno));
-+ xlog(L_ERROR, "my_svc_run() - select: %m");
- return;
-
- case 0:
-diff --git a/utils/statd/version.h b/utils/statd/version.h
-deleted file mode 100644
-index 12f16bd..0000000
---- a/utils/statd/version.h
-+++ /dev/null
-@@ -1,7 +0,0 @@
--/*
-- * Copyright (C) 1997-1999 Jeffrey A. Uphoff
-- *
-- * NSM for Linux.
-- */
--
--#define STATD_RELEASE "1.1.1"
diff --git a/nfs-utils-1.2.2-rc8.patch b/nfs-utils-1.2.2-rc8.patch
new file mode 100644
index 0000000..c2d6d0e
--- /dev/null
+++ b/nfs-utils-1.2.2-rc8.patch
@@ -0,0 +1,12162 @@
+diff --git a/.gitignore b/.gitignore
+index 632609e..4bff9e3 100644
+--- a/.gitignore
++++ b/.gitignore
+@@ -55,10 +55,15 @@ support/export/mount.h
+ support/export/mount_clnt.c
+ support/export/mount_xdr.c
+ support/include/mount.h
+-utils/statd/sm_inter.h
+-utils/statd/sm_inter_clnt.c
+-utils/statd/sm_inter_svc.c
+-utils/statd/sm_inter_xdr.c
++support/nsm/sm_inter.h
++support/nsm/sm_inter_clnt.c
++support/nsm/sm_inter_svc.c
++support/nsm/sm_inter_xdr.c
++support/include/sm_inter.h
++tests/nsm_client/nlm_sm_inter.h
++tests/nsm_client/nlm_sm_inter_clnt.c
++tests/nsm_client/nlm_sm_inter_svc.c
++tests/nsm_client/nlm_sm_inter_xdr.c
+ # cscope database files
+ cscope.*
+ # generic editor backup et al
+diff --git a/Makefile.am b/Makefile.am
+index b3a6e91..ae7cd16 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -2,7 +2,7 @@
+
+ AUTOMAKE_OPTIONS = foreign
+
+-SUBDIRS = tools support utils linux-nfs
++SUBDIRS = tools support utils linux-nfs tests
+
+ MAINTAINERCLEANFILES = Makefile.in
+
+diff --git a/aclocal/ipv6.m4 b/aclocal/ipv6.m4
+index 2490f3d..5ee8fb6 100644
+--- a/aclocal/ipv6.m4
++++ b/aclocal/ipv6.m4
+@@ -15,8 +15,8 @@ AC_DEFUN([AC_IPV6], [
+ fi
+
+ dnl IPv6-enabled networking functions required for IPv6
+- AC_CHECK_FUNCS([getnameinfo bindresvport_sa], ,
+- [AC_MSG_ERROR([Missing functions needed for IPv6.])])
++ AC_CHECK_FUNCS([getifaddrs getnameinfo bindresvport_sa], ,
++ [AC_MSG_ERROR([Missing library functions needed for IPv6.])])
+
+ dnl Need to detect presence of IPv6 networking at run time via
+ dnl getaddrinfo(3); old versions of glibc do not support ADDRCONFIG
+diff --git a/aclocal/libcap.m4 b/aclocal/libcap.m4
+new file mode 100644
+index 0000000..eabe507
+--- /dev/null
++++ b/aclocal/libcap.m4
+@@ -0,0 +1,15 @@
++dnl Checks for libcap.so
++dnl
++AC_DEFUN([AC_LIBCAP], [
++
++ dnl look for prctl
++ AC_CHECK_FUNC([prctl], , )
++
++ dnl look for the library; do not add to LIBS if found
++ AC_CHECK_LIB([cap], [cap_get_proc], [LIBCAP=-lcap], ,)
++ AC_SUBST(LIBCAP)
++
++ AC_CHECK_HEADERS([sys/capability.h], ,
++ [AC_MSG_ERROR([libcap headers not found.])])
++
++])dnl
+diff --git a/configure.ac b/configure.ac
+index 3ad415c..1dc4249 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -166,6 +166,9 @@ fi
+ dnl Check for TI-RPC library and headers
+ AC_LIBTIRPC
+
++dnl Check for -lcap
++AC_LIBCAP
++
+ # Check whether user wants TCP wrappers support
+ AC_TCP_WRAPPERS
+
+@@ -327,7 +330,7 @@ AC_FUNC_STAT
+ AC_FUNC_VPRINTF
+ AC_CHECK_FUNCS([alarm atexit dup2 fdatasync ftruncate getcwd \
+ gethostbyaddr gethostbyname gethostname getmntent \
+- getnameinfo getrpcbyname \
++ getnameinfo getrpcbyname getifaddrs \
+ gettimeofday hasmntopt inet_ntoa innetgr memset mkdir pathconf \
+ realpath rmdir select socket strcasecmp strchr strdup \
+ strerror strrchr strtol strtoul sigprocmask])
+@@ -402,6 +405,7 @@ AC_CONFIG_FILES([
+ support/include/Makefile
+ support/misc/Makefile
+ support/nfs/Makefile
++ support/nsm/Makefile
+ tools/Makefile
+ tools/locktest/Makefile
+ tools/nlmtest/Makefile
+@@ -416,6 +420,8 @@ AC_CONFIG_FILES([
+ utils/nfsd/Makefile
+ utils/nfsstat/Makefile
+ utils/showmount/Makefile
+- utils/statd/Makefile])
++ utils/statd/Makefile
++ tests/Makefile
++ tests/nsm_client/Makefile])
+ AC_OUTPUT
+
+diff --git a/support/Makefile.am b/support/Makefile.am
+index aa4d692..cb37733 100644
+--- a/support/Makefile.am
++++ b/support/Makefile.am
+@@ -1,6 +1,6 @@
+ ## Process this file with automake to produce Makefile.in
+
+-SUBDIRS = export include misc nfs
++SUBDIRS = export include misc nfs nsm
+
+ MAINTAINERCLEANFILES = Makefile.in
+
+diff --git a/support/export/client.c b/support/export/client.c
+index 5fcf355..6236561 100644
+--- a/support/export/client.c
++++ b/support/export/client.c
+@@ -297,7 +297,7 @@ name_cmp(char *a, char *b)
+ /* compare strings a and b, but only upto ',' in a */
+ while (*a && *b && *a != ',' && *a == *b)
+ a++, b++;
+- if (!*b && (!*a || !a == ',') )
++ if (!*b && (!*a || *a == ','))
+ return 0;
+ if (!*b) return 1;
+ if (!*a || *a == ',') return -1;
+diff --git a/support/export/export.c b/support/export/export.c
+index e5e6cb0..2943466 100644
+--- a/support/export/export.c
++++ b/support/export/export.c
+@@ -28,6 +28,22 @@ static int export_check(nfs_export *, struct hostent *, char *);
+ static nfs_export *
+ export_allowed_internal(struct hostent *hp, char *path);
+
++static void warn_duplicated_exports(nfs_export *exp, struct exportent *eep)
++{
++ if (exp->m_export.e_flags != eep->e_flags) {
++ xlog(L_ERROR, "incompatible duplicated export entries:");
++ xlog(L_ERROR, "\t%s:%s (0x%x) [IGNORED]", eep->e_hostname,
++ eep->e_path, eep->e_flags);
++ xlog(L_ERROR, "\t%s:%s (0x%x)", exp->m_export.e_hostname,
++ exp->m_export.e_path, exp->m_export.e_flags);
++ } else {
++ xlog(L_ERROR, "duplicated export entries:");
++ xlog(L_ERROR, "\t%s:%s", eep->e_hostname, eep->e_path);
++ xlog(L_ERROR, "\t%s:%s", exp->m_export.e_hostname,
++ exp->m_export.e_path);
++ }
++}
++
+ int
+ export_read(char *fname)
+ {
+@@ -36,27 +52,13 @@ export_read(char *fname)
+
+ setexportent(fname, "r");
+ while ((eep = getexportent(0,1)) != NULL) {
+- exp = export_lookup(eep->e_hostname, eep->e_path, 0);
+- if (!exp)
+- export_create(eep,0);
+- else {
+- if (exp->m_export.e_flags != eep->e_flags) {
+- xlog(L_ERROR, "incompatible duplicated export entries:");
+- xlog(L_ERROR, "\t%s:%s (0x%x) [IGNORED]", eep->e_hostname,
+- eep->e_path, eep->e_flags);
+- xlog(L_ERROR, "\t%s:%s (0x%x)", exp->m_export.e_hostname,
+- exp->m_export.e_path, exp->m_export.e_flags);
+- }
+- else {
+- xlog(L_ERROR, "duplicated export entries:");
+- xlog(L_ERROR, "\t%s:%s", eep->e_hostname, eep->e_path);
+- xlog(L_ERROR, "\t%s:%s", exp->m_export.e_hostname,
+- exp->m_export.e_path);
+- }
+- }
++ exp = export_lookup(eep->e_hostname, eep->e_path, 0);
++ if (!exp)
++ export_create(eep, 0);
++ else
++ warn_duplicated_exports(exp, eep);
+ }
+ endexportent();
+-
+ return 0;
+ }
+
+diff --git a/support/export/xtab.c b/support/export/xtab.c
+index 3b1dcce..2a43193 100644
+--- a/support/export/xtab.c
++++ b/support/export/xtab.c
+@@ -19,7 +19,9 @@
+ #include "exportfs.h"
+ #include "xio.h"
+ #include "xlog.h"
++#include "v4root.h"
+
++int v4root_needed;
+ static void cond_rename(char *newfile, char *oldfile);
+
+ static int
+@@ -36,6 +38,8 @@ xtab_read(char *xtab, char *lockfn, int is_export)
+ if ((lockid = xflock(lockfn, "r")) < 0)
+ return 0;
+ setexportent(xtab, "r");
++ if (is_export == 1)
++ v4root_needed = 1;
+ while ((xp = getexportent(is_export==0, 0)) != NULL) {
+ if (!(exp = export_lookup(xp->e_hostname, xp->e_path, is_export != 1)) &&
+ !(exp = export_create(xp, is_export!=1))) {
+@@ -48,6 +52,8 @@ xtab_read(char *xtab, char *lockfn, int is_export)
+ case 1:
+ exp->m_xtabent = 1;
+ exp->m_mayexport = 1;
++ if ((xp->e_flags & NFSEXP_FSID) && xp->e_fsid == 0)
++ v4root_needed = 0;
+ break;
+ case 2:
+ exp->m_exported = -1;/* may be exported */
+diff --git a/support/include/Makefile.am b/support/include/Makefile.am
+index f5a77ec..4b33ee9 100644
+--- a/support/include/Makefile.am
++++ b/support/include/Makefile.am
+@@ -9,6 +9,8 @@ noinst_HEADERS = \
+ nfs_mntent.h \
+ nfs_paths.h \
+ nfslib.h \
++ nfsrpc.h \
++ nsm.h \
+ rpcmisc.h \
+ tcpwrapper.h \
+ xio.h \
+diff --git a/support/include/exportfs.h b/support/include/exportfs.h
+index a5cf482..470b2ec 100644
+--- a/support/include/exportfs.h
++++ b/support/include/exportfs.h
+@@ -99,10 +99,19 @@ int xtab_mount_write(void);
+ int xtab_export_write(void);
+ void xtab_append(nfs_export *);
+
++int secinfo_addflavor(struct flav_info *, struct exportent *);
++
+ int rmtab_read(void);
+
+ struct nfskey * key_lookup(char *hname);
+
++struct export_features {
++ unsigned int flags;
++ unsigned int secinfo_flags;
++};
++
++struct export_features *get_export_features(void);
++
+ /* Record export error. */
+ extern int export_errno;
+
+diff --git a/support/include/ha-callout.h b/support/include/ha-callout.h
+index efb70fb..1164336 100644
+--- a/support/include/ha-callout.h
++++ b/support/include/ha-callout.h
+@@ -53,11 +53,7 @@ ha_callout(char *event, char *arg1, char *arg2, int arg3)
+ default: pid = waitpid(pid, &ret, 0);
+ }
+ sigaction(SIGCHLD, &oldact, &newact);
+-#ifdef dprintf
+- dprintf(N_DEBUG, "ha callout returned %d\n", WEXITSTATUS(ret));
+-#else
+ xlog(D_GENERAL, "ha callout returned %d\n", WEXITSTATUS(ret));
+-#endif
+ }
+
+ #endif
+diff --git a/support/include/nfs/export.h b/support/include/nfs/export.h
+index f7a99ba..1547a87 100644
+--- a/support/include/nfs/export.h
++++ b/support/include/nfs/export.h
+@@ -24,6 +24,17 @@
+ #define NFSEXP_FSID 0x2000
+ #define NFSEXP_CROSSMOUNT 0x4000
+ #define NFSEXP_NOACL 0x8000 /* reserved for possible ACL related use */
+-#define NFSEXP_ALLFLAGS 0xFFFF
++#define NFSEXP_V4ROOT 0x10000
++/*
++ * All flags supported by the kernel before addition of the
++ * export_features interface:
++ */
++#define NFSEXP_OLDFLAGS 0x7E3F
++/*
++ * Flags that can vary per flavor, for kernels before addition of the
++ * export_features interface:
++ */
++#define NFSEXP_OLD_SECINFO_FLAGS (NFSEXP_READONLY | NFSEXP_ROOTSQUASH \
++ | NFSEXP_ALLSQUASH)
+
+ #endif /* _NSF_EXPORT_H */
+diff --git a/support/include/nfsrpc.h b/support/include/nfsrpc.h
+index dff6af7..4db35ab 100644
+--- a/support/include/nfsrpc.h
++++ b/support/include/nfsrpc.h
+@@ -59,16 +59,6 @@ static inline void nfs_clear_rpc_createerr(void)
+ }
+
+ /*
+- * 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
+ */
+ extern rpcprog_t nfs_getrpcbyname(const rpcprog_t, const char *table[]);
+@@ -90,6 +80,18 @@ extern CLIENT *nfs_get_priv_rpcclient( const struct sockaddr *,
+ 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
+ */
+ extern char *nfs_sockaddr2universal(const struct sockaddr *);
+@@ -158,4 +160,4 @@ extern int nfs_rpc_ping(const struct sockaddr *sap,
+ const unsigned short protocol,
+ const struct timeval *timeout);
+
+-#endif /* __NFS_UTILS_NFSRPC_H */
++#endif /* !__NFS_UTILS_NFSRPC_H */
+diff --git a/support/include/nsm.h b/support/include/nsm.h
+new file mode 100644
+index 0000000..fb4d823
+--- /dev/null
++++ b/support/include/nsm.h
+@@ -0,0 +1,93 @@
++/*
++ * Copyright 2009 Oracle. All rights reserved.
++ *
++ * This file is part of nfs-utils.
++ *
++ * nfs-utils is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * nfs-utils is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with nfs-utils. If not, see .
++ */
++
++/*
++ * NSM for Linux.
++ */
++
++#ifndef NFS_UTILS_SUPPORT_NSM_H
++#define NFS_UTILS_SUPPORT_NSM_H
++
++#include
++#include
++#include
++
++#include
++#include
++
++#include "sm_inter.h"
++
++typedef unsigned int
++ (*nsm_populate_t)(const char *hostname,
++ const struct sockaddr *sap,
++ const struct mon *mon,
++ const time_t timestamp);
++
++/* file.c */
++
++extern _Bool nsm_setup_pathnames(const char *progname,
++ const char *parentdir);
++extern _Bool nsm_is_default_parentdir(void);
++extern _Bool nsm_drop_privileges(const int pidfd);
++
++extern int nsm_get_state(_Bool update);
++extern void nsm_update_kernel_state(const int state);
++
++extern unsigned int
++ nsm_retire_monitored_hosts(void);
++extern unsigned int
++ nsm_load_monitor_list(nsm_populate_t func);
++extern unsigned int
++ nsm_load_notify_list(nsm_populate_t func);
++
++extern _Bool nsm_insert_monitored_host(const char *hostname,
++ const struct sockaddr *sap, const struct mon *m);
++extern void nsm_delete_monitored_host(const char *hostname,
++ const char *mon_name, const char *my_name);
++extern void nsm_delete_notified_host(const char *hostname,
++ const char *mon_name, const char *my_name);
++extern size_t nsm_priv_to_hex(const char *priv, char *buf,
++ const size_t buflen);
++
++/* rpc.c */
++
++#define NSM_MAXMSGSIZE (2048u)
++
++extern uint32_t nsm_xmit_getport(const int sock,
++ const struct sockaddr_in *sin,
++ const unsigned long program,
++ const unsigned long version);
++extern uint32_t nsm_xmit_getaddr(const int sock,
++ const struct sockaddr_in6 *sin6,
++ const rpcprog_t program, const rpcvers_t version);
++extern uint32_t nsm_xmit_rpcbind(const int sock, const struct sockaddr *sap,
++ const rpcprog_t program, const rpcvers_t version);
++extern uint32_t nsm_xmit_notify(const int sock, const struct sockaddr *sap,
++ const socklen_t salen, const rpcprog_t program,
++ const char *mon_name, const int state);
++extern uint32_t nsm_xmit_nlmcall(const int sock, const struct sockaddr *sap,
++ const socklen_t salen, const struct mon *m,
++ const int state);
++extern uint32_t nsm_parse_reply(XDR *xdrs);
++extern unsigned long
++ nsm_recv_getport(XDR *xdrs);
++extern uint16_t nsm_recv_getaddr(XDR *xdrs);
++extern uint16_t nsm_recv_rpcbind(const sa_family_t family, XDR *xdrs);
++
++#endif /* !NFS_UTILS_SUPPORT_NSM_H */
+diff --git a/support/include/rpcmisc.h b/support/include/rpcmisc.h
+index f551a85..1b8f411 100644
+--- a/support/include/rpcmisc.h
++++ b/support/include/rpcmisc.h
+@@ -41,7 +41,12 @@ struct rpc_dtable {
+ (xdrproc_t)xdr_##res_type, sizeof(res_type), \
+ }
+
+-
++void nfs_svc_unregister(const rpcprog_t program,
++ const rpcvers_t version);
++unsigned int nfs_svc_create(char *name, const rpcprog_t program,
++ const rpcvers_t version,
++ void (*dispatch)(struct svc_req *, SVCXPRT *),
++ const uint16_t port);
+ void rpc_init(char *name, int prog, int vers,
+ void (*dispatch)(struct svc_req *, SVCXPRT *),
+ int defport);
+diff --git a/support/include/sockaddr.h b/support/include/sockaddr.h
+new file mode 100644
+index 0000000..732514b
+--- /dev/null
++++ b/support/include/sockaddr.h
+@@ -0,0 +1,237 @@
++/*
++ * Copyright 2009 Oracle. All rights reserved.
++ *
++ * This file is part of nfs-utils.
++ *
++ * nfs-utils is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * nfs-utils is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with nfs-utils. If not, see .
++ */
++
++#ifndef NFS_UTILS_SOCKADDR_H
++#define NFS_UTILS_SOCKADDR_H
++
++#include
++#include
++#include
++
++/*
++ * 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->sa_family == sa2->sa_family)
++ switch (sa1->sa_family) {
++ case AF_INET:
++ return compare_sockaddr4(sa1, sa2);
++ case AF_INET6:
++ return compare_sockaddr6(sa1, sa2);
++ }
++
++ return false;
++}
++
++#endif /* !NFS_UTILS_SOCKADDR_H */
+diff --git a/support/include/tcpwrapper.h b/support/include/tcpwrapper.h
+index 98cf806..f735106 100644
+--- a/support/include/tcpwrapper.h
++++ b/support/include/tcpwrapper.h
+@@ -5,14 +5,8 @@
+ #include
+ #include
+
+-extern int verboselog;
+-
+-extern int allow_severity;
+-extern int deny_severity;
+-
+-extern int good_client(char *daemon, struct sockaddr_in *addr);
+-extern int from_local (struct sockaddr_in *addr);
+-extern int check_default(char *daemon, struct sockaddr_in *addr,
+- u_long proc, u_long prog);
++extern int from_local(const struct sockaddr *sap);
++extern int check_default(char *name, struct sockaddr *sap,
++ const unsigned long program);
+
+ #endif /* TCP_WRAPPER_H */
+diff --git a/support/include/v4root.h b/support/include/v4root.h
+new file mode 100644
+index 0000000..706c15c
+--- /dev/null
++++ b/support/include/v4root.h
+@@ -0,0 +1,15 @@
++/*
++ * Copyright (C) 2009 Red Hat
++ * support/include/v4root.h
++ *
++ * Support routines for dynamic pseudo roots.
++ *
++ */
++
++#ifndef V4ROOT_H
++#define V4ROOT_H
++
++extern int v4root_needed;
++extern void v4root_set(void);
++
++#endif /* V4ROOT_H */
+diff --git a/support/misc/from_local.c b/support/misc/from_local.c
+index 89ccc4a..e2de969 100644
+--- a/support/misc/from_local.c
++++ b/support/misc/from_local.c
+@@ -37,32 +37,100 @@
+ static char sccsid[] = "@(#) from_local.c 1.3 96/05/31 15:52:57";
+ #endif
+
+-#ifdef TEST
+-#undef perror
++#ifdef HAVE_CONFIG_H
++#include
+ #endif
+
+ #include
+ #include
++#include
+ #include
+ #include
+ #include
+ #include
+ #include
+ #include
+-#include
+ #include
+ #include
+
++#include "sockaddr.h"
++#include "tcpwrapper.h"
++#include "xlog.h"
++
+ #ifndef TRUE
+ #define TRUE 1
+ #define FALSE 0
+ #endif
+
+- /*
+- * With virtual hosting, each hardware network interface can have multiple
+- * network addresses. On such machines the number of machine addresses can
+- * be surprisingly large.
+- */
++#ifdef HAVE_GETIFADDRS
++
++#include
++#include
++
++/**
++ * from_local - determine whether request comes from the local system
++ * @sap: pointer to socket address to check
++ *
++ * With virtual hosting, each hardware network interface can have
++ * multiple network addresses. On such machines the number of machine
++ * addresses can be surprisingly large.
++ *
++ * We also expect the local network configuration to change over time,
++ * so call getifaddrs(3) more than once, but not too often.
++ *
++ * Returns TRUE if the sockaddr contains an address of one of the local
++ * network interfaces. Otherwise FALSE is returned.
++ */
++int
++from_local(const struct sockaddr *sap)
++{
++ static struct ifaddrs *ifaddr = NULL;
++ static time_t last_update = 0;
++ struct ifaddrs *ifa;
++ unsigned int count;
++ time_t now;
++
++ if (time(&now) == ((time_t)-1)) {
++ xlog(L_ERROR, "%s: time(2): %m", __func__);
++
++ /* If we don't know what time it is, use the
++ * existing ifaddr list, if one exists */
++ now = last_update;
++ if (ifaddr == NULL)
++ now++;
++ }
++ if (now != last_update) {
++ xlog(D_GENERAL, "%s: updating local if addr list", __func__);
++
++ if (ifaddr)
++ freeifaddrs(ifaddr);
++
++ if (getifaddrs(&ifaddr) == -1) {
++ xlog(L_ERROR, "%s: getifaddrs(3): %m", __func__);
++ return FALSE;
++ }
++
++ last_update = now;
++ }
++
++ count = 0;
++ for (ifa = ifaddr; ifa; ifa = ifa->ifa_next) {
++ if ((ifa->ifa_flags & IFF_UP) &&
++ nfs_compare_sockaddr(sap, ifa->ifa_addr)) {
++ xlog(D_GENERAL, "%s: incoming address matches "
++ "local interface address", __func__);
++ return TRUE;
++ } else
++ count++;
++ }
++
++ xlog(D_GENERAL, "%s: checked %u local if addrs; "
++ "incoming address not found", __func__, count);
++ return FALSE;
++}
++
++#else /* !HAVE_GETIFADDRS */
++
+ static int num_local;
+ static int num_addrs;
+ static struct in_addr *addrs;
+@@ -81,7 +149,7 @@ static int grow_addrs(void)
+ new_num = (addrs == 0) ? 1 : num_addrs + num_addrs;
+ new_addrs = (struct in_addr *) malloc(sizeof(*addrs) * new_num);
+ if (new_addrs == 0) {
+- perror("portmap: out of memory");
++ xlog_warn("%s: out of memory", __func__);
+ return (0);
+ } else {
+ if (addrs != 0) {
+@@ -112,13 +180,13 @@ find_local(void)
+ */
+
+ if ((sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
+- perror("socket");
++ xlog_warn("%s: socket(2): %m", __func__);
+ return (0);
+ }
+ ifc.ifc_len = sizeof(buf);
+ ifc.ifc_buf = buf;
+ if (ioctl(sock, SIOCGIFCONF, (char *) &ifc) < 0) {
+- perror("SIOCGIFCONF");
++ xlog_warn("%s: ioctl(SIOCGIFCONF): %m", __func__);
+ (void) close(sock);
+ return (0);
+ }
+@@ -130,10 +198,10 @@ find_local(void)
+ if (ifr->ifr_addr.sa_family == AF_INET) { /* IP net interface */
+ ifreq = *ifr;
+ if (ioctl(sock, SIOCGIFFLAGS, (char *) &ifreq) < 0) {
+- perror("SIOCGIFFLAGS");
++ xlog_warn("%s: ioctl(SIOCGIFFLAGS): %m", __func__);
+ } else if (ifreq.ifr_flags & IFF_UP) { /* active interface */
+ if (ioctl(sock, SIOCGIFADDR, (char *) &ifreq) < 0) {
+- perror("SIOCGIFADDR");
++ xlog_warn("%s: ioctl(SIOCGIFADDR): %m", __func__);
+ } else {
+ if (num_local >= num_addrs)
+ if (grow_addrs() == 0)
+@@ -153,14 +221,28 @@ find_local(void)
+ return (num_local);
+ }
+
+-/* from_local - determine whether request comes from the local system */
++/**
++ * from_local - determine whether request comes from the local system
++ * @sap: pointer to socket address to check
++ *
++ * With virtual hosting, each hardware network interface can have
++ * multiple network addresses. On such machines the number of machine
++ * addresses can be surprisingly large.
++ *
++ * Returns TRUE if the sockaddr contains an address of one of the local
++ * network interfaces. Otherwise FALSE is returned.
++ */
+ int
+-from_local(struct sockaddr_in *addr)
++from_local(const struct sockaddr *sap)
+ {
++ const struct sockaddr_in *addr = (const struct sockaddr_in *)sap;
+ int i;
+
++ if (sap->sa_family != AF_INET)
++ return (FALSE);
++
+ if (addrs == 0 && find_local() == 0)
+- syslog(LOG_ERR, "cannot find any active local network interfaces");
++ xlog(L_ERROR, "Cannot find any active local network interfaces");
+
+ for (i = 0; i < num_local; i++) {
+ if (memcmp((char *) &(addr->sin_addr), (char *) &(addrs[i]),
+@@ -172,9 +254,8 @@ from_local(struct sockaddr_in *addr)
+
+ #ifdef TEST
+
+-main()
++int main(void)
+ {
+- char *inet_ntoa();
+ int i;
+
+ find_local();
+@@ -182,4 +263,6 @@ main()
+ printf("%s\n", inet_ntoa(addrs[i]));
+ }
+
+-#endif
++#endif /* TEST */
++
++#endif /* !HAVE_GETIFADDRS */
+diff --git a/support/misc/tcpwrapper.c b/support/misc/tcpwrapper.c
+index 1da6020..06b0a46 100644
+--- a/support/misc/tcpwrapper.c
++++ b/support/misc/tcpwrapper.c
+@@ -34,13 +34,12 @@
+ #ifdef HAVE_CONFIG_H
+ #include
+ #endif
++
+ #ifdef HAVE_LIBWRAP
+-#include
+ #include
+ #include
+ #include
+ #include
+-#include
+ #include
+ #include
+ #include
+@@ -49,108 +48,146 @@
+ #include
+ #include
+
++#include "sockaddr.h"
++#include "tcpwrapper.h"
+ #include "xlog.h"
+
+ #ifdef SYSV40
+ #include
+ #include
+-#endif
++#endif /* SYSV40 */
+
+-static void logit(int severity, struct sockaddr_in *addr,
+- u_long procnum, u_long prognum, char *text);
+-static int check_files(void);
++#define ALLOW 1
++#define DENY 0
+
+-/*
+- * These need to exist since they are externed
+- * public header files.
+- */
+-int verboselog = 0;
+-int allow_severity = LOG_INFO;
+-int deny_severity = LOG_WARNING;
++#ifdef IPV6_SUPPORTED
++static void
++present_address(const struct sockaddr *sap, char *buf, const size_t buflen)
++{
++ const struct sockaddr_in *sin = (const struct sockaddr_in *)sap;
++ const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sap;
++ socklen_t len = (socklen_t)buflen;
++
++ switch (sap->sa_family) {
++ case AF_INET:
++ if (inet_ntop(AF_INET, &sin->sin_addr, buf, len) != 0)
++ return;
++ case AF_INET6:
++ if (inet_ntop(AF_INET6, &sin6->sin6_addr, buf, len) != 0)
++ return;
++ }
+
+-#define log_bad_host(addr, proc, prog) \
+- logit(deny_severity, addr, proc, prog, "request from unauthorized host")
++ memset(buf, 0, buflen);
++ strncpy(buf, "unrecognized caller", buflen);
++}
++#else /* !IPV6_SUPPORTED */
++static void
++present_address(const struct sockaddr *sap, char *buf, const size_t buflen)
++{
++ const struct sockaddr_in *sin = (const struct sockaddr_in *)sap;
++ socklen_t len = (socklen_t)buflen;
+
+-#define ALLOW 1
+-#define DENY 0
++ if (sap->sa_family == AF_INET)
++ if (inet_ntop(AF_INET, &sin->sin_addr, buf, len) != 0)
++ return;
++
++ memset(buf, 0, buflen);
++ strncpy(buf, "unrecognized caller", (size_t)buflen);
++}
++#endif /* !IPV6_SUPPORTED */
+
+ typedef struct _haccess_t {
+- TAILQ_ENTRY(_haccess_t) list;
+- int access;
+- struct in_addr addr;
++ TAILQ_ENTRY(_haccess_t) list;
++ int allowed;
++ union nfs_sockaddr address;
+ } haccess_t;
+
+ #define HASH_TABLE_SIZE 1021
+ typedef struct _hash_head {
+ TAILQ_HEAD(host_list, _haccess_t) h_head;
+ } hash_head;
+-hash_head haccess_tbl[HASH_TABLE_SIZE];
+-static haccess_t *haccess_lookup(struct sockaddr_in *addr, u_long);
+-static void haccess_add(struct sockaddr_in *addr, u_long, int);
+
+-inline unsigned int strtoint(char *str)
++static hash_head haccess_tbl[HASH_TABLE_SIZE];
++
++static unsigned long
++strtoint(const char *str)
+ {
+- unsigned int n = 0;
+- int len = strlen(str);
+- int i;
++ unsigned long i, n = 0;
++ size_t len = strlen(str);
+
+- for (i=0; i < len; i++)
+- n+=((int)str[i])*i;
++ for (i = 0; i < len; i++)
++ n += (unsigned char)str[i] * i;
+
+ return n;
+ }
+-static inline int hashint(unsigned int num)
++
++static unsigned int
++hashint(const unsigned long num)
+ {
+- return num % HASH_TABLE_SIZE;
++ return (unsigned int)(num % HASH_TABLE_SIZE);
+ }
+-#define HASH(_addr, _prog) \
+- hashint((strtoint((_addr))+(_prog)))
+
+-void haccess_add(struct sockaddr_in *addr, u_long prog, int access)
++static unsigned int
++HASH(const char *addr, const unsigned long program)
++{
++ return hashint(strtoint(addr) + program);
++}
++
++static void
++haccess_add(const struct sockaddr *sap, const char *address,
++ const unsigned long program, const int allowed)
+ {
+ hash_head *head;
+- haccess_t *hptr;
+- int hash;
++ haccess_t *hptr;
++ unsigned int hash;
+
+ hptr = (haccess_t *)malloc(sizeof(haccess_t));
+ if (hptr == NULL)
+ return;
+
+- hash = HASH(inet_ntoa(addr->sin_addr), prog);
++ hash = HASH(address, program);
+ head = &(haccess_tbl[hash]);
+
+- hptr->access = access;
+- hptr->addr.s_addr = addr->sin_addr.s_addr;
++ hptr->allowed = allowed;
++ memcpy(&hptr->address, sap, (size_t)nfs_sockaddr_length(sap));
+
+ if (TAILQ_EMPTY(&head->h_head))
+ TAILQ_INSERT_HEAD(&head->h_head, hptr, list);
+ else
+ TAILQ_INSERT_TAIL(&head->h_head, hptr, list);
+ }
+-haccess_t *haccess_lookup(struct sockaddr_in *addr, u_long prog)
++
++static haccess_t *
++haccess_lookup(const struct sockaddr *sap, const char *address,
++ const unsigned long program)
+ {
+ hash_head *head;
+- haccess_t *hptr;
+- int hash;
++ haccess_t *hptr;
++ unsigned int hash;
+
+- hash = HASH(inet_ntoa(addr->sin_addr), prog);
++ hash = HASH(address, program);
+ head = &(haccess_tbl[hash]);
+
+ TAILQ_FOREACH(hptr, &head->h_head, list) {
+- if (hptr->addr.s_addr == addr->sin_addr.s_addr)
++ if (nfs_compare_sockaddr(&hptr->address.sa, sap))
+ return hptr;
+ }
+ return NULL;
+ }
+
+-int
+-good_client(daemon, addr)
+-char *daemon;
+-struct sockaddr_in *addr;
++static void
++logit(const char *address)
++{
++ xlog_warn("connect from %s denied: request from unauthorized host",
++ address);
++}
++
++static int
++good_client(char *name, struct sockaddr *sap)
+ {
+ struct request_info req;
+
+- request_init(&req, RQ_DAEMON, daemon, RQ_CLIENT_SIN, addr, 0);
++ request_init(&req, RQ_DAEMON, name, RQ_CLIENT_SIN, sap, 0);
+ sock_methods(&req);
+
+ if (hosts_access(&req))
+@@ -159,9 +196,8 @@ struct sockaddr_in *addr;
+ return DENY;
+ }
+
+-/* check_files - check to see if either access files have changed */
+-
+-static int check_files()
++static int
++check_files(void)
+ {
+ static time_t allow_mtime, deny_mtime;
+ struct stat astat, dstat;
+@@ -186,45 +222,48 @@ static int check_files()
+ return changed;
+ }
+
+-/* check_default - additional checks for NULL, DUMP, GETPORT and unknown */
+-
++/**
++ * check_default - additional checks for NULL, DUMP, GETPORT and unknown
++ * @name: pointer to '\0'-terminated ASCII string containing name of the
++ * daemon requesting the access check
++ * @sap: pointer to sockaddr containing network address of caller
++ * @program: RPC program number caller is attempting to access
++ *
++ * Returns TRUE if the caller is allowed access; otherwise FALSE is returned.
++ */
+ int
+-check_default(daemon, addr, proc, prog)
+-char *daemon;
+-struct sockaddr_in *addr;
+-u_long proc;
+-u_long prog;
++check_default(char *name, struct sockaddr *sap, const unsigned long program)
+ {
+ haccess_t *acc = NULL;
+ int changed = check_files();
++ char buf[INET6_ADDRSTRLEN];
++
++ present_address(sap, buf, sizeof(buf));
+
+- acc = haccess_lookup(addr, prog);
+- if (acc && changed == 0)
+- return (acc->access);
++ acc = haccess_lookup(sap, buf, program);
++ if (acc != NULL && changed == 0) {
++ xlog(D_GENERAL, "%s: access by %s %s (cached)", __func__,
++ buf, acc->allowed ? "ALLOWED" : "DENIED");
++ return acc->allowed;
++ }
+
+- if (!(from_local(addr) || good_client(daemon, addr))) {
+- log_bad_host(addr, proc, prog);
+- if (acc)
+- acc->access = FALSE;
+- else
+- haccess_add(addr, prog, FALSE);
++ if (!(from_local(sap) || good_client(name, sap))) {
++ logit(buf);
++ if (acc != NULL)
++ acc->allowed = FALSE;
++ else
++ haccess_add(sap, buf, program, FALSE);
++ xlog(D_GENERAL, "%s: access by %s DENIED", __func__, buf);
+ return (FALSE);
+ }
+
+- if (acc)
+- acc->access = TRUE;
+- else
+- haccess_add(addr, prog, TRUE);
++ if (acc != NULL)
++ acc->allowed = TRUE;
++ else
++ haccess_add(sap, buf, program, TRUE);
++ xlog(D_GENERAL, "%s: access by %s ALLOWED", __func__, buf);
+
+- return (TRUE);
++ return (TRUE);
+ }
+
+-/* logit - report events of interest via the syslog daemon */
+-
+-static void logit(int severity, struct sockaddr_in *addr,
+- u_long procnum, u_long prognum, char *text)
+-{
+- syslog(severity, "connect from %s denied: %s",
+- inet_ntoa(addr->sin_addr), text);
+-}
+-#endif
++#endif /* HAVE_LIBWRAP */
+diff --git a/support/nfs/Makefile.am b/support/nfs/Makefile.am
+index e9462fc..60400b2 100644
+--- a/support/nfs/Makefile.am
++++ b/support/nfs/Makefile.am
+@@ -4,7 +4,8 @@ noinst_LIBRARIES = libnfs.a
+ libnfs_a_SOURCES = exports.c rmtab.c xio.c rpcmisc.c rpcdispatch.c \
+ xlog.c xcommon.c wildmat.c nfsclient.c \
+ nfsexport.c getfh.c nfsctl.c rpc_socket.c getport.c \
+- svc_socket.c cacheio.c closeall.c nfs_mntent.c conffile.c
++ svc_socket.c cacheio.c closeall.c nfs_mntent.c conffile.c \
++ svc_create.c
+
+ MAINTAINERCLEANFILES = Makefile.in
+
+diff --git a/support/nfs/exports.c b/support/nfs/exports.c
+index 1aaebf4..a93941c 100644
+--- a/support/nfs/exports.c
++++ b/support/nfs/exports.c
+@@ -84,6 +84,31 @@ setexportent(char *fname, char *type)
+ first = 1;
+ }
+
++static void init_exportent (struct exportent *ee, int fromkernel)
++{
++ ee->e_flags = EXPORT_DEFAULT_FLAGS;
++ /* some kernels assume the default is sync rather than
++ * async. More recent kernels always report one or other,
++ * but this test makes sure we assume same as kernel
++ * Ditto for wgather
++ */
++ if (fromkernel) {
++ ee->e_flags &= ~NFSEXP_ASYNC;
++ ee->e_flags &= ~NFSEXP_GATHERED_WRITES;
++ }
++ ee->e_anonuid = 65534;
++ ee->e_anongid = 65534;
++ ee->e_squids = NULL;
++ ee->e_sqgids = NULL;
++ ee->e_mountpoint = NULL;
++ ee->e_fslocmethod = FSLOC_NONE;
++ ee->e_fslocdata = NULL;
++ ee->e_secinfo[0].flav = NULL;
++ ee->e_nsquids = 0;
++ ee->e_nsqgids = 0;
++ ee->e_uuid = NULL;
++}
++
+ struct exportent *
+ getexportent(int fromkernel, int fromexports)
+ {
+@@ -102,26 +127,7 @@ getexportent(int fromkernel, int fromexports)
+ has_default_opts = 0;
+ has_default_subtree_opts = 0;
+
+- def_ee.e_flags = EXPORT_DEFAULT_FLAGS;
+- /* some kernels assume the default is sync rather than
+- * async. More recent kernels always report one or other,
+- * but this test makes sure we assume same as kernel
+- * Ditto for wgather
+- */
+- if (fromkernel) {
+- def_ee.e_flags &= ~NFSEXP_ASYNC;
+- def_ee.e_flags &= ~NFSEXP_GATHERED_WRITES;
+- }
+- def_ee.e_anonuid = 65534;
+- def_ee.e_anongid = 65534;
+- def_ee.e_squids = NULL;
+- def_ee.e_sqgids = NULL;
+- def_ee.e_mountpoint = NULL;
+- def_ee.e_fslocmethod = FSLOC_NONE;
+- def_ee.e_fslocdata = NULL;
+- def_ee.e_secinfo[0].flav = NULL;
+- def_ee.e_nsquids = 0;
+- def_ee.e_nsqgids = 0;
++ init_exportent(&def_ee, fromkernel);
+
+ ok = getpath(def_ee.e_path, sizeof(def_ee.e_path));
+ if (ok <= 0)
+@@ -334,18 +340,7 @@ mkexportent(char *hname, char *path, char *options)
+ {
+ static struct exportent ee;
+
+- ee.e_flags = EXPORT_DEFAULT_FLAGS;
+- ee.e_anonuid = 65534;
+- ee.e_anongid = 65534;
+- ee.e_squids = NULL;
+- ee.e_sqgids = NULL;
+- ee.e_mountpoint = NULL;
+- ee.e_fslocmethod = FSLOC_NONE;
+- ee.e_fslocdata = NULL;
+- ee.e_secinfo[0].flav = NULL;
+- ee.e_nsquids = 0;
+- ee.e_nsqgids = 0;
+- ee.e_uuid = NULL;
++ init_exportent(&ee, 0);
+
+ xfree(ee.e_hostname);
+ ee.e_hostname = xstrdup(hname);
+@@ -385,7 +380,7 @@ static int valid_uuid(char *uuid)
+ * do nothing if it's already there. Returns the index of flavor
+ * in the resulting array in any case.
+ */
+-static int secinfo_addflavor(struct flav_info *flav, struct exportent *ep)
++int secinfo_addflavor(struct flav_info *flav, struct exportent *ep)
+ {
+ struct sec_entry *p;
+
+@@ -467,9 +462,20 @@ static void clearflags(int mask, unsigned int active, struct exportent *ep)
+ }
+ }
+
+-/* options that can vary per flavor: */
+-#define NFSEXP_SECINFO_FLAGS (NFSEXP_READONLY | NFSEXP_ROOTSQUASH \
+- | NFSEXP_ALLSQUASH)
++/*
++ * For those flags which are not allowed to vary by pseudoflavor,
++ * ensure that the export flags agree with the flags on each
++ * pseudoflavor:
++ */
++static void fix_pseudoflavor_flags(struct exportent *ep)
++{
++ struct export_features *ef;
++ struct sec_entry *p;
++
++ ef = get_export_features();
++ for (p = ep->e_secinfo; p->flav; p++)
++ p->flags |= ep->e_flags & ~ef->secinfo_flags;
++}
+
+ /*
+ * Parse option string pointed to by cp and set mount options accordingly.
+@@ -477,7 +483,6 @@ static void clearflags(int mask, unsigned int active, struct exportent *ep)
+ static int
+ parseopts(char *cp, struct exportent *ep, int warn, int *had_subtree_opt_ptr)
+ {
+- struct sec_entry *p;
+ int had_subtree_opt = 0;
+ char *flname = efname?efname:"command line";
+ int flline = efp?efp->x_line:0;
+@@ -507,25 +512,25 @@ parseopts(char *cp, struct exportent *ep, int warn, int *had_subtree_opt_ptr)
+ else if (strcmp(opt, "rw") == 0)
+ clearflags(NFSEXP_READONLY, active, ep);
+ else if (!strcmp(opt, "secure"))
+- ep->e_flags &= ~NFSEXP_INSECURE_PORT;
++ clearflags(NFSEXP_INSECURE_PORT, active, ep);
+ else if (!strcmp(opt, "insecure"))
+- ep->e_flags |= NFSEXP_INSECURE_PORT;
++ setflags(NFSEXP_INSECURE_PORT, active, ep);
+ else if (!strcmp(opt, "sync"))
+- ep->e_flags &= ~NFSEXP_ASYNC;
++ clearflags(NFSEXP_ASYNC, active, ep);
+ else if (!strcmp(opt, "async"))
+- ep->e_flags |= NFSEXP_ASYNC;
++ setflags(NFSEXP_ASYNC, active, ep);
+ else if (!strcmp(opt, "nohide"))
+- ep->e_flags |= NFSEXP_NOHIDE;
++ setflags(NFSEXP_NOHIDE, active, ep);
+ else if (!strcmp(opt, "hide"))
+- ep->e_flags &= ~NFSEXP_NOHIDE;
++ clearflags(NFSEXP_NOHIDE, active, ep);
+ else if (!strcmp(opt, "crossmnt"))
+- ep->e_flags |= NFSEXP_CROSSMOUNT;
++ setflags(NFSEXP_CROSSMOUNT, active, ep);
+ else if (!strcmp(opt, "nocrossmnt"))
+- ep->e_flags &= ~NFSEXP_CROSSMOUNT;
++ clearflags(NFSEXP_CROSSMOUNT, active, ep);
+ else if (!strcmp(opt, "wdelay"))
+- ep->e_flags |= NFSEXP_GATHERED_WRITES;
++ setflags(NFSEXP_GATHERED_WRITES, active, ep);
+ else if (!strcmp(opt, "no_wdelay"))
+- ep->e_flags &= ~NFSEXP_GATHERED_WRITES;
++ clearflags(NFSEXP_GATHERED_WRITES, active, ep);
+ else if (strcmp(opt, "root_squash") == 0)
+ setflags(NFSEXP_ROOTSQUASH, active, ep);
+ else if (!strcmp(opt, "no_root_squash"))
+@@ -536,22 +541,22 @@ parseopts(char *cp, struct exportent *ep, int warn, int *had_subtree_opt_ptr)
+ clearflags(NFSEXP_ALLSQUASH, active, ep);
+ else if (strcmp(opt, "subtree_check") == 0) {
+ had_subtree_opt = 1;
+- ep->e_flags &= ~NFSEXP_NOSUBTREECHECK;
++ clearflags(NFSEXP_NOSUBTREECHECK, active, ep);
+ } else if (strcmp(opt, "no_subtree_check") == 0) {
+ had_subtree_opt = 1;
+- ep->e_flags |= NFSEXP_NOSUBTREECHECK;
++ setflags(NFSEXP_NOSUBTREECHECK, active, ep);
+ } else if (strcmp(opt, "auth_nlm") == 0)
+- ep->e_flags &= ~NFSEXP_NOAUTHNLM;
++ clearflags(NFSEXP_NOAUTHNLM, active, ep);
+ else if (strcmp(opt, "no_auth_nlm") == 0)
+- ep->e_flags |= NFSEXP_NOAUTHNLM;
++ setflags(NFSEXP_NOAUTHNLM, active, ep);
+ else if (strcmp(opt, "secure_locks") == 0)
+- ep->e_flags &= ~NFSEXP_NOAUTHNLM;
++ clearflags(NFSEXP_NOAUTHNLM, active, ep);
+ else if (strcmp(opt, "insecure_locks") == 0)
+- ep->e_flags |= NFSEXP_NOAUTHNLM;
++ setflags(NFSEXP_NOAUTHNLM, active, ep);
+ else if (strcmp(opt, "acl") == 0)
+- ep->e_flags &= ~NFSEXP_NOACL;
++ clearflags(NFSEXP_NOACL, active, ep);
+ else if (strcmp(opt, "no_acl") == 0)
+- ep->e_flags |= NFSEXP_NOACL;
++ setflags(NFSEXP_NOACL, active, ep);
+ else if (strncmp(opt, "anonuid=", 8) == 0) {
+ char *oe;
+ ep->e_anonuid = strtol(opt+8, &oe, 10);
+@@ -583,11 +588,11 @@ bad_option:
+ char *oe;
+ if (strcmp(opt+5, "root") == 0) {
+ ep->e_fsid = 0;
+- ep->e_flags |= NFSEXP_FSID;
++ setflags(NFSEXP_FSID, active, ep);
+ } else {
+ ep->e_fsid = strtoul(opt+5, &oe, 0);
+ if (opt[5]!='\0' && *oe == '\0')
+- ep->e_flags |= NFSEXP_FSID;
++ setflags(NFSEXP_FSID, active, ep);
+ else if (valid_uuid(opt+5))
+ ep->e_uuid = strdup(opt+5);
+ else {
+@@ -628,22 +633,15 @@ bad_option:
+ } else {
+ xlog(L_ERROR, "%s:%d: unknown keyword \"%s\"\n",
+ flname, flline, opt);
+- ep->e_flags |= NFSEXP_ALLSQUASH | NFSEXP_READONLY;
++ setflags(NFSEXP_ALLSQUASH | NFSEXP_READONLY, active, ep);
+ goto bad_option;
+ }
+ free(opt);
+ while (isblank(*cp))
+ cp++;
+ }
+- /*
+- * Turn on nohide which will allow this export to cross over
+- * the 'mount --bind' mount point.
+- */
+- if (ep->e_fslocdata)
+- ep->e_flags |= NFSEXP_NOHIDE;
+
+- for (p = ep->e_secinfo; p->flav; p++)
+- p->flags |= ep->e_flags & ~NFSEXP_SECINFO_FLAGS;
++ fix_pseudoflavor_flags(ep);
+ ep->e_squids = squids;
+ ep->e_sqgids = sqgids;
+ ep->e_nsquids = nsquids;
+@@ -760,4 +758,34 @@ syntaxerr(char *msg)
+ xlog(L_ERROR, "%s:%d: syntax error: %s",
+ efname, efp?efp->x_line:0, msg);
+ }
+-
++struct export_features *get_export_features(void)
++{
++ static char *path = "/proc/fs/nfsd/export_features";
++ static struct export_features ef;
++ static int cached = 0;
++ char buf[50];
++ int c;
++ int fd;
++
++ if (cached)
++ return &ef;
++
++ ef.flags = NFSEXP_OLDFLAGS;
++ ef.secinfo_flags = NFSEXP_OLD_SECINFO_FLAGS;
++
++ fd = open(path, O_RDONLY);
++ if (fd == -1)
++ goto good;
++ fd = read(fd, buf, 50);
++ if (fd == -1)
++ goto err;
++ c = sscanf(buf, "%x %x", &ef.flags, &ef.secinfo_flags);
++ if (c != 2)
++ goto err;
++good:
++ cached = 1;
++ return &ef;
++err:
++ xlog(L_WARNING, "unexpected error reading %s", path);
++ return &ef;
++}
+diff --git a/support/nfs/getport.c b/support/nfs/getport.c
+index 4bdf556..c930539 100644
+--- a/support/nfs/getport.c
++++ b/support/nfs/getport.c
+@@ -45,6 +45,7 @@
+ #include
+ #endif
+
++#include "sockaddr.h"
+ #include "nfsrpc.h"
+
+ /*
+@@ -199,7 +200,63 @@ static CLIENT *nfs_gp_get_rpcbclient(struct sockaddr *sap,
+ return clnt;
+ }
+
+-/*
++/**
++ * nfs_get_proto - Convert a netid to an address family and protocol number
++ * @netid: C string containing a netid
++ * @family: OUT: address family
++ * @protocol: OUT: protocol number
++ *
++ * Returns 1 and fills in @protocol if the netid was recognized;
++ * otherwise zero is returned.
++ */
++#ifdef HAVE_LIBTIRPC
++int
++nfs_get_proto(const char *netid, sa_family_t *family, unsigned long *protocol)
++{
++ struct netconfig *nconf;
++ struct protoent *proto;
++
++ nconf = getnetconfigent(netid);
++ if (nconf == NULL)
++ return 0;
++
++ proto = getprotobyname(nconf->nc_proto);
++ if (proto == NULL) {
++ freenetconfigent(nconf);
++ return 0;
++ }
++
++ *family = AF_UNSPEC;
++ if (strcmp(nconf->nc_protofmly, NC_INET) == 0)
++ *family = AF_INET;
++ if (strcmp(nconf->nc_protofmly, NC_INET6) == 0)
++ *family = AF_INET6;
++ freenetconfigent(nconf);
++
++ *protocol = (unsigned long)proto->p_proto;
++ return 1;
++}
++#else /* !HAVE_LIBTIRPC */
++int
++nfs_get_proto(const char *netid, sa_family_t *family, unsigned long *protocol)
++{
++ struct protoent *proto;
++
++ proto = getprotobyname(netid);
++ if (proto == NULL)
++ return 0;
++
++ *family = AF_INET;
++ *protocol = (unsigned long)proto->p_proto;
++ return 1;
++}
++#endif /* !HAVE_LIBTIRPC */
++
++/**
++ * nfs_get_netid - Convert a protocol family and protocol name to a netid
++ * @family: protocol family
++ * @protocol: protocol number
++ *
+ * One of the arguments passed when querying remote rpcbind services
+ * via rpcbind v3 or v4 is a netid string. This replaces the pm_prot
+ * field used in legacy PMAP_GETPORT calls.
+@@ -213,13 +270,12 @@ static CLIENT *nfs_gp_get_rpcbclient(struct sockaddr *sap,
+ * first entry that matches @family and @protocol and whose netid string
+ * fits in the provided buffer.
+ *
+- * Returns a '\0'-terminated string if successful; otherwise NULL.
++ * Returns a '\0'-terminated string if successful. Caller must
++ * free the returned string. Otherwise NULL is returned, and
+ * rpc_createerr.cf_stat is set to reflect the error.
+ */
+ #ifdef HAVE_LIBTIRPC
+-
+-static char *nfs_gp_get_netid(const sa_family_t family,
+- const unsigned short protocol)
++char *nfs_get_netid(const sa_family_t family, const unsigned long protocol)
+ {
+ char *nc_protofmly, *nc_proto, *nc_netid;
+ struct netconfig *nconf;
+@@ -255,6 +311,9 @@ static char *nfs_gp_get_netid(const sa_family_t family,
+
+ nc_netid = strdup(nconf->nc_netid);
+ endnetconfig(handle);
++
++ if (nc_netid == NULL)
++ rpc_createerr.cf_stat = RPC_SYSTEMERROR;
+ return nc_netid;
+ }
+ endnetconfig(handle);
+@@ -263,8 +322,28 @@ out:
+ rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
+ return NULL;
+ }
++#else /* !HAVE_LIBTIRPC */
++char *nfs_get_netid(const sa_family_t family, const unsigned long protocol)
++{
++ struct protoent *proto;
++ char *netid;
+
+-#endif /* HAVE_LIBTIRPC */
++ if (family != AF_INET)
++ goto out;
++ proto = getprotobynumber((int)protocol);
++ if (proto == NULL)
++ goto out;
++
++ netid = strdup(proto->p_name);
++ if (netid == NULL)
++ rpc_createerr.cf_stat = RPC_SYSTEMERROR;
++ return netid;
++
++out:
++ rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
++ return NULL;
++}
++#endif /* !HAVE_LIBTIRPC */
+
+ /*
+ * Extract a port number from a universal address, and terminate the
+@@ -421,7 +500,7 @@ static int nfs_gp_init_rpcb_parms(const struct sockaddr *sap,
+ {
+ char *netid, *addr;
+
+- netid = nfs_gp_get_netid(sap->sa_family, protocol);
++ netid = nfs_get_netid(sap->sa_family, protocol);
+ if (netid == NULL)
+ return 0;
+
+@@ -627,8 +706,8 @@ int nfs_rpc_ping(const struct sockaddr *sap, const socklen_t salen,
+ const rpcprog_t program, const rpcvers_t version,
+ const unsigned short protocol, const struct timeval *timeout)
+ {
+- struct sockaddr_storage address;
+- struct sockaddr *saddr = (struct sockaddr *)&address;
++ union nfs_sockaddr address;
++ struct sockaddr *saddr = &address.sa;
+ CLIENT *client;
+ struct timeval tout = { -1, 0 };
+ int result = 0;
+@@ -696,8 +775,8 @@ unsigned short nfs_getport(const struct sockaddr *sap,
+ const rpcvers_t version,
+ const unsigned short protocol)
+ {
+- struct sockaddr_storage address;
+- struct sockaddr *saddr = (struct sockaddr *)&address;
++ union nfs_sockaddr address;
++ struct sockaddr *saddr = &address.sa;
+ struct timeval timeout = { -1, 0 };
+ unsigned short port = 0;
+ CLIENT *client;
+@@ -755,8 +834,8 @@ int nfs_getport_ping(struct sockaddr *sap, const socklen_t salen,
+ }
+
+ if (port != 0) {
+- struct sockaddr_storage address;
+- struct sockaddr *saddr = (struct sockaddr *)&address;
++ union nfs_sockaddr address;
++ struct sockaddr *saddr = &address.sa;
+
+ memcpy(saddr, sap, (size_t)salen);
+ nfs_set_port(saddr, port);
+@@ -807,8 +886,8 @@ unsigned short nfs_getlocalport(const rpcprot_t program,
+ const rpcvers_t version,
+ const unsigned short protocol)
+ {
+- struct sockaddr_storage address;
+- struct sockaddr *lb_addr = (struct sockaddr *)&address;
++ union nfs_sockaddr address;
++ struct sockaddr *lb_addr = &address.sa;
+ socklen_t lb_len = sizeof(*lb_addr);
+ unsigned short port = 0;
+
+@@ -891,8 +970,8 @@ unsigned short nfs_rpcb_getaddr(const struct sockaddr *sap,
+ const unsigned short protocol,
+ const struct timeval *timeout)
+ {
+- struct sockaddr_storage address;
+- struct sockaddr *saddr = (struct sockaddr *)&address;
++ union nfs_sockaddr address;
++ struct sockaddr *saddr = &address.sa;
+ CLIENT *client;
+ struct rpcb parms;
+ struct timeval tout = { -1, 0 };
+diff --git a/support/nfs/rpc_socket.c b/support/nfs/rpc_socket.c
+index 9c20f61..0e20824 100644
+--- a/support/nfs/rpc_socket.c
++++ b/support/nfs/rpc_socket.c
+@@ -26,6 +26,8 @@
+
+ #include
+ #include
++
++#include
+ #include
+ #include
+ #include
+@@ -38,6 +40,7 @@
+ #include
+ #include
+
++#include "sockaddr.h"
+ #include "nfsrpc.h"
+
+ #ifdef HAVE_LIBTIRPC
+@@ -51,6 +54,7 @@
+ #define NFSRPC_TIMEOUT_UDP (3)
+ #define NFSRPC_TIMEOUT_TCP (10)
+
++
+ /*
+ * Set up an RPC client for communicating via a AF_LOCAL socket.
+ *
+@@ -121,10 +125,10 @@ static int nfs_bind(const int sock, const sa_family_t family)
+
+ switch (family) {
+ case AF_INET:
+- return bind(sock, (struct sockaddr *)&sin,
++ return bind(sock, (struct sockaddr *)(char *)&sin,
+ (socklen_t)sizeof(sin));
+ case AF_INET6:
+- return bind(sock, (struct sockaddr *)&sin6,
++ return bind(sock, (struct sockaddr *)(char *)&sin6,
+ (socklen_t)sizeof(sin6));
+ }
+
+@@ -153,9 +157,9 @@ static int nfs_bindresvport(const int sock, const sa_family_t family)
+
+ switch (family) {
+ case AF_INET:
+- return bindresvport_sa(sock, (struct sockaddr *)&sin);
++ return bindresvport_sa(sock, (struct sockaddr *)(char *)&sin);
+ case AF_INET6:
+- return bindresvport_sa(sock, (struct sockaddr *)&sin6);
++ return bindresvport_sa(sock, (struct sockaddr *)(char *)&sin6);
+ }
+
+ errno = EAFNOSUPPORT;
+@@ -416,49 +420,6 @@ static CLIENT *nfs_get_tcpclient(const struct sockaddr *sap,
+ }
+
+ /**
+- * 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
+ * @salen: length of socket address
+diff --git a/support/nfs/svc_create.c b/support/nfs/svc_create.c
+new file mode 100644
+index 0000000..59ba505
+--- /dev/null
++++ b/support/nfs/svc_create.c
+@@ -0,0 +1,246 @@
++/*
++ * Copyright 2009 Oracle. All rights reserved.
++ *
++ * This file is part of nfs-utils.
++ *
++ * nfs-utils is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * nfs-utils is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with nfs-utils. If not, see .
++ */
++
++#ifdef HAVE_CONFIG_H
++#include
++#endif
++
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++
++#include
++
++#include
++#include
++
++#include
++#include
++
++#ifdef HAVE_TCP_WRAPPER
++#include "tcpwrapper.h"
++#endif
++
++#include "rpcmisc.h"
++#include "xlog.h"
++
++#ifdef HAVE_LIBTIRPC
++
++/*
++ * Set up an appropriate bind address, given @port and @nconf.
++ *
++ * Returns getaddrinfo(3) results if successful. Caller must
++ * invoke freeaddrinfo(3) on these results.
++ *
++ * Otherwise NULL is returned if an error occurs.
++ */
++__attribute_malloc__
++static struct addrinfo *
++svc_create_bindaddr(struct netconfig *nconf, const uint16_t port)
++{
++ struct addrinfo *ai = NULL;
++ struct addrinfo hint = {
++ .ai_flags = AI_PASSIVE | AI_NUMERICSERV,
++ };
++ char buf[8];
++ int error;
++
++ if (strcmp(nconf->nc_protofmly, NC_INET) == 0)
++ hint.ai_family = AF_INET;
++#ifdef IPV6_SUPPORTED
++ else if (strcmp(nconf->nc_protofmly, NC_INET6) == 0)
++ hint.ai_family = AF_INET6;
++#endif /* IPV6_SUPPORTED */
++ else {
++ xlog(D_GENERAL, "Unrecognized bind address family: %s",
++ nconf->nc_protofmly);
++ return NULL;
++ }
++
++ if (strcmp(nconf->nc_proto, NC_UDP) == 0)
++ hint.ai_protocol = (int)IPPROTO_UDP;
++ else if (strcmp(nconf->nc_proto, NC_TCP) == 0)
++ hint.ai_protocol = (int)IPPROTO_TCP;
++ else {
++ xlog(D_GENERAL, "Unrecognized bind address protocol: %s",
++ nconf->nc_proto);
++ return NULL;
++ }
++
++ (void)snprintf(buf, sizeof(buf), "%u", port);
++ error = getaddrinfo(NULL, buf, &hint, &ai);
++ if (error != 0) {
++ xlog(L_ERROR, "Failed to construct bind address: %s",
++ gai_strerror(error));
++ return NULL;
++ }
++
++ return ai;
++}
++
++static unsigned int
++svc_create_nconf(const char *name, const rpcprog_t program,
++ const rpcvers_t version,
++ void (*dispatch)(struct svc_req *, SVCXPRT *),
++ const uint16_t port, struct netconfig *nconf)
++{
++ struct t_bind bindaddr;
++ struct addrinfo *ai;
++ SVCXPRT *xprt;
++
++ ai = svc_create_bindaddr(nconf, port);
++ if (ai == NULL)
++ return 0;
++
++ bindaddr.addr.buf = ai->ai_addr;
++ bindaddr.qlen = SOMAXCONN;
++
++ xprt = svc_tli_create(RPC_ANYFD, nconf, &bindaddr, 0, 0);
++ freeaddrinfo(ai);
++ if (xprt == NULL) {
++ xlog(D_GENERAL, "Failed to create listener xprt "
++ "(%s, %u, %s)", name, version, nconf->nc_netid);
++ return 0;
++ }
++
++ if (!svc_reg(xprt, program, version, dispatch, nconf)) {
++ /* svc_reg(3) destroys @xprt in this case */
++ xlog(D_GENERAL, "Failed to register (%s, %u, %s)",
++ name, version, nconf->nc_netid);
++ return 0;
++ }
++
++ return 1;
++}
++
++/**
++ * nfs_svc_create - start up RPC svc listeners
++ * @name: C string containing name of new service
++ * @program: RPC program number to register
++ * @version: RPC version number to register
++ * @dispatch: address of function that handles incoming RPC requests
++ * @port: if not zero, transport listens on this port
++ *
++ * Sets up network transports for receiving RPC requests, and starts
++ * the RPC dispatcher. Returns the number of started network transports.
++ */
++unsigned int
++nfs_svc_create(__attribute__((unused)) char *name,
++ const rpcprog_t program, const rpcvers_t version,
++ void (*dispatch)(struct svc_req *, SVCXPRT *),
++ const uint16_t port)
++{
++ const struct sigaction create_sigaction = {
++ .sa_handler = SIG_IGN,
++ };
++ unsigned int visible, up;
++ struct netconfig *nconf;
++ void *handlep;
++
++ /*
++ * Ignore SIGPIPE to avoid exiting sideways when peers
++ * close their TCP connection while we're trying to reply
++ * to them.
++ */
++ (void)sigaction(SIGPIPE, &create_sigaction, NULL);
++
++ handlep = setnetconfig();
++ if (handlep == NULL) {
++ xlog(L_ERROR, "Failed to access local netconfig database: %s",
++ nc_sperror());
++ return 0;
++ }
++
++ visible = 0;
++ up = 0;
++ while ((nconf = getnetconfig(handlep)) != NULL) {
++ if (!(nconf->nc_flag & NC_VISIBLE))
++ continue;
++ visible++;
++ up += svc_create_nconf(name, program, version, dispatch,
++ port, nconf);
++ }
++
++ if (visible == 0)
++ xlog(L_ERROR, "Failed to find any visible netconfig entries");
++
++ if (endnetconfig(handlep) == -1)
++ xlog(L_ERROR, "Failed to close local netconfig database: %s",
++ nc_sperror());
++
++ return up;
++}
++
++/**
++ * nfs_svc_unregister - remove service registrations from local rpcbind database
++ * @program: RPC program number to unregister
++ * @version: RPC version number to unregister
++ *
++ * Removes all registrations for [ @program, @version ] .
++ */
++void
++nfs_svc_unregister(const rpcprog_t program, const rpcvers_t version)
++{
++ if (rpcb_unset(program, version, NULL) == FALSE)
++ xlog(D_GENERAL, "Failed to unregister program %lu, version %lu",
++ (unsigned long)program, (unsigned long)version);
++}
++
++#else /* !HAVE_LIBTIRPC */
++
++/**
++ * nfs_svc_create - start up RPC svc listeners
++ * @name: C string containing name of new service
++ * @program: RPC program number to register
++ * @version: RPC version number to register
++ * @dispatch: address of function that handles incoming RPC requests
++ * @port: if not zero, transport listens on this port
++ *
++ * Sets up network transports for receiving RPC requests, and starts
++ * the RPC dispatcher. Returns the number of started network transports.
++ */
++unsigned int
++nfs_svc_create(char *name, const rpcprog_t program, const rpcvers_t version,
++ void (*dispatch)(struct svc_req *, SVCXPRT *),
++ const uint16_t port)
++{
++ rpc_init(name, (int)program, (int)version, dispatch, (int)port);
++ return 1;
++}
++
++/**
++ * nfs_svc_unregister - remove service registrations from local rpcbind database
++ * @program: RPC program number to unregister
++ * @version: RPC version number to unregister
++ *
++ * Removes all registrations for [ @program, @version ] .
++ */
++void
++nfs_svc_unregister(const rpcprog_t program, const rpcvers_t version)
++{
++ if (pmap_unset((unsigned long)program, (unsigned long)version) == FALSE)
++ xlog(D_GENERAL, "Failed to unregister program %lu, version %lu",
++ (unsigned long)program, (unsigned long)version);
++}
++
++#endif /* !HAVE_LIBTIRPC */
+diff --git a/support/nsm/Makefile.am b/support/nsm/Makefile.am
+new file mode 100644
+index 0000000..2038e68
+--- /dev/null
++++ b/support/nsm/Makefile.am
+@@ -0,0 +1,45 @@
++## Process this file with automake to produce Makefile.in
++
++GENFILES_CLNT = sm_inter_clnt.c
++GENFILES_SVC = sm_inter_svc.c
++GENFILES_XDR = sm_inter_xdr.c
++GENFILES_H = sm_inter.h
++
++GENFILES = $(GENFILES_CLNT) $(GENFILES_SVC) $(GENFILES_XDR) $(GENFILES_H)
++
++EXTRA_DIST = sm_inter.x
++
++noinst_LIBRARIES = libnsm.a
++libnsm_a_SOURCES = $(GENFILES) file.c rpc.c
++
++BUILT_SOURCES = $(GENFILES)
++
++if CONFIG_RPCGEN
++RPCGEN = $(top_builddir)/tools/rpcgen/rpcgen
++$(RPCGEN):
++ make -C ../../tools/rpcgen all
++else
++RPCGEN = @RPCGEN_PATH@
++endif
++
++$(GENFILES_CLNT): %_clnt.c: %.x $(RPCGEN)
++ test -f $@ && rm -rf $@ || true
++ $(RPCGEN) -l -o $@ $<
++
++$(GENFILES_SVC): %_svc.c: %.x $(RPCGEN)
++ test -f $@ && rm -rf $@ || true
++ $(RPCGEN) -m -o $@ $<
++
++$(GENFILES_XDR): %_xdr.c: %.x $(RPCGEN)
++ test -f $@ && rm -rf $@ || true
++ $(RPCGEN) -c -o $@ $<
++
++$(GENFILES_H): %.h: %.x $(RPCGEN)
++ test -f $@ && rm -rf $@ || true
++ $(RPCGEN) -h -o $@ $<
++ rm -f $(top_builddir)/support/include/sm_inter.h
++ $(LN_S) ../nsm/sm_inter.h $(top_builddir)/support/include/sm_inter.h
++
++MAINTAINERCLEANFILES = Makefile.in
++
++CLEANFILES = $(GENFILES) $(top_builddir)/support/include/sm_inter.h
+diff --git a/support/nsm/file.c b/support/nsm/file.c
+new file mode 100644
+index 0000000..d469219
+--- /dev/null
++++ b/support/nsm/file.c
+@@ -0,0 +1,1067 @@
++/*
++ * Copyright 2009 Oracle. All rights reserved.
++ *
++ * This file is part of nfs-utils.
++ *
++ * nfs-utils is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * nfs-utils is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with nfs-utils. If not, see .
++ */
++
++/*
++ * NSM for Linux.
++ *
++ * Callback information and NSM state is stored in files, usually
++ * under /var/lib/nfs. A database of information contained in local
++ * files stores NLM callback data and what remote peers to notify of
++ * reboots.
++ *
++ * For each monitored remote peer, a text file is created under the
++ * directory specified by NSM_MONITOR_DIR. The name of the file
++ * is a valid DNS hostname. The hostname string must be a valid
++ * ASCII DNS name, and must not contain slash characters, white space,
++ * or '\0' (ie. anything that might have some special meaning in a
++ * path name).
++ *
++ * The contents of each file include seven blank-separated fields of
++ * text, finished with '\n'. The first field contains the network
++ * address of the NLM service to call back. The current implementation
++ * supports using only IPv4 addresses, so the only contents of this
++ * field are a network order IPv4 address expressed in 8 hexadecimal
++ * characters.
++ *
++ * The next four fields are text strings of hexadecimal characters,
++ * representing:
++ *
++ * 2. A 4 byte RPC program number of the NLM service to call back
++ * 3. A 4 byte RPC version number of the NLM service to call back
++ * 4. A 4 byte RPC procedure number of the NLM service to call back
++ * 5. A 16 byte opaque cookie that the NLM service uses to identify
++ * the monitored host
++ *
++ * The sixth field is the monitored host's mon_name, passed to statd
++ * via an SM_MON request.
++ *
++ * The seventh field is the my_name for this peer, which is the
++ * hostname of the local NLM (currently on Linux, the result of
++ * `uname -n`). This can be used as the source address/hostname
++ * when sending SM_NOTIFY requests.
++ *
++ * The NSM protocol does not limit the contents of these strings
++ * in any way except that they must fit into 1024 bytes. Our
++ * implementation requires that these strings not contain
++ * white space or '\0'.
++ */
++
++#ifdef HAVE_CONFIG_H
++#include
++#endif
++
++#include
++#include
++#include
++#include
++
++#include
++#include