diff --git a/nfs-utils-1.2.2-rc5.patch b/nfs-utils-1.2.2-rc5.patch
new file mode 100644
index 0000000..9b67b1f
--- /dev/null
+++ b/nfs-utils-1.2.2-rc5.patch
@@ -0,0 +1,6163 @@
+diff -up nfs-utils-1.2.1/configure.ac.orig nfs-utils-1.2.1/configure.ac
+--- nfs-utils-1.2.1/configure.ac.orig 2009-11-04 06:13:56.000000000 -0500
++++ nfs-utils-1.2.1/configure.ac 2010-01-12 06:07:40.754815941 -0500
+@@ -402,6 +402,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 +417,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 -up nfs-utils-1.2.1/.gitignore.orig nfs-utils-1.2.1/.gitignore
+--- nfs-utils-1.2.1/.gitignore.orig 2009-11-04 06:13:56.000000000 -0500
++++ nfs-utils-1.2.1/.gitignore 2010-01-12 06:07:40.753815840 -0500
+@@ -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 -up nfs-utils-1.2.1/Makefile.am.orig nfs-utils-1.2.1/Makefile.am
+--- nfs-utils-1.2.1/Makefile.am.orig 2009-11-04 06:13:56.000000000 -0500
++++ nfs-utils-1.2.1/Makefile.am 2010-01-12 06:07:40.753815840 -0500
+@@ -2,7 +2,7 @@
+
+ AUTOMAKE_OPTIONS = foreign
+
+-SUBDIRS = tools support utils linux-nfs
++SUBDIRS = tools support utils linux-nfs tests
+
+ MAINTAINERCLEANFILES = Makefile.in
+
+diff -up nfs-utils-1.2.1/support/export/client.c.orig nfs-utils-1.2.1/support/export/client.c
+--- nfs-utils-1.2.1/support/export/client.c.orig 2009-11-04 06:13:56.000000000 -0500
++++ nfs-utils-1.2.1/support/export/client.c 2010-01-12 06:07:40.755815903 -0500
+@@ -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 -up nfs-utils-1.2.1/support/include/ha-callout.h.orig nfs-utils-1.2.1/support/include/ha-callout.h
+--- nfs-utils-1.2.1/support/include/ha-callout.h.orig 2009-11-04 06:13:56.000000000 -0500
++++ nfs-utils-1.2.1/support/include/ha-callout.h 2010-01-12 06:07:40.756815715 -0500
+@@ -53,11 +53,7 @@ ha_callout(char *event, char *arg1, char
+ 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 -up nfs-utils-1.2.1/support/include/Makefile.am.orig nfs-utils-1.2.1/support/include/Makefile.am
+--- nfs-utils-1.2.1/support/include/Makefile.am.orig 2009-11-04 06:13:56.000000000 -0500
++++ nfs-utils-1.2.1/support/include/Makefile.am 2010-01-12 06:07:40.756815715 -0500
+@@ -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 -up nfs-utils-1.2.1/support/include/nfsrpc.h.orig nfs-utils-1.2.1/support/include/nfsrpc.h
+--- nfs-utils-1.2.1/support/include/nfsrpc.h.orig 2009-11-04 06:13:56.000000000 -0500
++++ nfs-utils-1.2.1/support/include/nfsrpc.h 2010-01-12 06:07:40.757815668 -0500
+@@ -90,6 +90,18 @@ extern CLIENT *nfs_get_priv_rpcclient(
+ 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 *);
+diff -up nfs-utils-1.2.1/support/include/nsm.h.orig nfs-utils-1.2.1/support/include/nsm.h
+--- nfs-utils-1.2.1/support/include/nsm.h.orig 2010-01-12 06:07:40.757815668 -0500
++++ nfs-utils-1.2.1/support/include/nsm.h 2010-01-12 06:07:40.758815846 -0500
+@@ -0,0 +1,66 @@
++/*
++ * 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);
++extern void nsm_delete_notified_host(const char *hostname);
++extern size_t nsm_priv_to_hex(const char *priv, char *buf,
++ const size_t buflen);
++
++#endif /* !NFS_UTILS_SUPPORT_NSM_H */
+diff -up nfs-utils-1.2.1/support/Makefile.am.orig nfs-utils-1.2.1/support/Makefile.am
+--- nfs-utils-1.2.1/support/Makefile.am.orig 2009-11-04 06:13:56.000000000 -0500
++++ nfs-utils-1.2.1/support/Makefile.am 2010-01-12 06:07:40.755815903 -0500
+@@ -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 -up nfs-utils-1.2.1/support/nfs/getport.c.orig nfs-utils-1.2.1/support/nfs/getport.c
+--- nfs-utils-1.2.1/support/nfs/getport.c.orig 2009-11-04 06:13:56.000000000 -0500
++++ nfs-utils-1.2.1/support/nfs/getport.c 2010-01-12 06:07:40.758815846 -0500
+@@ -199,7 +199,63 @@ static CLIENT *nfs_gp_get_rpcbclient(str
+ 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 +269,12 @@ static CLIENT *nfs_gp_get_rpcbclient(str
+ * 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 +310,9 @@ static char *nfs_gp_get_netid(const sa_f
+
+ 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 +321,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 +499,7 @@ static int nfs_gp_init_rpcb_parms(const
+ {
+ 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;
+
+diff -up nfs-utils-1.2.1/support/nsm/file.c.orig nfs-utils-1.2.1/support/nsm/file.c
+--- nfs-utils-1.2.1/support/nsm/file.c.orig 2010-01-12 06:07:40.760815569 -0500
++++ nfs-utils-1.2.1/support/nsm/file.c 2010-01-12 06:07:40.760815569 -0500
+@@ -0,0 +1,843 @@
++/*
++ * 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
++#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;
++}
++
++/**
++ * 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;
++}
++
++/**
++ * 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");
++
++ 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 true;
++}
++
++/**
++ * 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;
++ char *newpath = 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;
++
++ newpath = nsm_make_pathname(NSM_STATE_FILE ".new");
++ if (newpath == NULL) {
++ xlog(L_ERROR,
++ "Failed to create path for " NSM_STATE_FILE ".new");
++ state = 0;
++ goto out;
++ }
++
++ fd = open(newpath, O_CREAT | O_SYNC | O_WRONLY, 0644);
++ if (fd == -1) {
++ xlog(L_ERROR, "Failed to create %s: %m", newpath);
++ state = 0;
++ goto out;
++ }
++
++ result = write(fd, &state, sizeof(state));
++ if (exact_error_check(result, sizeof(state))) {
++ xlog(L_ERROR, "Failed to write %s: %m", newpath);
++ (void)close(fd);
++ (void)unlink(newpath);
++ state = 0;
++ goto out;
++ }
++
++ if (close(fd) == -1) {
++ xlog(L_ERROR, "Failed to close %s: %m", newpath);
++ (void)unlink(newpath);
++ state = 0;
++ goto out;
++ }
++
++ if (rename(newpath, path) == -1) {
++ xlog(L_ERROR, "Failed to rename %s -> %s: %m",
++ newpath, path);
++ (void)unlink(newpath);
++ state = 0;
++ 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. */
++ }
++
++out:
++ free(newpath);
++ 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;
++}
++
++/**
++ * 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;
++ }
++
++ fd = open(path, O_WRONLY | O_CREAT | O_SYNC, S_IRUSR | S_IWUSR);
++ if (fd == -1) {
++ xlog(L_ERROR, "Failed to insert: creating %s: %m", path);
++ 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)
++{
++ char *path;
++
++ path = nsm_make_record_pathname(directory, hostname);
++ if (path == NULL) {
++ xlog(L_ERROR, "Bad filename, not deleting");
++ return;
++ }
++
++ if (unlink(path) == -1)
++ xlog(L_ERROR, "Failed to unlink %s: %m", path);
++
++ free(path);
++}
++
++/**
++ * nsm_delete_monitored_host - delete on-disk record for monitored host
++ * @hostname: '\0'-terminated C string containing hostname of record to delete
++ *
++ */
++void
++nsm_delete_monitored_host(const char *hostname)
++{
++ nsm_delete_host(NSM_MONITOR_DIR, hostname);
++}
++
++/**
++ * nsm_delete_notified_host - delete on-disk host record after notification
++ * @hostname: '\0'-terminated C string containing hostname of record to delete
++ *
++ */
++void
++nsm_delete_notified_host(const char *hostname)
++{
++ nsm_delete_host(NSM_NOTIFY_DIR, hostname);
++}
+diff -up nfs-utils-1.2.1/support/nsm/Makefile.am.orig nfs-utils-1.2.1/support/nsm/Makefile.am
+--- nfs-utils-1.2.1/support/nsm/Makefile.am.orig 2010-01-12 06:07:40.759815628 -0500
++++ nfs-utils-1.2.1/support/nsm/Makefile.am 2010-01-12 06:07:40.759815628 -0500
+@@ -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
++
++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 -up nfs-utils-1.2.1/support/nsm/sm_inter.x.orig nfs-utils-1.2.1/support/nsm/sm_inter.x
+--- nfs-utils-1.2.1/support/nsm/sm_inter.x.orig 2010-01-12 06:07:40.761826674 -0500
++++ nfs-utils-1.2.1/support/nsm/sm_inter.x 2010-01-12 06:07:40.761826674 -0500
+@@ -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 -up nfs-utils-1.2.1/tests/Makefile.am.orig nfs-utils-1.2.1/tests/Makefile.am
+--- nfs-utils-1.2.1/tests/Makefile.am.orig 2010-01-12 06:07:40.761826674 -0500
++++ nfs-utils-1.2.1/tests/Makefile.am 2010-01-12 06:07:40.762888773 -0500
+@@ -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 -up nfs-utils-1.2.1/tests/nsm_client/Makefile.am.orig nfs-utils-1.2.1/tests/nsm_client/Makefile.am
+--- nfs-utils-1.2.1/tests/nsm_client/Makefile.am.orig 2010-01-12 06:07:40.762888773 -0500
++++ nfs-utils-1.2.1/tests/nsm_client/Makefile.am 2010-01-12 06:07:40.762888773 -0500
+@@ -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 -up nfs-utils-1.2.1/tests/nsm_client/nlm_sm_inter.x.orig nfs-utils-1.2.1/tests/nsm_client/nlm_sm_inter.x
+--- nfs-utils-1.2.1/tests/nsm_client/nlm_sm_inter.x.orig 2010-01-12 06:07:40.763888991 -0500
++++ nfs-utils-1.2.1/tests/nsm_client/nlm_sm_inter.x 2010-01-12 06:07:40.763888991 -0500
+@@ -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 -up nfs-utils-1.2.1/tests/nsm_client/nsm_client.c.orig nfs-utils-1.2.1/tests/nsm_client/nsm_client.c
+--- nfs-utils-1.2.1/tests/nsm_client/nsm_client.c.orig 2010-01-12 06:07:40.764889070 -0500
++++ nfs-utils-1.2.1/tests/nsm_client/nsm_client.c 2010-01-12 06:07:40.764889070 -0500
+@@ -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 -up nfs-utils-1.2.1/tests/nsm_client/README.orig nfs-utils-1.2.1/tests/nsm_client/README
+--- nfs-utils-1.2.1/tests/nsm_client/README.orig 2010-01-12 06:07:40.763888991 -0500
++++ nfs-utils-1.2.1/tests/nsm_client/README 2010-01-12 06:07:40.763888991 -0500
+@@ -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 -up nfs-utils-1.2.1/tests/statdb_dump.c.orig nfs-utils-1.2.1/tests/statdb_dump.c
+--- nfs-utils-1.2.1/tests/statdb_dump.c.orig 2010-01-12 06:07:40.765878933 -0500
++++ nfs-utils-1.2.1/tests/statdb_dump.c 2010-01-12 06:07:40.765878933 -0500
+@@ -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 -up nfs-utils-1.2.1/tests/t0001-statd-basic-mon-unmon.sh.orig nfs-utils-1.2.1/tests/t0001-statd-basic-mon-unmon.sh
+--- nfs-utils-1.2.1/tests/t0001-statd-basic-mon-unmon.sh.orig 2010-01-12 06:07:40.765878933 -0500
++++ nfs-utils-1.2.1/tests/t0001-statd-basic-mon-unmon.sh 2010-01-12 06:07:40.765878933 -0500
+@@ -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 -up nfs-utils-1.2.1/tests/test-lib.sh.orig nfs-utils-1.2.1/tests/test-lib.sh
+--- nfs-utils-1.2.1/tests/test-lib.sh.orig 2010-01-12 06:07:40.766878219 -0500
++++ nfs-utils-1.2.1/tests/test-lib.sh 2010-01-12 06:07:40.766878219 -0500
+@@ -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 -up nfs-utils-1.2.1/utils/gssd/gssd.c.orig nfs-utils-1.2.1/utils/gssd/gssd.c
+--- nfs-utils-1.2.1/utils/gssd/gssd.c.orig 2009-11-04 06:13:56.000000000 -0500
++++ nfs-utils-1.2.1/utils/gssd/gssd.c 2010-01-12 06:07:40.766878219 -0500
+@@ -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 -up nfs-utils-1.2.1/utils/gssd/gssd.h.orig nfs-utils-1.2.1/utils/gssd/gssd.h
+--- nfs-utils-1.2.1/utils/gssd/gssd.h.orig 2009-11-04 06:13:56.000000000 -0500
++++ nfs-utils-1.2.1/utils/gssd/gssd.h 2010-01-12 06:07:40.767886262 -0500
+@@ -60,7 +60,6 @@ enum {AUTHTYPE_KRB5, AUTHTYPE_SPKM3, AUT
+
+
+ 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 -up nfs-utils-1.2.1/utils/gssd/gssd_main_loop.c.orig nfs-utils-1.2.1/utils/gssd/gssd_main_loop.c
+--- nfs-utils-1.2.1/utils/gssd/gssd_main_loop.c.orig 2009-11-04 06:13:56.000000000 -0500
++++ nfs-utils-1.2.1/utils/gssd/gssd_main_loop.c 2010-01-12 06:07:40.768888711 -0500
+@@ -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 -up nfs-utils-1.2.1/utils/gssd/gssd_proc.c.orig nfs-utils-1.2.1/utils/gssd/gssd_proc.c
+--- nfs-utils-1.2.1/utils/gssd/gssd_proc.c.orig 2009-11-04 06:13:56.000000000 -0500
++++ nfs-utils-1.2.1/utils/gssd/gssd_proc.c 2010-01-12 06:07:40.769878502 -0500
+@@ -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 (
+
+ /*
+ * 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 (
+ * 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 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,
+ 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];
+-
+- if (clp->krb5_fd == -1) {
+- snprintf(kname, sizeof(kname), "%s/krb5", clp->dirname);
+- clp->krb5_fd = open(kname, O_RDWR);
+- }
+- if (clp->spkm3_fd == -1) {
+- snprintf(sname, sizeof(sname), "%s/spkm3", clp->dirname);
+- clp->spkm3_fd = open(sname, O_RDWR);
++ char name[PATH_MAX];
++ char gname[PATH_MAX];
++ char info_file_name[PATH_MAX];
++
++ if (clp->gssd_fd == -1) {
++ snprintf(gname, sizeof(gname), "%s/gssd", clp->dirname);
++ clp->gssd_fd = open(gname, 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 **namel
+
+ /* 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_i
+ 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;
+@@ -816,19 +905,43 @@ handle_krb5_upcall(struct clnt_info *clp
+ char **dirname;
+ int create_resp = -1;
+
+- 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)
+@@ -839,12 +952,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 +1018,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 +1034,7 @@ out:
+ return;
+
+ out_return_error:
+- do_error_downcall(clp->krb5_fd, uid, -1);
++ do_error_downcall(fd, uid, -1);
+ goto out;
+ }
+
+@@ -928,26 +1042,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 +1075,7 @@ handle_spkm3_upcall(struct clnt_info *cl
+ goto out_return_error;
+ }
+
+- do_downcall(clp->spkm3_fd, uid, &pd, &token);
++ do_downcall(fd, uid, &pd, &token);
+
+ out:
+ if (token.value)
+@@ -980,6 +1087,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 -up nfs-utils-1.2.1/utils/gssd/krb5_util.c.orig nfs-utils-1.2.1/utils/gssd/krb5_util.c
+--- nfs-utils-1.2.1/utils/gssd/krb5_util.c.orig 2009-11-04 06:13:56.000000000 -0500
++++ nfs-utils-1.2.1/utils/gssd/krb5_util.c 2010-01-12 06:07:40.770878533 -0500
+@@ -797,10 +797,9 @@ gssd_search_krb5_keytab(krb5_context con
+ */
+ 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;
+@@ -1096,7 +1095,8 @@ gssd_get_krb5_machine_cred_list(char ***
+ 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 +1186,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 +1226,7 @@ gssd_refresh_krb5_machine_credential(cha
+ 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 +1251,7 @@ gssd_refresh_krb5_machine_credential(cha
+ 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 -up nfs-utils-1.2.1/utils/gssd/krb5_util.h.orig nfs-utils-1.2.1/utils/gssd/krb5_util.h
+--- nfs-utils-1.2.1/utils/gssd/krb5_util.h.orig 2009-11-04 06:13:56.000000000 -0500
++++ nfs-utils-1.2.1/utils/gssd/krb5_util.h 2010-01-12 06:07:40.771878946 -0500
+@@ -30,7 +30,8 @@ void gssd_free_krb5_machine_cred_list(ch
+ 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 -up nfs-utils-1.2.1/utils/gssd/svcgssd_proc.c.orig nfs-utils-1.2.1/utils/gssd/svcgssd_proc.c
+--- nfs-utils-1.2.1/utils/gssd/svcgssd_proc.c.orig 2009-11-04 06:13:56.000000000 -0500
++++ nfs-utils-1.2.1/utils/gssd/svcgssd_proc.c 2010-01-12 06:07:40.772878601 -0500
+@@ -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_han
+ 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_han
+ }
+ 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, unsi
+ }
+ #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 -up nfs-utils-1.2.1/utils/mountd/auth.c.orig nfs-utils-1.2.1/utils/mountd/auth.c
+--- nfs-utils-1.2.1/utils/mountd/auth.c.orig 2009-11-04 06:13:56.000000000 -0500
++++ nfs-utils-1.2.1/utils/mountd/auth.c 2010-01-12 06:07:40.779888687 -0500
+@@ -169,8 +169,7 @@ auth_authenticate_internal(char *what, s
+ }
+ }
+ 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 -up nfs-utils-1.2.1/utils/mount/mount.c.orig nfs-utils-1.2.1/utils/mount/mount.c
+--- nfs-utils-1.2.1/utils/mount/mount.c.orig 2009-11-04 06:13:56.000000000 -0500
++++ nfs-utils-1.2.1/utils/mount/mount.c 2010-01-12 06:07:40.772878601 -0500
+@@ -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 -up nfs-utils-1.2.1/utils/mount/network.c.orig nfs-utils-1.2.1/utils/mount/network.c
+--- nfs-utils-1.2.1/utils/mount/network.c.orig 2009-11-04 06:13:56.000000000 -0500
++++ nfs-utils-1.2.1/utils/mount/network.c 2010-01-12 06:07:40.773889180 -0500
+@@ -193,8 +193,18 @@ static const unsigned int *nfs_default_p
+ }
+ #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 +253,6 @@ static int nfs_lookup(const char *hostna
+ }
+
+ /**
+- * 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 +274,8 @@ int nfs_gethostbyname(const char *hostna
+ * 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.
+ *
+@@ -1289,6 +1280,7 @@ nfs_nfs_version(struct mount_options *op
+ 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 +1292,8 @@ nfs_nfs_protocol(struct mount_options *o
+ 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 +1335,40 @@ nfs_nfs_port(struct mount_options *optio
+ }
+
+ /*
++ * 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 +1436,12 @@ nfs_mount_version(struct mount_options *
+ 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 +1481,35 @@ nfs_mount_port(struct mount_options *opt
+ 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 -up nfs-utils-1.2.1/utils/mount/network.h.orig nfs-utils-1.2.1/utils/mount/network.h
+--- nfs-utils-1.2.1/utils/mount/network.h.orig 2009-11-04 06:13:56.000000000 -0500
++++ nfs-utils-1.2.1/utils/mount/network.h 2010-01-12 06:07:40.774878676 -0500
+@@ -44,7 +44,8 @@ int nfs_probe_bothports(const struct soc
+ 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 *, cons
+
+ 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 -up nfs-utils-1.2.1/utils/mount/nfs4mount.c.orig nfs-utils-1.2.1/utils/mount/nfs4mount.c
+--- nfs-utils-1.2.1/utils/mount/nfs4mount.c.orig 2009-11-04 06:13:56.000000000 -0500
++++ nfs-utils-1.2.1/utils/mount/nfs4mount.c 2010-01-12 06:07:40.776889340 -0500
+@@ -217,8 +217,11 @@ int nfs4mount(const char *spec, const ch
+ 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 ch
+ 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 -up nfs-utils-1.2.1/utils/mount/nfs.man.orig nfs-utils-1.2.1/utils/mount/nfs.man
+--- nfs-utils-1.2.1/utils/mount/nfs.man.orig 2009-11-04 06:13:56.000000000 -0500
++++ nfs-utils-1.2.1/utils/mount/nfs.man 2010-01-12 06:07:40.775888996 -0500
+@@ -58,9 +58,17 @@ The server's hostname and export pathnam
+ 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 av
+ 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
+ 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
+ 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 o
+ .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 ref
+ .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 -up nfs-utils-1.2.1/utils/mount/nfsmount.c.orig nfs-utils-1.2.1/utils/mount/nfsmount.c
+--- nfs-utils-1.2.1/utils/mount/nfsmount.c.orig 2009-11-04 06:13:56.000000000 -0500
++++ nfs-utils-1.2.1/utils/mount/nfsmount.c 2010-01-12 06:07:40.777878312 -0500
+@@ -170,7 +170,7 @@ parse_options(char *old_opts, struct nfs
+ 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
+ *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
+ 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 -up nfs-utils-1.2.1/utils/mount/nfsumount.c.orig nfs-utils-1.2.1/utils/mount/nfsumount.c
+--- nfs-utils-1.2.1/utils/mount/nfsumount.c.orig 2009-11-04 06:13:56.000000000 -0500
++++ nfs-utils-1.2.1/utils/mount/nfsumount.c 2010-01-12 06:07:40.778899357 -0500
+@@ -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 mou
+ 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 -up nfs-utils-1.2.1/utils/mount/stropts.c.orig nfs-utils-1.2.1/utils/mount/stropts.c
+--- nfs-utils-1.2.1/utils/mount/stropts.c.orig 2009-11-04 06:13:56.000000000 -0500
++++ nfs-utils-1.2.1/utils/mount/stropts.c 2010-01-12 06:07:40.779888687 -0500
+@@ -38,6 +38,7 @@
+ #include "xcommon.h"
+ #include "mount.h"
+ #include "nls.h"
++#include "nfsrpc.h"
+ #include "mount_constants.h"
+ #include "stropts.h"
+ #include "error.h"
+@@ -76,12 +77,18 @@ extern char *progname;
+ extern int verbose;
+ extern int sloppy;
+
++union nfs_sockaddr {
++ struct sockaddr sa;
++ struct sockaddr_in s4;
++ struct sockaddr_in6 s6;
++};
++
+ struct nfsmount_info {
+ const char *spec, /* server:/path */
+ *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 +211,9 @@ static int nfs_append_clientaddr_option(
+ 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 +225,33 @@ static int nfs_append_clientaddr_option(
+ }
+
+ /*
+- * 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 +338,16 @@ static int nfs_set_version(struct nfsmou
+ */
+ 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 +393,13 @@ static int nfs_extract_server_addresses(
+ }
+
+ 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 +416,14 @@ static int nfs_construct_new_options(str
+ 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 +440,14 @@ static int nfs_construct_new_options(str
+ 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 +474,12 @@ static int nfs_construct_new_options(str
+ 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 +523,8 @@ nfs_rewrite_pmap_mount_options(struct mo
+ 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 +580,7 @@ static int nfs_try_mount_v3v2(struct nfs
+ return result;
+ }
+
+- if (!nfs_fix_mounthost_option(options)) {
++ if (!nfs_fix_mounthost_option(options, mi->hostname)) {
+ errno = EINVAL;
+ goto out_fail;
+ }
+@@ -601,7 +615,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 +625,18 @@ static int nfs_try_mount_v4(struct nfsmo
+ }
+
+ 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 +682,10 @@ static int nfs_try_mount(struct nfsmount
+ /*
+ * 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 -up nfs-utils-1.2.1/utils/nfsd/nfssvc.c.orig nfs-utils-1.2.1/utils/nfsd/nfssvc.c
+--- nfs-utils-1.2.1/utils/nfsd/nfssvc.c.orig 2009-11-04 06:13:56.000000000 -0500
++++ nfs-utils-1.2.1/utils/nfsd/nfssvc.c 2010-01-12 06:07:40.780888701 -0500
+@@ -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 -up nfs-utils-1.2.1/utils/showmount/showmount.c.orig nfs-utils-1.2.1/utils/showmount/showmount.c
+--- nfs-utils-1.2.1/utils/showmount/showmount.c.orig 2009-11-04 06:13:56.000000000 -0500
++++ nfs-utils-1.2.1/utils/showmount/showmount.c 2010-01-12 06:07:40.781878678 -0500
+@@ -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 -up nfs-utils-1.2.1/utils/statd/callback.c.orig nfs-utils-1.2.1/utils/statd/callback.c
+--- nfs-utils-1.2.1/utils/statd/callback.c.orig 2009-11-04 06:13:56.000000000 -0500
++++ nfs-utils-1.2.1/utils/statd/callback.c 2010-01-12 06:07:40.782830469 -0500
+@@ -35,12 +35,12 @@ sm_notify_1_svc(struct stat_chge *argp,
+ struct sockaddr_in *sin = nfs_getrpccaller_in(rqstp->rq_xprt);
+ char *ip_addr = xstrdup(inet_ntoa(sin->sin_addr));
+
+- 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);
+ }
+diff -up nfs-utils-1.2.1/utils/statd/Makefile.am.orig nfs-utils-1.2.1/utils/statd/Makefile.am
+--- nfs-utils-1.2.1/utils/statd/Makefile.am.orig 2009-11-04 06:13:56.000000000 -0500
++++ nfs-utils-1.2.1/utils/statd/Makefile.am 2010-01-12 06:07:40.781878678 -0500
+@@ -2,31 +2,26 @@
+
+ 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 \
+ 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 \
++ ../../support/nsm/libnsm.a \
+ ../../support/nfs/libnfs.a \
+ ../../support/misc/libmisc.a \
+ $(LIBWRAP) $(LIBNSL)
+-sm_notify_LDADD = $(LIBNSL)
++sm_notify_LDADD = ../../support/nsm/libnsm.a \
++ ../../support/nfs/libnfs.a \
++ $(LIBNSL)
+
+-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 -up nfs-utils-1.2.1/utils/statd/misc.c.orig nfs-utils-1.2.1/utils/statd/misc.c
+--- nfs-utils-1.2.1/utils/statd/misc.c.orig 2009-11-04 06:13:56.000000000 -0500
++++ nfs-utils-1.2.1/utils/statd/misc.c 2010-01-12 06:07:40.783888632 -0500
+@@ -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 -up nfs-utils-1.2.1/utils/statd/monitor.c.orig nfs-utils-1.2.1/utils/statd/monitor.c
+--- nfs-utils-1.2.1/utils/statd/monitor.c.orig 2009-11-04 06:13:56.000000000 -0500
++++ nfs-utils-1.2.1/utils/statd/monitor.c 2010-01-12 06:07:40.784878513 -0500
+@@ -23,14 +23,13 @@
+
+ #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.
+@@ -43,8 +42,7 @@ caller_is_localhost(struct svc_req *rqst
+
+ caller = sin->sin_addr;
+ if (caller.s_addr != htonl(INADDR_LOOPBACK)) {
+- note(N_WARNING,
+- "Call to statd from non-local host %s",
++ xlog_warn("Call to statd from non-local host %s",
+ inet_ntoa(caller));
+ return 0;
+ }
+@@ -61,14 +59,17 @@ sm_mon_1_svc(struct mon *argp, struct sv
+ 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;
++ struct sockaddr_in my_addr = {
++ .sin_family = AF_INET,
++ .sin_addr.s_addr = htonl(INADDR_LOOPBACK),
++ };
+ char *dnsname;
+ struct hostent *hostinfo = 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;
+ result.state = -1; /* State is undefined for STAT_FAIL. */
+@@ -79,7 +80,6 @@ sm_mon_1_svc(struct mon *argp, struct sv
+ */
+ if (!caller_is_localhost(rqstp))
+ goto failure;
+- my_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ /* 2. Reject any registrations for non-lockd services.
+ *
+@@ -92,8 +92,7 @@ sm_mon_1_svc(struct mon *argp, struct sv
+ 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 +104,12 @@ sm_mon_1_svc(struct mon *argp, struct sv
+
+ /* 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!");
++ xlog(L_ERROR, "POSSIBLE SPOOF/ATTACK ATTEMPT!");
+ goto failure;
+ } else if ((hostinfo = gethostbyname(mon_name)) == NULL) {
+- note(N_WARNING, "gethostbyname error for %s", mon_name);
++ xlog_warn("gethostbyname error for %s", mon_name);
+ goto failure;
+ }
+
+@@ -152,7 +151,7 @@ sm_mon_1_svc(struct mon *argp, struct sv
+ 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);
+@@ -168,11 +167,11 @@ sm_mon_1_svc(struct mon *argp, struct sv
+ * doesn't fail. (I should probably fix this assumption.)
+ */
+ if (!(clnt = nlist_new(my_name, mon_name, 0))) {
+- note(N_WARNING, "out of memory");
++ xlog_warn("out of memory");
+ goto failure;
+ }
+
+- NL_ADDR(clnt) = my_addr;
++ NL_ADDR(clnt) = my_addr.sin_addr;
+ NL_MY_PROG(clnt) = id->my_prog;
+ NL_MY_VERS(clnt) = id->my_vers;
+ NL_MY_PROC(clnt) = id->my_proc;
+@@ -182,40 +181,16 @@ sm_mon_1_svc(struct mon *argp, struct sv
+ /*
+ * 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 +207,50 @@ sm_mon_1_svc(struct mon *argp, struct sv
+ 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, 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);
++ const struct sockaddr_in *sin = (const struct sockaddr_in *)sap;
++ 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_ADDR(clnt) = sin->sin_addr;
++ 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, stru
+ 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, stru
+
+ /* 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;
+@@ -352,13 +303,13 @@ sm_unmon_1_svc(struct mon_id *argp, stru
+ 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);
+ nlist_free(&rtnl, clnt);
+
+ return (&result);
+@@ -367,7 +318,7 @@ sm_unmon_1_svc(struct mon_id *argp, stru
+ }
+
+ 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 +332,15 @@ sm_unmon_all_1_svc(struct my_id *argp, s
+ 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 +354,7 @@ sm_unmon_all_1_svc(struct my_id *argp, s
+ 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 +363,7 @@ sm_unmon_all_1_svc(struct my_id *argp, s
+ 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);
+ nlist_free(&rtnl, clnt);
+ ++count;
+ clnt = temp;
+@@ -419,8 +372,8 @@ sm_unmon_all_1_svc(struct my_id *argp, s
+ }
+
+ 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 -up nfs-utils-1.2.1/utils/statd/rmtcall.c.orig nfs-utils-1.2.1/utils/statd/rmtcall.c
+--- nfs-utils-1.2.1/utils/statd/rmtcall.c.orig 2009-11-04 06:13:56.000000000 -0500
++++ nfs-utils-1.2.1/utils/statd/rmtcall.c 2010-01-12 06:07:40.784878513 -0500
+@@ -43,7 +43,6 @@
+ #include "sm_inter.h"
+ #include "statd.h"
+ #include "notlist.h"
+-#include "log.h"
+ #include "ha-callout.h"
+
+ #if SIZEOF_SOCKLEN_T - 0 == 0
+@@ -81,7 +80,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 +90,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;
+ }
+@@ -150,7 +149,7 @@ xmit_call(struct sockaddr_in *sin,
+
+ /* 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__);
++ xlog(D_GENERAL, "%s: can't encode RPC message!", __func__);
+ xdr_destroy(xdrs);
+ return 0;
+ }
+@@ -160,9 +159,9 @@ xmit_call(struct sockaddr_in *sin,
+
+ if ((err = sendto(sockfd, msgbuf, msglen, 0,
+ (struct sockaddr *) sin, sizeof(*sin))) < 0) {
+- dprintf(N_WARNING, "%s: sendto failed: %m", __func__);
++ xlog_warn("%s: sendto failed: %m", __func__);
+ } else if (err != msglen) {
+- dprintf(N_WARNING, "%s: short write: %m", __func__);
++ xlog_warn("%s: short write: %m", __func__);
+ }
+
+ xdr_destroy(xdrs);
+@@ -182,7 +181,7 @@ recv_rply(struct sockaddr_in *sin, u_lon
+ /* Receive message */
+ if ((msglen = recvfrom(sockfd, msgbuf, sizeof(msgbuf), 0,
+ (struct sockaddr *) sin, &alen)) < 0) {
+- dprintf(N_WARNING, "%s: recvfrom failed: %m", __func__);
++ xlog_warn("%s: recvfrom failed: %m", __func__);
+ return NULL;
+ }
+
+@@ -194,19 +193,19 @@ recv_rply(struct sockaddr_in *sin, u_lon
+ 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__);
++ xlog_warn("%s: can't decode RPC message!", __func__);
+ goto done;
+ }
+
+ if (mesg.rm_reply.rp_stat != 0) {
+- note(N_WARNING, "%s: [%s] RPC status %d",
++ xlog_warn("%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",
++ xlog_warn("%s: [%s] RPC status %d",
+ __func__,
+ inet_ntoa(sin->sin_addr),
+ mesg.rm_reply.rp_acpt.ar_stat);
+@@ -224,14 +223,13 @@ recv_rply(struct sockaddr_in *sin, u_lon
+ strncpy (addr, inet_ntoa(lp->addr),
+ sizeof (addr) - 1);
+ addr [sizeof (addr) - 1] = '\0';
+- dprintf(N_WARNING, "%s: address mismatch: "
++ xlog_warn("%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!",
++ xlog_warn("%s: [%s] can't decode reply body!",
+ __func__,
+ inet_ntoa(sin->sin_addr));
+ lp = NULL;
+@@ -260,7 +258,7 @@ process_entry(notify_list *lp)
+ /* __u32 proc, vers, prog; */
+
+ if (NL_TIMES(lp) == 0) {
+- note(N_DEBUG, "%s: Cannot notify %s, giving up.",
++ xlog(D_GENERAL, "%s: Cannot notify %s, giving up",
+ __func__, inet_ntoa(NL_ADDR(lp)));
+ return 0;
+ }
+@@ -286,7 +284,7 @@ process_entry(notify_list *lp)
+
+ lp->xid = xmit_call(&sin, prog, vers, proc, func, objp);
+ if (!lp->xid) {
+- note(N_WARNING, "%s: failed to notify port %d",
++ xlog_warn("%s: failed to notify port %d",
+ __func__, ntohs(lp->port));
+ }
+ NL_TIMES(lp) -= 1;
+@@ -319,10 +317,10 @@ process_reply(FD_SET_TYPE *rfds)
+ nlist_insert_timer(¬ify, lp);
+ return 1;
+ }
+- note(N_WARNING, "%s: [%s] service %d not registered",
++ xlog_warn("%s: [%s] service %d not registered",
+ __func__, inet_ntoa(lp->addr), 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 +344,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 -up nfs-utils-1.2.1/utils/statd/simu.c.orig nfs-utils-1.2.1/utils/statd/simu.c
+--- nfs-utils-1.2.1/utils/statd/simu.c.orig 2009-11-04 06:13:56.000000000 -0500
++++ nfs-utils-1.2.1/utils/statd/simu.c 2010-01-12 06:07:40.785889142 -0500
+@@ -27,24 +27,26 @@ sm_simu_crash_1_svc (void *argp, struct
+ static char *result = NULL;
+ struct in_addr caller;
+
++ xlog(D_CALL, "Received SM_SIMU_CRASH");
++
+ if (sin->sin_family != AF_INET) {
+- note(N_WARNING, "Call to statd from non-AF_INET address");
++ xlog_warn("Call to statd from non-AF_INET address");
+ goto failure;
+ }
+
+ caller = sin->sin_addr;
+ if (caller.s_addr != htonl(INADDR_LOOPBACK)) {
+- note(N_WARNING, "Call to statd from non-local host %s",
++ xlog_warn("Call to statd from non-local host %s",
+ inet_ntoa(caller));
+ goto failure;
+ }
+
+ if (ntohs(sin->sin_port) >= 1024) {
+- note(N_WARNING, "Call to statd-simu-crash from unprivileged port");
++ xlog_warn("Call to statd-simu-crash from unprivileged port");
+ goto failure;
+ }
+
+- note (N_WARNING, "*** SIMULATING CRASH! ***");
++ xlog_warn("*** SIMULATING CRASH! ***");
+ my_svc_exit ();
+
+ if (rtnl)
+diff -up nfs-utils-1.2.1/utils/statd/simulate.c.orig nfs-utils-1.2.1/utils/statd/simulate.c
+--- nfs-utils-1.2.1/utils/statd/simulate.c.orig 2009-11-04 06:13:56.000000000 -0500
++++ nfs-utils-1.2.1/utils/statd/simulate.c 2010-01-12 06:07:40.786878392 -0500
+@@ -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 *monit
+ 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 *monit
+ 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 *unm
+ 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 *unm
+ 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
+ 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
+ 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 *moni
+ 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 *moni
+ 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, s
+ {
+ 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 -up nfs-utils-1.2.1/utils/statd/sm-notify.c.orig nfs-utils-1.2.1/utils/statd/sm-notify.c
+--- nfs-utils-1.2.1/utils/statd/sm-notify.c.orig 2010-01-12 06:06:44.313836268 -0500
++++ nfs-utils-1.2.1/utils/statd/sm-notify.c 2010-01-12 06:09:23.901815847 -0500
+@@ -8,6 +8,7 @@
+ #include
+ #endif
+
++#include
+ #include
+ #include
+ #include
+@@ -28,24 +29,9 @@
+ #include
+ #include
+
+-#define STATD_PATH_XTN "statd/"
+-
+-#ifndef BASEDIR
+-# ifdef NFS_STATEDIR
+-# define BASEDIR NFS_STATEDIR "/" STATD_PATH_XTN
+-# else
+-# define BASEDIR "/var/lib/nfs" "/" STATD_PATH_XTN
+-# 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"
+-
+-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;
++#include "xlog.h"
++#include "nsm.h"
++#include "nfsrpc.h"
+
+ #define NSM_PROG 100024
+ #define NSM_PROGRAM 100024
+@@ -58,7 +44,6 @@ char *_SM_BAK_PATH = DEFAULT_SM_BAK_PATH
+ struct nsm_host {
+ struct nsm_host * next;
+ char * name;
+- char * path;
+ struct sockaddr_storage addr;
+ struct addrinfo *ai;
+ time_t last_used;
+@@ -69,57 +54,22 @@ struct nsm_host {
+ };
+
+ static char nsm_hostname[256];
+-static uint32_t nsm_state;
++static int nsm_state;
+ 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 unsigned int nsm_get_state(int);
+ static void notify(void);
+ 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)
+-{
+- 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;
+-}
+-
+-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;
+- }
+-}
+-
+ static struct addrinfo *smn_lookup(const char *name)
+ {
+ struct addrinfo *ai, hint = {
+@@ -132,27 +82,47 @@ static struct addrinfo *smn_lookup(const
+ int error;
+
+ 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));
++ if (error) {
++ xlog(D_GENERAL, "getaddrinfo(3): %s", gai_strerror(error));
++ return NULL;
++ }
++
++ return ai;
++}
++
++__attribute_malloc__
++static struct nsm_host *
++smn_alloc_host(const char *hostname, const time_t timestamp)
++{
++ struct nsm_host *host;
++
++ host = calloc(1, sizeof(*host));
++ if (host == NULL)
++ goto out_nomem;
++
++ host->name = strdup(hostname);
++ if (host->name == NULL) {
++ 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 from notify list", host->name);
++
++ nsm_delete_notified_host(host->name);
++
+ free(host->name);
+ if (host->ai)
+ freeaddrinfo(host->ai);
+@@ -160,13 +130,37 @@ static void smn_forget_host(struct nsm_h
+ free(host);
+ }
+
++static unsigned int
++smn_get_host(const char *hostname,
++ __attribute__ ((unused)) const struct sockaddr *sap,
++ __attribute__ ((unused)) const struct mon *m,
++ const time_t timestamp)
++{
++ struct nsm_host *host;
++
++ host = smn_alloc_host(hostname, timestamp);
++ if (host == NULL)
++ return 0;
++
++ insert_host(host);
++ xlog(D_GENERAL, "Added host %s to notify list", hostname);
++ return 1;
++}
++
+ int
+ main(int argc, char **argv)
+ {
+ int c;
+ int force = 0;
++ char * progname;
++
++ progname = strrchr(argv[0], '/');
++ if (progname != NULL)
++ progname++;
++ else
++ progname = argv[0];
+
+- while ((c = getopt(argc, argv, "dm:np:v:qP:f")) != -1) {
++ while ((c = getopt(argc, argv, "dm:np:v:P:f")) != -1) {
+ switch (c) {
+ case 'f':
+ force = 1;
+@@ -178,7 +172,7 @@ 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);
+@@ -186,24 +180,9 @@ main(int argc, char **argv)
+ 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:
+@@ -213,18 +192,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);
+ }
+ }
+@@ -233,31 +220,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);
+ }
+
+@@ -273,8 +255,7 @@ usage: fprintf(stderr,
+
+ 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);
+@@ -298,8 +279,7 @@ notify(void)
+ retry:
+ sock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock < 0) {
+- nsm_log(LOG_ERR, "Failed to create RPC socket: %s",
+- strerror(errno));
++ xlog(L_ERROR, "Failed to create RPC socket: %m");
+ exit(1);
+ }
+ fcntl(sock, F_SETFL, O_NONBLOCK);
+@@ -311,7 +291,7 @@ notify(void)
+ if (opt_srcaddr) {
+ struct addrinfo *ai = smn_lookup(opt_srcaddr);
+ if (!ai) {
+- nsm_log(LOG_ERR,
++ xlog(L_ERROR,
+ "Not a valid hostname or address: \"%s\"",
+ opt_srcaddr);
+ exit(1);
+@@ -326,10 +306,9 @@ notify(void)
+ /* Use source port if provided on the command line,
+ * otherwise use bindresvport */
+ if (opt_srcport) {
+- smn_set_port(local_addr, opt_srcport);
++ nfs_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));
++ xlog(L_ERROR, "Failed to bind RPC socket: %m");
+ exit(1);
+ }
+ } else {
+@@ -348,7 +327,8 @@ notify(void)
+ if (opt_max_retry)
+ failtime = time(NULL) + opt_max_retry;
+
+- drop_privs();
++ if (!nsm_drop_privileges(-1))
++ exit(1);
+
+ while (hosts) {
+ struct pollfd pfd;
+@@ -385,7 +365,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;
+@@ -422,8 +402,7 @@ notify_host(int sock, struct nsm_host *h
+ 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;
+ }
+@@ -462,16 +441,16 @@ notify_host(int sock, struct nsm_host *h
+ first->ai_addrlen);
+ }
+
+- smn_set_port((struct sockaddr *)&host->addr, 0);
++ nfs_set_port((struct sockaddr *)&host->addr, 0);
+ host->retries = 0;
+ }
+
+ memcpy(dest, &host->addr, destlen);
+- if (smn_get_port(dest) == 0) {
++ if (nfs_get_port(dest) == 0) {
+ /* Build a PMAP packet */
+- nsm_log(LOG_DEBUG, "Sending portmap query to %s", host->name);
++ xlog(D_GENERAL, "Sending portmap query to %s", host->name);
+
+- smn_set_port(dest, 111);
++ nfs_set_port(dest, 111);
+ *p++ = htonl(100000);
+ *p++ = htonl(2);
+ *p++ = htonl(3);
+@@ -486,7 +465,7 @@ notify_host(int sock, struct nsm_host *h
+ *p++ = 0;
+ } else {
+ /* Build an SM_NOTIFY packet */
+- nsm_log(LOG_DEBUG, "Sending SM_NOTIFY to %s", host->name);
++ xlog(D_GENERAL, "Sending SM_NOTIFY to %s", host->name);
+
+ *p++ = htonl(NSM_PROGRAM);
+ *p++ = htonl(NSM_VERSION);
+@@ -506,8 +485,8 @@ notify_host(int sock, struct nsm_host *h
+ 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));
++ xlog_warn("Sending Reboot Notification to "
++ "'%s' failed: errno %d (%m)", host->name, errno);
+
+ return 0;
+ }
+@@ -528,7 +507,7 @@ recv_reply(int sock)
+ if (res < 0)
+ return;
+
+- nsm_log(LOG_DEBUG, "Received packet...");
++ xlog(D_GENERAL, "Received packet...");
+
+ p = msgbuf;
+ end = p + (res >> 2);
+@@ -547,7 +526,7 @@ recv_reply(int sock)
+ return;
+ sap = (struct sockaddr *)&hp->addr;
+
+- if (smn_get_port(sap) == 0) {
++ if (nfs_get_port(sap) == 0) {
+ /* This was a portmap request */
+ unsigned int port;
+
+@@ -559,11 +538,11 @@ recv_reply(int sock)
+ 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);
++ xlog(D_GENERAL, "No statd on %s", hp->name);
+ hp->timeout = NSM_MAX_TIMEOUT;
+ hp->send_next += NSM_MAX_TIMEOUT;
+ } else {
+- smn_set_port(sap, port);
++ nfs_set_port(sap, port);
+ if (hp->timeout >= NSM_MAX_TIMEOUT / 4)
+ hp->timeout = NSM_MAX_TIMEOUT / 4;
+ }
+@@ -575,7 +554,7 @@ recv_reply(int sock)
+ * packet)
+ */
+ if (p <= end) {
+- nsm_log(LOG_DEBUG, "Host %s notified successfully",
++ xlog(D_GENERAL, "Host %s notified successfully",
+ hp->name);
+ smn_forget_host(hp);
+ return;
+@@ -587,87 +566,6 @@ fail: /* Re-insert the host */
+ }
+
+ /*
+- * Back up all hosts from the sm directory to sm.bak
+- */
+-static void
+-backup_hosts(const char *dirname, const char *bakname)
+-{
+- struct dirent *de;
+- DIR *dir;
+-
+- if (!(dir = opendir(dirname))) {
+- nsm_log(LOG_WARNING,
+- "Failed to open %s: %s", dirname, strerror(errno));
+- return;
+- }
+-
+- 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);
+-}
+-
+-/*
+- * Get all entries from sm.bak and convert them to host entries
+- */
+-static void
+-get_hosts(const char *dirname)
+-{
+- 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;
+- }
+-
+- 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;
+- }
+-
+- snprintf(path, sizeof(path), "%s/%s", dirname, de->d_name);
+- if (stat(path, &stb) < 0)
+- continue;
+-
+- 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 */
+-
+- insert_host(host);
+- host = NULL;
+- }
+- closedir(dir);
+-
+- if (host)
+- free(host);
+-}
+-
+-/*
+ * Insert host into sorted list
+ */
+ static void
+@@ -714,84 +612,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
+@@ -801,61 +621,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 -up nfs-utils-1.2.1/utils/statd/sm-notify.man.orig nfs-utils-1.2.1/utils/statd/sm-notify.man
+--- nfs-utils-1.2.1/utils/statd/sm-notify.man.orig 2010-01-12 06:06:44.313836268 -0500
++++ nfs-utils-1.2.1/utils/statd/sm-notify.man 2010-01-12 06:07:40.788880334 -0500
+@@ -6,7 +6,7 @@
+ .SH NAME
+ sm-notify \- Send out NSM reboot notifications
+ .SH SYNOPSIS
+-.BI "/sbin/sm-notify [-dfq] [-m " time "] [-p " port "] [-P " path "] [-v " my_name " ]
++.BI "/sbin/sm-notify [-df] [-m " time "] [-p " port "] [-P " path "] [-v " my_name " ]
+ .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
+@@ -101,10 +101,6 @@ to bind to the indicated IP
+ 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.
+-.TP
+ .BI -P " /path/to/state/directory
+ If
+ .B sm-notify
+diff -up nfs-utils-1.2.1/utils/statd/stat.c.orig nfs-utils-1.2.1/utils/statd/stat.c
+--- nfs-utils-1.2.1/utils/statd/stat.c.orig 2009-11-04 06:13:56.000000000 -0500
++++ nfs-utils-1.2.1/utils/statd/stat.c 2010-01-12 06:07:40.789888702 -0500
+@@ -42,13 +42,15 @@ sm_stat_1_svc (struct sm_name *argp, str
+ {
+ static sm_stat_res result;
+
++ 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);
++ xlog_warn ("gethostbyname error for %s", argp->mon_name);
+ 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);
+ }
+ result.state = MY_STATE;
+ return(&result);
+diff -up nfs-utils-1.2.1/utils/statd/statd.c.orig nfs-utils-1.2.1/utils/statd/statd.c
+--- nfs-utils-1.2.1/utils/statd/statd.c.orig 2009-11-04 06:13:56.000000000 -0500
++++ nfs-utils-1.2.1/utils/statd/statd.c 2010-01-12 06:07:40.789888702 -0500
+@@ -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 **);
+@@ -109,17 +96,15 @@ sm_prog_1_wrapper (struct svc_req *rqstp
+ static void
+ killer (int sig)
+ {
+- note (N_FATAL, "Caught signal %d, un-registering and exiting.", sig);
+ pmap_unset (SM_PROG, SM_VERS);
+-
+- exit (0);
++ 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();
+ }
+@@ -141,7 +126,7 @@ static void log_modes(void)
+ if (run_mode & MODE_LOG_STDERR)
+ strcat(buf,"Log-STDERR ");
+
+- note(N_WARNING,buf);
++ xlog_warn(buf);
+ }
+
+ /*
+@@ -175,13 +160,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 +173,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 +218,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 +228,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 +236,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 +273,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 +304,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 +352,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 +371,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,7 +417,13 @@ int main (int argc, char **argv)
+ * pass on any SM_NOTIFY that arrives
+ */
+ load_state();
+- load_state_number();
++
++ 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);
++
+ pmap_unset (SM_PROG, SM_VERS);
+
+ /* this registers both UDP and TCP services */
+@@ -505,14 +433,15 @@ int main (int argc, char **argv)
+ 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();
++ if (!nsm_drop_privileges(pidfd))
++ exit(1);
+
+ for (;;) {
+ /*
+@@ -541,29 +470,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 -up nfs-utils-1.2.1/utils/statd/statd.h.orig nfs-utils-1.2.1/utils/statd/statd.h
+--- nfs-utils-1.2.1/utils/statd/statd.h.orig 2010-01-12 06:06:44.309847068 -0500
++++ nfs-utils-1.2.1/utils/statd/statd.h 2010-01-12 06:10:04.934826422 -0500
+@@ -11,30 +11,7 @@
+
+ #include "sm_inter.h"
+ #include "system.h"
+-#include "log.h"
+-
+-/*
+- * Paths and filenames.
+- */
+-#define STATD_PATH_XTN "statd/"
+-#if defined(NFS_STATEDIR)
+-# define DEFAULT_DIR_BASE NFS_STATEDIR "/" STATD_PATH_XTN
+-#else
+-# define DEFAULT_DIR_BASE "/var/lib/nfs/" STATD_PATH_XTN
+-#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.
+@@ -54,7 +31,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);
+
+ /*
+@@ -85,10 +61,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 -up nfs-utils-1.2.1/utils/statd/svc_run.c.orig nfs-utils-1.2.1/utils/statd/svc_run.c
+--- nfs-utils-1.2.1/utils/statd/svc_run.c.orig 2009-11-04 06:13:56.000000000 -0500
++++ nfs-utils-1.2.1/utils/statd/svc_run.c 2010-01-12 06:07:40.791878936 -0500
+@@ -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/nfs-utils.spec b/nfs-utils.spec
index b22071f..1afbd34 100644
--- a/nfs-utils.spec
+++ b/nfs-utils.spec
@@ -2,7 +2,7 @@ Summary: NFS utilities and supporting clients and daemons for the kernel NFS ser
Name: nfs-utils
URL: http://sourceforge.net/projects/nfs
Version: 1.2.1
-Release: 8%{?dist}
+Release: 9%{?dist}
Epoch: 1
# group all 32bit related archs
@@ -22,8 +22,7 @@ Patch00: nfs-utils-1.0.5-statdpath.patch
Patch01: nfs-utils-1.1.0-smnotify-path.patch
Patch02: nfs-utils-1.1.0-exp-subtree-warn-off.patch
-Patch100: nfs-utils-1.2.2-rc3.patch
-Patch101: nfs-utils-1.2.1-mount-ipv6-typo.patch
+Patch100: nfs-utils-1.2.2-rc5.patch
Patch200: nfs-utils-1.2.0-v4root-rel9.patch
@@ -78,7 +77,6 @@ This package also contains the mount.nfs and umount.nfs program.
%patch02 -p1
%patch100 -p1
-%patch101 -p1
%patch200 -p1
@@ -252,6 +250,9 @@ fi
%attr(4755,root,root) /sbin/umount.nfs4
%changelog
+* Mon Jan 12 2010 Steve Dickson 1.2.1-9
+- Updated to latest upstream RC release: nfs-utils-1-2-2-rc5
+
* Mon Jan 4 2010 Steve Dickson 1.2.1-8
- mount.nfs: don't use IPv6 unless IPV6_SUPPORTED is set