Blob Blame History Raw
-------------------------------------------------------------------------------
statd-replace-note-with-xlog-i
-------------------------------------------------------------------------------
statd: Replace note() with xlog() in rpc.statd

From: Chuck Lever <chuck.lever@oracle.com>

To facilitate code sharing between statd and sm-notify (and with other
components of nfs-utils), replace rpc.statd's note() with xlog().

Bonus: add debugging xlog() messages to report incoming RPC requests.
These additional messages are enabled when "-d" is specified on the
statd command line.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 support/include/ha-callout.h |    4 --
 utils/statd/Makefile.am      |    4 +-
 utils/statd/callback.c       |    4 +-
 utils/statd/log.c            |   95 ------------------------------------------
 utils/statd/log.h            |   42 -------------------
 utils/statd/misc.c           |   12 ++---
 utils/statd/monitor.c        |   47 +++++++++++----------
 utils/statd/rmtcall.c        |   36 ++++++++--------
 utils/statd/simu.c           |   10 +++-
 utils/statd/simulate.c       |   52 +++++++++++------------
 utils/statd/stat.c           |    8 ++--
 utils/statd/statd.c          |   57 +++++++++++--------------
 utils/statd/statd.h          |    9 ----
 utils/statd/svc_run.c        |    7 +--
 utils/statd/version.h        |    7 ---
 15 files changed, 118 insertions(+), 276 deletions(-)
 delete mode 100644 utils/statd/log.c
 delete mode 100644 utils/statd/log.h
 delete mode 100644 utils/statd/version.h


diff --git a/support/include/ha-callout.h b/support/include/ha-callout.h
index efb70fb..1164336 100644
--- a/support/include/ha-callout.h
+++ b/support/include/ha-callout.h
@@ -53,11 +53,7 @@ ha_callout(char *event, char *arg1, char *arg2, int arg3)
 		default: pid = waitpid(pid, &ret, 0);
   	}
 	sigaction(SIGCHLD, &oldact, &newact);
-#ifdef dprintf
-	dprintf(N_DEBUG, "ha callout returned %d\n", WEXITSTATUS(ret));
-#else
 	xlog(D_GENERAL, "ha callout returned %d\n", WEXITSTATUS(ret));
-#endif
 }
 
 #endif
diff --git a/utils/statd/Makefile.am b/utils/statd/Makefile.am
index 8a3ba4e..19ba7b4 100644
--- a/utils/statd/Makefile.am
+++ b/utils/statd/Makefile.am
@@ -13,9 +13,9 @@ 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 \
+	        sm_inter_clnt.c sm_inter_svc.c sm_inter_xdr.c \
 	        notlist.h statd.h system.h version.h sm_inter.h
 sm_notify_SOURCES = sm-notify.c
 
diff --git a/utils/statd/callback.c b/utils/statd/callback.c
index 8885238..2f98aeb 100644
--- a/utils/statd/callback.c
+++ b/utils/statd/callback.c
@@ -35,12 +35,12 @@ sm_notify_1_svc(struct stat_chge *argp, struct svc_req *rqstp)
 	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 --git a/utils/statd/log.c b/utils/statd/log.c
deleted file mode 100644
index a6ca996..0000000
--- a/utils/statd/log.c
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (C) 1995 Olaf Kirch
- * Modified by Jeffrey A. Uphoff, 1995, 1997, 1999.
- * Modified by H.J. Lu, 1998.
- * Modified by Lon Hohberger, Oct. 2000
- *
- * NSM for Linux.
- */
-
-/* 
- * 	log.c - logging functions for lockd/statd
- *	260295	 okir	started with simply syslog logging.
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <syslog.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdarg.h>
-#include <time.h>
-#include <sys/types.h>
-#include "log.h"
-#include "statd.h"
-
-static pid_t	mypid;
-								/* Turns on logging to console/stderr. */
-#if 0
-static int	opt_debug = 0;	/* Will be command-line option, eventually */
-#endif
-
-void log_init(void)
-{
-	if (!(run_mode & MODE_LOG_STDERR)) 
-		openlog(name_p, LOG_PID | LOG_NDELAY, LOG_DAEMON);
-
-	mypid = getpid();
-
-	note(N_WARNING,"Version %s Starting",version_p);
-}
-
-void log_background(void)
-{
-	/* NOP */
-}
-
-void die(char *fmt, ...)
-{
-	char	buffer[1024];
-	va_list	ap;
-
-	va_start(ap, fmt);
-	vsnprintf (buffer, 1024, fmt, ap);
-	va_end(ap);
-	buffer[1023]=0;
-
-	note(N_FATAL, "%s", buffer);
-
-#ifndef DEBUG
-	exit (2);
-#else
-	abort();	/* make a core */
-#endif
-}
-
-void note(int level, char *fmt, ...)
-{
-	char	buffer[1024];
-	va_list	ap;
-
-	va_start(ap, fmt);
-	vsnprintf (buffer, 1024, fmt, ap);
-	va_end(ap);
-	buffer[1023]=0;
-
-	if ((!(run_mode & MODE_LOG_STDERR)) && (level < N_DEBUG)) {
-		syslog(level, "%s", buffer);
-	} else if (run_mode & MODE_LOG_STDERR) {
-		/* Log everything, including dprintf() stuff to stderr */
-		time_t		now;
-		struct tm *	tm;
-
-		time(&now);
-		tm = localtime(&now);
-		fprintf (stderr, "%02d/%02d/%04d %02d:%02d:%02d %s[%d]: %s\n",
-			tm->tm_mon + 1, tm->tm_mday, tm->tm_year + 1900,
-			tm->tm_hour, tm->tm_min, tm->tm_sec,
-			name_p, mypid,
-			buffer);
-	}
-}
diff --git a/utils/statd/log.h b/utils/statd/log.h
deleted file mode 100644
index fc55d3c..0000000
--- a/utils/statd/log.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 1995 Olaf Kirch
- * Modified by Jeffrey A. Uphoff, 1996, 1997, 1999.
- * Modified by Lon Hohberger, Oct. 2000
- *
- * NSM for Linux.
- */
-
-/*
- * 	logging functionality
- *	260295	okir
- */
-
-#ifndef _LOCKD_LOG_H_
-#define _LOCKD_LOG_H_
-
-#include <syslog.h>
-
-void	log_init(void);
-void	log_background(void);
-void	log_enable(int facility);
-int	log_enabled(int facility);
-void	note(int level, char *fmt, ...);
-void	die(char *fmt, ...);
-
-/*
- * Map per-application severity to system severity. What's fatal for
- * lockd is merely an itching spot from the universe's point of view.
- */
-#define N_CRIT		LOG_CRIT
-#define N_FATAL		LOG_ERR
-#define N_ERROR		LOG_WARNING
-#define N_WARNING	LOG_NOTICE
-#define N_DEBUG		LOG_DEBUG
-
-#ifdef DEBUG
-#define dprintf		note
-#else
-#define dprintf		if (run_mode & MODE_LOG_STDERR) note
-#endif
-
-#endif /* _LOCKD_LOG_H_ */
diff --git a/utils/statd/misc.c b/utils/statd/misc.c
index 7256291..44af30e 100644
--- a/utils/statd/misc.c
+++ b/utils/statd/misc.c
@@ -29,8 +29,7 @@ xmalloc (size_t size)
     return ((void *)NULL);
 
   if (!(ptr = malloc (size)))
-    /* SHIT!  SHIT!  SHIT! */
-    die ("malloc failed");
+    xlog_err ("malloc failed");
 
   return (ptr);
 }
@@ -46,7 +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);
 }
@@ -62,16 +61,15 @@ xunlink (char *path, char *host)
 
 	tozap = malloc(strlen(path)+strlen(host)+2);
 	if (tozap == NULL) {
-		note(N_ERROR, "xunlink: malloc failed: errno %d (%s)", 
-			errno, strerror(errno));
+		xlog(L_ERROR, "xunlink: malloc failed: errno %d (%m)", errno);
 		return;
 	}
 	sprintf (tozap, "%s/%s", path, host);
 
 	if (unlink (tozap) == -1)
-		note(N_ERROR, "unlink (%s): %s", tozap, strerror (errno));
+		xlog(L_ERROR, "unlink (%s): %m", tozap);
 	else
-		dprintf (N_DEBUG, "Unlinked %s", tozap);
+		xlog(D_GENERAL, "Unlinked %s", tozap);
 
 	free(tozap);
 }
diff --git a/utils/statd/monitor.c b/utils/statd/monitor.c
index a2c9e2b..09f03da 100644
--- a/utils/statd/monitor.c
+++ b/utils/statd/monitor.c
@@ -43,8 +43,7 @@ caller_is_localhost(struct svc_req *rqstp)
 
 	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;
 	}
@@ -69,6 +68,8 @@ sm_mon_1_svc(struct mon *argp, struct svc_req *rqstp)
 	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. */
@@ -92,8 +93,7 @@ sm_mon_1_svc(struct mon *argp, struct svc_req *rqstp)
 	if (id->my_prog != 100021 ||
 	    (id->my_proc != 16 && id->my_proc != 24))
 	{
-		note(N_WARNING,
-			"Attempt to register callback to %d/%d",
+		xlog_warn("Attempt to register callback to %d/%d",
 			id->my_prog, id->my_proc);
 		goto failure;
 	}
@@ -105,12 +105,12 @@ sm_mon_1_svc(struct mon *argp, struct svc_req *rqstp)
 
 	/* must check for /'s in hostname!  See CERT's CA-96.09 for details. */
 	if (strchr(mon_name, '/') || mon_name[0] == '.') {
-		note(N_CRIT, "SM_MON request for hostname containing '/' "
+		xlog(L_ERROR, "SM_MON request for hostname containing '/' "
 		     "or starting '.': %s", mon_name);
-		note(N_CRIT, "POSSIBLE SPOOF/ATTACK ATTEMPT!");
+		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 +152,7 @@ sm_mon_1_svc(struct mon *argp, struct svc_req *rqstp)
 		    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,7 +168,7 @@ sm_mon_1_svc(struct mon *argp, struct svc_req *rqstp)
 	 * doesn't fail.  (I should probably fix this assumption.)
 	 */
 	if (!(clnt = nlist_new(my_name, mon_name, 0))) {
-		note(N_WARNING, "out of memory");
+		xlog_warn("out of memory");
 		goto failure;
 	}
 
@@ -188,7 +188,7 @@ sm_mon_1_svc(struct mon *argp, struct svc_req *rqstp)
 	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));
+		xlog(L_ERROR, "creat(%s) failed: %m", path);
 		nlist_free(NULL, clnt);
 		free(path);
 		goto failure;
@@ -205,7 +205,7 @@ sm_mon_1_svc(struct mon *argp, struct svc_req *rqstp)
 		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)",
+			xlog_warn("writing to %s failed: errno %d (%s)",
 				path, errno, strerror(errno));
 		}
 	}
@@ -215,7 +215,7 @@ sm_mon_1_svc(struct mon *argp, struct svc_req *rqstp)
 	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,7 +232,7 @@ sm_mon_1_svc(struct mon *argp, struct svc_req *rqstp)
 	return (&result);
 
 failure:
-	note(N_WARNING, "STAT_FAIL to %s for SM_MON of %s", my_name, mon_name);
+	xlog_warn("STAT_FAIL to %s for SM_MON of %s", my_name, mon_name);
 	return (&result);
 }
 
@@ -320,6 +320,8 @@ sm_unmon_1_svc(struct mon_id *argp, struct svc_req *rqstp)
 	struct my_id	*id = &argp->my_id;
 	char		*cp;
 
+	xlog(D_CALL, "Received SM_UNMON for %s from %s", mon_name, my_name);
+
 	result.state = MY_STATE;
 
 	if (!caller_is_localhost(rqstp))
@@ -333,9 +335,8 @@ sm_unmon_1_svc(struct mon_id *argp, struct svc_req *rqstp)
 
 	/* Check if we're monitoring anyone. */
 	if (rtnl == NULL) {
-		note(N_WARNING,
-			"Received SM_UNMON request from %s for %s while not "
-			"monitoring any hosts.", my_name, argp->mon_name);
+		xlog_warn("Received SM_UNMON request from %s for %s while not "
+			"monitoring any hosts", my_name, argp->mon_name);
 		return (&result);
 	}
 	clnt = rtnl;
@@ -352,7 +353,7 @@ sm_unmon_1_svc(struct mon_id *argp, struct svc_req *rqstp)
 			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: */
@@ -367,7 +368,7 @@ sm_unmon_1_svc(struct mon_id *argp, struct svc_req *rqstp)
 	}
 
  failure:
-	note(N_WARNING, "Received erroneous SM_UNMON request from %s for %s",
+	xlog_warn("Received erroneous SM_UNMON request from %s for %s",
 		my_name, mon_name);
 	return (&result);
 }
@@ -381,13 +382,15 @@ sm_unmon_all_1_svc(struct my_id *argp, struct svc_req *rqstp)
 	notify_list	*clnt;
 	char		*my_name = argp->my_name;
 
+	xlog(D_CALL, "Received SM_UNMON_ALL for %s", my_name);
+
 	if (!caller_is_localhost(rqstp))
 		goto failure;
 
 	result.state = MY_STATE;
 
 	if (rtnl == NULL) {
-		note(N_WARNING, "Received SM_UNMON_ALL request from %s "
+		xlog_warn("Received SM_UNMON_ALL request from %s "
 			"while not monitoring any hosts", my_name);
 		return (&result);
 	}
@@ -401,7 +404,7 @@ sm_unmon_all_1_svc(struct my_id *argp, struct svc_req *rqstp)
 			char            mon_name[SM_MAXSTRLEN + 1];
 			notify_list	*temp;
 
-			dprintf(N_DEBUG,
+			xlog(D_GENERAL,
 				"UNMONITORING (SM_UNMON_ALL) %s for %s",
 				NL_MON_NAME(clnt), NL_MY_NAME(clnt));
 			strncpy(mon_name, NL_MON_NAME(clnt),
@@ -419,8 +422,8 @@ sm_unmon_all_1_svc(struct my_id *argp, struct svc_req *rqstp)
 	}
 
 	if (!count) {
-		dprintf(N_DEBUG, "SM_UNMON_ALL request from %s with no "
-			"SM_MON requests from it.", my_name);
+		xlog(D_GENERAL, "SM_UNMON_ALL request from %s with no "
+			"SM_MON requests from it", my_name);
 	}
 
  failure:
diff --git a/utils/statd/rmtcall.c b/utils/statd/rmtcall.c
index cc1a4a4..5700fc7 100644
--- a/utils/statd/rmtcall.c
+++ b/utils/statd/rmtcall.c
@@ -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_long *portp)
 	/* 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_long *portp)
 	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_long *portp)
 			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(&notify, 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(&notify, lp);
@@ -346,8 +344,8 @@ process_notify_list(void)
 			nlist_remove(&notify, entry);
 			nlist_insert_timer(&notify, entry);
 		} else {
-			note(N_ERROR,
-				"%s: Can't callback %s (%d,%d), giving up.",
+			xlog(L_ERROR,
+				"%s: Can't callback %s (%d,%d), giving up",
 					__func__,
 					NL_MY_NAME(entry),
 					NL_MY_PROG(entry),
diff --git a/utils/statd/simu.c b/utils/statd/simu.c
index a7ecb85..7df04d9 100644
--- a/utils/statd/simu.c
+++ b/utils/statd/simu.c
@@ -27,24 +27,26 @@ sm_simu_crash_1_svc (void *argp, struct svc_req *rqstp)
   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 --git a/utils/statd/simulate.c b/utils/statd/simulate.c
index de8f1c9..4ed1468 100644
--- a/utils/statd/simulate.c
+++ b/utils/statd/simulate.c
@@ -38,7 +38,9 @@ extern void svc_exit (void);
 void
 simulator (int argc, char **argv)
 {
-  log_enable (1);
+  xlog_stderr (1);
+  xlog_syslog (0);
+  xlog_open ("statd simulator");
 
   if (argc == 2)
     if (!strcasecmp (*argv, "crash"))
@@ -61,7 +63,7 @@ simulator (int argc, char **argv)
       simulate_mon (*(&argv[1]), *(&argv[2]), *(&argv[3]), *(&argv[4]),
 		    *(&argv[5]));
   }
-  die ("WTF?  Give me something I can use!");
+  xlog_err ("WTF?  Give me something I can use!");
 }
 
 static void
@@ -72,11 +74,11 @@ simulate_mon (char *calling, char *monitoring, char *as, char *proggy,
   sm_stat_res *result;
   mon mon;
 
-  dprintf (N_DEBUG, "Calling %s (as %s) to monitor %s", calling, as,
+  xlog (D_GENERAL, "Calling %s (as %s) to monitor %s", calling, as,
 	   monitoring);
 
   if ((client = clnt_create (calling, SM_PROG, SM_VERS, "udp")) == NULL)
-    die ("%s", clnt_spcreateerror ("clnt_create"));
+    xlog_err ("%s", clnt_spcreateerror ("clnt_create"));
 
   memcpy (mon.priv, fool, SM_PRIV_SIZE);
   mon.mon_id.my_id.my_name = xstrdup (as);
@@ -87,16 +89,15 @@ simulate_mon (char *calling, char *monitoring, char *as, char *proggy,
   mon.mon_id.mon_name = monitoring;
 
   if (!(result = sm_mon_1 (&mon, client)))
-    die ("%s", clnt_sperror (client, "sm_mon_1"));
+    xlog_err ("%s", clnt_sperror (client, "sm_mon_1"));
 
   free (mon.mon_id.my_id.my_name);
 
   if (result->res_stat != STAT_SUCC) {
-    note (N_FATAL, "SM_MON request failed, state: %d", result->state);
-    exit (0);
+    xlog_err ("SM_MON request failed, state: %d", result->state);
   } else {
-    dprintf (N_DEBUG, "SM_MON result successful, state: %d\n", result->state);
-    dprintf (N_DEBUG, "Waiting for callback.");
+    xlog (D_GENERAL, "SM_MON result successful, state: %d\n", result->state);
+    xlog (D_GENERAL, "Waiting for callback");
     daemon_simulator ();
     exit (0);
   }
@@ -109,11 +110,11 @@ simulate_unmon (char *calling, char *unmonitoring, char *as, char *proggy)
   sm_stat *result;
   mon_id mon_id;
 
-  dprintf (N_DEBUG, "Calling %s (as %s) to unmonitor %s", calling, as,
+  xlog (D_GENERAL, "Calling %s (as %s) to unmonitor %s", calling, as,
 	   unmonitoring);
 
   if ((client = clnt_create (calling, SM_PROG, SM_VERS, "udp")) == NULL)
-    die ("%s", clnt_spcreateerror ("clnt_create"));
+    xlog_err ("%s", clnt_spcreateerror ("clnt_create"));
 
   mon_id.my_id.my_name = xstrdup (as);
   mon_id.my_id.my_prog = atoi (proggy) * SIM_SM_PROG;
@@ -122,10 +123,10 @@ simulate_unmon (char *calling, char *unmonitoring, char *as, char *proggy)
   mon_id.mon_name = unmonitoring;
 
   if (!(result = sm_unmon_1 (&mon_id, client)))
-    die ("%s", clnt_sperror (client, "sm_unmon_1"));
+    xlog_err ("%s", clnt_sperror (client, "sm_unmon_1"));
 
   free (mon_id.my_id.my_name);
-  dprintf (N_DEBUG, "SM_UNMON request returned state: %d\n", result->state);
+  xlog (D_GENERAL, "SM_UNMON request returned state: %d\n", result->state);
   exit (0);
 }
 
@@ -136,10 +137,10 @@ simulate_unmon_all (char *calling, char *as, char *proggy)
   sm_stat *result;
   my_id my_id;
 
-  dprintf (N_DEBUG, "Calling %s (as %s) to unmonitor all hosts", calling, as);
+  xlog (D_GENERAL, "Calling %s (as %s) to unmonitor all hosts", calling, as);
 
   if ((client = clnt_create (calling, SM_PROG, SM_VERS, "udp")) == NULL)
-    die ("%s", clnt_spcreateerror ("clnt_create"));
+    xlog_err ("%s", clnt_spcreateerror ("clnt_create"));
 
   my_id.my_name = xstrdup (as);
   my_id.my_prog = atoi (proggy) * SIM_SM_PROG;
@@ -147,10 +148,10 @@ simulate_unmon_all (char *calling, char *as, char *proggy)
   my_id.my_proc = SIM_SM_MON;
 
   if (!(result = sm_unmon_all_1 (&my_id, client)))
-    die ("%s", clnt_sperror (client, "sm_unmon_all_1"));
+    xlog_err ("%s", clnt_sperror (client, "sm_unmon_all_1"));
 
   free (my_id.my_name);
-  dprintf (N_DEBUG, "SM_UNMON_ALL request returned state: %d\n", result->state);
+  xlog (D_GENERAL, "SM_UNMON_ALL request returned state: %d\n", result->state);
   exit (0);
 }
 
@@ -160,10 +161,10 @@ simulate_crash (char *host)
   CLIENT *client;
 
   if ((client = clnt_create (host, SM_PROG, SM_VERS, "udp")) == NULL)
-    die ("%s", clnt_spcreateerror ("clnt_create"));
+    xlog_err ("%s", clnt_spcreateerror ("clnt_create"));
 
   if (!sm_simu_crash_1 (NULL, client))
-    die ("%s", clnt_sperror (client, "sm_simu_crash_1"));
+    xlog_err ("%s", clnt_sperror (client, "sm_simu_crash_1"));
 
   exit (0);
 }
@@ -176,18 +177,18 @@ simulate_stat (char *calling, char *monitoring)
   sm_stat_res *result;
   
   if ((client = clnt_create (calling, SM_PROG, SM_VERS, "udp")) == NULL)
-    die ("%s", clnt_spcreateerror ("clnt_create"));
+    xlog_err ("%s", clnt_spcreateerror ("clnt_create"));
 
   checking.mon_name = monitoring;
 
   if (!(result = sm_stat_1 (&checking, client)))
-    die ("%s", clnt_sperror (client, "sm_stat_1"));
+    xlog_err ("%s", clnt_sperror (client, "sm_stat_1"));
 
   if (result->res_stat == STAT_SUCC)
-    dprintf (N_DEBUG, "STAT_SUCC from %s for %s, state: %d", calling,
+    xlog (D_GENERAL, "STAT_SUCC from %s for %s, state: %d", calling,
 	     monitoring, result->state);
   else
-    dprintf (N_DEBUG, "STAT_FAIL from %s for %s, state: %d", calling,
+    xlog (D_GENERAL, "STAT_FAIL from %s for %s, state: %d", calling,
 	     monitoring, result->state);
 
   exit (0);
@@ -196,9 +197,8 @@ simulate_stat (char *calling, char *monitoring)
 static void
 sim_killer (int sig)
 {
-  note (N_FATAL, "Simulator caught signal %d, un-registering and exiting.", sig);
   pmap_unset (sim_port, SIM_SM_VERS);
-  exit (0);
+  xlog_err ("Simulator caught signal %d, un-registering and exiting", sig);
 }
 
 static void
@@ -219,7 +219,7 @@ sim_sm_mon_1_svc (struct status *argp, struct svc_req *rqstp)
 {
   static char *result;
 
-  dprintf (N_DEBUG, "Recieved state %d for mon_name %s (opaque \"%s\")",
+  xlog (D_GENERAL, "Recieved state %d for mon_name %s (opaque \"%s\")",
 	   argp->state, argp->mon_name, argp->priv);
   svc_exit ();
   return ((void *)&result);
diff --git a/utils/statd/stat.c b/utils/statd/stat.c
index 799239f..477f632 100644
--- a/utils/statd/stat.c
+++ b/utils/statd/stat.c
@@ -42,13 +42,15 @@ sm_stat_1_svc (struct sm_name *argp, struct svc_req *rqstp)
 {
   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 --git a/utils/statd/statd.c b/utils/statd/statd.c
index 1c5247e..6148952 100644
--- a/utils/statd/statd.c
+++ b/utils/statd/statd.c
@@ -26,7 +26,6 @@
 #include <sys/wait.h>
 #include <grp.h>
 #include "statd.h"
-#include "version.h"
 #include "nfslib.h"
 
 /* Socket operations */
@@ -50,8 +49,7 @@ 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
@@ -109,17 +107,15 @@ sm_prog_1_wrapper (struct svc_req *rqstp, register SVCXPRT *transp)
 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 +137,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 +171,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,8 +184,8 @@ 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));
+			xlog_warn("truncating pid file failed: errno %d (%m)\n",
+				errno);
 		}
 	}
 }
@@ -206,8 +201,8 @@ static void drop_privs(void)
 	}
 
 	if (st.st_uid == 0) {
-		note(N_WARNING, "statd running as root. chown %s to choose different user\n",
-		    SM_DIR);
+		xlog_warn("Running as 'root'.  "
+			"chown %s to choose different user\n", SM_DIR);
 		return;
 	}
 	/* better chown the pid file before dropping, as if it
@@ -215,14 +210,14 @@ static void drop_privs(void)
 	 */
 	if (pidfd >= 0) {
 		if (fchown(pidfd, st.st_uid, st.st_gid) < 0) {
-			note(N_ERROR, "Unable to change owner of %s: %d (%s)",
+			xlog(L_ERROR, "Unable to change owner of %s: %d (%s)",
 					SM_DIR, strerror (errno));
 		}
 	}
 	setgroups(0, NULL);
 	if (setgid(st.st_gid) == -1
 	    || setuid(st.st_uid) == -1) {
-		note(N_ERROR, "Fail to drop privileges");
+		xlog(L_ERROR, "Fail to drop privileges");
 		exit(1);
 	}
 }
@@ -266,6 +261,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 +271,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 +279,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;
@@ -383,7 +373,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. */
@@ -455,7 +444,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();
 
@@ -505,7 +500,7 @@ 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]);
@@ -552,7 +547,7 @@ load_state_number(void)
 		return;
 
 	if (read(fd, &MY_STATE, sizeof(MY_STATE)) != sizeof(MY_STATE)) {
-		note(N_WARNING, "Unable to read state from '%s': errno %d (%s)",
+		xlog_warn("Unable to read state from '%s': errno %d (%s)",
 				SM_STAT_PATH, errno, strerror(errno));
 	}
 	close(fd);
@@ -561,7 +556,7 @@ load_state_number(void)
 		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)",
+			xlog_warn("Writing to '%s' failed: errno %d (%s)",
 				file, errno, strerror(errno));
 		close(fd);
 	}
diff --git a/utils/statd/statd.h b/utils/statd/statd.h
index 88ba208..085f32d 100644
--- a/utils/statd/statd.h
+++ b/utils/statd/statd.h
@@ -11,7 +11,7 @@
 
 #include "sm_inter.h"
 #include "system.h"
-#include "log.h"
+#include "xlog.h"
 
 /*
  * Paths and filenames.
@@ -84,10 +84,3 @@ extern int run_mode;
  * another host.... */
 #define STATIC_HOSTNAME 8	/* Always use the hostname set by -n */
 #define	MODE_NO_NOTIFY	16	/* Don't notify peers of a reboot */
-/*
- * Program name and version pointers -- See statd.c for the reasoning
- * as to why they're global.
- */
-extern char *name_p;		/* program basename */
-extern const char *version_p;	/* program version */
-
diff --git a/utils/statd/svc_run.c b/utils/statd/svc_run.c
index 14ee663..d98ecee 100644
--- a/utils/statd/svc_run.c
+++ b/utils/statd/svc_run.c
@@ -101,12 +101,12 @@ my_svc_run(void)
 
 			tv.tv_sec  = NL_WHEN(notify) - now;
 			tv.tv_usec = 0;
-			dprintf(N_DEBUG, "Waiting for reply... (timeo %d)",
+			xlog(D_GENERAL, "Waiting for reply... (timeo %d)",
 							tv.tv_sec);
 			selret = select(FD_SETSIZE, &readfds,
 				(void *) 0, (void *) 0, &tv);
 		} else {
-			dprintf(N_DEBUG, "Waiting for client connections.");
+			xlog(D_GENERAL, "Waiting for client connections");
 			selret = select(FD_SETSIZE, &readfds,
 				(void *) 0, (void *) 0, (struct timeval *) 0);
 		}
@@ -116,8 +116,7 @@ my_svc_run(void)
 			if (errno == EINTR || errno == ECONNREFUSED
 			 || errno == ENETUNREACH || errno == EHOSTUNREACH)
 				continue;
-			note(N_ERROR, "my_svc_run() - select: %s",
-				strerror (errno));
+			xlog(L_ERROR, "my_svc_run() - select: %m");
 			return;
 
 		case 0:
diff --git a/utils/statd/version.h b/utils/statd/version.h
deleted file mode 100644
index 12f16bd..0000000
--- a/utils/statd/version.h
+++ /dev/null
@@ -1,7 +0,0 @@
-/*
- * Copyright (C) 1997-1999 Jeffrey A. Uphoff
- *
- * NSM for Linux.
- */
-
-#define STATD_RELEASE "1.1.1"
-------------------------------------------------------------------------------
statd-replace-nsm_log-with-xlo
-------------------------------------------------------------------------------
statd: Replace nsm_log() with xlog() in sm-notify command

From: Chuck Lever <chuck.lever@oracle.com>

To facilitate code sharing between statd and sm-notify (and with other
components of nfs-utils), replace sm-notify's nsm_log() with xlog().

Since opt_quiet is used in only a handful of insignificant cases, it
is removed.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 utils/statd/Makefile.am   |    3 +
 utils/statd/sm-notify.c   |  163 ++++++++++++++++++---------------------------
 utils/statd/sm-notify.man |    6 --
 3 files changed, 67 insertions(+), 105 deletions(-)


diff --git a/utils/statd/Makefile.am b/utils/statd/Makefile.am
index 19ba7b4..f64cd7a 100644
--- a/utils/statd/Makefile.am
+++ b/utils/statd/Makefile.am
@@ -24,7 +24,8 @@ statd_LDADD = ../../support/export/libexport.a \
 	      ../../support/nfs/libnfs.a \
 	      ../../support/misc/libmisc.a \
 	      $(LIBWRAP) $(LIBNSL)
-sm_notify_LDADD = $(LIBNSL)
+sm_notify_LDADD = ../../support/nfs/libnfs.a \
+		  $(LIBNSL)
 
 EXTRA_DIST = sim_sm_inter.x sm_inter.x $(man8_MANS) COPYRIGHT simulate.c
 
diff --git a/utils/statd/sm-notify.c b/utils/statd/sm-notify.c
index 72dcff4..1d4403a 100644
--- a/utils/statd/sm-notify.c
+++ b/utils/statd/sm-notify.c
@@ -28,6 +28,8 @@
 #include <errno.h>
 #include <grp.h>
 
+#include "xlog.h"
+
 #ifndef BASEDIR
 # ifdef NFS_STATEDIR
 #  define BASEDIR		NFS_STATEDIR
@@ -69,12 +71,10 @@ struct nsm_host {
 static char		nsm_hostname[256];
 static uint32_t		nsm_state;
 static int		opt_debug = 0;
-static int		opt_quiet = 0;
 static int		opt_update_state = 1;
 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);
@@ -84,7 +84,6 @@ 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);
@@ -130,21 +129,12 @@ static struct addrinfo *smn_lookup(const char *name)
 	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 NULL;
+	return ai;
 }
 
 static void smn_forget_host(struct nsm_host *host)
@@ -163,8 +153,15 @@ main(int argc, char **argv)
 {
 	int	c;
 	int	force = 0;
+	char *	progname;
 
-	while ((c = getopt(argc, argv, "dm:np:v:qP:f")) != -1) {
+	progname = strrchr(argv[0], '/');
+	if (progname != NULL)
+		progname++;
+	else
+		progname = argv[0];
+
+	while ((c = getopt(argc, argv, "dm:np:v:P:f")) != -1) {
 		switch (c) {
 		case 'f':
 			force = 1;
@@ -184,9 +181,6 @@ 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"));
@@ -196,7 +190,7 @@ main(int argc, char **argv)
 			    _SM_STATE_PATH == NULL ||
 			    _SM_DIR_PATH == NULL ||
 			    _SM_BAK_PATH == NULL) {
-				nsm_log(LOG_ERR, "unable to allocate memory");
+				fprintf(stderr, "unable to allocate memory");
 				exit(1);
 			}
 			strcat(strcpy(_SM_STATE_PATH, _SM_BASE_PATH), "/state");
@@ -211,18 +205,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) {
 			/* already run, don't try again */
-			nsm_log(LOG_NOTICE, "Already notifying clients; Exiting!");
+			xlog(L_NOTICE, "Already notifying clients; Exiting!");
 			exit(0);
 		}
 	}
@@ -231,8 +233,7 @@ 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);
 	}
 
@@ -241,7 +242,7 @@ usage:		fprintf(stderr,
 
 	/* If there are not hosts to notify, just exit */
 	if (!hosts) {
-		nsm_log(LOG_DEBUG, "No hosts to notify; exiting");
+		xlog(D_GENERAL, "No hosts to notify; exiting");
 		return 0;
 	}
 
@@ -250,12 +251,10 @@ usage:		fprintf(stderr,
 	set_kernel_nsm_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);
 		}
 
@@ -271,8 +270,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);
@@ -296,8 +294,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);
@@ -309,7 +306,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,8 +323,7 @@ notify(void)
 	if (opt_srcport) {
 		smn_set_port(local_addr, opt_srcport);
 		if (bind(sock, local_addr, sizeof(struct sockaddr_in)) < 0) {
-			nsm_log(LOG_ERR, "Failed to bind RPC socket: %s",
-				strerror(errno));
+			xlog(L_ERROR, "Failed to bind RPC socket: %m");
 			exit(1);
 		}
 	} else {
@@ -383,7 +379,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;
@@ -420,8 +416,7 @@ notify_host(int sock, struct nsm_host *host)
 	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;
 		}
@@ -467,7 +462,7 @@ notify_host(int sock, struct nsm_host *host)
 	memcpy(dest, &host->addr, destlen);
 	if (smn_get_port(dest) == 0) {
 		/* Build a PMAP packet */
-		nsm_log(LOG_DEBUG, "Sending portmap query to %s", host->name);
+		xlog(D_GENERAL, "Sending portmap query to %s", host->name);
 
 		smn_set_port(dest, 111);
 		*p++ = htonl(100000);
@@ -484,7 +479,7 @@ notify_host(int sock, struct nsm_host *host)
 		*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);
@@ -504,8 +499,8 @@ notify_host(int sock, struct nsm_host *host)
 	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;
 }
@@ -526,7 +521,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);
@@ -557,7 +552,7 @@ 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 {
@@ -573,7 +568,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;
@@ -594,8 +589,7 @@ backup_hosts(const char *dirname, const char *bakname)
 	DIR		*dir;
 
 	if (!(dir = opendir(dirname))) {
-		nsm_log(LOG_WARNING,
-			"Failed to open %s: %s", dirname, strerror(errno));
+		xlog_warn("Failed to open %s: %m", dirname);
 		return;
 	}
 
@@ -607,11 +601,8 @@ backup_hosts(const char *dirname, const char *bakname)
 
 		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);
-		}
+		if (rename(src, dst) < 0)
+			xlog_warn("Failed to rename %s -> %s: %m", src, dst);
 	}
 	closedir(dir);
 }
@@ -627,8 +618,7 @@ get_hosts(const char *dirname)
 	DIR		*dir;
 
 	if (!(dir = opendir(dirname))) {
-		nsm_log(LOG_WARNING,
-			"Failed to open %s: %s", dirname, strerror(errno));
+		xlog_warn("Failed to open %s: %m", dirname);
 		return;
 	}
 
@@ -642,7 +632,7 @@ get_hosts(const char *dirname)
 		if (host == NULL)
 			host = calloc(1, sizeof(*host));
 		if (host == NULL) {
-			nsm_log(LOG_WARNING, "Unable to allocate memory");
+			xlog_warn("Unable to allocate memory");
 			return;
 		}
 
@@ -723,17 +713,14 @@ nsm_get_state(int update)
 	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);
-		}
+		xlog_warn("%s: %m", _SM_STATE_PATH);
+		xlog_warn("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",
+			xlog_warn("%s: bad file size, setting state = 1",
 				_SM_STATE_PATH);
 			state = 1;
 			update = 1;
@@ -749,17 +736,17 @@ nsm_get_state(int update)
 		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);
+			xlog(L_ERROR, "Cannot create %s: %m", newfile);
 			exit(1);
 		}
 		if (write(fd, &state, sizeof(state)) != sizeof(state)) {
-			nsm_log(LOG_ERR,
+			xlog(L_ERROR,
 				"Failed to write state to %s", newfile);
 			exit(1);
 		}
 		close(fd);
 		if (rename(newfile, _SM_STATE_PATH) < 0) {
-			nsm_log(LOG_ERR,
+			xlog(L_ERROR,
 				"Cannot create %s: %m", _SM_STATE_PATH);
 			exit(1);
 		}
@@ -770,27 +757,6 @@ nsm_get_state(int update)
 }
 
 /*
- * 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
  * program exits.
@@ -806,8 +772,8 @@ static int record_pid(void)
 	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));
+		xlog_warn("Writing to pid file failed: errno %d (%m)",
+				errno);
 	}
 	close(fd);
 	return 1;
@@ -827,16 +793,15 @@ static void drop_privs(void)
 	}
 
 	if (st.st_uid == 0) {
-		nsm_log(LOG_WARNING,
-			"sm-notify running as root. chown %s to choose different user",
-		    _SM_DIR_PATH);
+		xlog_warn("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");
+		xlog(L_ERROR, "Fail to drop privileges");
 		exit(1);
 	}
 }
@@ -851,8 +816,8 @@ static void set_kernel_nsm_state(int state)
 		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));
+			xlog_warn("Writing to '%s' failed: errno %d (%m)",
+				file, errno);
 		}
 		close(fd);
 	}
diff --git a/utils/statd/sm-notify.man b/utils/statd/sm-notify.man
index dd03b8d..a5c1cc5 100644
--- a/utils/statd/sm-notify.man
+++ b/utils/statd/sm-notify.man
@@ -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
-------------------------------------------------------------------------------
statd-replace-smn_-get-set-_po
-------------------------------------------------------------------------------
statd: replace smn_{get,set}_port() with the shared equivalents

From: Chuck Lever <chuck.lever@oracle.com>

Clean up.  Use shared port management functions instead of duplicating
this functionality in sm-notify.  This is now easy because sm-notify
is linked with libnfs.a, where nfs_{get,set}_port() reside.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 utils/statd/sm-notify.c |   40 +++++++---------------------------------
 1 files changed, 7 insertions(+), 33 deletions(-)


diff --git a/utils/statd/sm-notify.c b/utils/statd/sm-notify.c
index 1d4403a..ea4ce23 100644
--- a/utils/statd/sm-notify.c
+++ b/utils/statd/sm-notify.c
@@ -29,6 +29,7 @@
 #include <grp.h>
 
 #include "xlog.h"
+#include "nfsrpc.h"
 
 #ifndef BASEDIR
 # ifdef NFS_STATEDIR
@@ -90,33 +91,6 @@ 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 = {
@@ -321,7 +295,7 @@ 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) {
 			xlog(L_ERROR, "Failed to bind RPC socket: %m");
 			exit(1);
@@ -455,16 +429,16 @@ notify_host(int sock, struct nsm_host *host)
 						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 */
 		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);
@@ -540,7 +514,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;
 
@@ -556,7 +530,7 @@ recv_reply(int sock)
 			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;
 		}
-------------------------------------------------------------------------------
statd-fix-address-copy-in-sm-n
-------------------------------------------------------------------------------
statd: fix address copy in sm-notify.c

From: Chuck Lever <chuck.lever@oracle.com>

One of the memcpy(3) callsites in notify_host() uses the size of the
copy target to prevent an overrun of the target buffer.  However, the
target is a sockaddr_storage, while the source is never larger than
a sockaddr_in6.

Copying off the end of a sockaddr_in6 returned by getaddrinfo(3) could
potentially cause a segfault if the allocated memory containing the
socket address is near the end of a page, and the next page is
protected.

Introduced by commit 93e355bf.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 utils/statd/sm-notify.c |   14 ++++++++------
 1 files changed, 8 insertions(+), 6 deletions(-)


diff --git a/utils/statd/sm-notify.c b/utils/statd/sm-notify.c
index ea4ce23..1983ef6 100644
--- a/utils/statd/sm-notify.c
+++ b/utils/statd/sm-notify.c
@@ -61,6 +61,7 @@ struct nsm_host {
 	char *			name;
 	char *			path;
 	struct sockaddr_storage	addr;
+	socklen_t		addrlen;
 	struct addrinfo		*ai;
 	time_t			last_used;
 	time_t			send_next;
@@ -377,7 +378,6 @@ notify_host(int sock, struct nsm_host *host)
 {
 	struct sockaddr_storage address;
 	struct sockaddr *dest = (struct sockaddr *)&address;
-	socklen_t destlen = sizeof(address);
 	static unsigned int	xid = 0;
 	uint32_t		msgbuf[MAXMSGSIZE], *p;
 	unsigned int		len;
@@ -409,10 +409,11 @@ notify_host(int sock, struct nsm_host *host)
 	 */
 	if (host->retries >= 4) {
 		/* don't rotate if there is only one addrinfo */
-		if (host->ai->ai_next == NULL)
+		if (host->ai->ai_next == NULL) {
 			memcpy(&host->addr, host->ai->ai_addr,
 						host->ai->ai_addrlen);
-		else {
+			host->addrlen = host->ai->ai_addrlen;
+		} else {
 			struct addrinfo *first = host->ai;
 			struct addrinfo **next = &host->ai;
 
@@ -427,13 +428,14 @@ notify_host(int sock, struct nsm_host *host)
 			*next = first;
 			memcpy(&host->addr, first->ai_addr,
 						first->ai_addrlen);
+			host->addrlen = host->ai->ai_addrlen;
 		}
 
 		nfs_set_port((struct sockaddr *)&host->addr, 0);
 		host->retries = 0;
 	}
 
-	memcpy(dest, &host->addr, destlen);
+	memcpy(dest, &host->addr, host->addrlen);
 	if (nfs_get_port(dest) == 0) {
 		/* Build a PMAP packet */
 		xlog(D_GENERAL, "Sending portmap query to %s", host->name);
@@ -472,10 +474,10 @@ notify_host(int sock, struct nsm_host *host)
 	}
 	len = (p - msgbuf) << 2;
 
-	if (sendto(sock, msgbuf, len, 0, dest, destlen) < 0)
+	if (sendto(sock, msgbuf, len, 0, dest, host->addrlen) < 0)
 		xlog_warn("Sending Reboot Notification to "
 			"'%s' failed: errno %d (%m)", host->name, errno);
-	
+
 	return 0;
 }
 
-------------------------------------------------------------------------------
libnsm-a-move-the-sm_inter-xdr
-------------------------------------------------------------------------------
libnsm.a: Move the sm_inter XDR pieces to libnsm.a

From: Chuck Lever <chuck.lever@oracle.com>

Clean up: Move the .x file and the generated C source for NSM to
libnsm.a, echoing the architecture of mountd and exportfs.  This makes
the NSM protocol definitions, data types, and XDR routines available
to be shared across nfs-utils.

This simplifies the addition of other NSM-related code (for example
for testing or providing clustering support), and also provides
public data type definitions that can be used to make sense of the
contents of statd's on-disk database.

Because sim_sm_inter.x still resides in utils/statd, I've left some
rpcgen build magic in utils/statd/Makefile.am.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 .gitignore              |    9 ++-
 configure.ac            |    1 
 support/Makefile.am     |    2 -
 support/nsm/Makefile.am |   45 ++++++++++++++++
 support/nsm/sm_inter.x  |  131 +++++++++++++++++++++++++++++++++++++++++++++++
 utils/statd/Makefile.am |   16 ++----
 utils/statd/sm_inter.x  |  131 -----------------------------------------------
 7 files changed, 188 insertions(+), 147 deletions(-)
 create mode 100644 support/nsm/Makefile.am
 create mode 100644 support/nsm/sm_inter.x
 delete mode 100644 utils/statd/sm_inter.x


diff --git a/.gitignore b/.gitignore
index 632609e..03e4606 100644
--- a/.gitignore
+++ b/.gitignore
@@ -55,10 +55,11 @@ 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
 # cscope database files
 cscope.*
 # generic editor backup et al
diff --git a/configure.ac b/configure.ac
index 3ad415c..1c79b21 100644
--- a/configure.ac
+++ b/configure.ac
@@ -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
diff --git a/support/Makefile.am b/support/Makefile.am
index aa4d692..cb37733 100644
--- a/support/Makefile.am
+++ b/support/Makefile.am
@@ -1,6 +1,6 @@
 ## Process this file with automake to produce Makefile.in
 
-SUBDIRS = export include misc nfs
+SUBDIRS = export include misc nfs nsm
 
 MAINTAINERCLEANFILES = Makefile.in
 
diff --git a/support/nsm/Makefile.am b/support/nsm/Makefile.am
new file mode 100644
index 0000000..4b8110d
--- /dev/null
+++ b/support/nsm/Makefile.am
@@ -0,0 +1,45 @@
+## Process this file with automake to produce Makefile.in
+
+GENFILES_CLNT	= sm_inter_clnt.c
+GENFILES_SVC	= sm_inter_svc.c
+GENFILES_XDR	= sm_inter_xdr.c
+GENFILES_H	= sm_inter.h
+
+GENFILES	= $(GENFILES_CLNT) $(GENFILES_SVC) $(GENFILES_XDR) $(GENFILES_H)
+
+EXTRA_DIST	= sm_inter.x
+
+noinst_LIBRARIES = libnsm.a
+libnsm_a_SOURCES = $(GENFILES)
+
+BUILT_SOURCES = $(GENFILES)
+
+if CONFIG_RPCGEN
+RPCGEN	= $(top_builddir)/tools/rpcgen/rpcgen
+$(RPCGEN):
+	make -C ../../tools/rpcgen all
+else
+RPCGEN = @RPCGEN_PATH@
+endif
+
+$(GENFILES_CLNT): %_clnt.c: %.x $(RPCGEN)
+	test -f $@ && rm -rf $@ || true
+	$(RPCGEN) -l -o $@ $<
+
+$(GENFILES_SVC): %_svc.c: %.x $(RPCGEN)
+	test -f $@ && rm -rf $@ || true
+	$(RPCGEN) -m -o $@ $<
+
+$(GENFILES_XDR): %_xdr.c: %.x $(RPCGEN)
+	test -f $@ && rm -rf $@ || true
+	$(RPCGEN) -c -o $@ $<
+
+$(GENFILES_H): %.h: %.x $(RPCGEN)
+	test -f $@ && rm -rf $@ || true
+	$(RPCGEN) -h -o $@ $<
+	rm -f $(top_builddir)/support/include/sm_inter.h
+	$(LN_S) ../nsm/sm_inter.h $(top_builddir)/support/include/sm_inter.h
+
+MAINTAINERCLEANFILES = Makefile.in
+
+CLEANFILES = $(GENFILES) $(top_builddir)/support/include/sm_inter.h
diff --git a/support/nsm/sm_inter.x b/support/nsm/sm_inter.x
new file mode 100644
index 0000000..d8e0ad7
--- /dev/null
+++ b/support/nsm/sm_inter.x
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 1986 Sun Microsystems, Inc.
+ * Modified by Jeffrey A. Uphoff, 1995, 1997-1999.
+ * Modified by Olaf Kirch, 1996.
+ * Modified by H.J. Lu, 1998.
+ *
+ * NSM for Linux.
+ */
+
+/*
+ * Copyright (c) 2009, Sun Microsystems, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * - Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * - Neither the name of Sun Microsystems, Inc. nor the names of its
+ *   contributors may be used to endorse or promote products derived
+ *   from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Status monitor protocol specification
+ */
+
+#ifdef RPC_CLNT
+%#include <string.h>
+#endif
+
+program SM_PROG { 
+	version SM_VERS  {
+		/* res_stat = stat_succ if status monitor agrees to monitor */
+		/* res_stat = stat_fail if status monitor cannot monitor */
+		/* if res_stat == stat_succ, state = state number of site sm_name */
+		struct sm_stat_res			 SM_STAT(struct sm_name) = 1;
+
+		/* res_stat = stat_succ if status monitor agrees to monitor */
+		/* res_stat = stat_fail if status monitor cannot monitor */
+		/* stat consists of state number of local site */
+		struct sm_stat_res			 SM_MON(struct mon) = 2;
+
+		/* stat consists of state number of local site */
+		struct sm_stat				 SM_UNMON(struct mon_id) = 3;
+
+		/* stat consists of state number of local site */
+		struct sm_stat				 SM_UNMON_ALL(struct my_id) = 4;
+
+		void					 SM_SIMU_CRASH(void) = 5;
+
+		void					 SM_NOTIFY(struct stat_chge) = 6;
+
+	} = 1;
+} = 100024;
+
+const	SM_MAXSTRLEN = 1024;
+const	SM_PRIV_SIZE = 16;
+
+struct sm_name {
+	string mon_name<SM_MAXSTRLEN>;
+};
+
+struct my_id {
+	string	 my_name<SM_MAXSTRLEN>;		/* name of the site iniates the monitoring request*/
+	int	my_prog;			/* rpc program # of the requesting process */
+	int	my_vers;			/* rpc version # of the requesting process */
+	int	my_proc;			/* rpc procedure # of the requesting process */
+};
+
+struct mon_id {
+	string	mon_name<SM_MAXSTRLEN>;		/* name of the site to be monitored */
+	struct my_id my_id;
+};
+
+
+struct mon {
+	struct mon_id mon_id;
+	opaque priv[SM_PRIV_SIZE]; 		/* private information to store at monitor for requesting process */
+};
+
+struct stat_chge {
+	string	mon_name<SM_MAXSTRLEN>;		/* name of the site that had the state change */
+	int     state;
+};
+
+/*
+ * state # of status monitor monitonically increases each time
+ * status of the site changes:
+ * an even number (>= 0) indicates the site is down and
+ * an odd number (> 0) indicates the site is up;
+ */
+struct sm_stat {
+	int state;		/* state # of status monitor */
+};
+
+enum res {
+	stat_succ = 0,		/* status monitor agrees to monitor */
+	stat_fail = 1		/* status monitor cannot monitor */
+};
+
+struct sm_stat_res {
+	res res_stat;
+	int state;
+};
+
+/* 
+ * structure of the status message sent back by the status monitor
+ * when monitor site status changes
+ */
+struct status {
+	string mon_name<SM_MAXSTRLEN>;
+	int state;
+	opaque priv[SM_PRIV_SIZE]; /* stored private information */
+};
+
+%#define SM_INTER_X
diff --git a/utils/statd/Makefile.am b/utils/statd/Makefile.am
index f64cd7a..d9731b7 100644
--- a/utils/statd/Makefile.am
+++ b/utils/statd/Makefile.am
@@ -2,32 +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 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 \
-	        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 = ../../support/nfs/libnfs.a \
+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 --git a/utils/statd/sm_inter.x b/utils/statd/sm_inter.x
deleted file mode 100644
index d8e0ad7..0000000
--- a/utils/statd/sm_inter.x
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Copyright (C) 1986 Sun Microsystems, Inc.
- * Modified by Jeffrey A. Uphoff, 1995, 1997-1999.
- * Modified by Olaf Kirch, 1996.
- * Modified by H.J. Lu, 1998.
- *
- * NSM for Linux.
- */
-
-/*
- * Copyright (c) 2009, Sun Microsystems, Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * - Redistributions of source code must retain the above copyright notice,
- *   this list of conditions and the following disclaimer.
- * - Redistributions in binary form must reproduce the above copyright notice,
- *   this list of conditions and the following disclaimer in the documentation
- *   and/or other materials provided with the distribution.
- * - Neither the name of Sun Microsystems, Inc. nor the names of its
- *   contributors may be used to endorse or promote products derived
- *   from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-/*
- * Status monitor protocol specification
- */
-
-#ifdef RPC_CLNT
-%#include <string.h>
-#endif
-
-program SM_PROG { 
-	version SM_VERS  {
-		/* res_stat = stat_succ if status monitor agrees to monitor */
-		/* res_stat = stat_fail if status monitor cannot monitor */
-		/* if res_stat == stat_succ, state = state number of site sm_name */
-		struct sm_stat_res			 SM_STAT(struct sm_name) = 1;
-
-		/* res_stat = stat_succ if status monitor agrees to monitor */
-		/* res_stat = stat_fail if status monitor cannot monitor */
-		/* stat consists of state number of local site */
-		struct sm_stat_res			 SM_MON(struct mon) = 2;
-
-		/* stat consists of state number of local site */
-		struct sm_stat				 SM_UNMON(struct mon_id) = 3;
-
-		/* stat consists of state number of local site */
-		struct sm_stat				 SM_UNMON_ALL(struct my_id) = 4;
-
-		void					 SM_SIMU_CRASH(void) = 5;
-
-		void					 SM_NOTIFY(struct stat_chge) = 6;
-
-	} = 1;
-} = 100024;
-
-const	SM_MAXSTRLEN = 1024;
-const	SM_PRIV_SIZE = 16;
-
-struct sm_name {
-	string mon_name<SM_MAXSTRLEN>;
-};
-
-struct my_id {
-	string	 my_name<SM_MAXSTRLEN>;		/* name of the site iniates the monitoring request*/
-	int	my_prog;			/* rpc program # of the requesting process */
-	int	my_vers;			/* rpc version # of the requesting process */
-	int	my_proc;			/* rpc procedure # of the requesting process */
-};
-
-struct mon_id {
-	string	mon_name<SM_MAXSTRLEN>;		/* name of the site to be monitored */
-	struct my_id my_id;
-};
-
-
-struct mon {
-	struct mon_id mon_id;
-	opaque priv[SM_PRIV_SIZE]; 		/* private information to store at monitor for requesting process */
-};
-
-struct stat_chge {
-	string	mon_name<SM_MAXSTRLEN>;		/* name of the site that had the state change */
-	int     state;
-};
-
-/*
- * state # of status monitor monitonically increases each time
- * status of the site changes:
- * an even number (>= 0) indicates the site is down and
- * an odd number (> 0) indicates the site is up;
- */
-struct sm_stat {
-	int state;		/* state # of status monitor */
-};
-
-enum res {
-	stat_succ = 0,		/* status monitor agrees to monitor */
-	stat_fail = 1		/* status monitor cannot monitor */
-};
-
-struct sm_stat_res {
-	res res_stat;
-	int state;
-};
-
-/* 
- * structure of the status message sent back by the status monitor
- * when monitor site status changes
- */
-struct status {
-	string mon_name<SM_MAXSTRLEN>;
-	int state;
-	opaque priv[SM_PRIV_SIZE]; /* stored private information */
-};
-
-%#define SM_INTER_X
-------------------------------------------------------------------------------
libnsm-a-introduce-common-rout
-------------------------------------------------------------------------------
libnsm.a: Introduce common routines to handle persistent storage

From: Chuck Lever <chuck.lever@oracle.com>

rpc.statd and sm-notify access the same set of files under
/var/lib/nfs/statd, but both have their own code base to handle this.
They should share this code.

In addition, the on-disk format used by statd and friends is
considered a formal interface, so this new code will codify the API
and provide documentation for it.

The shared code handles switching from the default parent statd
directory, reducing privileges at start-up, and managing the NSM
state files, in addition to handling normal operations on the
monitored host and notification lists on disk.

The new code is simply a copy of the same logic that was used in
rpc.statd and sm-notify, but wrapped in a nice API.  There should be
minimal behavioral and no on-disk format changes with the new
libnsm.a code.

The new code is more careful to check for bad corner cases.
Occassionally this code may not allow an operation that was permitted
in the past, but hopefully the error reporting has improved enough
that it should be easy to track down any problems.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 support/include/Makefile.am |    1 
 support/include/nsm.h       |   62 +++
 support/nsm/Makefile.am     |    2 
 support/nsm/file.c          |  770 +++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 834 insertions(+), 1 deletions(-)
 create mode 100644 support/include/nsm.h
 create mode 100644 support/nsm/file.c


diff --git a/support/include/Makefile.am b/support/include/Makefile.am
index f5a77ec..027f37f 100644
--- a/support/include/Makefile.am
+++ b/support/include/Makefile.am
@@ -9,6 +9,7 @@ noinst_HEADERS = \
 	nfs_mntent.h \
 	nfs_paths.h \
 	nfslib.h \
+	nsm.h \
 	rpcmisc.h \
 	tcpwrapper.h \
 	xio.h \
diff --git a/support/include/nsm.h b/support/include/nsm.h
new file mode 100644
index 0000000..594f0d9
--- /dev/null
+++ b/support/include/nsm.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2009 Oracle.  All rights reserved.
+ *
+ * This file is part of nfs-utils.
+ *
+ * nfs-utils is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * nfs-utils is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with nfs-utils.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * NSM for Linux.
+ */
+
+#ifndef _NFS_UTILS_SUPPORT_NSM_H
+#define _NFS_UTILS_SUPPORT_NSM_H
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <netdb.h>
+#include <time.h>
+
+#include "sm_inter.h"
+
+typedef unsigned int
+		(*nsm_populate_t)(const char *hostname,
+				const struct sockaddr *sap,
+				const struct mon *mon,
+				const time_t timestamp);
+
+/* file.c */
+
+extern int	nsm_setup_pathnames(const char *progname, const char *parentdir);
+extern int	nsm_is_default_parentdir(void);
+extern int	nsm_drop_privileges(const int pidfd);
+
+extern int	nsm_get_state(int 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 int	nsm_insert_monitored_host(const char *hostname,
+			const struct sockaddr *sap, const struct mon *mon);
+extern void	nsm_delete_monitored_host(const char *hostname);
+extern void	nsm_delete_notified_host(const char *hostname);
+
+#endif	/* !_NFS_UTILS_SUPPORT_NSM_H */
diff --git a/support/nsm/Makefile.am b/support/nsm/Makefile.am
index 4b8110d..e359a43 100644
--- a/support/nsm/Makefile.am
+++ b/support/nsm/Makefile.am
@@ -10,7 +10,7 @@ GENFILES	= $(GENFILES_CLNT) $(GENFILES_SVC) $(GENFILES_XDR) $(GENFILES_H)
 EXTRA_DIST	= sm_inter.x
 
 noinst_LIBRARIES = libnsm.a
-libnsm_a_SOURCES = $(GENFILES)
+libnsm_a_SOURCES = $(GENFILES) file.c
 
 BUILT_SOURCES = $(GENFILES)
 
diff --git a/support/nsm/file.c b/support/nsm/file.c
new file mode 100644
index 0000000..77ab073
--- /dev/null
+++ b/support/nsm/file.c
@@ -0,0 +1,770 @@
+/*
+ * Copyright 2009 Oracle.  All rights reserved.
+ *
+ * This file is part of nfs-utils.
+ *
+ * nfs-utils is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * nfs-utils is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with nfs-utils.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * NSM for Linux.
+ *
+ * Callback information and NSM state is stored in files, usually
+ * under /var/lib/nfs.  A database of information contained in local
+ * files stores NLM callback data and what remote peers to notify of
+ * reboots.
+ *
+ * For each monitored remote peer, a text file is created under the
+ * directory specified by NSM_MONITOR_DIR.  The name of the file
+ * is a valid DNS hostname.  The hostname string must be a valid
+ * ASCII DNS name, and must not contain slash characters, white space,
+ * or '\0' (ie. anything that might have some special meaning in a
+ * path name).
+ *
+ * The contents of each file include seven blank-separated fields of
+ * text, finished with '\n'.  The first field contains the network
+ * address of the NLM service to call back.  The current implementation
+ * supports using only IPv4 addresses, so the only contents of this
+ * field are a network order IPv4 address expressed in 8 hexadecimal
+ * characters.
+ *
+ * The next four fields are text strings of hexadecimal characters,
+ * representing:
+ *
+ * 2. A 4 byte RPC program number of the NLM service to call back
+ * 3. A 4 byte RPC version number of the NLM service to call back
+ * 4. A 4 byte RPC procedure number of the NLM service to call back
+ * 5. A 16 byte opaque cookie that the NLM service uses to identify
+ *    the monitored host
+ *
+ * The sixth field is the monitored host's mon_name, passed to statd
+ * via an SM_MON request.
+ *
+ * The seventh field is the my_name for this peer, which is the
+ * hostname of the local NLM (currently on Linux, the result of
+ * `uname -n`).  This can be used as the source address/hostname
+ * when sending SM_NOTIFY requests.
+ *
+ * The NSM protocol does not limit the contents of these strings
+ * in any way except that they must fit into 1024 bytes.  Our
+ * implementation requires that these strings not contain
+ * white space or '\0'.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <libgen.h>
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <grp.h>
+
+#include "xlog.h"
+#include "nsm.h"
+
+#define 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 int
+__error_check(const int len, const size_t buflen)
+{
+	return (len < 0) || ((size_t)len >= buflen);
+}
+
+static int
+__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.  
+ *
+ * Caller must free the returned result.
+ */
+static char *
+nsm_make_record_pathname(const char *dirname, 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 '/').
+	 */
+	for (c = hostname; *c != '\0'; c++)
+		if (*c == '/' || isspace(*c)) {
+			xlog(D_GENERAL, "Hostname contains invalid characters");
+			return NULL;
+		}
+
+	size = strlen(nsm_base_dirname) + strlen(dirname) + 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, dirname, 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.  
+ *
+ * Caller must free the returned result.
+ */
+static char *
+nsm_make_pathname(const char *dirname)
+{
+	size_t size;
+	char *path;
+	int len;
+
+	size = strlen(nsm_base_dirname) + strlen(dirname) + 2;
+	if (size > PATH_MAX)
+		return NULL;
+
+	path = malloc(size);
+	if (path == NULL)
+		return NULL;
+
+	len = snprintf(path, size, "%s/%s", nsm_base_dirname, dirname);
+	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 errors are directed to stderr.
+ *
+ * Returns 1 and sets up our pathnames, if @parentdir was valid;
+ * otherwise 0 is returned.
+ */
+int
+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) {
+		fprintf(stderr, "%s: Failed to stat %s: %m",
+				progname, parentdir);
+		return 0;
+	}
+
+	/* Ensure we have a clean directory pathname */
+	strncpy(buf, parentdir, sizeof(buf));
+	path = dirname(buf);
+	if (*path == '.') {
+		fprintf(stderr, "%s: Unusable directory %s", progname, parentdir);
+		return 0;
+	}
+
+	strncpy(nsm_base_dirname, path, sizeof(nsm_base_dirname));
+	return 1;
+}
+
+/**
+ * nsm_is_default_parentdir - check if parent directory is default
+ *
+ * Returns 1 if the active statd parent directory, set by
+ * nsm_change_pathname(), is the same as the built-in default
+ * parent directory.
+ */
+int
+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 1 if successful, or 0 if some error occurred.
+ *
+ * Set our effective UID and GID to that of our on-disk database.
+ */
+int
+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 0;
+	}
+
+	if (st.st_uid == 0) {
+		xlog_warn("Running as root.  "
+			"chown %s to choose different user", nsm_base_dirname);
+		return 1;
+	}
+
+	if (chdir(nsm_base_dirname) == -1) {
+		xlog(L_ERROR, "Failed to change working directory to %s: %m",
+				nsm_base_dirname);
+		return 0;
+	}
+
+	/*
+	 * 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 0;
+	}
+
+	/*
+	 * 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 0;
+	}
+
+	xlog(D_CALL, "Effective UID, GID: %u, %u", st.st_uid, st.st_gid);
+	return 1;
+}
+
+/**
+ * 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(int 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 create NSM state path name");
+		goto out;
+	}
+
+	fd = open(path, O_RDONLY);
+	if (fd < 0) {
+		if (errno != ENOENT) {
+			xlog(L_ERROR, "Failed to open NSM state file %s: %m",
+					path);
+			goto out;
+		}
+
+		xlog(L_NOTICE, "Initializing NSM state");
+		state = 1;
+		update = 1;
+		goto update;
+	}
+
+	result = read(fd, &state, sizeof(state));
+	if (__exact_error_check(result, sizeof(state))) {
+		xlog_warn("Failed to read NSM state file %s: %m",
+				path);
+		xlog(L_NOTICE, "Initializing NSM state");
+		state = 1;
+		update = 1;
+		goto update;
+	}
+
+	if (!(state & 1))
+		state++;
+
+update:
+	(void)close(fd);
+
+	if (update) {
+		char *newpath;
+
+		state += 2;
+
+		newpath = nsm_make_pathname(NSM_STATE_FILE ".new");
+		if (path == NULL) {
+			xlog(L_ERROR, "Failed to create new NSM state path name");
+			state = 0;
+			goto out;
+		}
+
+		fd = open(newpath, O_CREAT | O_SYNC | O_WRONLY, 0644);
+		if (fd < 0) {
+			xlog(L_ERROR, "Failed to create NSM state file %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 NSM state file %s: %m",
+					newpath);
+			(void)close(fd);
+			state = 0;
+			goto out;
+		}
+
+		if (close(fd) == -1) {
+			xlog(L_ERROR, "Failed to close NSM state file %s: %m",
+					newpath);
+			state = 0;
+			goto out;
+		}
+
+		if (rename(newpath, path) < 0) {
+			xlog(L_ERROR, "Failed to rename NSM state file %s: %m",
+					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 persistant 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 < 0) {
+		xlog(D_GENERAL, "Failed to open kernel NSM state file "
+				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 != 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) < 0)
+			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);
+	}
+
+	closedir(dir);
+	return count;
+}
+
+/**
+ * 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 1 if successful, otherwise 0 if some error occurs.
+ */
+int
+nsm_insert_monitored_host(const char *hostname, const struct sockaddr *sap,
+		const struct mon *mon)
+{
+	const struct sockaddr_in *sin = (const struct sockaddr_in *)sap;
+	static char line[4096];
+	char *c, *path;
+	int result;
+	ssize_t len;
+	int i, 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 0;
+	}
+
+	fd = open(path, O_WRONLY | O_CREAT | O_SYNC, S_IRUSR | S_IWUSR);
+	if (fd < 0) {
+		xlog(L_ERROR, "Failed to insert: creating %s: %m", path);
+		return 0;
+	}
+
+	c = line + sprintf(line, "%08x %08x %08x %08x ",
+			sin->sin_addr.s_addr,
+			mon->mon_id.my_id.my_prog,
+			mon->mon_id.my_id.my_vers,
+			mon->mon_id.my_id.my_proc);
+
+	for (i = 0; i < SM_PRIV_SIZE; i++)
+		c += sprintf(c, "%02x", 0xff & mon->priv[i]);
+
+	c += sprintf(c, " %s %s\n", mon->mon_id.mon_name,
+			mon->mon_id.my_id.my_name);
+
+	result = 1;
+	len = write(fd, line, c - line);
+	if (__exact_error_check(len, c - line)) {
+		xlog_warn("Failed to insert: writing %s: %m", path);
+		(void)unlink(path);
+		result = 0;
+	}
+
+	if (close(fd) == -1) {
+		xlog(L_ERROR, "Failed to insert: closing %s: %m", path);
+		(void)unlink(path);
+		result = 0;
+	}
+
+	free(path);
+	return result;
+}
+
+/*
+ * Stuff a 'struct mon' with callback data, and call @func.
+ *
+ * Returns the count of in-core records created.
+ */
+static unsigned int
+nsm_parse_line(const char *hostname, const time_t timestamp, char *line,
+		nsm_populate_t func)
+{
+	struct sockaddr_in sin = {
+		.sin_family	= AF_INET,
+	};
+	struct mon mon;
+	int count, i;
+	char *c;
+
+	c = strchr(line, '\n');
+	if (c)
+		*c = '\0';
+
+	count = sscanf(line, "%8x %8x %8x %8x ",
+			&sin.sin_addr.s_addr,
+			&mon.mon_id.my_id.my_prog,
+			&mon.mon_id.my_id.my_vers,
+			&mon.mon_id.my_id.my_proc);
+	if (count != 4)
+		return 0;
+
+	c = line + 36;
+	for (i = 0; i < SM_PRIV_SIZE; i++) {
+		unsigned int tmp;
+		if (sscanf(c, "%2x", &tmp) != 1)
+			return 0;
+		mon.priv[i] = tmp;
+		c += 2;
+	}
+
+	c++;
+	mon.mon_id.mon_name = c;
+	while (*c && *c != ' ')
+		c++;
+	if (*c)
+		*c++ = '\0';
+	while (*c == ' ')
+		c++;
+	mon.mon_id.my_id.my_name = c;
+
+	return func(hostname, (struct sockaddr *)&sin, &mon, 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 *dirname, const char *filename, nsm_populate_t func)
+{
+	char *path, *line = NULL;
+	unsigned int result = 0;
+	struct stat stb;
+	size_t len = 0;
+	FILE *f;
+
+	path = nsm_make_record_pathname(dirname, filename);
+	if (path == NULL)
+		goto out_err;
+
+	if (stat(path, &stb) < 0) {
+		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;
+	}
+
+	if (getline(&line, &len, f) == -1) {
+		xlog(L_ERROR, "Failed to read %s: %m", path);
+		goto out_close;
+	}
+
+	result = nsm_parse_line(filename, stb.st_mtime, line, func);
+	if (!result)
+		xlog(L_ERROR, "Bad callback data in %s", path);
+	free(line);
+
+out_close:
+	fclose(f);
+out_freepath:
+	free(path);
+out_err:
+	return result;
+}
+
+static unsigned int
+nsm_load_dir(const char *dirname, nsm_populate_t func)
+{
+	unsigned int count;
+	struct dirent *de;
+	char *path;
+	DIR *dir;
+
+	path = nsm_make_pathname(dirname);
+	if (path == NULL) {
+		xlog(L_ERROR, "Failed to allocate path for directory %s",
+				dirname);
+		return 0;
+	}
+
+	dir = opendir(path);
+	free(path);
+	if (dir == NULL) {
+		xlog(L_ERROR, "Failed to open directory %s: %m",
+				dirname);
+		return 0;
+	}
+
+	count = 0;
+	while ((de = readdir(dir)) != NULL) {
+		if (de->d_type != DT_REG)
+			continue;
+		if (de->d_name[0] == '.')
+			continue;
+
+		count += nsm_load_host(dirname, de->d_name, func);
+	}
+
+	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 *dirname, const char *hostname)
+{
+	char *path;
+
+	path = nsm_make_record_pathname(dirname, 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);
+}
-------------------------------------------------------------------------------
statd-use-the-new-nsm_-file-c--0
-------------------------------------------------------------------------------
statd: Use the new nsm_ file.c calls in sm_notify

From: Chuck Lever <chuck.lever@oracle.com>

Replace open-coded accesses to on-disk NSM information in sm_notify.c
with calls to the new API.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 utils/statd/sm-notify.c |  278 ++++++++++-------------------------------------
 1 files changed, 57 insertions(+), 221 deletions(-)


diff --git a/utils/statd/sm-notify.c b/utils/statd/sm-notify.c
index 1983ef6..6466d8a 100644
--- a/utils/statd/sm-notify.c
+++ b/utils/statd/sm-notify.c
@@ -29,25 +29,9 @@
 #include <grp.h>
 
 #include "xlog.h"
+#include "nsm.h"
 #include "nfsrpc.h"
 
-#ifndef BASEDIR
-# ifdef NFS_STATEDIR
-#  define BASEDIR		NFS_STATEDIR
-# else
-#  define BASEDIR		"/var/lib/nfs"
-# endif
-#endif
-
-#define DEFAULT_SM_STATE_PATH	BASEDIR "/state"
-#define	DEFAULT_SM_DIR_PATH	BASEDIR "/sm"
-#define	DEFAULT_SM_BAK_PATH	DEFAULT_SM_DIR_PATH ".bak"
-
-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;
-
 #define NSM_PROG	100024
 #define NSM_PROGRAM	100024
 #define NSM_VERSION	1
@@ -59,7 +43,6 @@ char *_SM_BAK_PATH = DEFAULT_SM_BAK_PATH;
 struct nsm_host {
 	struct nsm_host *	next;
 	char *			name;
-	char *			path;
 	struct sockaddr_storage	addr;
 	socklen_t		addrlen;
 	struct addrinfo		*ai;
@@ -78,17 +61,12 @@ static unsigned int	opt_max_retry = 15 * 60;
 static char *		opt_srcaddr = 0;
 static uint16_t		opt_srcport = 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 int		record_pid(void);
-static void		drop_privs(void);
-static void		set_kernel_nsm_state(int state);
 
 static struct nsm_host *	hosts = NULL;
 
@@ -112,10 +90,38 @@ static struct addrinfo *smn_lookup(const char *name)
 	return ai;
 }
 
+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);
@@ -123,6 +129,23 @@ static void smn_forget_host(struct nsm_host *host)
 	free(host);
 }
 
+static unsigned int
+smn_get_host(const char *hostname,
+		__attribute__((unused)) const struct sockaddr *sap,
+		__attribute__((unused)) const struct mon *mon,
+		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)
 {
@@ -157,20 +180,8 @@ main(int argc, char **argv)
 			opt_srcaddr = optarg;
 			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) {
-				fprintf(stderr, "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:
@@ -196,7 +207,7 @@ usage:		fprintf(stderr,
 	xlog_open(progname);
 	xlog(L_NOTICE, "Version " VERSION " starting");
 
-	if (strcmp(_SM_BASE_PATH, BASEDIR) == 0) {
+	if (nsm_is_default_parentdir()) {
 		if (record_pid() == 0 && force == 0 && opt_update_state == 1) {
 			/* already run, don't try again */
 			xlog(L_NOTICE, "Already notifying clients; Exiting!");
@@ -212,18 +223,16 @@ usage:		fprintf(stderr,
 		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) {
+	(void)nsm_retire_monitored_hosts();
+	if (!nsm_load_notify_list(smn_get_host)) {
 		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) {
 		xlog(L_NOTICE, "Backgrounding to notify hosts...\n");
@@ -317,7 +326,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;
@@ -556,82 +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))) {
-		xlog_warn("Failed to open %s: %m", dirname);
-		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)
-			xlog_warn("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))) {
-		xlog_warn("Failed to open %s: %m", dirname);
-		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) {
-			xlog_warn("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
@@ -678,60 +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) {
-		xlog_warn("%s: %m", _SM_STATE_PATH);
-		xlog_warn("Creating %s, set initial state 1",
-			_SM_STATE_PATH);
-		state = 1;
-		update = 1;
-	} else {
-		if (read(fd, &state, sizeof(state)) != sizeof(state)) {
-			xlog_warn("%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) {
-			xlog(L_ERROR, "Cannot create %s: %m", newfile);
-			exit(1);
-		}
-		if (write(fd, &state, sizeof(state)) != sizeof(state)) {
-			xlog(L_ERROR,
-				"Failed to write state to %s", newfile);
-			exit(1);
-		}
-		close(fd);
-		if (rename(newfile, _SM_STATE_PATH) < 0) {
-			xlog(L_ERROR,
-				"Cannot create %s: %m", _SM_STATE_PATH);
-			exit(1);
-		}
-		sync();
-	}
-
-	return state;
-}
-
 /*
  * Record pid in /var/run/sm-notify.pid
  * This file should remain until a reboot, even if the
@@ -754,47 +634,3 @@ static int record_pid(void)
 	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) {
-		xlog_warn("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) {
-		xlog(L_ERROR, "Fail to drop privileges");
-		exit(1);
-	}
-}
-
-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)) {
-			xlog_warn("Writing to '%s' failed: errno %d (%m)",
-				file, errno);
-		}
-		close(fd);
-	}
-}
-------------------------------------------------------------------------------
statd-use-the-new-nsm_-file-c-
-------------------------------------------------------------------------------
statd: Use the new nsm_ file.c calls in rpc.statd

From: Chuck Lever <chuck.lever@oracle.com>

Replace open-coded accesses to on-disk NSM information in rpc.statd
with calls to the new API.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 utils/statd/misc.c    |   24 --------
 utils/statd/monitor.c |  140 ++++++++++++++++---------------------------------
 utils/statd/statd.c   |  116 ++++-------------------------------------
 utils/statd/statd.h   |   23 --------
 4 files changed, 57 insertions(+), 246 deletions(-)


diff --git a/utils/statd/misc.c b/utils/statd/misc.c
index 44af30e..f2a086f 100644
--- a/utils/statd/misc.c
+++ b/utils/statd/misc.c
@@ -49,27 +49,3 @@ xstrdup (const char *string)
 
   return (result);
 }
-
-
-/*
- * Unlinking a file.
- */
-void
-xunlink (char *path, char *host)
-{
-	char *tozap;
-
-	tozap = malloc(strlen(path)+strlen(host)+2);
-	if (tozap == NULL) {
-		xlog(L_ERROR, "xunlink: malloc failed: errno %d (%m)", errno);
-		return;
-	}
-	sprintf (tozap, "%s/%s", path, host);
-
-	if (unlink (tozap) == -1)
-		xlog(L_ERROR, "unlink (%s): %m", tozap);
-	else
-		xlog(D_GENERAL, "Unlinked %s", tozap);
-
-	free(tozap);
-}
diff --git a/utils/statd/monitor.c b/utils/statd/monitor.c
index 09f03da..8d9f663 100644
--- a/utils/statd/monitor.c
+++ b/utils/statd/monitor.c
@@ -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.
@@ -60,11 +59,12 @@ sm_mon_1_svc(struct mon *argp, struct svc_req *rqstp)
 	char		*mon_name = argp->mon_id.mon_name,
 			*my_name  = argp->mon_id.my_id.my_name;
 	struct my_id	*id = &argp->mon_id.my_id;
-	char            *path;
 	char		*cp;
-	int             fd;
 	notify_list	*clnt;
-	struct in_addr	my_addr;
+	struct sockaddr_in my_addr = {
+		.sin_family		= AF_INET,
+		.sin_addr.s_addr	= htonl(INADDR_LOOPBACK),
+	};
 	char		*dnsname;
 	struct hostent	*hostinfo = NULL;
 
@@ -80,7 +80,6 @@ sm_mon_1_svc(struct mon *argp, struct svc_req *rqstp)
 	 */
 	if (!caller_is_localhost(rqstp))
 		goto failure;
-	my_addr.s_addr = htonl(INADDR_LOOPBACK);
 
 	/* 2.	Reject any registrations for non-lockd services.
 	 *
@@ -172,7 +171,7 @@ sm_mon_1_svc(struct mon *argp, struct svc_req *rqstp)
 		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,39 +181,15 @@ sm_mon_1_svc(struct mon *argp, struct svc_req *rqstp)
 	/*
 	 * Now, Create file on stable storage for host.
 	 */
-
-	path=xmalloc(strlen(SM_DIR)+strlen(dnsname)+2);
-	sprintf(path, "%s/%s", SM_DIR, dnsname);
-	if ((fd = open(path, O_WRONLY|O_SYNC|O_CREAT|O_APPEND,
-		       S_IRUSR|S_IWUSR)) < 0) {
-		/* Didn't fly.  We won't monitor. */
-		xlog(L_ERROR, "creat(%s) failed: %m", path);
+	if (!nsm_insert_monitored_host(dnsname,
+					(struct sockaddr *)&my_addr, argp)) {
 		nlist_free(NULL, clnt);
-		free(path);
 		goto failure;
 	}
-	{
-		char buf[LINELEN + 1 + SM_MAXSTRLEN*2 + 4];
-		char *e;
-		int i;
-		e = buf + sprintf(buf, "%08x %08x %08x %08x ",
-				  my_addr.s_addr, id->my_prog,
-				  id->my_vers, id->my_proc);
-		for (i=0; i<SM_PRIV_SIZE; i++)
-			e += sprintf(e, "%02x", 0xff & (argp->priv[i]));
-		if (e+1-buf != LINELEN) abort();
-		e += sprintf(e, " %s %s\n", mon_name, my_name);
-		if (write(fd, buf, e-buf) != (e-buf)) {
-			xlog_warn("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);
 	xlog(D_GENERAL, "MONITORING %s for %s", mon_name, my_name);
  success:
 	result.res_stat = STAT_SUCC;
@@ -236,71 +211,46 @@ failure:
 	return (&result);
 }
 
-void load_state(void)
+static unsigned int
+load_one_host(const char *hostname, const struct sockaddr *sap,
+		const struct mon *mon,
+		__attribute__((unused)) const time_t timestamp)
 {
-	DIR *d;
-	struct dirent *de;
-	char buf[LINELEN + 1 + SM_MAXSTRLEN + 2];
-
-	d = opendir(SM_DIR);
-	if (!d)
-		return;
-	while ((de = readdir(d))) {
-		char *path;
-		FILE *f;
-		int p;
-
-		if (de->d_name[0] == '.')
-			continue;
-		path = xmalloc(strlen(SM_DIR)+strlen(de->d_name)+2);
-		sprintf(path, "%s/%s", SM_DIR, de->d_name);
-		f = fopen(path, "r");
-		free(path);
-		if (f == NULL)
-			continue;
-		while (fgets(buf, sizeof(buf), f) != NULL) {
-			int addr, proc, prog, vers;
-			char priv[SM_PRIV_SIZE];
-			char *monname, *myname;
-			char *b;
-			int i;
-			notify_list	*clnt;
-
-			buf[sizeof(buf)-1] = 0;
-			b = strchr(buf, '\n');
-			if (b) *b = 0;
-			sscanf(buf, "%x %x %x %x ",
-			       &addr, &prog, &vers, &proc);
-			b = buf+36;
-			for (i=0; i<SM_PRIV_SIZE; i++) {
-				sscanf(b, "%2x", &p);
-				priv[i] = p;
-				b += 2;
-			}
-			b++;
-			monname = b;
-			while (*b && *b != ' ') b++;
-			if (*b) *b++ = '\0';
-			while (*b == ' ') b++;
-			myname = b;
-			clnt = nlist_new(myname, monname, 0);
-			if (!clnt)
-				break;
-			NL_ADDR(clnt).s_addr = addr;
-			NL_MY_PROG(clnt) = prog;
-			NL_MY_VERS(clnt) = vers;
-			NL_MY_PROC(clnt) = proc;
-			clnt->dns_name = xstrdup(de->d_name);
-			memcpy(NL_PRIV(clnt), priv, SM_PRIV_SIZE);
-			nlist_insert(&rtnl, clnt);
-		}
-		fclose(f);
+	const struct sockaddr_in *sin = (const struct sockaddr_in *)sap;
+	notify_list *clnt;
+
+	clnt = nlist_new(mon->mon_id.my_id.my_name,
+				mon->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) = mon->mon_id.my_id.my_prog;
+	NL_MY_VERS(clnt) = mon->mon_id.my_id.my_vers;
+	NL_MY_PROC(clnt) = mon->mon_id.my_id.my_proc;
+	memcpy(NL_PRIV(clnt), mon->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.
@@ -359,7 +309,7 @@ sm_unmon_1_svc(struct mon_id *argp, struct svc_req *rqstp)
 			/* 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);
@@ -413,7 +363,7 @@ sm_unmon_all_1_svc(struct my_id *argp, struct svc_req *rqstp)
 			temp = NL_NEXT(clnt);
 			/* PRC: do the HA callout: */
 			ha_callout("del-client", mon_name, my_name, -1);
-			xunlink(SM_DIR, clnt->dns_name);
+			nsm_delete_monitored_host(clnt->dns_name);
 			nlist_free(&rtnl, clnt);
 			++count;
 			clnt = temp;
diff --git a/utils/statd/statd.c b/utils/statd/statd.c
index 6148952..72c9b41 100644
--- a/utils/statd/statd.c
+++ b/utils/statd/statd.c
@@ -25,25 +25,15 @@
 #include <sys/resource.h>
 #include <sys/wait.h>
 #include <grp.h>
+
 #include "statd.h"
 #include "nfslib.h"
+#include "nsm.h"
 
 /* Socket operations */
 #include <sys/types.h>
 #include <sys/socket.h>
 
-/* Added to enable specification of state directory path at run-time
- * j_carlos_gomez@yahoo.com
- */
-
-char * DIR_BASE = DEFAULT_DIR_BASE;
-
-char *  SM_DIR = DEFAULT_SM_DIR;
-char *  SM_BAK_DIR =  DEFAULT_SM_BAK_DIR;
-char *  SM_STAT_PATH = DEFAULT_SM_STAT_PATH;
-
-/* ----- end of state directory path stuff ------- */
-
 int	run_mode = 0;		/* foreground logging mode */
 
 /* LH - I had these local to main, but it seemed silly to have 
@@ -73,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 **);
@@ -190,38 +179,6 @@ static void truncate_pidfile(void)
 	}
 }
 
-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) {
-		xlog_warn("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) {
-			xlog(L_ERROR, "Unable to change owner of %s: %d (%s)",
-					SM_DIR, strerror (errno));
-		}
-	}
-	setgroups(0, NULL);
-	if (setgid(st.st_gid) == -1
-	    || setuid(st.st_uid) == -1) {
-		xlog(L_ERROR, "Fail to drop privileges");
-		exit(1);
-	}
-}
-
 static void run_sm_notify(int outport)
 {
 	char op[20];
@@ -316,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) {
@@ -421,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]);
@@ -490,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 */
@@ -507,7 +440,8 @@ int main (int argc, char **argv)
 		pipefds[1] = -1;
 	}
 
-	drop_privs();
+	if (!nsm_drop_privileges(pidfd))
+		exit(1);
 
 	for (;;) {
 		/*
@@ -536,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)) {
-		xlog_warn("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))
-			xlog_warn("Writing to '%s' failed: errno %d (%s)",
-				file, errno, strerror(errno));
-		close(fd);
-	}
-
-}
diff --git a/utils/statd/statd.h b/utils/statd/statd.h
index 085f32d..542a877 100644
--- a/utils/statd/statd.h
+++ b/utils/statd/statd.h
@@ -14,28 +14,6 @@
 #include "xlog.h"
 
 /*
- * Paths and filenames.
- */
-#if defined(NFS_STATEDIR)
-# define DEFAULT_DIR_BASE	NFS_STATEDIR "/"
-#else
-# define DEFAULT_DIR_BASE	"/var/lib/nfs/"
-#endif
-
-#define DEFAULT_SM_DIR		DEFAULT_DIR_BASE "sm"
-#define DEFAULT_SM_BAK_DIR	DEFAULT_DIR_BASE "sm.bak"
-#define DEFAULT_SM_STAT_PATH	DEFAULT_DIR_BASE "state"
-
-/* Added to support run-time specification of state directory path.
- * j_carlos_gomez@yahoo.com
- */
-
-extern char * DIR_BASE;
-extern char *  SM_DIR;
-extern char *  SM_BAK_DIR;
-extern char *  SM_STAT_PATH;
-
-/*
  * Status definitions.
  */
 #define STAT_FAIL	stat_fail
@@ -53,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);
 
 /*
-------------------------------------------------------------------------------
libnsm-a-add-rpc-construction-
-------------------------------------------------------------------------------
libnsm.a: Add RPC construction helper functions

From: Chuck Lever <chuck.lever@oracle.com>

To manage concurrency, both statd and sm-notify construct raw RPC
requests in socket buffers, and use a minimal request scheduler
to send these requests and manage replies.  Both statd and sm-notify
open code the RPC request construction.

Introduce helper functions that can construct and send raw
NSMPROC_NOTIFY, NLM downcalls, and portmapper calls over a datagram
socket, and receive and parse their replies.  Support for IPv6 and
RPCB_GETADDR is featured.  This code (and the IPv6 support it
introduces) can now be shared by statd and sm-notify, eliminating
code and bug duplication.

This implementation is based on what's in utils/statd/rmtcall.c now,
but is wrapped up in a nice API and includes extra error checking.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 support/include/nsm.h   |   25 ++
 support/nsm/Makefile.am |    2 
 support/nsm/rpc.c       |  505 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 531 insertions(+), 1 deletions(-)
 create mode 100644 support/nsm/rpc.c


diff --git a/support/include/nsm.h b/support/include/nsm.h
index 594f0d9..96b23f2 100644
--- a/support/include/nsm.h
+++ b/support/include/nsm.h
@@ -59,4 +59,29 @@ extern int	nsm_insert_monitored_host(const char *hostname,
 extern void	nsm_delete_monitored_host(const char *hostname);
 extern void	nsm_delete_notified_host(const char *hostname);
 
+/* rpc.c */
+
+#define NSM_MAXMSGSIZE	(2048 / sizeof(uint32_t))
+
+extern uint32_t nsm_xmit_getport(const int sock,
+			const struct sockaddr_in *sin,
+			const unsigned long program,
+			const unsigned long version);
+extern uint32_t nsm_xmit_getaddr(const int sock,
+			const struct sockaddr_in6 *sin6,
+			const rpcprog_t program, const rpcvers_t version);
+extern uint32_t nsm_xmit_rpcbind(const int sock, const struct sockaddr *sap,
+			const rpcprog_t program, const rpcvers_t version);
+extern uint32_t nsm_xmit_notify(const int sock, const struct sockaddr *sap,
+			const socklen_t salen, const rpcprog_t program,
+			const char *mon_name, const int state);
+extern uint32_t nsm_xmit_nlmcall(const int sock, const struct sockaddr *sap,
+			const socklen_t salen, const struct mon *mon,
+			const int state);
+extern uint32_t nsm_parse_reply(XDR *xdrs);
+extern unsigned long
+		nsm_recv_getport(XDR *xdrs);
+extern uint16_t nsm_recv_getaddr(XDR *xdrs);
+extern uint16_t nsm_recv_rpcbind(const int family, XDR *xdrs);
+
 #endif	/* !_NFS_UTILS_SUPPORT_NSM_H */
diff --git a/support/nsm/Makefile.am b/support/nsm/Makefile.am
index e359a43..2038e68 100644
--- a/support/nsm/Makefile.am
+++ b/support/nsm/Makefile.am
@@ -10,7 +10,7 @@ GENFILES	= $(GENFILES_CLNT) $(GENFILES_SVC) $(GENFILES_XDR) $(GENFILES_H)
 EXTRA_DIST	= sm_inter.x
 
 noinst_LIBRARIES = libnsm.a
-libnsm_a_SOURCES = $(GENFILES) file.c
+libnsm_a_SOURCES = $(GENFILES) file.c rpc.c
 
 BUILT_SOURCES = $(GENFILES)
 
diff --git a/support/nsm/rpc.c b/support/nsm/rpc.c
new file mode 100644
index 0000000..a9d3a62
--- /dev/null
+++ b/support/nsm/rpc.c
@@ -0,0 +1,505 @@
+/*
+ * Copyright 2009 Oracle.  All rights reserved.
+ *
+ * This file is part of nfs-utils.
+ *
+ * nfs-utils is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * nfs-utils is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with nfs-utils.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * NSM for Linux.
+ *
+ * Instead of using ONC or TI RPC library calls, statd constructs
+ * RPC calls directly in socket buffers.  This allows a single
+ * socket to be concurrently shared among several different RPC
+ * programs and versions using a simple RPC request dispatcher.
+ *
+ * This file contains the details of RPC header and call
+ * construction and reply parsing, and a method for creating a
+ * socket for use with these functions.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif	/* HAVE_CONFIG_H */
+
+#include <time.h>
+#include <netdb.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#include <net/if.h>
+#include <arpa/inet.h>
+
+#include <rpc/rpc.h>
+#include <rpc/pmap_prot.h>
+#include <rpc/pmap_rmt.h>
+
+#ifdef HAVE_LIBTIRPC
+#include <netconfig.h>
+#include <rpc/rpcb_prot.h>
+#endif	/* HAVE_LIBTIRPC */
+
+#include "xlog.h"
+#include "nfsrpc.h"
+#include "nsm.h"
+#include "sm_inter.h"
+
+/*
+ * Returns a fresh XID appropriate for RPC over UDP -- never zero.
+ */
+static uint32_t
+nsm_next_xid(void)
+{
+	static uint32_t nsm_xid = 0;
+	struct timeval now;
+
+	if (nsm_xid == 0) {
+		(void)gettimeofday(&now, NULL);
+		nsm_xid = getpid() ^ now.tv_sec ^ now.tv_usec;
+	}
+
+	return nsm_xid++;
+}
+
+/*
+ * Select a fresh XID and construct an RPC header in @mesg.
+ * Always use AUTH_NULL credentials and verifiers.
+ *
+ * Returns the new XID.
+ */
+static uint32_t
+nsm_init_rpc_header(const rpcprog_t program, const rpcvers_t version,
+			const rpcproc_t procedure, struct rpc_msg *mesg)
+{
+	struct call_body *cb = &mesg->rm_call;
+
+	mesg->rm_xid = nsm_next_xid();
+	mesg->rm_direction = CALL;
+
+	cb->cb_rpcvers = RPC_MSG_VERSION;
+	cb->cb_prog = program;
+	cb->cb_vers = version;
+	cb->cb_proc = procedure;
+
+	cb->cb_cred.oa_flavor = AUTH_NULL;
+	cb->cb_cred.oa_base = (caddr_t) NULL;
+	cb->cb_cred.oa_length = 0;
+	cb->cb_verf.oa_flavor = AUTH_NULL;
+	cb->cb_verf.oa_base = (caddr_t) NULL;
+	cb->cb_verf.oa_length = 0;
+
+	return mesg->rm_xid;
+}
+
+/*
+ * Send a completed RPC call on a socket.
+ *
+ * Returns 1 if all the bytes were sent successfully; otherwise
+ * zero if any error occurred.
+ */
+static int
+nsm_rpc_sendto(const int sock, const struct sockaddr *sap,
+			const socklen_t salen, XDR *xdrs, void *buf)
+{
+	const unsigned int len = xdr_getpos(xdrs);
+	ssize_t err;
+
+	err = sendto(sock, buf, len, 0, sap, salen);
+	if ((err < 0) || ((size_t)err != len)) {
+		xlog(L_ERROR, "%s: sendto failed: %m", __func__);
+		return 0;
+	}
+	return 1;
+}
+
+/**
+ * nsm_xmit_getport - post a PMAP_GETPORT call on a socket descriptor
+ * @sock: datagram socket descriptor
+ * @sin: pointer to AF_INET socket address of server
+ * @program: RPC program number to query
+ * @version: RPC version number to query
+ *
+ * Send a PMAP_GETPORT call to the portmap daemon at @sin using
+ * socket descriptor @sock.  This request queries the RPC program
+ * [program, version, IPPROTO_UDP].
+ *
+ * NB: PMAP_GETPORT works only for IPv4 hosts.  This implementation
+ *     works only over UDP, and queries only UDP registrations.
+ *
+ * Returns the XID of the call, or zero if an error occurred.
+ */
+uint32_t
+nsm_xmit_getport(const int sock, const struct sockaddr_in *sin,
+			const unsigned long program,
+			const unsigned long version)
+{
+	struct pmap parms = {
+		.pm_prog	= program,
+		.pm_vers	= version,
+		.pm_prot	= IPPROTO_UDP,
+		.pm_port	= 0,
+	};
+	unsigned int msgbuf[NSM_MAXMSGSIZE];
+	struct rpc_msg mesg;
+	uint32_t xid;
+	int err = 0;
+	XDR xdr;
+
+	xlog(D_CALL, "Sending PMAP_GETPORT for %u, %u, udp", program, version);
+
+	xid = nsm_init_rpc_header(PMAPPROG, PMAPVERS,
+					(rpcproc_t)PMAPPROC_GETPORT, &mesg);
+
+	xdrmem_create(&xdr, (caddr_t)msgbuf, sizeof(msgbuf), XDR_ENCODE);
+
+	if (xdr_callmsg(&xdr, &mesg) && xdr_pmap(&xdr, &parms)) {
+		struct sockaddr_in addr = *sin;
+
+		addr.sin_port = htons((uint16_t)PMAPPORT);
+		err = nsm_rpc_sendto(sock, (struct sockaddr *)&addr,
+					(socklen_t)sizeof(addr), &xdr, msgbuf);
+	} else
+		xlog(L_ERROR, "%s: can't encode PMAP_GETPORT call", __func__);
+
+	xdr_destroy(&xdr);
+
+	return err? xid : 0;
+}
+
+/**
+ * nsm_xmit_getaddr - post an RPCB_GETADDR call on a socket descriptor
+ * @sock: datagram socket descriptor
+ * @sin: pointer to AF_INET6 socket address of server
+ * @program: RPC program number to query
+ * @version: RPC version number to query
+ *
+ * Send an RPCB_GETADDR call to the rpcbind daemon at @sap using
+ * socket descriptor @sock.  This request queries the RPC program
+ * [program, version, "udp6"].
+ *
+ * NB: RPCB_GETADDR works for both IPv4 and IPv6 hosts.  This
+ *     implementation works only over UDP and AF_INET6, and queries
+ *     only "udp6" registrations.
+ *
+ * Returns the XID of the call, or zero if an error occurred.
+ */
+#ifdef HAVE_LIBTIRPC
+uint32_t
+nsm_xmit_getaddr(const int sock, const struct sockaddr_in6 *sin6,
+			const rpcprog_t program, const rpcvers_t version)
+{
+	struct rpcb parms = {
+		.r_prog		= program,
+		.r_vers		= version,
+		.r_netid	= "udp6",
+		.r_owner	= "",
+	};
+	unsigned int msgbuf[NSM_MAXMSGSIZE];
+	struct rpc_msg mesg;
+	uint32_t xid;
+	int err = 0;
+	XDR xdr;
+
+	xlog(D_CALL, "Sending RPCB_GETADDR for %u, %u, udp6", program, version);
+
+	xid = nsm_init_rpc_header(RPCBPROG, RPCBVERS,
+					(rpcproc_t)RPCBPROC_GETADDR, &mesg);
+
+	parms.r_addr = nfs_sockaddr2universal((struct sockaddr *)sin6);
+	if (parms.r_addr == NULL) {
+		xlog(L_ERROR, "%s: can't encode socket address", __func__);
+		return 0;
+	}
+
+	xdrmem_create(&xdr, (caddr_t)msgbuf, sizeof(msgbuf), XDR_ENCODE);
+
+	if (xdr_callmsg(&xdr, &mesg) && xdr_rpcb(&xdr, &parms)) {
+		struct sockaddr_in6 addr = *sin6;
+
+		addr.sin6_port = htons((uint16_t)PMAPPORT);
+		err = nsm_rpc_sendto(sock, (struct sockaddr *)&addr,
+					(socklen_t)sizeof(addr), &xdr, msgbuf);
+	} else
+		xlog(L_ERROR, "%s: can't encode RPCB_GETADDR call", __func__);
+
+	xdr_destroy(&xdr);
+	free(parms.r_addr);
+
+	return err? xid : 0;
+}
+#else	/* !HAVE_LIBTIRPC */
+uint32_t
+nsm_xmit_getaddr(const int sock __attribute__((unused)),
+			const struct sockaddr_in6 *sin6 __attribute__((unused)),
+			const rpcprog_t program __attribute__((unused)),
+			const rpcvers_t version __attribute__((unused)))
+{
+	return 0;
+}
+#endif	/* !HAVE_LIBTIRPC */
+
+/**
+ * nsm_xmit_rpcbind - post an rpcbind request
+ * @sock: datagram socket descriptor
+ * @sap: pointer to socket address of server
+ * @program: RPC program number to query
+ * @version: RPC version number to query
+ *
+ * Send an rpcbind query to the rpcbind daemon at @sap using
+ * socket descriptor @sock.
+ *
+ * NB: This implementation works only over UDP, but can query IPv4 or IPv6
+ *     hosts.  It queries only UDP registrations.
+ *
+ * Returns the XID of the call, or zero if an error occurred.
+ */
+uint32_t
+nsm_xmit_rpcbind(const int sock, const struct sockaddr *sap,
+			const rpcprog_t program, const rpcvers_t version)
+{
+	switch (sap->sa_family) {
+	case AF_INET:
+		return nsm_xmit_getport(sock, (const struct sockaddr_in *)sap,
+						program, version);
+	case AF_INET6:
+		return nsm_xmit_getaddr(sock, (const struct sockaddr_in6 *)sap,
+						program, version);
+	}
+	return 0;
+}
+
+/**
+ * nsm_xmit_notify - post an NSMPROC_NOTIFY call on a socket descriptor
+ * @sock: datagram socket descriptor
+ * @sap: pointer to socket address of peer to notify (port already filled in)
+ * @salen: length of socket address
+ * @program: RPC program number to use
+ * @mon_name: mon_name of local peer (ie the rebooting system)
+ * @state: state of local peer
+ *
+ * Send an NSMPROC_NOTIFY call to the peer at @sap using socket descriptor @sock.
+ * This request notifies the peer that we have rebooted.
+ *
+ * NB: This implementation works only over UDP, but supports both AF_INET
+ *     and AF_INET6.
+ *
+ * Returns the XID of the call, or zero if an error occurred.
+ */
+uint32_t
+nsm_xmit_notify(const int sock, const struct sockaddr *sap,
+			const socklen_t salen, const rpcprog_t program,
+			const char *mon_name, const int state)
+{
+	struct stat_chge state_change = {
+		.mon_name	= strdup(mon_name),
+		.state		= state,
+	};
+	unsigned int msgbuf[NSM_MAXMSGSIZE];
+	struct rpc_msg mesg;
+	uint32_t xid;
+	int err = 0;
+	XDR xdr;
+
+	if (state_change.mon_name == NULL) {
+		xlog(L_ERROR, "%s: no memory", __func__);
+		return 0;
+	}
+
+	xlog(D_CALL, "Sending SM_NOTIFY for %s", mon_name);
+
+	xid = nsm_init_rpc_header(program, SM_VERS, SM_NOTIFY, &mesg);
+
+	xdrmem_create(&xdr, (caddr_t)msgbuf, sizeof(msgbuf), XDR_ENCODE);
+
+	if (xdr_callmsg(&xdr, &mesg) && xdr_stat_chge(&xdr, &state_change)) {
+		err = nsm_rpc_sendto(sock, sap, salen, &xdr, msgbuf);
+	} else
+		xlog(L_ERROR, "%s: can't encode NSMPROC_NOTIFY call",
+				__func__);
+
+	xdr_destroy(&xdr);
+	free(state_change.mon_name);
+
+	return err? xid : 0;
+}
+
+/**
+ * nsm_xmit_nlmcall - post an unnamed call to local NLM on a socket descriptor
+ * @sock: datagram socket descriptor
+ * @sap: address/port of NLM service to contact
+ * @salen: size of @sap
+ * @mon: callback data defining RPC call to make
+ * @state: state of rebooting host
+ *
+ * Send an unnamed call (previously requested via NSMPROC_MON) to the
+ * specified local UDP-based RPC service using socket descriptor @sock.
+ *
+ * NB: This implementation works only over UDP, but supports both AF_INET
+ *     and AF_INET6.
+ *
+ * Returns the XID of the call, or zero if an error occurred.
+ */
+uint32_t
+nsm_xmit_nlmcall(const int sock, const struct sockaddr *sap,
+			const socklen_t salen, const struct mon *mon,
+			const int state)
+{
+	struct status new_status = {
+		.mon_name	= mon->mon_id.mon_name,
+		.state		= state,
+	};
+	const struct my_id *id = &mon->mon_id.my_id;
+	unsigned int msgbuf[NSM_MAXMSGSIZE];
+	struct rpc_msg mesg;
+	uint32_t xid;
+	int err = 0;
+	XDR xdr;
+
+	xlog(D_CALL, "Sending NLM downcall for %s", mon->mon_id.mon_name);
+
+	xid = nsm_init_rpc_header(id->my_prog, id->my_vers, id->my_proc, &mesg);
+
+	memcpy(&new_status.priv, &mon->priv, sizeof(new_status.priv));
+	xdrmem_create(&xdr, (caddr_t)msgbuf, sizeof(msgbuf), XDR_ENCODE);
+
+	if (xdr_callmsg(&xdr, &mesg) && xdr_status(&xdr, &new_status))
+		err = nsm_rpc_sendto(sock, sap, salen, &xdr, msgbuf);
+	else
+		xlog(L_ERROR, "%s: can't encode NLM downcall", __func__);
+
+	xdr_destroy(&xdr);
+
+	return err? xid : 0;
+}
+
+/**
+ * nsm_parse_reply - parse and validate the header in an RPC reply
+ * @xdrs: pointer to XDR
+ *
+ * Returns the XID of the reply, or zero if an error occurred.
+ */
+uint32_t
+nsm_parse_reply(XDR *xdrs)
+{
+	struct rpc_msg mesg = {
+		.rm_reply.rp_acpt.ar_results.proc	= (xdrproc_t)xdr_void,
+	};
+
+	if (!xdr_replymsg(xdrs, &mesg)) {
+		xlog(L_ERROR, "%s: can't decode RPC reply", __func__);
+		return 0;
+	}
+
+	if (mesg.rm_reply.rp_stat != 0) {
+		xlog(L_ERROR, "%s: [0x%x] RPC status %d", 
+			__func__, mesg.rm_xid, mesg.rm_reply.rp_stat);
+		return 0;
+	}
+
+	if (mesg.rm_reply.rp_acpt.ar_stat != 0) {
+		xlog(L_ERROR, "%s: [0x%x] RPC accept status %d",
+			__func__, mesg.rm_xid, mesg.rm_reply.rp_acpt.ar_stat);
+		return 0;
+	}
+
+	return mesg.rm_xid;
+}
+
+/**
+ * nsm_recv_getport - parse PMAP_GETPORT reply
+ * @xdrs: pointer to XDR
+ *
+ * Returns the port number from the RPC reply, or zero
+ * if an error occurred.
+ */
+unsigned long
+nsm_recv_getport(XDR *xdrs)
+{
+	unsigned long port = 0;
+
+	if (!xdr_u_long(xdrs, &port))
+		xlog(L_ERROR, "%s: can't decode pmap reply",
+			__func__);
+	if (port > UINT16_MAX) {
+		xlog(L_ERROR, "%s: bad port number",
+			__func__);
+		port = 0;
+	}
+
+	xlog(D_CALL, "Received PMAP_GETPORT result: %lu", port);
+	return port;
+}
+
+/**
+ * nsm_recv_getaddr - parse RPCB_GETADDR reply
+ * @xdrs: pointer to XDR
+ *
+ * Returns the port number from the RPC reply, or zero
+ * if an error occurred.
+ */
+uint16_t
+nsm_recv_getaddr(XDR *xdrs)
+{
+	int port;
+	char *uaddr = NULL;
+
+	if (!xdr_wrapstring(xdrs, &uaddr))
+		xlog(L_ERROR, "%s: can't decode rpcb reply",
+			__func__);
+
+	if ((uaddr == NULL) || (uaddr[0] == '\0')) {
+		xlog(D_CALL, "Received RPCB_GETADDR result: "
+				"program not registered");
+		return 0;
+	}
+
+	port = nfs_universal2port(uaddr);
+
+	xdr_free((xdrproc_t)xdr_wrapstring, (char *)&uaddr);
+
+	if (port < 0 || port > UINT16_MAX) {
+		xlog(L_ERROR, "%s: bad port number",
+			__func__);
+		return 0;
+	}
+
+	xlog(D_CALL, "Received RPCB_GETADDR result: %d", port);
+	return port;
+}
+
+/**
+ * nsm_recv_rpcbind - parse rpcbind reply
+ * @af: address family of reply
+ * @xdrs: pointer to XDR
+ *
+ * Returns the port number from the RPC reply, or zero
+ * if an error occurred.
+ */
+uint16_t
+nsm_recv_rpcbind(const int family, XDR *xdrs)
+{
+	switch (family) {
+	case AF_INET:
+		return nsm_recv_getport(xdrs);
+	case AF_INET6:
+		return nsm_recv_getaddr(xdrs);
+	}
+	return 0;
+}
-------------------------------------------------------------------------------
statd-support-sending-sm_notif
-------------------------------------------------------------------------------
statd: Support sending SM_NOTIFY requests to IPv6 remotes

From: Chuck Lever <chuck.lever@oracle.com>

Replace the open code to construct SM_NOTIFY and PMAP_GETPORT RPC
requests with calls to our new library routines that support
IPv6 and RPCB_GETADDR as well.

This change allows sm-notify to send RPCB_GETADDR, but it won't do
that until the main sm-notify socket supports PF_INET6, and the DNS
resolution logic is updated to return IPv6 addresses.

I would prefer to use high level RPC client library calls to send
these requests instead of this open coded solution.  That would give
us a number of benefits, including support for sending SM_NOTIFY via
TCP so we can penetrate firewalls that block UDP.

However, that would also involve replacing the scheduler logic in
sm-notify with something like fork(3) or pthreads, to deal with the
synchronous RPC client library calls.  That's probably too much for
folks to swallow right now.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 utils/statd/sm-notify.c |  110 +++++++++++------------------------------------
 1 files changed, 26 insertions(+), 84 deletions(-)


diff --git a/utils/statd/sm-notify.c b/utils/statd/sm-notify.c
index 6466d8a..9ed5df1 100644
--- a/utils/statd/sm-notify.c
+++ b/utils/statd/sm-notify.c
@@ -38,7 +38,6 @@
 #define NSM_TIMEOUT	2
 #define NSM_NOTIFY	6
 #define NSM_MAX_TIMEOUT	120	/* don't make this too big */
-#define MAXMSGSIZE	256
 
 struct nsm_host {
 	struct nsm_host *	next;
@@ -388,14 +387,6 @@ notify_host(int sock, struct nsm_host *host)
 {
 	struct sockaddr_storage address;
 	struct sockaddr *dest = (struct sockaddr *)&address;
-	static unsigned int	xid = 0;
-	uint32_t		msgbuf[MAXMSGSIZE], *p;
-	unsigned int		len;
-
-	if (!xid)
-		xid = getpid() + time(NULL);
-	if (!host->xid)
-		host->xid = xid++;
 
 	if (host->ai == NULL) {
 		host->ai = smn_lookup(host->name);
@@ -406,12 +397,6 @@ notify_host(int sock, struct nsm_host *host)
 		}
 	}
 
-	memset(msgbuf, 0, sizeof(msgbuf));
-	p = msgbuf;
-	*p++ = htonl(host->xid);
-	*p++ = 0;
-	*p++ = htonl(2);
-
 	/* If we retransmitted 4 times, reset the port to force
 	 * a new portmap lookup (in case statd was restarted).
 	 * We also rotate through multiple IP addresses at this
@@ -446,48 +431,13 @@ notify_host(int sock, struct nsm_host *host)
 	}
 
 	memcpy(dest, &host->addr, host->addrlen);
-	if (nfs_get_port(dest) == 0) {
-		/* Build a PMAP packet */
-		xlog(D_GENERAL, "Sending portmap query to %s", host->name);
-
-		nfs_set_port(dest, 111);
-		*p++ = htonl(100000);
-		*p++ = htonl(2);
-		*p++ = htonl(3);
-
-		/* Auth and verf */
-		*p++ = 0; *p++ = 0;
-		*p++ = 0; *p++ = 0;
-
-		*p++ = htonl(NSM_PROGRAM);
-		*p++ = htonl(NSM_VERSION);
-		*p++ = htonl(IPPROTO_UDP);
-		*p++ = 0;
-	} else {
-		/* Build an SM_NOTIFY packet */
-		xlog(D_GENERAL, "Sending SM_NOTIFY to %s", host->name);
-
-		*p++ = htonl(NSM_PROGRAM);
-		*p++ = htonl(NSM_VERSION);
-		*p++ = htonl(NSM_NOTIFY);
-
-		/* Auth and verf */
-		*p++ = 0; *p++ = 0;
-		*p++ = 0; *p++ = 0;
-
-		/* state change */
-		len = strlen(nsm_hostname);
-		*p++ = htonl(len);
-		memcpy(p, nsm_hostname, len);
-		p += (len + 3) >> 2;
-		*p++ = htonl(nsm_state);
-	}
-	len = (p - msgbuf) << 2;
-
-	if (sendto(sock, msgbuf, len, 0, dest, host->addrlen) < 0)
-		xlog_warn("Sending Reboot Notification to "
-			"'%s' failed: errno %d (%m)", host->name, errno);
-
+	if (nfs_get_port(dest) == 0)
+		host->xid = nsm_xmit_rpcbind(sock, dest,
+				NSM_PROGRAM, NSM_VERSION);
+	else
+		host->xid = nsm_xmit_notify(sock, dest, host->addrlen,
+				NSM_PROGRAM, nsm_hostname, nsm_state);
+	
 	return 0;
 }
 
@@ -499,40 +449,32 @@ recv_reply(int sock)
 {
 	struct nsm_host	*hp;
 	struct sockaddr *sap;
-	uint32_t	msgbuf[MAXMSGSIZE], *p, *end;
+	uint32_t	msgbuf[NSM_MAXMSGSIZE];
 	uint32_t	xid;
-	int		res;
+	ssize_t		msglen;
+	XDR		xdr, *xdrs = &xdr;
 
-	res = recv(sock, msgbuf, sizeof(msgbuf), 0);
-	if (res < 0)
+	msglen = recv(sock, msgbuf, sizeof(msgbuf), 0);
+	if (msglen < 0)
 		return;
 
 	xlog(D_GENERAL, "Received packet...");
 
-	p = msgbuf;
-	end = p + (res >> 2);
-
-	xid = ntohl(*p++);
-	if (*p++ != htonl(1)	/* must be REPLY */
-	 || *p++ != htonl(0)	/* must be ACCEPTED */
-	 || *p++ != htonl(0)	/* must be NULL verifier */
-	 || *p++ != htonl(0)
-	 || *p++ != htonl(0))	/* must be SUCCESS */
-		return;
+	xdrmem_create(xdrs, (caddr_t)msgbuf, msglen, XDR_DECODE);
+	xid = nsm_parse_reply(xdrs);
+	if (xid == 0)
+		goto out;
 
 	/* Before we look at the data, find the host struct for
 	   this reply */
 	if ((hp = find_host(xid)) == NULL)
-		return;
+		goto out;
 	sap = (struct sockaddr *)&hp->addr;
 
 	if (nfs_get_port(sap) == 0) {
-		/* This was a portmap request */
-		unsigned int	port;
+		uint16_t port;
 
-		port = ntohl(*p++);
-		if (p > end)
-			goto fail;
+		port = nsm_recv_rpcbind(sap->sa_family, xdrs);
 
 		hp->send_next = time(NULL);
 		if (port == 0) {
@@ -553,16 +495,16 @@ recv_reply(int sock)
 		 * check that we didn't read past the end of the
 		 * packet)
 		 */
-		if (p <= end) {
-			xlog(D_GENERAL, "Host %s notified successfully",
-					hp->name);
-			smn_forget_host(hp);
-			return;
-		}
+		xlog(D_GENERAL, "Host %s notified successfully",
+				hp->name);
+		smn_forget_host(hp);
+		goto out;
 	}
 
-fail:	/* Re-insert the host */
 	insert_host(hp);
+
+out:
+	xdr_destroy(xdrs);
 }
 
 /*
-------------------------------------------------------------------------------
statd-update-rmtcall-c
-------------------------------------------------------------------------------
statd: Update rmtcall.c

From: Chuck Lever <chuck.lever@oracle.com>

Replace the open code to construct NLM downcalls and PMAP_GETPORT RPC
requests with calls to our new library routines.

This clean up removes redundant code in rmtcall.c, and enables the
possibility of making NLM downcalls via IPv6 transports.  We won't
support that for a long while, however.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 utils/statd/rmtcall.c |  143 +++++++++----------------------------------------
 1 files changed, 26 insertions(+), 117 deletions(-)


diff --git a/utils/statd/rmtcall.c b/utils/statd/rmtcall.c
index 5700fc7..139b1de 100644
--- a/utils/statd/rmtcall.c
+++ b/utils/statd/rmtcall.c
@@ -45,13 +45,15 @@
 #include "notlist.h"
 #include "ha-callout.h"
 
+#include "nsm.h"
+#include "nfsrpc.h"
+
 #if SIZEOF_SOCKLEN_T - 0 == 0
 #define socklen_t int
 #endif
 
 #define MAXMSGSIZE	(2048 / sizeof(unsigned int))
 
-static unsigned long	xid = 0;	/* RPC XID counter */
 static int		sockfd = -1;	/* notify socket */
 
 /*
@@ -103,80 +105,15 @@ statd_get_socket(void)
 	return sockfd;
 }
 
-static unsigned long
-xmit_call(struct sockaddr_in *sin,
-	  u_int32_t prog, u_int32_t vers, u_int32_t proc,
-	  xdrproc_t func, void *obj)
-/* 		__u32 prog, __u32 vers, __u32 proc, xdrproc_t func, void *obj) */
-{
-	unsigned int		msgbuf[MAXMSGSIZE], msglen;
-	struct rpc_msg		mesg;
-	struct pmap		pmap;
-	XDR			xdr, *xdrs = &xdr;
-	int			err;
-
-	if (!xid)
-		xid = getpid() + time(NULL);
-
-	mesg.rm_xid = ++xid;
-	mesg.rm_direction = CALL;
-	mesg.rm_call.cb_rpcvers = 2;
-	if (sin->sin_port == 0) {
-		sin->sin_port = htons(PMAPPORT);
-		mesg.rm_call.cb_prog = PMAPPROG;
-		mesg.rm_call.cb_vers = PMAPVERS;
-		mesg.rm_call.cb_proc = PMAPPROC_GETPORT;
-		pmap.pm_prog = prog;
-		pmap.pm_vers = vers;
-		pmap.pm_prot = IPPROTO_UDP;
-		pmap.pm_port = 0;
-		func = (xdrproc_t) xdr_pmap;
-		obj  = &pmap;
-	} else {
-		mesg.rm_call.cb_prog = prog;
-		mesg.rm_call.cb_vers = vers;
-		mesg.rm_call.cb_proc = proc;
-	}
-	mesg.rm_call.cb_cred.oa_flavor = AUTH_NULL;
-	mesg.rm_call.cb_cred.oa_base = (caddr_t) NULL;
-	mesg.rm_call.cb_cred.oa_length = 0;
-	mesg.rm_call.cb_verf.oa_flavor = AUTH_NULL;
-	mesg.rm_call.cb_verf.oa_base = (caddr_t) NULL;
-	mesg.rm_call.cb_verf.oa_length = 0;
-
-	/* Create XDR memory object for encoding */
-	xdrmem_create(xdrs, (caddr_t) msgbuf, sizeof(msgbuf), XDR_ENCODE);
-
-	/* Encode the RPC header part and payload */
-	if (!xdr_callmsg(xdrs, &mesg) || !func(xdrs, obj)) {
-		xlog(D_GENERAL, "%s: can't encode RPC message!", __func__);
-		xdr_destroy(xdrs);
-		return 0;
-	}
-
-	/* Get overall length of datagram */
-	msglen = xdr_getpos(xdrs);
-
-	if ((err = sendto(sockfd, msgbuf, msglen, 0,
-			(struct sockaddr *) sin, sizeof(*sin))) < 0) {
-		xlog_warn("%s: sendto failed: %m", __func__);
-	} else if (err != msglen) {
-		xlog_warn("%s: short write: %m", __func__);
-	}
-
-	xdr_destroy(xdrs);
-
-	return err == msglen? xid : 0;
-}
-
 static notify_list *
 recv_rply(struct sockaddr_in *sin, u_long *portp)
 {
-	unsigned int		msgbuf[MAXMSGSIZE], msglen;
-	struct rpc_msg		mesg;
+	unsigned int		msgbuf[NSM_MAXMSGSIZE];
+	ssize_t			msglen;
 	notify_list		*lp = NULL;
 	XDR			xdr, *xdrs = &xdr;
 	socklen_t		alen = sizeof(*sin);
+	uint32_t		xid;
 
 	/* Receive message */
 	if ((msglen = recvfrom(sockfd, msgbuf, sizeof(msgbuf), 0,
@@ -187,36 +124,15 @@ recv_rply(struct sockaddr_in *sin, u_long *portp)
 
 	/* Create XDR object for decoding buffer */
 	xdrmem_create(xdrs, (caddr_t) msgbuf, msglen, XDR_DECODE);
-
-	memset(&mesg, 0, sizeof(mesg));
-	mesg.rm_reply.rp_acpt.ar_results.where = NULL;
-	mesg.rm_reply.rp_acpt.ar_results.proc = (xdrproc_t) xdr_void;
-
-	if (!xdr_replymsg(xdrs, &mesg)) {
-		xlog_warn("%s: can't decode RPC message!", __func__);
-		goto done;
-	}
-
-	if (mesg.rm_reply.rp_stat != 0) {
-		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) {
-		xlog_warn("%s: [%s] RPC status %d",
-				__func__,
-				inet_ntoa(sin->sin_addr),
-				mesg.rm_reply.rp_acpt.ar_stat);
+	xid = nsm_parse_reply(xdrs);
+	if (xid == 0)
 		goto done;
-	}
 
 	for (lp = notify; lp != NULL; lp = lp->next) {
 		/* LH - this was a bug... it should have been checking
 		 * the xid from the response message from the client,
 		 * not the static, internal xid */
-		if (lp->xid != mesg.rm_xid)
+		if (lp->xid != xid)
 			continue;
 		if (lp->addr.s_addr != sin->sin_addr.s_addr) {
 			char addr [18];
@@ -227,15 +143,8 @@ recv_rply(struct sockaddr_in *sin, u_long *portp)
 				"expected %s, got %s", __func__,
 				addr, inet_ntoa(sin->sin_addr));
 		}
-		if (lp->port == 0) {
-			if (!xdr_u_long(xdrs, portp)) {
-				xlog_warn("%s: [%s] can't decode reply body!",
-					__func__,
-					inet_ntoa(sin->sin_addr));
-				lp = NULL;
-				goto done;
-			}
-		}
+		if (lp->port == 0)
+			*portp = nsm_recv_rpcbind(AF_INET, xdrs);
 		break;
 	}
 
@@ -251,11 +160,7 @@ static int
 process_entry(notify_list *lp)
 {
 	struct sockaddr_in	sin;
-	struct status		new_status;
-	xdrproc_t		func;
-	void			*objp;
-	u_int32_t		proc, vers, prog;
-/* 	__u32			proc, vers, prog; */
+	struct sockaddr		*sap = (struct sockaddr *)&sin;
 
 	if (NL_TIMES(lp) == 0) {
 		xlog(D_GENERAL, "%s: Cannot notify %s, giving up",
@@ -268,21 +173,25 @@ process_entry(notify_list *lp)
 	sin.sin_port   = lp->port;
 	/* LH - moved address into switch */
 
-	prog = NL_MY_PROG(lp);
-	vers = NL_MY_VERS(lp);
-	proc = NL_MY_PROC(lp);
-
 	/* __FORCE__ loopback for callbacks to lockd ... */
 	/* Just in case we somehow ignored it thus far */
 	sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
 
-	func = (xdrproc_t) xdr_status;
-	objp = &new_status;
-	new_status.mon_name = NL_MON_NAME(lp);
-	new_status.state    = NL_STATE(lp);
-	memcpy(new_status.priv, NL_PRIV(lp), SM_PRIV_SIZE);
+	if (nfs_get_port(sap) == 0)
+		lp->xid = nsm_xmit_rpcbind(sockfd, sap,
+				NL_MY_PROG(lp), NL_MY_VERS(lp));
+	else {
+		struct mon mon;
+
+		memcpy(mon.priv, NL_PRIV(lp), SM_PRIV_SIZE);
+		mon.mon_id.mon_name = NL_MON_NAME(lp);
+		mon.mon_id.my_id.my_prog = NL_MY_PROG(lp);
+		mon.mon_id.my_id.my_vers = NL_MY_VERS(lp);
+		mon.mon_id.my_id.my_proc = NL_MY_PROC(lp);
 
-	lp->xid = xmit_call(&sin, prog, vers, proc, func, objp);
+		lp->xid = nsm_xmit_nlmcall(sockfd, sap, sizeof(sin),
+						&mon, NL_STATE(lp));
+	}
 	if (!lp->xid) {
 		xlog_warn("%s: failed to notify port %d",
 				__func__, ntohs(lp->port));
-------------------------------------------------------------------------------
statd-factor-socket-creation-o
-------------------------------------------------------------------------------
statd: factor socket creation out of notify()

From: Chuck Lever <chuck.lever@oracle.com>

The first half of notify() creates the main socket that sm-notify uses
to do its job.  To make adding IPv6 support simpler, refactor that piece
into a separate function.

The logic is modified slightly so that exit(3) is invoked only in
main().  This is not required, but it makes the code slightly easier to
understand and maintain.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 utils/statd/sm-notify.c |  134 ++++++++++++++++++++++++++---------------------
 1 files changed, 75 insertions(+), 59 deletions(-)


diff --git a/utils/statd/sm-notify.c b/utils/statd/sm-notify.c
index 9ed5df1..b80d90d 100644
--- a/utils/statd/sm-notify.c
+++ b/utils/statd/sm-notify.c
@@ -60,7 +60,7 @@ static unsigned int	opt_max_retry = 15 * 60;
 static char *		opt_srcaddr = 0;
 static uint16_t		opt_srcport = 0;
 
-static void		notify(void);
+static void		notify(const int sock);
 static int		notify_host(int, struct nsm_host *);
 static void		recv_reply(int);
 static void		insert_host(struct nsm_host *);
@@ -145,10 +145,74 @@ smn_get_host(const char *hostname,
 	return 1;
 }
 
+/*
+ * Prepare a socket for sending RPC requests
+ *
+ * Returns a bound datagram socket file descriptor, or -1 if
+ * an error occurs.
+ */
+static int
+smn_create_socket(const char *srcaddr, const uint16_t srcport)
+{
+	struct sockaddr_storage address;
+	struct sockaddr *local_addr = (struct sockaddr *)&address;
+	int sock, retry_cnt = 0;
+
+retry:
+	sock = socket(AF_INET, SOCK_DGRAM, 0);
+	if (sock < 0) {
+		xlog(L_ERROR, "Failed to create RPC socket: %m");
+		return -1;
+	}
+	fcntl(sock, F_SETFL, O_NONBLOCK);
+
+	memset(&address, 0, sizeof(address));
+	local_addr->sa_family = AF_INET;	/* Default to IPv4 */
+
+	/* Bind source IP if provided on command line */
+	if (srcaddr) {
+		struct addrinfo *ai = smn_lookup(srcaddr);
+		if (!ai) {
+			xlog(L_ERROR,
+				"Not a valid hostname or address: \"%s\"",
+				srcaddr);
+			return -1;
+		}
+
+		/* We know it's IPv4 at this point */
+		memcpy(local_addr, ai->ai_addr, ai->ai_addrlen);
+
+		freeaddrinfo(ai);
+	}
+
+	/* Use source port if provided on the command line,
+	 * otherwise use bindresvport */
+	if (srcport) {
+		nfs_set_port(local_addr, srcport);
+		if (bind(sock, local_addr, sizeof(struct sockaddr_in)) < 0) {
+			xlog(L_ERROR, "Failed to bind RPC socket: %m");
+			return -1;
+		}
+	} else {
+		struct servent *se;
+		struct sockaddr_in *sin = (struct sockaddr_in *)local_addr;
+		(void) bindresvport(sock, sin);
+		/* try to avoid known ports */
+		se = getservbyport(sin->sin_port, "udp");
+		if (se && retry_cnt < 100) {
+			retry_cnt++;
+			close(sock);
+			goto retry;
+		}
+	}
+
+	return sock;
+}
+
 int
 main(int argc, char **argv)
 {
-	int	c;
+	int	c, sock;
 	int	force = 0;
 	char *	progname;
 
@@ -246,7 +310,14 @@ usage:		fprintf(stderr,
 		close(2);
 	}
 
-	notify();
+	sock = smn_create_socket(opt_srcaddr, opt_srcport);
+	if (sock < 0)
+		exit(1);
+
+	if (!nsm_drop_privileges(-1))
+		exit(1);
+
+	notify(sock);
 
 	if (hosts) {
 		struct nsm_host	*hp;
@@ -266,68 +337,13 @@ usage:		fprintf(stderr,
  * Notify hosts
  */
 static void
-notify(void)
+notify(const int sock)
 {
-	struct sockaddr_storage address;
-	struct sockaddr *local_addr = (struct sockaddr *)&address;
 	time_t	failtime = 0;
-	int	sock = -1;
-	int retry_cnt = 0;
-
- retry:
-	sock = socket(AF_INET, SOCK_DGRAM, 0);
-	if (sock < 0) {
-		xlog(L_ERROR, "Failed to create RPC socket: %m");
-		exit(1);
-	}
-	fcntl(sock, F_SETFL, O_NONBLOCK);
-
-	memset(&address, 0, sizeof(address));
-	local_addr->sa_family = AF_INET;	/* Default to IPv4 */
-
-	/* Bind source IP if provided on command line */
-	if (opt_srcaddr) {
-		struct addrinfo *ai = smn_lookup(opt_srcaddr);
-		if (!ai) {
-			xlog(L_ERROR,
-				"Not a valid hostname or address: \"%s\"",
-				opt_srcaddr);
-			exit(1);
-		}
-
-		/* We know it's IPv4 at this point */
-		memcpy(local_addr, ai->ai_addr, ai->ai_addrlen);
-
-		freeaddrinfo(ai);
-	}
-
-	/* Use source port if provided on the command line,
-	 * otherwise use bindresvport */
-	if (opt_srcport) {
-		nfs_set_port(local_addr, opt_srcport);
-		if (bind(sock, local_addr, sizeof(struct sockaddr_in)) < 0) {
-			xlog(L_ERROR, "Failed to bind RPC socket: %m");
-			exit(1);
-		}
-	} else {
-		struct servent *se;
-		struct sockaddr_in *sin = (struct sockaddr_in *)local_addr;
-		(void) bindresvport(sock, sin);
-		/* try to avoid known ports */
-		se = getservbyport(sin->sin_port, "udp");
-		if (se && retry_cnt < 100) {
-			retry_cnt++;
-			close(sock);
-			goto retry;
-		}
-	}
 
 	if (opt_max_retry)
 		failtime = time(NULL) + opt_max_retry;
 
-	if (!nsm_drop_privileges(-1))
-		exit(1);
-
 	while (hosts) {
 		struct pollfd	pfd;
 		time_t		now = time(NULL);
-------------------------------------------------------------------------------
statd-support-creating-a-pf_in
-------------------------------------------------------------------------------
statd: Support creating a PF_INET6 socket in smn_create_socket()

From: Chuck Lever <chuck.lever@oracle.com>

Socket creation is unfortunately complicated by the need to handle the
case where sm-notify is built with IPv6 support, but the local system
has disabled it entirely at run-time (ie, socket(3) returns
EAFNOSUPPORT when we try to create an AF_INET6 socket).

The run-time address family setting is made available in the global
variable nsm_family.  This setting can control the family of the
socket's bind address and what kind of addresses we want returned by
smn_lookup().  This support is added in subsequent patches.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 utils/statd/sm-notify.c |   80 ++++++++++++++++++++++++++++++++++++++++++++---
 1 files changed, 75 insertions(+), 5 deletions(-)


diff --git a/utils/statd/sm-notify.c b/utils/statd/sm-notify.c
index b80d90d..15f68fc 100644
--- a/utils/statd/sm-notify.c
+++ b/utils/statd/sm-notify.c
@@ -54,6 +54,7 @@ struct nsm_host {
 
 static char		nsm_hostname[256];
 static uint32_t		nsm_state;
+static int		nsm_family = AF_INET;
 static int		opt_debug = 0;
 static int		opt_update_state = 1;
 static unsigned int	opt_max_retry = 15 * 60;
@@ -145,6 +146,78 @@ smn_get_host(const char *hostname,
 	return 1;
 }
 
+#ifdef IPV6_SUPPORTED
+static int smn_socket(void)
+{
+	int sock;
+
+	/*
+	 * We have to use an AF_INET socket here if IPV6_SUPPORTED
+	 * is enabled at build time, but the end user's system has
+	 * disabled all IPv6 support at run time.
+	 */
+	sock = socket(AF_INET6, SOCK_DGRAM, 0);
+	if (sock < 0) {
+		if (errno != EAFNOSUPPORT) {
+			xlog(L_ERROR, "Failed to create RPC socket: %m");
+			return -1;
+		}
+		sock = socket(AF_INET, SOCK_DGRAM, 0);
+		if (sock < 0) {
+			xlog(L_ERROR, "Failed to create RPC socket: %m");
+			return -1;
+		}
+	} else
+		nsm_family = AF_INET6;
+
+	if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) {
+		xlog(L_ERROR, "fcntl(3) on network socket failed: %m");
+		goto out_close;
+	}
+
+	/*
+	 * TI-RPC over IPv6 (udp6/tcp6) does not handle IPv4.  However,
+	 * since sm-notify open-codes all of its RPC support, it can
+	 * use a single socket and let the local network stack provide
+	 * the correct mapping between address families automatically.
+	 * This is the same thing that is done in the kernel.
+	 */
+	if (nsm_family == AF_INET6) {
+		const int zero = 0;
+		if (setsockopt(sock, SOL_IPV6, IPV6_V6ONLY,
+				(char *)&zero, sizeof(zero)) == -1) {
+			xlog(L_ERROR, "setsockopt(3) on network socket failed: %m");
+			goto out_close;
+		}
+	}
+
+	return sock;
+
+out_close:
+	(void)close(sock);
+	return -1;
+}
+#else	/* !IPV6_SUPPORTED */
+static int smn_socket(void)
+{
+	int sock;
+
+	sock = socket(AF_INET, SOCK_DGRAM, 0);
+	if (sock < 0) {
+		xlog(L_ERROR, "Failed to create RPC socket: %m");
+		return -1;
+	}
+
+	if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) {
+		xlog(L_ERROR, "fcntl(3) on network socket failed: %m");
+		(void)close(sock);
+		return -1;
+	}
+
+	return sock;
+}
+#endif	/* !IPV6_SUPPORTED */
+ 
 /*
  * Prepare a socket for sending RPC requests
  *
@@ -159,12 +232,9 @@ smn_create_socket(const char *srcaddr, const uint16_t srcport)
 	int sock, retry_cnt = 0;
 
 retry:
-	sock = socket(AF_INET, SOCK_DGRAM, 0);
-	if (sock < 0) {
-		xlog(L_ERROR, "Failed to create RPC socket: %m");
+	sock = smn_socket();
+	if (sock < 0)
 		return -1;
-	}
-	fcntl(sock, F_SETFL, O_NONBLOCK);
 
 	memset(&address, 0, sizeof(address));
 	local_addr->sa_family = AF_INET;	/* Default to IPv4 */
-------------------------------------------------------------------------------
statd-ipv6-support-in-reserved
-------------------------------------------------------------------------------
statd: IPv6 support in reserved port binding in smn_create_socket()

From: Chuck Lever <chuck.lever@oracle.com>

This patch updates the "bind to an arbitrary privileged port" arm of
smn_create_socket() so it can deal with IPv6 bind addresses.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 utils/statd/sm-notify.c |   33 +++++++++++++++++++++++++++++----
 1 files changed, 29 insertions(+), 4 deletions(-)


diff --git a/utils/statd/sm-notify.c b/utils/statd/sm-notify.c
index 15f68fc..0069a99 100644
--- a/utils/statd/sm-notify.c
+++ b/utils/statd/sm-notify.c
@@ -218,6 +218,26 @@ static int smn_socket(void)
 }
 #endif	/* !IPV6_SUPPORTED */
  
+#ifdef HAVE_LIBTIRPC
+static int
+smn_bindresvport(int sock, struct sockaddr *sap)
+{
+	return bindresvport_sa(sock, sap);
+}
+
+#else	/* !HAVE_LIBTIRPC */
+static int
+smn_bindresvport(int sock, struct sockaddr *sap)
+{
+	if (sap->sa_family != AF_INET) {
+		errno = EAFNOSUPPORT;
+		return -1;
+	}
+
+	return bindresvport(sock, (struct sockaddr_in *)sap);
+}
+#endif	/* !HAVE_LIBTIRPC */
+
 /*
  * Prepare a socket for sending RPC requests
  *
@@ -265,13 +285,18 @@ retry:
 		}
 	} else {
 		struct servent *se;
-		struct sockaddr_in *sin = (struct sockaddr_in *)local_addr;
-		(void) bindresvport(sock, sin);
+
+		if (smn_bindresvport(sock, local_addr) == -1) {
+			xlog(L_ERROR, "bindresvport(3): %m");
+			(void)close(sock);
+			return -1;
+		}
+
 		/* try to avoid known ports */
-		se = getservbyport(sin->sin_port, "udp");
+		se = getservbyport(nfs_get_port(local_addr), "udp");
 		if (se && retry_cnt < 100) {
 			retry_cnt++;
-			close(sock);
+			(void)close(sock);
 			goto retry;
 		}
 	}
-------------------------------------------------------------------------------
statd-use-getaddrinfo-3-to-gen
-------------------------------------------------------------------------------
statd: Use getaddrinfo(3) to generate bind address in smn_create_socket()

From: Chuck Lever <chuck.lever@oracle.com>

This patch updates the "bind to a user-specified port" arm of
smn_create_socket() so it can deal with IPv6 bind addresses.

A single getaddrinfo(3) call can convert a user-specified bind address
or hostname to a socket address, optionally plant a provided port
number, or whip up an appropriate wildcard address for use as the main
socket's bind address.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 utils/statd/sm-notify.c |   81 ++++++++++++++++++++++++++++++-----------------
 1 files changed, 52 insertions(+), 29 deletions(-)


diff --git a/utils/statd/sm-notify.c b/utils/statd/sm-notify.c
index 0069a99..9639e92 100644
--- a/utils/statd/sm-notify.c
+++ b/utils/statd/sm-notify.c
@@ -59,7 +59,7 @@ static int		opt_debug = 0;
 static int		opt_update_state = 1;
 static unsigned int	opt_max_retry = 15 * 60;
 static char *		opt_srcaddr = 0;
-static uint16_t		opt_srcport = 0;
+static char *		opt_srcport = 0;
 
 static void		notify(const int sock);
 static int		notify_host(int, struct nsm_host *);
@@ -218,6 +218,40 @@ static int smn_socket(void)
 }
 #endif	/* !IPV6_SUPPORTED */
  
+/*
+ * If admin specified a source address or srcport, then convert those
+ * to a sockaddr and return it.   Otherwise, return an ANYADDR address.
+ */
+static struct addrinfo *
+smn_bind_address(const char *srcaddr, char *srcport)
+{
+	struct addrinfo gai_hint = {
+		.ai_family	= nsm_family,
+ 		.ai_protocol	= IPPROTO_UDP,
+ 	};
+	struct addrinfo *gai_results;
+	int error;
+
+	if (srcaddr == NULL)
+		gai_hint.ai_flags |= AI_PASSIVE;
+	if (srcport == NULL)
+		srcport = "";
+ 
+	error = getaddrinfo(srcaddr, srcport, &gai_hint, &gai_results);
+	switch (error) {
+	case 0:
+		return gai_results;
+	case EAI_SYSTEM:
+		xlog(L_ERROR, "Failed to determine bind address: %m");
+		break;
+	default:
+		xlog(L_ERROR, "Failed to determine bind address: %s",
+				gai_strerror(error));
+	}
+
+	return NULL;
+}
+
 #ifdef HAVE_LIBTIRPC
 static int
 smn_bindresvport(int sock, struct sockaddr *sap)
@@ -245,62 +279,51 @@ smn_bindresvport(int sock, struct sockaddr *sap)
  * an error occurs.
  */
 static int
-smn_create_socket(const char *srcaddr, const uint16_t srcport)
+smn_create_socket(const char *srcaddr, char *srcport)
 {
-	struct sockaddr_storage address;
-	struct sockaddr *local_addr = (struct sockaddr *)&address;
 	int sock, retry_cnt = 0;
+	struct addrinfo *ai;
 
 retry:
 	sock = smn_socket();
 	if (sock < 0)
 		return -1;
 
-	memset(&address, 0, sizeof(address));
-	local_addr->sa_family = AF_INET;	/* Default to IPv4 */
-
-	/* Bind source IP if provided on command line */
-	if (srcaddr) {
-		struct addrinfo *ai = smn_lookup(srcaddr);
-		if (!ai) {
-			xlog(L_ERROR,
-				"Not a valid hostname or address: \"%s\"",
-				srcaddr);
-			return -1;
-		}
-
-		/* We know it's IPv4 at this point */
-		memcpy(local_addr, ai->ai_addr, ai->ai_addrlen);
-
-		freeaddrinfo(ai);
-	}
+	ai = smn_bind_address(srcaddr, srcport);
+	if (!ai)
+		return -1;
 
 	/* Use source port if provided on the command line,
 	 * otherwise use bindresvport */
 	if (srcport) {
-		nfs_set_port(local_addr, srcport);
-		if (bind(sock, local_addr, sizeof(struct sockaddr_in)) < 0) {
+		if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) {
 			xlog(L_ERROR, "Failed to bind RPC socket: %m");
-			return -1;
+			(void)close(sock);
+			sock = -1;
+			goto out;
 		}
 	} else {
 		struct servent *se;
 
-		if (smn_bindresvport(sock, local_addr) == -1) {
+		if (smn_bindresvport(sock, ai->ai_addr) == -1) {
 			xlog(L_ERROR, "bindresvport(3): %m");
 			(void)close(sock);
-			return -1;
+			sock = -1;
+			goto out;
 		}
 
 		/* try to avoid known ports */
-		se = getservbyport(nfs_get_port(local_addr), "udp");
+		se = getservbyport(nfs_get_port(ai->ai_addr), "udp");
 		if (se && retry_cnt < 100) {
 			retry_cnt++;
 			(void)close(sock);
+			freeaddrinfo(ai);
 			goto retry;
 		}
 	}
 
+out:
+	freeaddrinfo(ai);
 	return sock;
 }
 
@@ -332,7 +355,7 @@ main(int argc, char **argv)
 			opt_update_state = 0;
 			break;
 		case 'p':
-			opt_srcport = atoi(optarg);
+			opt_srcport = optarg;
 			break;
 		case 'v':
 			opt_srcaddr = optarg;
-------------------------------------------------------------------------------
statd-support-ipv6-dns-lookups
-------------------------------------------------------------------------------
statd: Support IPv6 DNS lookups in smn_lookup

From: Chuck Lever <chuck.lever@oracle.com>

When IPV6_SUPPORTED is enabled and the local system has IPv6 support,
request AF_INET6 and AF_INET addresses from the DNS resolver.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 utils/statd/sm-notify.c |   10 ++++++++--
 1 files changed, 8 insertions(+), 2 deletions(-)


diff --git a/utils/statd/sm-notify.c b/utils/statd/sm-notify.c
index 9639e92..4fda046 100644
--- a/utils/statd/sm-notify.c
+++ b/utils/statd/sm-notify.c
@@ -32,6 +32,10 @@
 #include "nsm.h"
 #include "nfsrpc.h"
 
+#ifndef HAVE_DECL_AI_ADDRCONFIG
+#define AI_ADDRCONFIG	0
+#endif
+
 #define NSM_PROG	100024
 #define NSM_PROGRAM	100024
 #define NSM_VERSION	1
@@ -73,10 +77,12 @@ static struct nsm_host *	hosts = NULL;
 static struct addrinfo *smn_lookup(const char *name)
 {
 	struct addrinfo	*ai, hint = {
-#if HAVE_DECL_AI_ADDRCONFIG
 		.ai_flags	= AI_ADDRCONFIG,
-#endif	/* HAVE_DECL_AI_ADDRCONFIG */
+#ifdef IPV6_SUPPORTED
+		.ai_family	= AF_UNSPEC,
+#else	/* !IPV6_SUPPORTED */
 		.ai_family	= AF_INET,
+#endif	/* !IPV6_SUPPORTED */
 		.ai_protocol	= IPPROTO_UDP,
 	};
 	int error;
-------------------------------------------------------------------------------
nfs-utils-add-helpers-for-addr
-------------------------------------------------------------------------------
nfs-utils: Add helpers for address comparison

From: Chuck Lever <chuck.lever@oracle.com>

Introduce inline helpers for comparing socket addresses.  I considered
adding these in a library, but these are small, and are significantly
more efficient when inline.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 support/include/compaddr.h |  107 ++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 107 insertions(+), 0 deletions(-)
 create mode 100644 support/include/compaddr.h


diff --git a/support/include/compaddr.h b/support/include/compaddr.h
new file mode 100644
index 0000000..7b0d03a
--- /dev/null
+++ b/support/include/compaddr.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2009 Oracle.  All rights reserved.
+ *
+ * This file is part of nfs-utils.
+ *
+ * nfs-utils is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * nfs-utils is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with nfs-utils.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __NFS_UTILS_COMPADDR_H
+#define __NFS_UTILS_COMPADDR_H
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#ifdef IPV6_SUPPORTED
+static inline socklen_t
+sockaddr_length(const struct sockaddr *sap)
+{
+	switch (sap->sa_family) {
+	case AF_INET:
+		return sizeof(struct sockaddr_in);
+	case AF_INET6:
+		return sizeof(struct sockaddr_in6);
+	}
+	return 0;
+}
+#else	/* !IPV6_SUPPORTED */
+static inline socklen_t
+sockaddr_length(const struct sockaddr *sap)
+{
+	if (sap->sa_family == AF_INET)
+		return sizeof(struct sockaddr_in);
+	return 0;
+}
+#endif	/* !IPV6_SUPPORTED */
+
+#ifdef IPV6_SUPPORTED
+static inline int
+compare_sockaddr6(const struct sockaddr *sa1, const struct sockaddr *sa2)
+{
+	const struct sockaddr_in6 *sin1 = (const struct sockaddr_in6 *)sa1;
+	const struct sockaddr_in6 *sin2 = (const struct sockaddr_in6 *)sa2;
+
+	if ((IN6_IS_ADDR_LINKLOCAL(&sin1->sin6_addr) &&
+	     IN6_IS_ADDR_LINKLOCAL(&sin2->sin6_addr)) ||
+	    (IN6_IS_ADDR_SITELOCAL(&sin1->sin6_addr) &&
+	     IN6_IS_ADDR_SITELOCAL(&sin2->sin6_addr)))
+		if (sin1->sin6_scope_id != sin2->sin6_scope_id)
+			return 0;
+
+	return IN6_ARE_ADDR_EQUAL(&sin1->sin6_addr, &sin2->sin6_addr);
+}
+#else	/* !IPV6_SUPPORTED */
+static inline int
+compare_sockaddr6(__attribute__((unused)) const struct sockaddr *sa1,
+		__attribute__((unused)) const struct sockaddr *sa2)
+{
+	return 0;
+}
+#endif	/* !IPV6_SUPPORTED */
+
+static inline int
+compare_sockaddr4(const struct sockaddr *sa1, const struct sockaddr *sa2)
+{
+	const struct sockaddr_in *sin1 = (const struct sockaddr_in *)sa1;
+	const struct sockaddr_in *sin2 = (const struct sockaddr_in *)sa2;
+	return sin1->sin_addr.s_addr == sin2->sin_addr.s_addr;
+}
+
+static inline int
+compare_sockaddr(const struct sockaddr *sa1, const struct sockaddr *sa2)
+{
+	if (sa1->sa_family == sa2->sa_family)
+		switch (sa1->sa_family) {
+		case AF_INET:
+			return compare_sockaddr4(sa1, sa2);
+		case AF_INET6:
+			return compare_sockaddr6(sa1, sa2);
+		}
+
+	return 0;
+}
+
+static inline int
+is_v4_loopback(const struct sockaddr *sap)
+{
+	const struct sockaddr_in *sin = (const struct sockaddr_in *)sap;
+
+	if (sin->sin_family != AF_INET)
+		return 0;
+	if (sin->sin_addr.s_addr != htonl(INADDR_LOOPBACK))
+		return 0;
+        return 1;
+}
+
+#endif	/* !__NFS_UTILS_COMPADDR_H */
-------------------------------------------------------------------------------
statd-introduce-statd-version-
-------------------------------------------------------------------------------
statd: Introduce statd version of matchhostname()

From: Chuck Lever <chuck.lever@oracle.com>

For the near future, statd will support IPv6 but exportfs will not.
Thus statd will need a version of matchhostname() that can deal
properly with IPv6 remotes.  To reduce the risk of breaking exportfs,
introduce a separate version of matchhostname() for statd to use while
exportfs continues to use the existing AF_INET-only implementation.

When IPv6 support is enabled, returned IPv4 addresses are mapped v4
AF_INET6 addresses, making the address list comparison logic simpler.
Support for link-local addresses is achieved by comparing full socket
addresses, not just the in_addr portion of each address, when
comparing AF_INET6 addresses for equality.

Using getaddrinfo(3) here means matchhostname() now supports
international domain name translation.  With versions of glibc newer
than 2.3, all incoming DNS labels are converted to ASCII.  Thus we
have a stronger guarantee that any of the returned canonical
hostnames, including both ASCII hostnames and IDNA labels, can be
compared properly using strcasecmp(3).

Note that statd will never send matchhostname() a hostname string
containing export wildcards, so is_hostame() is not needed in the
statd version of matchhostname().  This saves some computational
expense when comparing hostnames.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 utils/statd/Makefile.am |    5 +-
 utils/statd/callback.c  |    5 +-
 utils/statd/hostname.c  |  122 +++++++++++++++++++++++++++++++++++++++++++++++
 utils/statd/monitor.c   |    5 +-
 utils/statd/notlist.c   |    4 +-
 utils/statd/statd.h     |    2 -
 6 files changed, 131 insertions(+), 12 deletions(-)
 create mode 100644 utils/statd/hostname.c


diff --git a/utils/statd/Makefile.am b/utils/statd/Makefile.am
index d9731b7..a94c012 100644
--- a/utils/statd/Makefile.am
+++ b/utils/statd/Makefile.am
@@ -6,14 +6,13 @@ RPCPREFIX	= rpc.
 KPREFIX		= @kprefix@
 sbin_PROGRAMS	= statd sm-notify
 dist_sbin_SCRIPTS	= start-statd
-statd_SOURCES = callback.c notlist.c misc.c monitor.c \
+statd_SOURCES = callback.c notlist.c misc.c monitor.c hostname.c \
 	        simu.c stat.c statd.c svc_run.c rmtcall.c \
 	        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 \
+statd_LDADD = ../../support/nsm/libnsm.a \
 	      ../../support/nfs/libnfs.a \
 	      ../../support/misc/libmisc.a \
 	      $(LIBWRAP) $(LIBNSL)
diff --git a/utils/statd/callback.c b/utils/statd/callback.c
index 2f98aeb..56163d5 100644
--- a/utils/statd/callback.c
+++ b/utils/statd/callback.c
@@ -13,7 +13,6 @@
 #include <arpa/inet.h>
 
 #include "rpcmisc.h"
-#include "misc.h"
 #include "statd.h"
 #include "notlist.h"
 
@@ -52,8 +51,8 @@ sm_notify_1_svc(struct stat_chge *argp, struct svc_req *rqstp)
 	 */
 	for (lp = rtnl ; lp ; lp = lp->next)
 		if (NL_STATE(lp) != argp->state &&
-		    (matchhostname(argp->mon_name, lp->dns_name) ||
-		     matchhostname(ip_addr, lp->dns_name))) {
+		    (nsm_matchhostname(argp->mon_name, lp->dns_name) ||
+		     nsm_matchhostname(ip_addr, lp->dns_name))) {
 			NL_STATE(lp) = argp->state;
 			call = nlist_clone(lp);
 			nlist_insert(&notify, call);
diff --git a/utils/statd/hostname.c b/utils/statd/hostname.c
new file mode 100644
index 0000000..a0bbea3
--- /dev/null
+++ b/utils/statd/hostname.c
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2009 Oracle.  All rights reserved.
+ *
+ * This file is part of nfs-utils.
+ *
+ * nfs-utils is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * nfs-utils is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with nfs-utils.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * NSM for Linux.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <stdlib.h>
+#include <strings.h>
+#include <netdb.h>
+
+#include "statd.h"
+#include "xlog.h"
+#include "compaddr.h"
+
+#ifndef HAVE_DECL_AI_ADDRCONFIG
+#define AI_ADDRCONFIG	0
+#endif
+
+/*
+ * Look up the hostname; report exceptional errors.  Caller must
+ * call freeaddrinfo(3) if a valid addrinfo is returned.
+ */
+static struct addrinfo *
+get_addrinfo(const char *hostname, const struct addrinfo* gai_hint)
+{
+	struct addrinfo *gai_results;
+	int error;
+
+	error = getaddrinfo(hostname, NULL, gai_hint, &gai_results);
+	switch (error) {
+	case 0:
+		return gai_results;
+	case EAI_NONAME:
+		break;
+	default:
+		xlog(L_ERROR, "%s: failed to resolve host %s: %s",
+				__func__, hostname, gai_strerror(error));
+	}
+
+	return NULL;
+}
+
+/**
+ * nsm_matchhostname - check if two hostnames are equivalent
+ * @hostname1: C string containing hostname
+ * @hostname2: C string containing hostname
+ *
+ * Returns 1 if the hostnames are the same, the hostnames resolve
+ * to the same canonical name, or the hostnames resolve to at least
+ * one address that is the same.  Zero is returned if the hostnames
+ * do not match in any of these ways, if either hostname contains
+ * wildcard characters, if either hostname is a netgroup name, or
+ * if an error occurs.
+ */
+int
+nsm_matchhostname(const char *hostname1, const char *hostname2)
+{
+	struct addrinfo *ai1, *ai2, *gai_results1 = NULL, *gai_results2 = NULL;
+	static const struct addrinfo gai_hint = {
+#ifdef IPV6_SUPPORTED
+		.ai_family	= AF_INET6,
+		.ai_flags	= AI_ADDRCONFIG | AI_CANONNAME | AI_V4MAPPED,
+#else	/* !IPV6_SUPPORTED */
+		.ai_family	= AF_INET,
+		.ai_flags	= AI_ADDRCONFIG | AI_CANONNAME,
+#endif	/* !IPV6_SUPPORTED */
+		.ai_protocol	= IPPROTO_UDP,
+	};
+	int result = 0;
+
+	if (strcasecmp(hostname1, hostname2) == 0)
+		return 1;
+
+	gai_results1 = get_addrinfo(hostname1, &gai_hint);
+	if (gai_results1 == NULL)
+		goto out;
+	gai_results2 = get_addrinfo(hostname2, &gai_hint);
+	if (gai_results2 == NULL)
+		goto out;
+
+	if (strcasecmp(gai_results1->ai_canonname,
+				gai_results2->ai_canonname) == 0) {
+		result = 1;
+		goto out;
+	}
+
+	for (ai1 = gai_results1; ai1; ai1 = ai1->ai_next)
+		for (ai2 = gai_results2; ai2; ai2 = ai2->ai_next)
+			if (compare_sockaddr(ai1->ai_addr, ai2->ai_addr)) {
+				result = 1;
+				goto out;
+			}
+
+out:
+	freeaddrinfo(gai_results2);
+	freeaddrinfo(gai_results1);
+	return result;
+}
diff --git a/utils/statd/monitor.c b/utils/statd/monitor.c
index 8d9f663..a70b848 100644
--- a/utils/statd/monitor.c
+++ b/utils/statd/monitor.c
@@ -22,7 +22,6 @@
 #include <dirent.h>
 
 #include "rpcmisc.h"
-#include "misc.h"
 #include "nsm.h"
 #include "statd.h"
 #include "notlist.h"
@@ -145,7 +144,7 @@ sm_mon_1_svc(struct mon *argp, struct svc_req *rqstp)
 	clnt = rtnl;
 
 	while ((clnt = nlist_gethost(clnt, mon_name, 0))) {
-		if (matchhostname(NL_MY_NAME(clnt), my_name) &&
+		if (nsm_matchhostname(NL_MY_NAME(clnt), my_name) &&
 		    NL_MY_PROC(clnt) == id->my_proc &&
 		    NL_MY_PROG(clnt) == id->my_prog &&
 		    NL_MY_VERS(clnt) == id->my_vers &&
@@ -298,7 +297,7 @@ sm_unmon_1_svc(struct mon_id *argp, struct svc_req *rqstp)
 	 * entry winds up in the list the way I'm currently handling them.)
 	 */
 	while ((clnt = nlist_gethost(clnt, mon_name, 0))) {
-		if (matchhostname(NL_MY_NAME(clnt), my_name) &&
+		if (nsm_matchhostname(NL_MY_NAME(clnt), my_name) &&
 			NL_MY_PROC(clnt) == id->my_proc &&
 			NL_MY_PROG(clnt) == id->my_prog &&
 			NL_MY_VERS(clnt) == id->my_vers) {
diff --git a/utils/statd/notlist.c b/utils/statd/notlist.c
index 1698c26..0ae94c8 100644
--- a/utils/statd/notlist.c
+++ b/utils/statd/notlist.c
@@ -17,7 +17,6 @@
 #endif
 
 #include <string.h>
-#include "misc.h"
 #include "statd.h"
 #include "notlist.h"
 
@@ -234,7 +233,8 @@ nlist_gethost(notify_list *list, char *host, int myname)
 	notify_list	*lp;
 
 	for (lp = list; lp; lp = lp->next) {
-		if (matchhostname(host, myname? NL_MY_NAME(lp) : NL_MON_NAME(lp)))
+		if (nsm_matchhostname(host,
+					myname? NL_MY_NAME(lp) : NL_MON_NAME(lp)))
 			return lp;
 	}
 
diff --git a/utils/statd/statd.h b/utils/statd/statd.h
index 542a877..c53b70a 100644
--- a/utils/statd/statd.h
+++ b/utils/statd/statd.h
@@ -22,7 +22,7 @@
 /*
  * Function prototypes.
  */
-extern void	change_state(void);
+extern int	nsm_matchhostname(const char *hostname1, const char *hostname2);
 extern void	my_svc_run(void);
 extern void	notify_hosts(void);
 extern void	shuffle_dirs(void);
-------------------------------------------------------------------------------
statd-add-nsm_present_address-
-------------------------------------------------------------------------------
statd: add nsm_present_address() API

From: Chuck Lever <chuck.lever@oracle.com>

Add an API to convert a socket addres to a presentation address
string.  This is used for displaying error messages and the like.

We prefer getnameinfo(3) over inet_?to?(3) as it supports IPv6 scope
IDs.  Since statd has to build on systems whose glibc does not have
getnameinfo(3), an inet_?to?(3) version is also provided.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 utils/statd/hostname.c |   68 ++++++++++++++++++++++++++++++++++++++++++++++++
 utils/statd/statd.h    |    2 +
 2 files changed, 70 insertions(+), 0 deletions(-)


diff --git a/utils/statd/hostname.c b/utils/statd/hostname.c
index a0bbea3..ba65acd 100644
--- a/utils/statd/hostname.c
+++ b/utils/statd/hostname.c
@@ -29,6 +29,7 @@
 #include <sys/socket.h>
 
 #include <stdlib.h>
+#include <string.h>
 #include <strings.h>
 #include <netdb.h>
 
@@ -40,6 +41,73 @@
 #define AI_ADDRCONFIG	0
 #endif
 
+/**
+ * nsm_present_address - convert sockaddr to presentation address
+ * @sap: pointer to socket address to convert
+ * @salen: length of socket address
+ * @buf: pointer to buffer to fill in
+ * @buflen: length of buffer
+ *
+ * Convert the passed-in sockaddr-style address to presentation format.
+ * The presentation format address is placed in @buf and is
+ * '\0'-terminated.  Callers who don't know the size of the socket
+ * address can pass a zero.
+ *
+ * Returns 1 if successful; otherwise zero.
+ *
+ * getnameinfo(3) is preferred, since it can parse IPv6 scope IDs.
+ * An alternate version of present_address() is available to deal with
+ * older glibcs that do not have getnameinfo(3).
+ */
+#ifdef IPV6_SUPPORTED
+int
+nsm_present_address(const struct sockaddr *sap, socklen_t salen,
+		char *buf, const size_t buflen)
+{
+	int error;
+
+	if (salen == 0) {
+		salen = sockaddr_length(sap);
+		if (salen == 0) {
+			xlog(L_ERROR, "%s: Unsupported address family",
+					__func__);
+			return 0;
+		}
+	}
+
+	error = getnameinfo(sap, salen, buf, buflen, NULL, 0, NI_NUMERICHOST);
+	if (error) {
+		xlog(L_ERROR, "%s: getnameinfo: %s",
+				__func__, gai_strerror(error));
+		return 0;
+	}
+
+	return 1;
+}
+
+#else	/* !IPV6_SUPPORTED */
+
+#include <arpa/inet.h>
+
+int
+nsm_present_address(const struct sockaddr *sap,
+		__attribute__((unused)) socklen_t salen,
+		char *buf, const size_t buflen)
+{
+	const struct sockaddr_in *sin = (const struct sockaddr_in *)sap;
+
+	if (sin->sin_family != AF_INET) {
+		xlog(L_ERROR, "%s: Unsupported address family", __func__);
+		return 0;
+	}
+
+	memset(buf, 0, buflen);
+	if (!inet_ntop(AF_INET, (char *)&sin->sin_addr, buf, buflen))
+		return 0;
+	return 1;
+}
+#endif	/* !IPV6_SUPPORTED */
+
 /*
  * Look up the hostname; report exceptional errors.  Caller must
  * call freeaddrinfo(3) if a valid addrinfo is returned.
diff --git a/utils/statd/statd.h b/utils/statd/statd.h
index c53b70a..71a77e2 100644
--- a/utils/statd/statd.h
+++ b/utils/statd/statd.h
@@ -23,6 +23,8 @@
  * Function prototypes.
  */
 extern int	nsm_matchhostname(const char *hostname1, const char *hostname2);
+extern int	nsm_present_address(const struct sockaddr *sap, socklen_t salen,
+					char *buf, const size_t buflen);
 extern void	my_svc_run(void);
 extern void	notify_hosts(void);
 extern void	shuffle_dirs(void);
-------------------------------------------------------------------------------
statd-add-ipv6-support-in-sm_n
-------------------------------------------------------------------------------
statd: add IPv6 support in sm_notify_1_svc()

From: Chuck Lever <chuck.lever@oracle.com>

We have all the pieces in place, so update sm_notify_1_svc() to handle
SM_NOTIFY requests sent from IPv6 remotes.

This also eliminates a memory leak: the strdup'd memory containing the
callers' presentation address was never freed.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 utils/statd/callback.c |   67 ++++++++++++++++++++++++++++++++++++++++++++----
 1 files changed, 61 insertions(+), 6 deletions(-)


diff --git a/utils/statd/callback.c b/utils/statd/callback.c
index 56163d5..1a4b800 100644
--- a/utils/statd/callback.c
+++ b/utils/statd/callback.c
@@ -10,7 +10,7 @@
 #include <config.h>
 #endif
 
-#include <arpa/inet.h>
+#include <netdb.h>
 
 #include "rpcmisc.h"
 #include "statd.h"
@@ -22,17 +22,67 @@
 
 /* 
  * Services SM_NOTIFY requests.
- * Any clients that have asked us to monitor that host are put on
- * the global callback list, which is processed as soon as statd
- * returns to svc_run.
+ *
+ * When NLM uses an SM_MON request to tell statd to monitor a remote,
+ * the request contains a "mon_name" argument.  This is usually the
+ * "caller_name" argument of an NLMPROC_LOCK request.  On Linux, the
+ * NLM can send statd the remote's IP address instead of its
+ * caller_name.  The NSM protocol does not allow both the remote's
+ * caller_name and it's IP address to be sent in the same SM_MON
+ * request.
+ *
+ * The remote's caller_name is useful because it makes it simple
+ * to identify rebooting remotes by matching the "mon_name" argument
+ * they sent via an SM_NOTIFY request.
+ *
+ * The caller_name string may not be a fully qualified domain name,
+ * or even registered in the DNS database, however.  Having the
+ * remote's IP address is useful because then there is no ambiguity
+ * about where to send an SM_NOTIFY after the local system reboots.
+ *
+ * Without the actual caller_name, however, statd must use an
+ * heuristic to match an incoming SM_NOTIFY request to one of the
+ * hosts it is currently monitoring.  The incoming mon_name in an
+ * SM_NOTIFY address is converted to a list of IP addresses using
+ * DNS.  Each mon_name on statd's monitor list is also converted to
+ * an address list, and the two lists are checked to see if there is
+ * a matching address.
+ *
+ * There are some risks to this strategy:
+ *
+ *   1.  The external DNS database is not reliable.  It can change
+ *       over time, or the forward and reverse mappings could be
+ *       inconsistent.
+ *
+ *   2.  Local DNS resolution can produce different results for the
+ *       mon_name than the results the remote might see for the same
+ *       query, especially if the remote did not send a caller_name
+ *       or mon_name that is a fully qualified domain name.
+ *
+ *   3.  If the remote does not have a DNS entry at all (or if the
+ *       remote can resolve itself, but the local host can't resolve
+ *       the remote's hostname), the remote cannot be monitored, and
+ *       therefore NLM locking cannot be provided for that host.
+ *
+ *   4.  If statd's monitor list becomes substantial, finding a match
+ *       generates a not inconsequential amount of DNS traffic.
+ *
+ *   5.  statd is a single-threaded service.  When DNS becomes slow or
+ *       unresponsive, statd also becomes slow or unresponsive.
+ *
+ * Note that the caller_name is passed from NFS client to server, but the
+ * client never knows what mon_name the server might use to notify it of
+ * a reboot.  On Linux, the client extracts the server's name from the
+ * devname it was passed by the mount command.  This is often not a
+ * fully-qualified domain name.
  */
 void *
 sm_notify_1_svc(struct stat_chge *argp, struct svc_req *rqstp)
 {
 	notify_list    *lp, *call;
 	static char    *result = NULL;
-	struct sockaddr_in *sin = nfs_getrpccaller_in(rqstp->rq_xprt);
-	char *ip_addr = xstrdup(inet_ntoa(sin->sin_addr));
+	struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt);
+	static char	ip_addr[NI_MAXHOST];
 
 	xlog(D_CALL, "Received SM_NOTIFY from %s, state: %d",
 				argp->mon_name, argp->state);
@@ -44,6 +94,11 @@ sm_notify_1_svc(struct stat_chge *argp, struct svc_req *rqstp)
 		return ((void *) &result);
 	}
 
+	if (!nsm_present_address(sap, 0, ip_addr, sizeof(ip_addr))) {
+		xlog_warn("Unrecognized sender address");
+		return (void *)&result;
+	}
+
 	/* okir change: statd doesn't remove the remote host from its
 	 * internal monitor list when receiving an SM_NOTIFY call from
 	 * it. Lockd will want to continue monitoring the remote host
-------------------------------------------------------------------------------
statd-support-ipv6-is-caller_i
-------------------------------------------------------------------------------
statd: Support IPv6 is caller_is_localhost()

From: Chuck Lever <chuck.lever@oracle.com>

For now statd is not going to support NLM upcalls and downcalls on
IPv6 transports.

However, the upcalls (SM_MON, etc.) arrive on the same socket that
receives calls from remotes.  So caller_is_localhost() at least has
to be smart enough to notice that the caller is not AF_INET, and to
display non-AF_INET addresses appropriately.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 utils/statd/monitor.c |   23 +++++++++++++++--------
 1 files changed, 15 insertions(+), 8 deletions(-)


diff --git a/utils/statd/monitor.c b/utils/statd/monitor.c
index a70b848..7b6c84b 100644
--- a/utils/statd/monitor.c
+++ b/utils/statd/monitor.c
@@ -26,26 +26,33 @@
 #include "statd.h"
 #include "notlist.h"
 #include "ha-callout.h"
+#include "compaddr.h"
 
 notify_list *		rtnl = NULL;	/* Run-time notify list. */
 
 /*
  * Reject requests from non-loopback addresses in order
  * to prevent attack described in CERT CA-99.05.
+ *
+ * Although the kernel contacts statd only via IPv4 transports,
+ * the statd service can receive other requests, such as
+ * SM_NOTIFY, via IPv6.
  */
 static int
 caller_is_localhost(struct svc_req *rqstp)
 {
-	struct sockaddr_in *sin = nfs_getrpccaller_in(rqstp->rq_xprt);
-	struct in_addr	caller;
+	struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt);
+	static char buf[NI_MAXHOST];
 
-	caller = sin->sin_addr;
-	if (caller.s_addr != htonl(INADDR_LOOPBACK)) {
-		xlog_warn("Call to statd from non-local host %s",
-			inet_ntoa(caller));
-		return 0;
-	}
+	if (!is_v4_loopback(sap))
+		goto out_nonlocal;
 	return 1;
+
+out_nonlocal:
+	if (!nsm_present_address(sap, 0, buf, sizeof(buf)))
+		buf[0] = '\0';
+	xlog_warn("SM_MON/SM_UNMON call from non-local host %s", buf);
+	return 0;
 }
 
 /*
-------------------------------------------------------------------------------
statd-support-ipv6-in-sm_simu_
-------------------------------------------------------------------------------
statd: Support IPv6 in sm_simu_crash_1_svc

From: Chuck Lever <chuck.lever@oracle.com>

SM_SIMU_CRASH is not used by the Linux NLM, but for consistency, let's
make similar changes as in utils/statd/monitor.c:caller_is_localhost().

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 utils/statd/simu.c |   36 ++++++++++++++++++++----------------
 1 files changed, 20 insertions(+), 16 deletions(-)


diff --git a/utils/statd/simu.c b/utils/statd/simu.c
index 7df04d9..f3f9569 100644
--- a/utils/statd/simu.c
+++ b/utils/statd/simu.c
@@ -8,41 +8,39 @@
 #include <config.h>
 #endif
 
+#include <netdb.h>
 #include <arpa/inet.h>
 
 #include "rpcmisc.h"
 #include "statd.h"
 #include "notlist.h"
+#include "nfsrpc.h"
+#include "compaddr.h"
 
 extern void my_svc_exit (void);
 
 
 /*
  * Services SM_SIMU_CRASH requests.
+ *
+ * Although the local NLM contacts statd only via IPv4 transports,
+ * the statd service can receive other requests, such as
+ * SM_NOTIFY, via IPv6.
  */
 void *
-sm_simu_crash_1_svc (void *argp, struct svc_req *rqstp)
+sm_simu_crash_1_svc (__attribute__((unused)) void *argp, struct svc_req *rqstp)
 {
-  struct sockaddr_in *sin = nfs_getrpccaller_in(rqstp->rq_xprt);
+  struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt);
+  static char buf[NI_MAXHOST];
   static char *result = NULL;
-  struct in_addr caller;
 
   xlog(D_CALL, "Received SM_SIMU_CRASH");
 
-  if (sin->sin_family != AF_INET) {
-    xlog_warn("Call to statd from non-AF_INET address");
-    goto failure;
-  }
+  if (!is_v4_loopback(sap))
+    goto out_nonlocal;
 
-  caller = sin->sin_addr;
-  if (caller.s_addr != htonl(INADDR_LOOPBACK)) {
-    xlog_warn("Call to statd from non-local host %s",
-      inet_ntoa(caller));
-    goto failure;
-  }
-
-  if (ntohs(sin->sin_port) >= 1024) {
-    xlog_warn("Call to statd-simu-crash from unprivileged port");
+  if (nfs_get_port(sap) >= IPPORT_RESERVED) {
+    xlog_warn("SM_SIMU_CRASH call from unprivileged port");
     goto failure;
   }
 
@@ -54,4 +52,10 @@ sm_simu_crash_1_svc (void *argp, struct svc_req *rqstp)
 
  failure:
   return ((void *)&result);
+
+ out_nonlocal:
+  if (!nsm_present_address(sap, 0, buf, sizeof(buf)))
+    buf[0] = '\0';
+  xlog_warn("SM_SIMU_CRASH call from non-local host %s", buf);
+  goto failure;
 }
-------------------------------------------------------------------------------
statd-support-ipv6-in-sm_mon_1
-------------------------------------------------------------------------------
statd: Support IPv6 in sm_mon_1_svc()

From: Chuck Lever <chuck.lever@oracle.com>

Replace deprecated gethostbyname(3) and gethostbyaddr(3) calls.  Also
address a couple of memory leaks.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 utils/statd/hostname.c |   27 +++++++++++++++++++++++++++
 utils/statd/monitor.c  |   31 ++++++++++++++++++-------------
 utils/statd/statd.h    |    2 ++
 3 files changed, 47 insertions(+), 13 deletions(-)


diff --git a/utils/statd/hostname.c b/utils/statd/hostname.c
index ba65acd..6a6fb58 100644
--- a/utils/statd/hostname.c
+++ b/utils/statd/hostname.c
@@ -133,6 +133,33 @@ get_addrinfo(const char *hostname, const struct addrinfo* gai_hint)
 }
 
 /**
+ * nsm_forward_lookup - hostname or presentation address to sockaddr list
+ * @hostname: C string containing hostname or presentation address
+ *
+ * Returns addrinfo list, including the host's canonical DNS name,
+ * or NULL if an error occurs.  Caller must free addrinfo list via
+ * freeaddrinfo(3).
+ *
+ * AI_ADDRCONFIG should prevent us from monitoring a host that we can't
+ * reach.  If IPv6 is not enabled on this system, then we don't want to
+ * monitor remote hosts that have only IPv6 addresses.
+ */
+struct addrinfo *
+nsm_forward_lookup(const char *hostname)
+{
+	static const struct addrinfo gai_hint = {
+#ifdef IPV6_SUPPORTED
+		.ai_family	= AF_UNSPEC,
+#else	/* !IPV6_SUPPORTED */
+		.ai_family	= AF_INET,
+#endif	/* !IPV6_SUPPORTED */
+		.ai_flags	= AI_CANONNAME | AI_ADDRCONFIG
+	};
+
+	return get_addrinfo(hostname, &gai_hint);
+}
+
+/**
  * nsm_matchhostname - check if two hostnames are equivalent
  * @hostname1: C string containing hostname
  * @hostname2: C string containing hostname
diff --git a/utils/statd/monitor.c b/utils/statd/monitor.c
index 7b6c84b..fb74aba 100644
--- a/utils/statd/monitor.c
+++ b/utils/statd/monitor.c
@@ -71,8 +71,8 @@ sm_mon_1_svc(struct mon *argp, struct svc_req *rqstp)
 		.sin_family		= AF_INET,
 		.sin_addr.s_addr	= htonl(INADDR_LOOPBACK),
 	};
-	char		*dnsname;
-	struct hostent	*hostinfo = NULL;
+	struct addrinfo *gai_results;
+	char *dnsname = NULL;
 
 	xlog(D_CALL, "Received SM_MON for %s from %s", mon_name, my_name);
 
@@ -114,9 +114,6 @@ sm_mon_1_svc(struct mon *argp, struct svc_req *rqstp)
 		     "or starting '.': %s", mon_name);
 		xlog(L_ERROR, "POSSIBLE SPOOF/ATTACK ATTEMPT!");
 		goto failure;
-	} else if ((hostinfo = gethostbyname(mon_name)) == NULL) {
-		xlog_warn("gethostbyname error for %s", mon_name);
-		goto failure;
 	}
 
 	/* my_name must not have white space */
@@ -129,15 +126,21 @@ sm_mon_1_svc(struct mon *argp, struct svc_req *rqstp)
 	 * Now choose a hostname to use for matching.  We cannot
 	 * really trust much in the incoming NOTIFY, so to make
 	 * sure that multi-homed hosts work nicely, we get an
-	 * FQDN now, and use that for matching
+	 * FQDN now, and use that for matching.  If the kernel
+	 * handed us an IP presentation address, getaddrinfo(3)
+	 * copies that into ai_canonname.
 	 */
-	hostinfo = gethostbyaddr(hostinfo->h_addr,
-				 hostinfo->h_length,
-				 hostinfo->h_addrtype);
-	if (hostinfo)
-		dnsname = xstrdup(hostinfo->h_name);
-	else
-		dnsname = xstrdup(my_name);
+	gai_results = nsm_forward_lookup(mon_name);
+	if (gai_results == NULL) {
+		xlog(L_WARNING, "No canonical hostname found for %s", mon_name);
+		goto failure;
+	}
+	dnsname = strdup(gai_results->ai_canonname);
+	freeaddrinfo(gai_results);
+	if (dnsname == NULL) {
+		xlog(L_ERROR, "Failed to allocate memory");
+		goto failure;
+	}
 
 	/* Now check to see if this is a duplicate, and warn if so.
 	 * I will also return STAT_FAIL. (I *think* this is how I should
@@ -163,6 +166,7 @@ sm_mon_1_svc(struct mon *argp, struct svc_req *rqstp)
 				mon_name, my_name);
 
 			/* But we'll let you pass anyway. */
+			free(dnsname);
 			goto success;
 		}
 		clnt = NL_NEXT(clnt);
@@ -173,6 +177,7 @@ sm_mon_1_svc(struct mon *argp, struct svc_req *rqstp)
 	 * doesn't fail.  (I should probably fix this assumption.)
 	 */
 	if (!(clnt = nlist_new(my_name, mon_name, 0))) {
+		free(dnsname);
 		xlog_warn("out of memory");
 		goto failure;
 	}
diff --git a/utils/statd/statd.h b/utils/statd/statd.h
index 71a77e2..5ee3ea6 100644
--- a/utils/statd/statd.h
+++ b/utils/statd/statd.h
@@ -25,6 +25,8 @@
 extern int	nsm_matchhostname(const char *hostname1, const char *hostname2);
 extern int	nsm_present_address(const struct sockaddr *sap, socklen_t salen,
 					char *buf, const size_t buflen);
+extern struct addrinfo *
+		nsm_forward_lookup(const char *hostname);
 extern void	my_svc_run(void);
 extern void	notify_hosts(void);
 extern void	shuffle_dirs(void);
-------------------------------------------------------------------------------
statd-support-ipv6-in-sm_stat_
-------------------------------------------------------------------------------
statd: Support IPv6 in sm_stat_1_svc()

From: Chuck Lever <chuck.lever@oracle.com>

SM_STAT is usually not used by most contemporary NSM implementations,
but for consistency, it gets the same treatment as sm_mon_1_svc(),
since both should use the same logic to determine whether a mon_name
is able to be monitored.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 utils/statd/stat.c |    8 +++++---
 1 files changed, 5 insertions(+), 3 deletions(-)


diff --git a/utils/statd/stat.c b/utils/statd/stat.c
index 477f632..14caa16 100644
--- a/utils/statd/stat.c
+++ b/utils/statd/stat.c
@@ -38,19 +38,21 @@
  *   3/ That's what we always did in the past.
  */
 struct sm_stat_res * 
-sm_stat_1_svc (struct sm_name *argp, struct svc_req *rqstp)
+sm_stat_1_svc(struct sm_name *argp, __attribute__((unused)) struct svc_req *rqstp)
 {
   static sm_stat_res result;
+  struct addrinfo *ai;
 
   xlog(D_CALL, "Received SM_STAT from %s", argp->mon_name);
 
-  if (gethostbyname (argp->mon_name) == NULL) {
-    xlog_warn ("gethostbyname error for %s", argp->mon_name);
+  ai = nsm_forward_lookup(argp->mon_name);
+  if (ai == NULL) {
     result.res_stat = STAT_FAIL;
     xlog (D_GENERAL, "STAT_FAIL for %s", argp->mon_name);
   } else {
     result.res_stat = STAT_SUCC;
     xlog (D_GENERAL, "STAT_SUCC for %s", argp->mon_name);
+    freeaddrinfo(ai);
   }
   result.state = MY_STATE;
   return(&result);
-------------------------------------------------------------------------------
libnsm-a-retain-cap_net_bind-w
-------------------------------------------------------------------------------
libnsm.a: retain CAP_NET_BIND when dropping privileges

From: Chuck Lever <chuck.lever@oracle.com>

I'm about to switch the order of listener creation and dropping root
privileges.  rpc.statd will drop privileges first, then create its
listeners.  The reason for the new ordering is explained in a
subsequent patch.

However, for non-TI-RPC builds, rpc_init() needs to use a privileged
port to do pmap registrations.  For both TI-RPC and non-TI-RPC builds,
CAP_NET_BIND is required in case the user requested a privileged
listener port.

So that these requirements are met, nsm_drop_privileges() will now
retain CAP_NET_BIND while dropping root.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 aclocal/libcap.m4       |   15 +++++++++++++++
 configure.ac            |    3 +++
 support/nsm/file.c      |   44 +++++++++++++++++++++++++++++++++++++++++++-
 utils/statd/Makefile.am |    4 ++--
 4 files changed, 63 insertions(+), 3 deletions(-)
 create mode 100644 aclocal/libcap.m4


diff --git a/aclocal/libcap.m4 b/aclocal/libcap.m4
new file mode 100644
index 0000000..eabe507
--- /dev/null
+++ b/aclocal/libcap.m4
@@ -0,0 +1,15 @@
+dnl Checks for libcap.so
+dnl
+AC_DEFUN([AC_LIBCAP], [
+
+  dnl look for prctl
+  AC_CHECK_FUNC([prctl], , )
+
+  dnl look for the library; do not add to LIBS if found
+  AC_CHECK_LIB([cap], [cap_get_proc], [LIBCAP=-lcap], ,)
+  AC_SUBST(LIBCAP)
+
+  AC_CHECK_HEADERS([sys/capability.h], ,
+                   [AC_MSG_ERROR([libcap headers not found.])])
+
+])dnl
diff --git a/configure.ac b/configure.ac
index 1c79b21..8f979b9 100644
--- a/configure.ac
+++ b/configure.ac
@@ -166,6 +166,9 @@ fi
 dnl Check for TI-RPC library and headers
 AC_LIBTIRPC
 
+dnl Check for -lcap
+AC_LIBCAP
+
 # Check whether user wants TCP wrappers support
 AC_TCP_WRAPPERS
 
diff --git a/support/nsm/file.c b/support/nsm/file.c
index 77ab073..126a706 100644
--- a/support/nsm/file.c
+++ b/support/nsm/file.c
@@ -67,6 +67,8 @@
 #endif
 
 #include <sys/types.h>
+#include <sys/capability.h>
+#include <sys/prctl.h>
 #include <sys/stat.h>
 
 #include <ctype.h>
@@ -237,6 +239,37 @@ nsm_is_default_parentdir(void)
 	return strcmp(nsm_base_dirname, NSM_DEFAULT_STATEDIR) == 0;
 }
 
+/*
+ * Clear all capabilities but CAP_NET_BIND_SERVICE.  This permits
+ * callers to acquire privileged source ports, but all other root
+ * capabilities are disallowed.
+ */
+static int
+statd_clear_capabilities(void)
+{
+	bool_t result;
+	cap_t caps;
+
+	result = 0;
+
+	caps = cap_from_text("cap_net_bind_service=ep");
+	if (caps == NULL) {
+		xlog(L_ERROR, "Failed to allocate working storage: %m");
+		return result;
+	}
+
+	if (cap_set_proc(caps) == -1) {
+		xlog(L_ERROR, "Failed to set capability flags: %m");
+		goto out_free;
+	}
+
+	result = 1;
+
+out_free:
+	(void)cap_free(caps);
+	return result;
+}
+
 /**
  * nsm_drop_privileges - drop root privileges
  * @pidfd: file descriptor of a pid file
@@ -284,6 +317,14 @@ nsm_drop_privileges(const int pidfd)
 		if (fchown(pidfd, st.st_uid, st.st_gid) == -1)
 			xlog_warn("Failed to change owner of pidfile: %m");
 
+	/*
+	 * Don't clear capabilities when dropping root.
+	 */
+        if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == -1) {
+                xlog(L_ERROR, "prctl(PR_SET_KEEPCAPS) failed: %m");
+		return 0;
+	}
+
 	if (setgroups(0, NULL) == -1) {
 		xlog(L_ERROR, "Failed to drop supplementary groups: %m");
 		return 0;
@@ -301,7 +342,8 @@ nsm_drop_privileges(const int pidfd)
 	}
 
 	xlog(D_CALL, "Effective UID, GID: %u, %u", st.st_uid, st.st_gid);
-	return 1;
+
+	return statd_clear_capabilities();
 }
 
 /**
diff --git a/utils/statd/Makefile.am b/utils/statd/Makefile.am
index a94c012..1744791 100644
--- a/utils/statd/Makefile.am
+++ b/utils/statd/Makefile.am
@@ -15,10 +15,10 @@ BUILT_SOURCES = $(GENFILES)
 statd_LDADD = ../../support/nsm/libnsm.a \
 	      ../../support/nfs/libnfs.a \
 	      ../../support/misc/libmisc.a \
-	      $(LIBWRAP) $(LIBNSL)
+	      $(LIBWRAP) $(LIBNSL) $(LIBCAP)
 sm_notify_LDADD = ../../support/nsm/libnsm.a \
 		  ../../support/nfs/libnfs.a \
-		  $(LIBNSL)
+		  $(LIBNSL) $(LIBCAP)
 
 EXTRA_DIST = sim_sm_inter.x $(man8_MANS) COPYRIGHT simulate.c
 
-------------------------------------------------------------------------------
statd-support-ti-rpc-statd-lis
-------------------------------------------------------------------------------
statd: Support TI-RPC statd listener

From: Chuck Lever <chuck.lever@oracle.com>

If TI-RPC is available, use it to create statd's svc listener.  If
not, use the old function, rpc_init(), to create statd's listener.

IPv6 can be supported if TI-RPC is available.  In this case,
/etc/netconfig is searched to determine which transports to advertise.

Add the new listener creation API in libnfs.a since other components
of nfs-utils (such as rpc.mountd) will eventually want to share it.

A little re-arrangement of when the statd listener is created is done
to make unregistration of the statd service more reliable.  As it is
now, the statd service is never unregistered when it exits.  After it
is gone, other programs usually hang when trying to access statd or
see if it's running, since the registration is still there but statd
itself does not respond.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 support/include/rpcmisc.h |    7 +
 support/nfs/Makefile.am   |    3 -
 support/nfs/svc_create.c  |  252 +++++++++++++++++++++++++++++++++++++++++++++
 utils/statd/statd.c       |   38 ++++++-
 4 files changed, 291 insertions(+), 9 deletions(-)
 create mode 100644 support/nfs/svc_create.c


diff --git a/support/include/rpcmisc.h b/support/include/rpcmisc.h
index f551a85..1b8f411 100644
--- a/support/include/rpcmisc.h
+++ b/support/include/rpcmisc.h
@@ -41,7 +41,12 @@ struct rpc_dtable {
 		(xdrproc_t)xdr_##res_type, sizeof(res_type), \
 	}
 
-
+void		nfs_svc_unregister(const rpcprog_t program,
+				const rpcvers_t version);
+unsigned int	nfs_svc_create(char *name, const rpcprog_t program,
+				const rpcvers_t version,
+				void (*dispatch)(struct svc_req *, SVCXPRT *),
+				const uint16_t port);
 void		rpc_init(char *name, int prog, int vers,
 				void (*dispatch)(struct svc_req *, SVCXPRT *),
 				int defport);
diff --git a/support/nfs/Makefile.am b/support/nfs/Makefile.am
index e9462fc..60400b2 100644
--- a/support/nfs/Makefile.am
+++ b/support/nfs/Makefile.am
@@ -4,7 +4,8 @@ noinst_LIBRARIES = libnfs.a
 libnfs_a_SOURCES = exports.c rmtab.c xio.c rpcmisc.c rpcdispatch.c \
 		   xlog.c xcommon.c wildmat.c nfsclient.c \
 		   nfsexport.c getfh.c nfsctl.c rpc_socket.c getport.c \
-		   svc_socket.c cacheio.c closeall.c nfs_mntent.c conffile.c
+		   svc_socket.c cacheio.c closeall.c nfs_mntent.c conffile.c \
+		   svc_create.c
 
 MAINTAINERCLEANFILES = Makefile.in
 
diff --git a/support/nfs/svc_create.c b/support/nfs/svc_create.c
new file mode 100644
index 0000000..5af7724
--- /dev/null
+++ b/support/nfs/svc_create.c
@@ -0,0 +1,252 @@
+/*
+ * Copyright 2009 Oracle.  All rights reserved.
+ *
+ * This file is part of nfs-utils.
+ *
+ * nfs-utils is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * nfs-utils is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with nfs-utils.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * NSM for Linux.
+ *
+ * Convert incoming NSM RPC requests into local function calls.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <memory.h>
+#include <signal.h>
+#include <unistd.h>
+#include <netdb.h>
+
+#include <netinet/in.h>
+
+#include <sys/socket.h>
+#include <sys/resource.h>
+
+#include <rpc/rpc.h>
+#include <rpc/svc.h>
+
+#ifdef HAVE_TCP_WRAPPER
+#include "tcpwrapper.h"
+#endif
+
+#include "rpcmisc.h"
+#include "xlog.h"
+
+#ifdef HAVE_LIBTIRPC
+
+/*
+ * Set up an appropriate bind address, given @port and @nconf.
+ *
+ * Returns getaddrinfo(3) results if successful.  Caller must
+ * invoke freeaddrinfo(3) on these results.
+ *
+ * Otherwise NULL is returned if an error occurs.
+ */
+static struct addrinfo *
+svc_create_bindaddr(struct netconfig *nconf, const uint16_t port)
+{
+	struct addrinfo gai_hint = {
+		.ai_flags	= AI_PASSIVE | AI_NUMERICSERV,
+	};
+	struct addrinfo *gai_results;
+	char buf[8];
+	int error;
+
+	if (strcmp(nconf->nc_protofmly, NC_INET) == 0)
+		gai_hint.ai_family = AF_INET;
+#ifdef IPV6_SUPPORTED
+	else if (strcmp(nconf->nc_protofmly, NC_INET6) == 0)
+		gai_hint.ai_family = AF_INET6;
+#endif	/* IPV6_SUPPORTED */
+	else {
+		xlog(L_ERROR, "Unrecognized bind address family: %s",
+			nconf->nc_protofmly);
+		return NULL;
+	}
+
+	if (strcmp(nconf->nc_proto, NC_UDP) == 0)
+		gai_hint.ai_protocol = IPPROTO_UDP;
+	else if (strcmp(nconf->nc_proto, NC_TCP) == 0)
+		gai_hint.ai_protocol = IPPROTO_TCP;
+	else {
+		xlog(L_ERROR, "Unrecognized bind address protocol: %s",
+			nconf->nc_proto);
+		return NULL;
+	}
+
+	(void)snprintf(buf, sizeof(buf), "%u", port);
+	error = getaddrinfo(NULL, buf, &gai_hint, &gai_results);
+	switch (error) {
+	case 0:
+		return gai_results;
+	case EAI_SYSTEM:
+		xlog(L_ERROR, "Failed to construct bind address: %m");
+		break;
+	default:
+		xlog(L_ERROR, "Failed to construct bind address: %s",
+			gai_strerror(error));
+		break;
+	}
+
+	return NULL;
+}
+
+static int
+svc_create_nconf(const char *name, const rpcprog_t program,
+		const rpcvers_t version,
+		void (*dispatch)(struct svc_req *, SVCXPRT *),
+		const uint16_t port, struct netconfig *nconf)
+{
+	struct addrinfo *gai_results;
+	struct t_bind bindaddr;
+	SVCXPRT	*xprt;
+
+	gai_results = svc_create_bindaddr(nconf, port);
+	if (gai_results == NULL)
+		return 0;
+
+	bindaddr.addr.buf = gai_results->ai_addr;
+	bindaddr.qlen = SOMAXCONN;
+
+	xprt = svc_tli_create(RPC_ANYFD, nconf, &bindaddr, 0, 0);
+	freeaddrinfo(gai_results);
+	if (xprt == NULL) {
+		xlog(D_GENERAL, "Failed to create NSM xprt");
+		return 0;
+	}
+
+	if (!svc_reg(xprt, program, version, dispatch, nconf)) {
+		/* svc_reg(3) destroys @xprt in this case */
+		xlog(D_GENERAL, "Failed to register (%s, %u, %s)",
+				name, version, nconf->nc_proto);
+		return 0;
+	}
+
+	return 1;
+}
+
+/**
+ * nfs_svc_create - start up RPC svc listeners
+ * @name: C string containing name of new service
+ * @program: RPC program number to register
+ * @version: RPC version number to register
+ * @dispatch: address of function that handles incoming RPC requests
+ * @port: if not zero, transport listens on this port
+ *
+ * Sets up network transports for receiving RPC requests, and starts
+ * the RPC dispatcher.  Returns the number of started network transports.
+ */
+unsigned int
+nfs_svc_create(__attribute__((unused)) char *name,
+		const rpcprog_t program, const rpcvers_t version,
+		void (*dispatch)(struct svc_req *, SVCXPRT *),
+		const uint16_t port)
+{
+	const struct sigaction create_sigaction = {
+		.sa_handler	= SIG_IGN,
+	};
+	unsigned int visible, up;
+	struct netconfig *nconf;
+	void *handlep;
+
+	/*
+	 * Ignore SIGPIPE to avoid exiting sideways when peers
+	 * close their TCP connection while we're trying to reply
+	 * to them.
+	 */
+	(void)sigaction(SIGPIPE, &create_sigaction, NULL);
+
+	handlep = setnetconfig();
+	if (handlep == NULL) {
+		xlog(L_ERROR, "Failed to access local netconfig database: %s",
+			nc_sperror());
+		return 0;
+	}
+
+	visible = 0;
+	up = 0;
+	while ((nconf = getnetconfig(handlep)) != NULL) {
+		if (!(nconf->nc_flag & NC_VISIBLE))
+			continue;
+		visible++;
+		up += svc_create_nconf(name, program, version, dispatch,
+						port, nconf);
+	}
+
+	if (visible == 0)
+		xlog(L_ERROR, "Failed to find any visible netconfig entries");
+
+	if (endnetconfig(handlep) == -1)
+		xlog(L_ERROR, "Failed to close local netconfig database: %s",
+			nc_sperror());
+
+	return up;
+}
+
+/**
+ * nfs_svc_unregister - remove service registrations from local rpcbind database
+ * @program: RPC program number to unregister
+ * @version: RPC version number to unregister
+ *
+ * Removes all registrations for [ @program, @version ] .
+ */
+void
+nfs_svc_unregister(const rpcprog_t program, const rpcvers_t version)
+{
+	(void)rpcb_unset(program, version, NULL);
+}
+
+#else	/* !HAVE_LIBTIRPC */
+
+/**
+ * nfs_svc_create - start up RPC svc listeners
+ * @name: C string containing name of new service
+ * @program: RPC program number to register
+ * @version: RPC version number to register
+ * @dispatch: address of function that handles incoming RPC requests
+ * @port: if not zero, transport listens on this port
+ *
+ * Sets up network transports for receiving RPC requests, and starts
+ * the RPC dispatcher.  Returns the number of started network transports.
+ */
+unsigned int
+nfs_svc_create(char *name, const rpcprog_t program, const rpcvers_t version,
+		void (*dispatch)(struct svc_req *, SVCXPRT *),
+		const uint16_t port)
+{
+	rpc_init(name, program, version, dispatch, port);
+	return 1;
+}
+
+/**
+ * nfs_svc_unregister - remove service registrations from local rpcbind database
+ * @program: RPC program number to unregister
+ * @version: RPC version number to unregister
+ *
+ * Removes all registrations for [ @program, @version ] .
+ */
+void
+nfs_svc_unregister(const rpcprog_t program, const rpcvers_t version)
+{
+	(void)pmap_unset(program, version);
+}
+
+#endif	/* !HAVE_LIBTIRPC */
diff --git a/utils/statd/statd.c b/utils/statd/statd.c
index 72c9b41..7be6454 100644
--- a/utils/statd/statd.c
+++ b/utils/statd/statd.c
@@ -90,13 +90,18 @@ sm_prog_1_wrapper (struct svc_req *rqstp, register SVCXPRT *transp)
 #define sm_prog_1 sm_prog_1_wrapper
 #endif
 
+static void
+statd_unregister(void) {
+	nfs_svc_unregister(SM_PROG, SM_VERS);
+}
+
 /*
  * Signal handler.
  */
 static void 
 killer (int sig)
 {
-	pmap_unset (SM_PROG, SM_VERS);
+	statd_unregister ();
 	xlog_err ("Caught signal %d, un-registering and exiting", sig);
 }
 
@@ -125,6 +130,9 @@ static void log_modes(void)
 		strcat(buf,"No-Daemon ");
 	if (run_mode & MODE_LOG_STDERR)
 		strcat(buf,"Log-STDERR ");
+#ifdef HAVE_LIBTIRPC
+	strcat(buf, "TI-RPC ");
+#endif
 
 	xlog_warn(buf);
 }
@@ -424,10 +432,29 @@ int main (int argc, char **argv)
 	xlog(D_GENERAL, "Local NSM state number: %d", MY_STATE);
 	nsm_update_kernel_state(MY_STATE);
 
-	pmap_unset (SM_PROG, SM_VERS);
+	/*
+	 * ORDER
+	 * Clear old listeners while still root, to override any
+	 * permission checking done by rpcbind.
+	 */
+	statd_unregister();
+
+	/*
+	 * ORDER
+	 */
+	if (!nsm_drop_privileges(pidfd))
+		exit(1);
 
-	/* this registers both UDP and TCP services */
-	rpc_init("statd", SM_PROG, SM_VERS, sm_prog_1, port);
+	/*
+	 * ORDER
+	 * Create RPC listeners after dropping privileges.  This permits
+	 * statd to unregister its own listeners when it exits.
+	 */
+	if (nfs_svc_create("statd", SM_PROG, SM_VERS, sm_prog_1, port) == 0) {
+		xlog(L_ERROR, "failed to create RPC listeners, exiting");
+		exit(1);
+	}
+	atexit(statd_unregister);
 
 	/* If we got this far, we have successfully started, so notify parent */
 	if (pipefds[1] > 0) {
@@ -440,9 +467,6 @@ int main (int argc, char **argv)
 		pipefds[1] = -1;
 	}
 
-	if (!nsm_drop_privileges(pidfd))
-		exit(1);
-
 	for (;;) {
 		/*
 		 * Handle incoming requests:  SM_NOTIFY socket requests, as
-------------------------------------------------------------------------------
statd-update-rpc-statd-8-and-s
-------------------------------------------------------------------------------
statd: update rpc.statd(8) and sm-notify(8) to reflect IPv6 support

From: Chuck Lever <chuck.lever@oracle.com>

Expand and clarify the explanation of NSM operation on Linux, and
provide the same text in both man pages.

Update descriptions of the command line options to match the operation
of the current implementation.

Introduce sections discussing security and operational issues, and
IPv6 operation.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 utils/statd/sm-notify.man |  407 +++++++++++++++++++++++++-----------
 utils/statd/statd.man     |  508 ++++++++++++++++++++++++++++++++-------------
 2 files changed, 641 insertions(+), 274 deletions(-)


diff --git a/utils/statd/sm-notify.man b/utils/statd/sm-notify.man
index a5c1cc5..163713e 100644
--- a/utils/statd/sm-notify.man
+++ b/utils/statd/sm-notify.man
@@ -1,162 +1,315 @@
-.\"
-.\" sm-notify(8)
+.\"@(#)sm-notify.8"
 .\"
 .\" Copyright (C) 2004 Olaf Kirch <okir@suse.de>
-.TH sm-notify 8 "19 Mar 2007
+.\"
+.\" Rewritten by Chuck Lever <chuck.lever@oracle.com>, 2009.
+.\" Copyright 2009 Oracle.  All rights reserved.
+.\"
+.TH SM-NOTIFY 8 "1 November 2009
 .SH NAME
-sm-notify \- Send out NSM reboot notifications
+sm-notify \- send reboot notifications to NFS peers
 .SH SYNOPSIS
-.BI "/sbin/sm-notify [-df] [-m " time "] [-p " port "] [-P " path "] [-v " my_name " ]
+.BI "/usr/sbin/sm-notify [-dfn] [-m " minutes "] [-v " name "] [-p " notify-port "] [-P " path "]
 .SH DESCRIPTION
-File locking over NFS (v2 and v3) requires a facility to notify peers in
-case of a reboot, so that clients can reclaim locks after
-a server crash, and/or
-servers can release locks held by the rebooted client.
+File locks are not part of persistent file system state.
+Lock state is thus lost when a host reboots.
+.PP
+Network file systems must also detect when lock state is lost
+because a remote host has rebooted.
+After an NFS client reboots, an NFS server must release all file locks
+held by applications that were running on that client.
+After a server reboots, a client must remind the
+server of file locks held by applications running on that client.
 .PP
-This is a two-step process: during normal
-operations, a mechanism is required to keep track of which
-hosts need to be informed of a reboot. And of course,
-notifications need to be sent out during reboot.
-The protocol used for this is called NSM, for
-.IR "Network Status Monitor" .
+For NFS version 2 and version 3, the
+.I Network Status Monitor
+protocol (or NSM for short)
+is used to notify NFS peers of reboots.
+On Linux, two separate user-space components constitute the NSM service:
+.TP
+.B sm-notify
+A helper program that notifies NFS peers after the local system reboots
+.TP
+.B rpc.statd
+A daemon that listens for reboot notifications from other hosts, and
+manages the list of hosts to be notified when the local system reboots
 .PP
-This implementation separates these into separate program.
+The local NFS lock manager alerts its local
 .B rpc.statd
-tracks hosts which need to be notified and this
+of each remote peer that should be monitored.
+When the local system reboots, the
 .B sm-notify
-performs the notification.  When
+command notifies the NSM service on monitored peers of the reboot.
+When a remote reboots, that peer notifies the local
+.BR rpc.statd ,
+which in turn passes the reboot notification
+back to the local NFS lock manager.
+.SH NSM OPERATION IN DETAIL
+The first file locking interaction between an NFS client and server causes
+the NFS lock managers on both peers to contact their local NSM service to
+store information about the opposite peer.
+On Linux, the local lock manager contacts
+.BR rpc.statd .
+.PP
+.B rpc.statd
+records information about each monitored NFS peer on persistent storage.
+This information describes how to contact a remote peer
+in case the local system reboots,
+how to recognize which monitored peer is reporting a reboot,
+and how to notify the local lock manager when a monitored peer
+indicates it has rebooted.
+.PP
+An NFS client sends a hostname, known as the client's
+.IR caller_name ,
+in each file lock request.
+An NFS server can use this hostname to send asynchronous GRANT
+calls to a client, or to notify the client it has rebooted.
+.PP
+The Linux NFS server can provide the client's
+.I caller_name
+or the client's network address to
+.BR rpc.statd .
+For the purposes of the NSM protocol,
+this name or address is known as the monitored peer's
+.IR mon_name .
+In addition, the local lock manager tells
 .B rpc.statd
-is started it will typically started
+what it thinks its own hostname is.
+For the purposes of the NSM protocol,
+this hostname is known as
+.IR my_name .
+.PP
+There is no equivalent interaction between an NFS server and a client
+to inform the client of the server's
+.IR caller_name .
+Therefore NFS clients do not actually know what
+.I mon_name
+an NFS server might use in an SM_NOTIFY request.
+The Linux NFS client records the server's hostname used on the mount command
+to identify rebooting NFS servers.
+.SS Reboot notification
+When the local system reboots, the
+.B sm-notify
+command reads the list of monitored peers from persistent storage and
+sends an SM_NOTIFY request to the NSM service on each listed remote peer.
+It uses the
+.I mon_name
+string as the destination.
+To identify which host has rebooted, the
 .B sm-notify
-but this is configurable.
-.SS Operation
-For each NFS client or server machine to be monitored,
+command normally sends the results of
+.BR gethostname (3)
+as the
+.I my_name
+string.
+The remote
 .B rpc.statd
-creates a file in
-.BR /var/lib/nfs/sm ", "
-and removes the file if monitoring is no longer required.
+matches incoming SM_NOTIFY requests using this string,
+or the caller's network address,
+to one or more peers on its own monitor list.
 .PP
-When the machine is rebooted,
+If
+.B rpc.statd
+does not find a peer on its monitor list that matches
+an incoming SM_NOTIFY request,
+the notification is not forwarded to the local lock manager.
+In addition, each peer has its own
+.IR "NSM state number" ,
+a 32-bit integer that is bumped after each reboot by the
 .B sm-notify
-iterates through these files and notifies the peer
-.B statd
-server on those machines.
+command.
+.B rpc.statd
+uses this number to distinguish between actual reboots
+and replayed notifications.
 .PP
-Each machine has an
-.I "NSM state" ,
-which is basically an integer counter that is incremented
-each time the machine reboots. This counter is stored
-in
-.BR /var/lib/nfs/state ,
-and updated by
-.BR sm-notify .
-.SS Security
-.B sm-notify
-has little need for root privileges and so drops them as soon as
-possible.
-It continues to need to make changes to the
-.B sm
-and
-.B sm.bak
-directories so to be able to drop privileges, these must be writable
-by a non-privileged user.  If these directories are owned by a
-non-root user,
-.B sm-notify
-will drop privilege to match that user once it has created sockets for
-sending out request (for which it needs privileged) but before it
-processes any reply (which is the most likely source of possible
-privilege abuse).
+Part of NFS lock recovery is rediscovering
+which peers need to be monitored again.
+The
+.B sm-notify
+command clears the monitor list on persistent storage after each reboot.
 .SH OPTIONS
 .TP
-.BI -m " failtime
-When notifying hosts,
+.B -d
+Keeps
+.B sm-notify
+attached to its controlling terminal and running in the foreground
+so that notification progress may be monitored directly.
+.TP
+.B -f
+Send notifications even if
 .B sm-notify
-will try to contact each host for up to 15 minutes,
-and will give up if unable to reach it within this time
-frame.
+has already run since the last system reboot.
+.TP
+.BI -m " retry-time
+Specifies the length of time, in minutes, to continue retrying
+notifications to unresponsive hosts.
+If this option is not specified,
+.B sm-notify
+attempts to send notifications for 15 minutes.
+Specifying a value of 0 causes
+.B sm-notify
+to continue sending notifications to unresponsive peers
+until it is manually killed.
 .IP
-Using the
-.B -m
-option, you can override this. A value of 0 tells
-sm-notify to retry indefinitely; any other value is
-interpreted as the maximum retry time in minutes.
+Notifications are retried if sending fails,
+the remote does not respond,
+the remote's NSM service is not registered,
+or if there is a DNS failure
+which prevents the remote's
+.I mon_name
+from being resolved to an address.
+.IP
+Hosts are not removed from the notification list until a valid
+reply has been received.
+However, the SM_NOTIFY procedure has a void result.
+There is no way for
+.B sm-notify
+to tell if the remote recognized the sender and has started
+appropriate lock recovery.
 .TP
-.BI -v " ipaddr-or-hostname
-This option tells
-.B sm-notify
-to bind to the specified
-.IR ipaddr ,
-(or the ipaddr of the given
-.IR hostname )
-so that all notification packets originate from this address.
-This is useful for NFS failover.  The given name is also used as the
-.I name
-of this host in the NSM request.
+.B -n
+Prevents
+.B sm-notify
+from updating the local system's NSM state number.
 .TP
 .BI -p " port
-instructs
+Specifies the source port number
 .B sm-notify
-to bind to the indicated IP
-.IR port
-number. If this option is not given, it will try to bind to
-a randomly chosen privileged port below 1024.
+should use when sending reboot notifications.
+If this option is not specified, a randomly chosen ephemeral port is used.
+.IP
+This option can be used to traverse a firewall between client and server.
 .TP
-.BI -P " /path/to/state/directory
-If
+.BI "\-P, " "" \-\-state\-directory\-path " pathname
+Specifies the pathname of the parent directory
+where NSM state information resides.
+If this option is not specified,
+.B sm-notify
+uses
+.I /var/lib/nfs
+by default.
+.IP
+After starting,
 .B sm-notify
-should look in a no-standard place of state file, the path can be
-given here.  The directories
-.B sm
-and
-.B sm.bak
-and the file
-.B state
-must exist in that directory with the standard names.
+attempts to set its effective UID and GID to the owner
+and group of this directory.
 .TP
-.B -f
-If the state path has not been reset with
-.BR -P ,
+.BI -v " ipaddr " | " hostname
+Specifies the network address from which to send reboot notifications,
+and the
+.I mon_name
+argument to use when sending SM_NOTIFY requests.
+If this option is not specified,
 .B sm-notify
-will normally create a file in
-.B /var/run
-to indicate that it has been
-run.  If this file is found when
+uses a wildcard address as the transport bind address,
+and uses the results of
+.BR gethostname (3)
+as the
+.I mon_name
+argument.
+.IP
+The
+.I ipaddr
+form can be expressed as either an IPv4 or an IPv6 presentation address.
+.IP
+This option can be useful in multi-homed configurations where
+the remote requires notification from a specific network address.
+.SH SECURITY
+The
 .B sm-notify
-starts, it will not run again (as it is normally only needed once per
-reboot).
-If
-.B -f
-(for
-.BR force )
-is given,
+command must be started as root to acquire privileges needed
+to access the state information database.
+It drops root privileges
+as soon as it starts up to reduce the risk of a privilege escalation attack.
+.PP
+During normal operation,
+the effective user ID it chooses is the owner of the state directory.
+This allows it to continue to access files in that directory after it
+has dropped its root privileges.
+To control which user ID
+.B rpc.statd
+chooses, simply use
+.BR chown (1)
+to set the owner of
+the state directory.
+.SH ADDITIONAL NOTES
+Lock recovery after a reboot is critical to maintaining data integrity
+and preventing unnecessary application hangs.
+.PP
+To help
+.B rpc.statd
+match SM_NOTIFY requests to NLM requests, a number of best practices
+should be observed, including:
+.IP
+The UTS nodename of your systems should match the DNS names that NFS
+peers use to contact them
+.IP
+The UTS nodenames of your systems should always be fully qualified domain names
+.IP
+The forward and reverse DNS mapping of the UTS nodenames should be
+consistent
+.IP
+The hostname the client uses to mount the server should match the server's
+.I mon_name
+in SM_NOTIFY requests it sends
+.IP
+The use of network addresses as a
+.I mon_name
+or a
+.I my_name
+string should be avoided when
+interoperating with non-Linux NFS implementations.
+.PP
+Unmounting an NFS file system does not necessarily stop
+either the NFS client or server from monitoring each other.
+Both may continue monitoring each other for a time in case subsequent
+NFS traffic between the two results in fresh mounts and additional
+file locking.
+.PP
+On Linux, if the
+.B lockd
+kernel module is unloaded during normal operation,
+all remote NFS peers are unmonitored.
+This can happen on an NFS client, for example,
+if an automounter removes all NFS mount
+points due to inactivity.
+.SS IPv6 and TI-RPC support
+TI-RPC is a pre-requisite for supporting NFS on IPv6.
+If TI-RPC support is built into the
 .B sm-notify
-will run even if the file in
-.B /var/run
-is present.
-.TP
-.B -n
-Do not update the NSM state. This is for testing only.  Setting this
-flag implies
-.BR -f .
-.TP
-.B -d
-Enables debugging.
-By default,
+command ,it will choose an appropriate IPv4 or IPv6 transport
+based on the network address returned by DNS for each remote peer.
+It should be fully compatible with remote systems
+that do not support TI-RPC or IPv6.
+.PP
+Currently, the
 .B sm-notify
-forks and puts itself in the background after obtaining the
-list of hosts from
-.BR /var/lib/nfs/sm .
+command supports sending notification only via datagram transport protocols.
 .SH FILES
-.BR /var/lib/nfs/state
-.br
-.BR /var/lib/nfs/sm/*
+.TP 2.5i
+.I /var/lib/nfs/sm
+directory containing monitor list
+.TP 2.5i
+.I /var/lib/nfs/sm.bak
+directory containing notify list
+.TP 2.5i
+.I /var/lib/nfs/state
+NSM state number for this host
+.TP 2.5i
+.I /proc/sys/fs/nfs/nsm_local_state
+kernel's copy of the NSM state number
+.SH SEE ALSO
+.BR rpc.statd (8),
+.BR nfs (5),
+.BR uname (2),
+.BR hostname (7)
+.PP
+RFC 1094 - "NFS: Network File System Protocol Specification"
 .br
-.BR /var/lib/nfs/sm.bak/*
+RFC 1813 - "NFS Version 3 Protocol Specification"
 .br
-.BR /var/run/sm-notify.pid
-.SH SEE ALSO
-.BR rpc.nfsd(8),
-.BR portmap(8)
+OpenGroup Protocols for Interworking: XNFS, Version 3W - Chapter 11
 .SH AUTHORS
-.br
 Olaf Kirch <okir@suse.de>
+.br
+Chuck Lever <chuck.lever@oracle.com>
diff --git a/utils/statd/statd.man b/utils/statd/statd.man
index e8be9f3..421d7f6 100644
--- a/utils/statd/statd.man
+++ b/utils/statd/statd.man
@@ -1,191 +1,403 @@
-.\"
-.\" statd(8)
+.\"@(#)rpc.statd.8"
 .\"
 .\" Copyright (C) 1999 Olaf Kirch <okir@monad.swb.de>
 .\" Modified by Jeffrey A. Uphoff, 1999, 2002, 2005.
 .\" Modified by Lon Hohberger, 2000.
 .\" Modified by Paul Clements, 2004.
-.TH rpc.statd 8 "31 Aug 2004"
+.\"
+.\" Rewritten by Chuck Lever <chuck.lever@oracle.com>, 2009.
+.\" Copyright 2009 Oracle.  All rights reserved.
+.\"
+.TH RPC.STATD 8 "1 November 2009
 .SH NAME
-rpc.statd \- NSM status monitor
+rpc.statd \- NSM service daemon
 .SH SYNOPSIS
-.B "rpc.statd [-FNL] [-d] [-?] [-n " name "] [-o " port "] [-p " port "] [-H " prog "] [-V]"
+.BI "rpc.statd [-dh?FLNvVw] [-H " prog "] [-n " my-name "] [-o " outgoing-port "] [-p " listener-port "] [-P " path " ]
 .SH DESCRIPTION
-The
+File locks are not part of persistent file system state.
+Lock state is thus lost when a host reboots.
+.PP
+Network file systems must also detect when lock state is lost
+because a remote host has rebooted.
+After an NFS client reboots, an NFS server must release all file locks
+held by applications that were running on that client.
+After a server reboots, a client must remind the
+server of file locks held by applications running on that client.
+.PP
+For NFS version 2 [RFC1094] and NFS version 3 [RFC1813], the
+.I Network Status Monitor
+protocol (or NSM for short)
+is used to notify NFS peers of reboots.
+On Linux, two separate user-space components constitute the NSM service:
+.TP
 .B rpc.statd
-server implements the NSM (Network Status Monitor) RPC protocol.
-This service is somewhat misnamed, since it doesn't actually provide
-active monitoring as one might suspect; instead, NSM implements a
-reboot notification service. It is used by the NFS file locking service,
-.BR rpc.lockd ,
-to implement lock recovery when the NFS server machine crashes and
-reboots.
-.SS Operation
-For each NFS client or server machine to be monitored,
-.B rpc.statd
-creates a file in
-.BR /var/lib/nfs/sm .
-When starting, it normally runs
+A daemon that listens for reboot notifications from other hosts, and
+manages the list of hosts to be notified when the local system reboots
+.TP
+.B sm-notify
+A helper program that notifies NFS peers after the local system reboots
+.PP
+The local NFS lock manager alerts its local
+.B rpc.statd
+of each remote peer that should be monitored.
+When the local system reboots, the
 .B sm-notify
-to iterate through these files and notify the
-peer
+command notifies the NSM service on monitored peers of the reboot.
+When a remote reboots, that peer notifies the local
+.BR rpc.statd ,
+which in turn passes the reboot notification
+back to the local NFS lock manager.
+.SH NSM OPERATION IN DETAIL
+The first file locking interaction between an NFS client and server causes
+the NFS lock managers on both peers to contact their local NSM service to
+store information about the opposite peer.
+On Linux, the local lock manager contacts
+.BR rpc.statd .
+.PP
+.B rpc.statd
+records information about each monitored NFS peer on persistent storage.
+This information describes how to contact a remote peer
+in case the local system reboots,
+how to recognize which monitored peer is reporting a reboot,
+and how to notify the local lock manager when a monitored peer
+indicates it has rebooted.
+.PP
+An NFS client sends a hostname, known as the client's
+.IR caller_name ,
+in each file lock request.
+An NFS server can use this hostname to send asynchronous GRANT
+calls to a client, or to notify the client it has rebooted.
+.PP
+The Linux NFS server can provide the client's
+.I caller_name
+or the client's network address to
+.BR rpc.statd .
+For the purposes of the NSM protocol,
+this name or address is known as the monitored peer's
+.IR mon_name .
+In addition, the local lock manager tells
 .B rpc.statd
-on those machines.
+what it thinks its own hostname is.
+For the purposes of the NSM protocol,
+this hostname is known as
+.IR my_name .
+.PP
+There is no equivalent interaction between an NFS server and a client
+to inform the client of the server's
+.IR caller_name .
+Therefore NFS clients do not actually know what
+.I mon_name
+an NFS server might use in an SM_NOTIFY request.
+The Linux NFS client uses the server hostname from the mount command
+to identify rebooting NFS servers.
+.SS Reboot notification
+When the local system reboots, the
+.B sm-notify
+command reads the list of monitored peers from persistent storage and
+sends an SM_NOTIFY request to the NSM service on each listed remote peer.
+It uses the
+.I mon_name
+string as the destination.
+To identify which host has rebooted, the
+.B sm-notify
+command normally sends the results of
+.BR gethostname (3)
+as the
+.I my_name
+string.
+The remote
+.B rpc.statd
+matches incoming SM_NOTIFY requests using this string,
+or the caller's network address,
+to one or more peers on its own monitor list.
+.PP
+If
+.B rpc.statd
+does not find a peer on its monitor list that matches
+an incoming SM_NOTIFY request,
+the notification is not forwarded to the local lock manager.
+In addition, each peer has its own
+.IR "NSM state number" ,
+a 32-bit integer that is bumped after each reboot by the
+.B sm-notify
+command.
+.B rpc.statd
+uses this number to distinguish between actual reboots
+and replayed notifications.
+.PP
+Part of NFS lock recovery is rediscovering
+which peers need to be monitored again.
+The
+.B sm-notify
+command clears the monitor list on persistent storage after each reboot.
 .SH OPTIONS
 .TP
-.B -F
-By default,
+.BR -d , " --no-syslog
+Causes
 .B rpc.statd
-forks and puts itself in the background when started. The
-.B -F
-argument tells it to remain in the foreground. This option is
-mainly for debugging purposes.
-.TP
-.B -d
-By default,
-.B rpc.statd
-sends logging messages via
-.BR syslog (3)
-to system log.  The
-.B -d
-argument forces it to log verbose output to
-.B stderr
-instead. This option is mainly for debugging purposes, and may only
-be used in conjunction with the
+to write log messages on
+.I stderr
+instead of to the system log,
+if the
 .B -F
-parameter.
+option was also specified.
 .TP
-.BI "\-n," "" " \-\-name " name 
-specify a name for
-.B rpc.statd
-to use as the local hostname. By default,
-.BR rpc.statd
-will call
-.BR gethostname (2)
-to get the local hostname. Specifying
-a local hostname may be useful for machines with more than one
-interfaces.
+.BR -F , " --foreground
+Keeps
+.B rpc.statd
+attached to its controlling terminal so that NSM
+operation can be monitored directly or run under a debugger.
+If this option is not specified,
+.B rpc.statd
+backgrounds itself soon after it starts.
 .TP
-.BI "\-o," "" " \-\-outgoing\-port "  port
-specify a port for
-.B rpc.statd
-to send outgoing status requests from.  By default,
-.BR rpc.statd
-will ask
-.BR portmap (8)
-to assign it a port number.  As of this writing, there is not
-a standard port number that
-.BR portmap
-always or usually assigns.  Specifying
-a port may be useful when implementing a firewall.
+.BR -h , " -?" , " --help
+Causes
+.B rpc.statd
+to display usage information on
+.I stderr
+and then exit.
 .TP
-.BI "\-p," "" " \-\-port " port
-specify a port for
-.B rpc.statd
-to listen on.  By default,
-.BR rpc.statd
-will ask
-.BR portmap (8)
-to assign it a port number.  As of this writing, there is not
-a standard port number that
-.BR portmap
-always or usually assigns.  Specifying
-a port may be useful when implementing a firewall.
+.BI "\-H," "" " \-\-ha-callout " prog
+Specifies a high availability callout program.
+If this option is not specified, no callouts are performed.
+See the
+.B High-availability callouts
+section below for details.
 .TP
-.BI "\-P," "" " \-\-state\-directory\-path "  directory
-specify a directory in which to place statd state information.
-If this option is not specified the default of 
-.BR /var/lib/nfs
-is used.
+.BR -L , " --no-notify
+Prevents
+.B rpc.statd
+from running the
+.B sm-notify
+command when it starts up,
+preserving the existing NSM state number and monitor list.
+.IP
+Note: the
+.B sm-notify
+command contains a check to ensure it runs only once after each system reboot.
+This prevents spurious reboot notification if
+.B rpc.statd
+restarts without the
+.B -L
+option.
 .TP
-.B -N
-Causes statd to run in the notify-only mode. When started in this mode, the
-statd program will check its state directory, send notifications to any
-monitored nodes, and exit once the notifications have been sent. This mode is
-used to enable Highly Available NFS implementations (i.e. HA-NFS).
-This mode is deprecated \-
+.BI "\-n, " "" "\-\-name " ipaddr " | " hostname
+Specifies the bind address used for RPC listener sockets.
+The
+.I ipaddr
+form can be expressed as either an IPv4 or an IPv6 presentation address.
+If this option is not specified,
+.B rpc.statd
+uses a wildcard address as the transport bind address.
+.IP
+This string is also passed to the
 .B sm-notify
-should be used directly instead.
+command to be used as the source address from which
+to send reboot notification requests.
+See 
+.BR sm-notify (8)
+for details.
 .TP
-.BR -L , " --no-notify
-Inhibits the running of
-.BR sm-notify .
-If
+.BR -N
+Causes
+.B rpc.statd
+to run the
 .B sm-notify
-is run by some other script at boot time, there is no need for
-.B statd
-to start sm-notify itself.  This can be appropriate if starting of
-statd needs to be delayed until it is actually need.  In such cases
+command, and then exit.
+Since the
+.B sm-notify
+command can also be run directly, this option is deprecated.
+.TP
+.BI "\-o," "" " \-\-outgoing\-port "  port
+Specifies the source port number the
 .B sm-notify
-should still be run at boot time.
+command should use when sending reboot notifications.
+See
+.BR sm-notify (8)
+for details.
 .TP
-.BI "\-H, " "" " \-\-ha-callout " prog
-Specify a high availability callout program, which will receive callouts
-for all client monitor and unmonitor requests. This allows
+.BI "\-p," "" " \-\-port " port
+Specifies the port number used for RPC listener sockets.
+If this option is not specified,
 .B rpc.statd
-to be used in a High Availability NFS (HA-NFS) environment. The
-program will be run with 3 arguments:  The first is either
-.B add-client
-or
-.B del-client
-depending on the reason for the callout.
-The second will be the name of the client.
-The third will be the name of the server as known to the client.
+chooses a random ephemeral port for each listener socket.
+.IP
+This option can be used to fix the port value of its listeners when
+SM_NOTIFY requests must traverse a firewall between clients and servers.
 .TP
-.B -?
-Causes
+.BI "\-P, " "" \-\-state\-directory\-path " pathname
+Specifies the pathname of the parent directory
+where NSM state information resides.
+If this option is not specified,
 .B rpc.statd
-to print out command-line help and exit.
+uses
+.I /var/lib/nfs
+by default.
+.IP
+After starting,
+.B rpc.statd
+attempts to set its effective UID and GID to the owner
+and group of this directory.
 .TP
-.B -V
+.BR -v ", " -V ", " --version
 Causes
 .B rpc.statd
-to print out version information and exit.
-
-
-
-.SH TCP_WRAPPERS SUPPORT
-This
+to display version information on
+.I stderr
+and then exit.
+.SH SECURITY
+The
+.B rpc.statd
+daemon must be started as root to acquire privileges needed
+to create sockets with privileged source ports, and to access the
+state information database.
+Because
+.B rpc.statd
+maintains a long-running network service, however, it drops root privileges
+as soon as it starts up to reduce the risk of a privilege escalation attack.
+.PP
+During normal operation,
+the effective user ID it chooses is the owner of the state directory.
+This allows it to continue to access files in that directory after it
+has dropped its root privileges.
+To control which user ID
+.B rpc.statd
+chooses, simply use
+.BR chown (1)
+to set the owner of
+the state directory.
+.PP
+You can also protect your
 .B rpc.statd
-version is protected by the
+listeners using the
+.B tcp_wrapper
+library or
+.BR iptables (8).
+Note that the
+.B tcp_wrapper
+library supports only IPv4 networking.
+To use the
 .B tcp_wrapper
-library. You have to give the clients access to
-.B rpc.statd
-if they should be allowed to use it. To allow connects from clients of
-the .bar.com domain you could use the following line in /etc/hosts.allow:
-
-statd: .bar.com
-
-You have to use the daemon name 
+library, add the hostnames of peers that should be allowed access to
+.IR /etc/hosts.allow .
+Use the daemon name
 .B statd
-for the daemon name (even if the binary has a different name).
-
-For further information please have a look at the
+even if the
+.B rpc.statd
+binary has a different filename.
+.P
+For further information see the
 .BR tcpd (8)
 and
 .BR hosts_access (5)
-manual pages.
-
-.SH SIGNALS
-.BR SIGUSR1
-causes
-.B rpc.statd
-to re-read the notify list from disk
-and send notifications to clients. This can be used in High Availability NFS
-(HA-NFS) environments to notify clients to reacquire file locks upon takeover
-of an NFS export from another server.
-
+man pages.
+.SH ADDITIONAL NOTES
+Lock recovery after a reboot is critical to maintaining data integrity
+and preventing unnecessary application hangs.
+.PP
+To help
+.B rpc.statd
+match SM_NOTIFY requests to NLM requests, a number of best practices
+should be observed, including:
+.IP
+The UTS nodename of your systems should match the DNS names that NFS
+peers use to contact them
+.IP
+The UTS nodenames of your systems should always be fully qualified domain names
+.IP
+The forward and reverse DNS mapping of the UTS nodenames should be
+consistent
+.IP
+The hostname the client uses to mount the server should match the server's
+.I mon_name
+in SM_NOTIFY requests it sends
+.IP
+The use of network addresses as a
+.I mon_name
+or a
+.I my_name
+string should be avoided when
+interoperating with non-Linux NFS implementations.
+.PP
+Unmounting an NFS file system does not necessarily stop
+either the NFS client or server from monitoring each other.
+Both may continue monitoring each other for a time in case subsequent
+NFS traffic between the two results in fresh mounts and additional
+file locking.
+.PP
+On Linux, if the
+.B lockd
+kernel module is unloaded during normal operation,
+all remote NFS peers are unmonitored.
+This can happen on an NFS client, for example,
+if an automounter removes all NFS mount
+points due to inactivity.
+.SS High-availability callouts
+.B rpc.statd
+can exec a special callout program during processing of
+successful SM_MON, SM_UNMON, and SM_UNMON_ALL requests.
+Such a program may be used in High Availability NFS (HA-NFS)
+environments to track lock state that may need to be migrated after
+a system reboot.
+.PP
+The name of the callout program is specified with the
+.B -H
+option.
+The program is run with 3 arguments:
+The first is either
+.B add-client
+or
+.B del-client
+depending on the reason for the callout.
+The second is the
+.I mon_name
+of the monitored peer.
+The third is the
+.I caller_name
+of the requesting lock manager.
+.SS IPv6 and TI-RPC support
+TI-RPC is a pre-requisite for supporting NFS on IPv6.
+If TI-RPC support is built into
+.BR rpc.statd ,
+it attempts to start listeners on network transports marked
+'visible' in
+.IR /etc/netconfig .
+As long as at least one network transport listener starts successfully,
+.B rpc.statd
+will operate.
 .SH FILES
-.BR /var/lib/nfs/state
+.TP 2.5i
+.I /var/lib/nfs/sm
+directory containing monitor list
+.TP 2.5i
+.I /var/lib/nfs/sm.bak
+directory containing notify list
+.TP 2.5i
+.I /var/lib/nfs/state
+NSM state number for this host
+.TP 2.5i
+.I /var/run/run.statd.pid
+pid file
+.TP 2.5i
+.I /etc/netconfig
+network transport capability database
+.SH SEE ALSO
+.BR sm-notify (8),
+.BR nfs (5),
+.BR rpc.nfsd (8),
+.BR rpcbind (8),
+.BR tcpd (8),
+.BR hosts_access (5),
+.BR iptables (8),
+.BR netconfig (5)
+.sp
+RFC 1094 - "NFS: Network File System Protocol Specification"
 .br
-.BR /var/lib/nfs/sm/*
+RFC 1813 - "NFS Version 3 Protocol Specification"
 .br
-.BR /var/lib/nfs/sm.bak/*
-.SH SEE ALSO
-.BR rpc.nfsd(8),
-.BR portmap(8)
+OpenGroup Protocols for Interworking: XNFS, Version 3W - Chapter 11
 .SH AUTHORS
-.br
 Jeff Uphoff <juphoff@users.sourceforge.net>
 .br
 Olaf Kirch <okir@monad.swb.de>
@@ -195,3 +407,5 @@ H.J. Lu <hjl@gnu.org>
 Lon Hohberger <hohberger@missioncriticallinux.com>
 .br
 Paul Clements <paul.clements@steeleye.com>
+.br
+Chuck Lever <chuck.lever@oracle.com>
-------------------------------------------------------------------------------
statd-use-my_name-when-sending
-------------------------------------------------------------------------------
statd: Use my_name when sending SM_NOTIFY requests

From: Chuck Lever <chuck.lever@oracle.com>

The mon_name argument of an SM_NOTIFY request is a string that
identifies the rebooting host.

sm-notify should send the my_name provided by the local lockd at the
time the remote was monitored, rather than cocking up a mon_name
argument based on the present return value of gethostname(3).  If the
local system's hostname happened to change after the last reboot, then
the string returned by gethostname(3) will not be recognized by the
remote.  Thus the remote will never initiate lock recovery for this
host.

The existing behavior of using the -v command line option as the
mon_name argument is preserved, but we now prevent sending an IP
presentation address, as some non-Linux implementations don't
recognize addresses as valid mon_names.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 utils/statd/sm-notify.c   |   54 ++++++++++++++++++++++++++++++++++++---------
 utils/statd/sm-notify.man |   28 +++++++++++------------
 utils/statd/statd.man     |   14 ++----------
 3 files changed, 58 insertions(+), 38 deletions(-)


diff --git a/utils/statd/sm-notify.c b/utils/statd/sm-notify.c
index 4fda046..b73c85d 100644
--- a/utils/statd/sm-notify.c
+++ b/utils/statd/sm-notify.c
@@ -46,6 +46,7 @@
 struct nsm_host {
 	struct nsm_host *	next;
 	char *			name;
+	char *			my_name;
 	struct sockaddr_storage	addr;
 	socklen_t		addrlen;
 	struct addrinfo		*ai;
@@ -56,7 +57,7 @@ struct nsm_host {
 	unsigned int		xid;
 };
 
-static char		nsm_hostname[256];
+static char		nsm_hostname[SM_MAXSTRLEN + 1];
 static uint32_t		nsm_state;
 static int		nsm_family = AF_INET;
 static int		opt_debug = 0;
@@ -97,7 +98,7 @@ static struct addrinfo *smn_lookup(const char *name)
 }
 
 static struct nsm_host *
-smn_alloc_host(const char *hostname, const time_t timestamp)
+smn_alloc_host(const char *hostname, const char *my_name, const time_t timestamp)
 {
 	struct nsm_host	*host;
 
@@ -111,6 +112,13 @@ smn_alloc_host(const char *hostname, const time_t timestamp)
 		goto out_nomem;
 	}
 
+	host->my_name = strdup(my_name);
+	if (host->my_name == NULL) {
+		free(host->name);
+		free(host);
+		goto out_nomem;
+	}
+
 	host->last_used = timestamp;
 	host->timeout = NSM_TIMEOUT;
 	host->retries = 100;		/* force address retry */
@@ -128,6 +136,7 @@ static void smn_forget_host(struct nsm_host *host)
 
 	nsm_delete_notified_host(host->name);
 
+	free(host->my_name);
 	free(host->name);
 	if (host->ai)
 		freeaddrinfo(host->ai);
@@ -138,12 +147,17 @@ static void smn_forget_host(struct nsm_host *host)
 static unsigned int
 smn_get_host(const char *hostname,
 		__attribute__((unused)) const struct sockaddr *sap,
-		__attribute__((unused)) const struct mon *mon,
-		const time_t timestamp)
+		const struct mon *mon, const time_t timestamp)
 {
 	struct nsm_host	*host;
+	char *my_name;
+
+	if (opt_srcaddr)
+		my_name = nsm_hostname;
+	else
+		my_name = mon->mon_id.my_id.my_name;
 
-	host = smn_alloc_host(hostname, timestamp);
+	host = smn_alloc_host(hostname, my_name, timestamp);
 	if (host == NULL)
 		return 0;
 
@@ -336,6 +350,9 @@ out:
 int
 main(int argc, char **argv)
 {
+	struct addrinfo *results, hint = {
+		.ai_family	= AF_UNSPEC,
+	};
 	int	c, sock;
 	int	force = 0;
 	char *	progname;
@@ -403,11 +420,26 @@ usage:		fprintf(stderr,
 	}
 
 	if (opt_srcaddr) {
-		strncpy(nsm_hostname, opt_srcaddr, sizeof(nsm_hostname)-1);
-	} else
-	if (gethostname(nsm_hostname, sizeof(nsm_hostname)) < 0) {
-		xlog(L_ERROR, "Failed to obtain name of local host: %m");
-		exit(1);
+		hint.ai_flags = AI_NUMERICHOST;
+		if (getaddrinfo(opt_srcaddr, NULL, &hint, &results))
+			/* not a presentation address - use it */
+			strncpy(nsm_hostname, opt_srcaddr, sizeof(nsm_hostname));
+		else {
+			/* was a presentation address - look it up */
+			int error;
+
+			freeaddrinfo(results);
+			hint.ai_flags = AI_CANONNAME;
+			error = getaddrinfo(opt_srcaddr, NULL, &hint, &results);
+			if (error) {
+				xlog(L_ERROR, "Bind address %s is unusable: %s",
+						opt_srcaddr, gai_strerror(error));
+				exit(1);
+			}
+			strncpy(nsm_hostname, results->ai_canonname,
+							sizeof(nsm_hostname));
+			freeaddrinfo(results);
+		}
 	}
 
 	(void)nsm_retire_monitored_hosts();
@@ -576,7 +608,7 @@ notify_host(int sock, struct nsm_host *host)
 				NSM_PROGRAM, NSM_VERSION);
 	else
 		host->xid = nsm_xmit_notify(sock, dest, host->addrlen,
-				NSM_PROGRAM, nsm_hostname, nsm_state);
+				NSM_PROGRAM, host->my_name, nsm_state);
 	
 	return 0;
 }
diff --git a/utils/statd/sm-notify.man b/utils/statd/sm-notify.man
index 163713e..7a1cbfa 100644
--- a/utils/statd/sm-notify.man
+++ b/utils/statd/sm-notify.man
@@ -97,11 +97,9 @@ It uses the
 string as the destination.
 To identify which host has rebooted, the
 .B sm-notify
-command normally sends the results of
-.BR gethostname (3)
-as the
+command normally sends
 .I my_name
-string.
+string recorded when that remote was monitored.
 The remote
 .B rpc.statd
 matches incoming SM_NOTIFY requests using this string,
@@ -202,15 +200,22 @@ argument to use when sending SM_NOTIFY requests.
 If this option is not specified,
 .B sm-notify
 uses a wildcard address as the transport bind address,
-and uses the results of
-.BR gethostname (3)
-as the
+and uses the
+.I my_name
+recorded when the remote was monitored as the
 .I mon_name
-argument.
+argument when sending SM_NOTIFY requests.
 .IP
 The
 .I ipaddr
 form can be expressed as either an IPv4 or an IPv6 presentation address.
+If the
+.I ipaddr
+form is used, the
+.B sm-notify
+command converts this address to a hostname for use as the
+.I mon_name
+argument when sending SM_NOTIFY requests.
 .IP
 This option can be useful in multi-homed configurations where
 the remote requires notification from a specific network address.
@@ -252,13 +257,6 @@ consistent
 The hostname the client uses to mount the server should match the server's
 .I mon_name
 in SM_NOTIFY requests it sends
-.IP
-The use of network addresses as a
-.I mon_name
-or a
-.I my_name
-string should be avoided when
-interoperating with non-Linux NFS implementations.
 .PP
 Unmounting an NFS file system does not necessarily stop
 either the NFS client or server from monitoring each other.
diff --git a/utils/statd/statd.man b/utils/statd/statd.man
index 421d7f6..9310f0e 100644
--- a/utils/statd/statd.man
+++ b/utils/statd/statd.man
@@ -100,11 +100,9 @@ It uses the
 string as the destination.
 To identify which host has rebooted, the
 .B sm-notify
-command normally sends the results of
-.BR gethostname (3)
-as the
+command sends the
 .I my_name
-string.
+string recorded when that remote was monitored.
 The remote
 .B rpc.statd
 matches incoming SM_NOTIFY requests using this string,
@@ -295,7 +293,6 @@ man pages.
 .SH ADDITIONAL NOTES
 Lock recovery after a reboot is critical to maintaining data integrity
 and preventing unnecessary application hangs.
-.PP
 To help
 .B rpc.statd
 match SM_NOTIFY requests to NLM requests, a number of best practices
@@ -312,13 +309,6 @@ consistent
 The hostname the client uses to mount the server should match the server's
 .I mon_name
 in SM_NOTIFY requests it sends
-.IP
-The use of network addresses as a
-.I mon_name
-or a
-.I my_name
-string should be avoided when
-interoperating with non-Linux NFS implementations.
 .PP
 Unmounting an NFS file system does not necessarily stop
 either the NFS client or server from monitoring each other.
-------------------------------------------------------------------------------
statd-send-unqualified-and-ful
-------------------------------------------------------------------------------
statd: Send unqualified and fully qualified mon_name in SM_NOTIFY

From: Chuck Lever <chuck.lever@oracle.com>

During any file locking interaction between an NFS client and server,
the client tells the server what hostname to use as the mon_name
argument of an SM_NOTIFY request.

The server, however, never tells the client what mon_name argument
it will use when sending an SM_NOTIFY request.  In order to recognize
the server, clients usually guess what mon_name the server might send
by using the server hostname provided by the user on the mount
command line.

Sometimes, however, the user provides an unqualified server name.  The
server might then call the client back with a fully qualified domain
name, which might not match.

Solaris, and perhaps other implementations, attempt to mitigate this
problem by sending two SM_NOTIFY requests: one with an unqualified
mon_name argument, and one with a fully qualified mon_name.

Implement such a scheme for sm-notify.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 utils/statd/sm-notify.c |   19 ++++++++++++++++---
 1 files changed, 16 insertions(+), 3 deletions(-)


diff --git a/utils/statd/sm-notify.c b/utils/statd/sm-notify.c
index b73c85d..28b8021 100644
--- a/utils/statd/sm-notify.c
+++ b/utils/statd/sm-notify.c
@@ -149,13 +149,13 @@ smn_get_host(const char *hostname,
 		__attribute__((unused)) const struct sockaddr *sap,
 		const struct mon *mon, const time_t timestamp)
 {
+	char *c, my_name[SM_MAXSTRLEN + 1];
 	struct nsm_host	*host;
-	char *my_name;
 
 	if (opt_srcaddr)
-		my_name = nsm_hostname;
+		strncpy(my_name, nsm_hostname, sizeof(my_name) - 1);
 	else
-		my_name = mon->mon_id.my_id.my_name;
+		strncpy(my_name, mon->mon_id.my_id.my_name, sizeof(my_name) - 1);
 
 	host = smn_alloc_host(hostname, my_name, timestamp);
 	if (host == NULL)
@@ -163,6 +163,19 @@ smn_get_host(const char *hostname,
 
 	insert_host(host);
 	xlog(D_GENERAL, "Added host %s to notify list", hostname);
+
+	/* See if we should send an unqualified copy of my_name as well */
+	c = strchr(my_name, '.');
+	if (c) {
+		*c = '\0';
+		host = smn_alloc_host(hostname, my_name, timestamp);
+		if (host != NULL) {
+			insert_host(host);
+			xlog(D_GENERAL, "Added unqualified copy of %s "
+					"to notify list", hostname);
+		}
+	}
+
 	return 1;
 }
 
-------------------------------------------------------------------------------
statd-catch-all-for-fixes-whil
-------------------------------------------------------------------------------
statd: Catch-all for fixes while we test

From: Chuck Lever <chuck.lever@oracle.com>


---

 support/nsm/file.c |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)


diff --git a/support/nsm/file.c b/support/nsm/file.c
index 126a706..f9cd6f6 100644
--- a/support/nsm/file.c
+++ b/support/nsm/file.c
@@ -89,8 +89,8 @@
 /*
  * Some distributions place statd's files in a subdirectory
  */
-#define NSM_PATH_EXTENSION
-//#define NSM_PATH_EXTENSION	"/statd"
+//#define NSM_PATH_EXTENSION
+#define NSM_PATH_EXTENSION	"/statd"
 
 #define NSM_DEFAULT_STATEDIR		NFS_STATEDIR NSM_PATH_EXTENSION
 
-------------------------------------------------------------------------------
tcpwrappers-use-xlog-instead-o
-------------------------------------------------------------------------------
tcpwrappers: Use xlog() instead of perror(3) and syslog(2)

From: Chuck Lever <chuck.lever@oracle.com>

Clean up: Replace calls to syslog(2) and perror(3) in from_local.c
with calls to xlog().  perror(3) especially should be reported, but
would never be seen in the system log.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 support/misc/from_local.c |   18 ++++++++++--------
 1 files changed, 10 insertions(+), 8 deletions(-)


diff --git a/support/misc/from_local.c b/support/misc/from_local.c
index 89ccc4a..f63dd03 100644
--- a/support/misc/from_local.c
+++ b/support/misc/from_local.c
@@ -37,8 +37,8 @@
 static char sccsid[] = "@(#) from_local.c 1.3 96/05/31 15:52:57";
 #endif
 
-#ifdef TEST
-#undef perror
+#ifdef HAVE_CONFIG_H
+#include <config.h>
 #endif
 
 #include <sys/types.h>
@@ -53,6 +53,8 @@ static char sccsid[] = "@(#) from_local.c 1.3 96/05/31 15:52:57";
 #include <stdlib.h>
 #include <string.h>
 
+#include "xlog.h"
+
 #ifndef TRUE
 #define	TRUE	1
 #define FALSE	0
@@ -81,7 +83,7 @@ static int grow_addrs(void)
     new_num = (addrs == 0) ? 1 : num_addrs + num_addrs;
     new_addrs = (struct in_addr *) malloc(sizeof(*addrs) * new_num);
     if (new_addrs == 0) {
-	perror("portmap: out of memory");
+	xlog_warn("%s: out of memory", __func__);
 	return (0);
     } else {
 	if (addrs != 0) {
@@ -112,13 +114,13 @@ find_local(void)
      */
 
     if ((sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
-	perror("socket");
+	xlog_warn("socket: %m");
 	return (0);
     }
     ifc.ifc_len = sizeof(buf);
     ifc.ifc_buf = buf;
     if (ioctl(sock, SIOCGIFCONF, (char *) &ifc) < 0) {
-	perror("SIOCGIFCONF");
+	xlog_warn("ioctl(SIOCGIFCONF): %m");
 	(void) close(sock);
 	return (0);
     }
@@ -130,10 +132,10 @@ find_local(void)
 	if (ifr->ifr_addr.sa_family == AF_INET) {	/* IP net interface */
 	    ifreq = *ifr;
 	    if (ioctl(sock, SIOCGIFFLAGS, (char *) &ifreq) < 0) {
-		perror("SIOCGIFFLAGS");
+		xlog_warn("ioctl(SIOCGIFFLAGS): %m");
 	    } else if (ifreq.ifr_flags & IFF_UP) {	/* active interface */
 		if (ioctl(sock, SIOCGIFADDR, (char *) &ifreq) < 0) {
-		    perror("SIOCGIFADDR");
+		    xlog_warn("ioctl(SIOCGIFADDR) %m");
 		} else {
 		    if (num_local >= num_addrs)
 			if (grow_addrs() == 0)
@@ -160,7 +162,7 @@ from_local(struct sockaddr_in *addr)
     int     i;
 
     if (addrs == 0 && find_local() == 0)
-	syslog(LOG_ERR, "cannot find any active local network interfaces");
+	xlog(L_ERROR, "Cannot find any active local network interfaces");
 
     for (i = 0; i < num_local; i++) {
 	if (memcmp((char *) &(addr->sin_addr), (char *) &(addrs[i]),
-------------------------------------------------------------------------------
tcp_wrappers-use-getifaddrs-3-
-------------------------------------------------------------------------------
tcp_wrappers: Use getifaddrs(3) if it is available

From: Chuck Lever <chuck.lever@oracle.com>

After glibc 2.3.3, getifaddrs(3) can return AF_INET6 addresses for
local network interfaces.  Using the library call is easier than
trying to update the open code in from_local(), and means we have
less to maintain in nfs-utils.

Since from_local() can now support IPv6, change its synopsis to take a
"struct sockaddr *" .

Note that the original code discovers local addresses once.  These
days, with wifi, DHCP, and NetworkManager, the local network
configuration can change dynamically over time.  So, call getifaddrs()
more often ensure from_local() has up-to-date network configuration
information.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 aclocal/ipv6.m4              |    4 +-
 configure.ac                 |    2 -
 support/include/tcpwrapper.h |    2 -
 support/misc/from_local.c    |   87 ++++++++++++++++++++++++++++++++++++++----
 support/misc/tcpwrapper.c    |    2 -
 5 files changed, 84 insertions(+), 13 deletions(-)


diff --git a/aclocal/ipv6.m4 b/aclocal/ipv6.m4
index 2490f3d..5ee8fb6 100644
--- a/aclocal/ipv6.m4
+++ b/aclocal/ipv6.m4
@@ -15,8 +15,8 @@ AC_DEFUN([AC_IPV6], [
     fi
 
     dnl IPv6-enabled networking functions required for IPv6
-    AC_CHECK_FUNCS([getnameinfo bindresvport_sa], ,
-                   [AC_MSG_ERROR([Missing functions needed for IPv6.])])
+    AC_CHECK_FUNCS([getifaddrs getnameinfo bindresvport_sa], ,
+                   [AC_MSG_ERROR([Missing library functions needed for IPv6.])])
 
     dnl Need to detect presence of IPv6 networking at run time via
     dnl getaddrinfo(3); old versions of glibc do not support ADDRCONFIG
diff --git a/configure.ac b/configure.ac
index 8f979b9..7dffeaf 100644
--- a/configure.ac
+++ b/configure.ac
@@ -330,7 +330,7 @@ AC_FUNC_STAT
 AC_FUNC_VPRINTF
 AC_CHECK_FUNCS([alarm atexit dup2 fdatasync ftruncate getcwd \
                gethostbyaddr gethostbyname gethostname getmntent \
-               getnameinfo getrpcbyname \
+               getnameinfo getrpcbyname getifaddrs \
                gettimeofday hasmntopt inet_ntoa innetgr memset mkdir pathconf \
                realpath rmdir select socket strcasecmp strchr strdup \
                strerror strrchr strtol strtoul sigprocmask])
diff --git a/support/include/tcpwrapper.h b/support/include/tcpwrapper.h
index 98cf806..f1145bd 100644
--- a/support/include/tcpwrapper.h
+++ b/support/include/tcpwrapper.h
@@ -11,7 +11,7 @@ extern int allow_severity;
 extern int deny_severity;
 
 extern int good_client(char *daemon, struct sockaddr_in *addr);
-extern int from_local (struct sockaddr_in *addr);
+extern int from_local(const struct sockaddr *sap);
 extern int check_default(char *daemon, struct sockaddr_in *addr,
 			 u_long proc, u_long prog);
 
diff --git a/support/misc/from_local.c b/support/misc/from_local.c
index f63dd03..21d25f2 100644
--- a/support/misc/from_local.c
+++ b/support/misc/from_local.c
@@ -60,11 +60,66 @@ static char sccsid[] = "@(#) from_local.c 1.3 96/05/31 15:52:57";
 #define FALSE	0
 #endif
 
- /*
-  * With virtual hosting, each hardware network interface can have multiple
-  * network addresses. On such machines the number of machine addresses can
-  * be surprisingly large.
-  */
+#ifdef HAVE_GETIFADDRS
+
+#include <ifaddrs.h>
+#include <time.h>
+
+#include "compaddr.h"
+
+/**
+ * from_local - determine whether request comes from the local system
+ * @sap: pointer to socket address to check
+ *
+ * With virtual hosting, each hardware network interface can have
+ * multiple network addresses. On such machines the number of machine
+ * addresses can be surprisingly large.
+ *
+ * We also expect the local network configuration to change over time,
+ * so call getifaddrs(3) often.
+ *
+ * Returns TRUE if the sockaddr contains an address of one of the local
+ * network interfaces.  Otherwise FALSE is returned.
+ */
+int
+from_local(const struct sockaddr *sap)
+{
+	static struct ifaddrs *ifaddr = NULL;
+	static time_t last_update = 0;		/* in seconds */
+	struct ifaddrs *ifa;
+	int result = FALSE;
+	time_t now;
+
+	/* Don't call getifaddrs(3) too often */
+	if (time(&now) == ((time_t)-1)) {
+		xlog(L_ERROR, "time: %m");
+		goto out;
+	}
+	if (now != last_update) {
+		if (ifaddr)
+			freeifaddrs(ifaddr);
+
+		if (getifaddrs(&ifaddr) == -1) {
+			xlog(L_ERROR, "getifaddrs: %m");
+			goto out;
+		}
+
+		last_update = now;
+	}
+
+	for (ifa = ifaddr; ifa; ifa = ifa->ifa_next)
+		if (ifa->ifa_flags & IFF_UP)
+			if (compare_sockaddr(sap, ifa->ifa_addr)) {
+				result = TRUE;
+				break;
+			}
+
+out:
+	return result;
+}
+
+#else	/* !HAVE_GETIFADDRS */
+
 static int num_local;
 static int num_addrs;
 static struct in_addr *addrs;
@@ -155,15 +210,29 @@ find_local(void)
     return (num_local);
 }
 
-/* from_local - determine whether request comes from the local system */
+/**
+ * from_local - determine whether request comes from the local system
+ * @sap: pointer to socket address to check
+ *
+ * With virtual hosting, each hardware network interface can have
+ * multiple network addresses. On such machines the number of machine
+ * addresses can be surprisingly large.
+ *
+ * Returns TRUE if the sockaddr contains an address of one of the local
+ * network interfaces.  Otherwise FALSE is returned.
+ */
 int
-from_local(struct sockaddr_in *addr)
+from_local(const struct sockaddr *sap)
 {
+    const struct sockaddr_in *addr = (const struct sockaddr_in *)sap;
     int     i;
 
     if (addrs == 0 && find_local() == 0)
 	xlog(L_ERROR, "Cannot find any active local network interfaces");
 
+    if (sap->sa_family != AF_INET)
+	return (FALSE);
+
     for (i = 0; i < num_local; i++) {
 	if (memcmp((char *) &(addr->sin_addr), (char *) &(addrs[i]),
 		   sizeof(struct in_addr)) == 0)
@@ -184,4 +253,6 @@ main()
 	printf("%s\n", inet_ntoa(addrs[i]));
 }
 
-#endif
+#endif	/* TEST */
+
+#endif	/* !HAVE_GETIFADDRS */
diff --git a/support/misc/tcpwrapper.c b/support/misc/tcpwrapper.c
index 1da6020..af626ad 100644
--- a/support/misc/tcpwrapper.c
+++ b/support/misc/tcpwrapper.c
@@ -202,7 +202,7 @@ u_long  prog;
 	if (acc && changed == 0)
 		return (acc->access);
 
-	if (!(from_local(addr) || good_client(daemon, addr))) {
+	if (!(from_local((struct sockaddr *)addr) || good_client(daemon, addr))) {
 		log_bad_host(addr, proc, prog);
 		if (acc)
 			acc->access = FALSE;
-------------------------------------------------------------------------------
tcp_wrapper-squelch-compiler-w
-------------------------------------------------------------------------------
tcp_wrapper: Squelch compiler warning in support/misc/tcpwrapper.c

From: Chuck Lever <chuck.lever@oracle.com>

Eliminate these compiler warnings:

tcpwrapper.c: In function ‘logit’:
tcpwrapper.c:225: warning: unused parameter ‘procnum’
tcpwrapper.c:225: warning: unused parameter ‘prognum’

Actually, @procnum is not used anywhere in tcpwrapper.c, so let's just
get rid of it.

Since there is only one logit() call site in tcpwrapper.c, the macro
wrapper just adds needless visual clutter.  Let's get rid of that too.

Finally, both mountd and statd now use xlog(), which adds an
appropriate program name prefix to every message.  Replace the
open-coded syslog(2) call with an xlog() call in order to consistently
identify the reporting RPC service.

This results in our tcpwrapper shim ignoring the local "deny_severity"
setting.  Since no nfs-utils caller sets either allow_severity or
deny_severity, I don't think this will be much of a problem.  We could
easily get rid of those too, as the tcpd.h header already defines
them.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 support/include/tcpwrapper.h  |    3 +--
 support/misc/tcpwrapper.c     |   43 ++++++++++++++++++++---------------------
 utils/mountd/mount_dispatch.c |    2 +-
 utils/statd/statd.c           |    2 +-
 4 files changed, 24 insertions(+), 26 deletions(-)


diff --git a/support/include/tcpwrapper.h b/support/include/tcpwrapper.h
index f1145bd..842d590 100644
--- a/support/include/tcpwrapper.h
+++ b/support/include/tcpwrapper.h
@@ -12,7 +12,6 @@ extern int deny_severity;
 
 extern int good_client(char *daemon, struct sockaddr_in *addr);
 extern int from_local(const struct sockaddr *sap);
-extern int check_default(char *daemon, struct sockaddr_in *addr,
-			 u_long proc, u_long prog);
+extern int check_default(char *daemon, struct sockaddr_in *addr, u_long prog);
 
 #endif /* TCP_WRAPPER_H */
diff --git a/support/misc/tcpwrapper.c b/support/misc/tcpwrapper.c
index af626ad..2180100 100644
--- a/support/misc/tcpwrapper.c
+++ b/support/misc/tcpwrapper.c
@@ -56,8 +56,6 @@
 #include <rpc/rpcent.h>
 #endif
 
-static void logit(int severity, struct sockaddr_in *addr,
-		  u_long procnum, u_long prognum, char *text);
 static int check_files(void);
 
 /*
@@ -68,9 +66,6 @@ int     verboselog = 0;
 int     allow_severity = LOG_INFO;
 int     deny_severity = LOG_WARNING;
 
-#define log_bad_host(addr, proc, prog) \
-  logit(deny_severity, addr, proc, prog, "request from unauthorized host")
-
 #define ALLOW 1
 #define DENY 0
 
@@ -143,6 +138,16 @@ haccess_t *haccess_lookup(struct sockaddr_in *addr, u_long prog)
 	return NULL;
 }
 
+static void
+logit(const struct sockaddr_in *sin)
+{
+	char buf[INET_ADDRSTRLEN];
+
+	inet_ntop(AF_INET, &sin->sin_addr, buf, sizeof(buf));
+	xlog_warn("connect from %s denied: request from unauthorized host",
+		buf);
+}
+
 int
 good_client(daemon, addr)
 char *daemon;
@@ -186,14 +191,17 @@ static int check_files()
 	return changed;
 }
 
-/* check_default - additional checks for NULL, DUMP, GETPORT and unknown */
-
+/**
+ * check_default - additional checks for NULL, DUMP, GETPORT and unknown
+ * @daemon: pointer to '\0'-terminated ASCII string containing name of the
+ *		daemon requesting the access check
+ * @addr: pointer to socket address containing address of caller
+ * @prog: RPC program number caller is trying to access
+ *
+ * Returns TRUE if the caller is allowed access; otherwise FALSE is returned.
+ */
 int
-check_default(daemon, addr, proc, prog)
-char *daemon;
-struct sockaddr_in *addr;
-u_long  proc;
-u_long  prog;
+check_default(char *daemon, struct sockaddr_in *addr, u_long prog)
 {
 	haccess_t *acc = NULL;
 	int changed = check_files();
@@ -203,7 +211,7 @@ u_long  prog;
 		return (acc->access);
 
 	if (!(from_local((struct sockaddr *)addr) || good_client(daemon, addr))) {
-		log_bad_host(addr, proc, prog);
+		logit(addr);
 		if (acc)
 			acc->access = FALSE;
 		else 
@@ -218,13 +226,4 @@ u_long  prog;
 
     return (TRUE);
 }
-
-/* logit - report events of interest via the syslog daemon */
-
-static void logit(int severity, struct sockaddr_in *addr,
-		  u_long procnum, u_long prognum, char *text)
-{
-	syslog(severity, "connect from %s denied: %s",
-	       inet_ntoa(addr->sin_addr), text);
-}
 #endif
diff --git a/utils/mountd/mount_dispatch.c b/utils/mountd/mount_dispatch.c
index 199fcec..d2802ef 100644
--- a/utils/mountd/mount_dispatch.c
+++ b/utils/mountd/mount_dispatch.c
@@ -75,7 +75,7 @@ mount_dispatch(struct svc_req *rqstp, SVCXPRT *transp)
 
 	/* remote host authorization check */
 	if (sin->sin_family == AF_INET &&
-	    !check_default("mountd", sin, rqstp->rq_proc, MOUNTPROG)) {
+	    !check_default("mountd", sin, MOUNTPROG)) {
 		svcerr_auth (transp, AUTH_FAILED);
 		return;
 	}
diff --git a/utils/statd/statd.c b/utils/statd/statd.c
index 7be6454..fa3c6d5 100644
--- a/utils/statd/statd.c
+++ b/utils/statd/statd.c
@@ -79,7 +79,7 @@ sm_prog_1_wrapper (struct svc_req *rqstp, register SVCXPRT *transp)
 
 	/* remote host authorization check */
 	if (sin->sin_family == AF_INET &&
-	    !check_default("statd", sin, rqstp->rq_proc, SM_PROG)) {
+	    !check_default("statd", sin, SM_PROG)) {
 		svcerr_auth (transp, AUTH_FAILED);
 		return;
 	}
-------------------------------------------------------------------------------
tcpwrapper-add-support-for-ipv
-------------------------------------------------------------------------------
tcpwrapper: Add support for IPv6

From: Chuck Lever <chuck.lever@oracle.com>

Assuming the tcp_wrappers library can actually support IPv6 addresses,
here's a crack at IPv6 support in nfs-utils' TCP wrapper shim.

Some reorganization is done to limit the number of times that @sap
is converted to a presentation address string.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 support/include/tcpwrapper.h  |    4 +
 support/misc/tcpwrapper.c     |  127 +++++++++++++++++++++++++++--------------
 utils/mountd/mount_dispatch.c |    6 +-
 utils/statd/statd.c           |    5 --
 utils/statd/statd.man         |    3 -
 5 files changed, 89 insertions(+), 56 deletions(-)


diff --git a/support/include/tcpwrapper.h b/support/include/tcpwrapper.h
index 842d590..5fca31a 100644
--- a/support/include/tcpwrapper.h
+++ b/support/include/tcpwrapper.h
@@ -10,8 +10,8 @@ extern int verboselog;
 extern int allow_severity;
 extern int deny_severity;
 
-extern int good_client(char *daemon, struct sockaddr_in *addr);
 extern int from_local(const struct sockaddr *sap);
-extern int check_default(char *daemon, struct sockaddr_in *addr, u_long prog);
+extern int check_default(char *daemon, struct sockaddr *sap,
+			const u_long program);
 
 #endif /* TCP_WRAPPER_H */
diff --git a/support/misc/tcpwrapper.c b/support/misc/tcpwrapper.c
index 2180100..4295693 100644
--- a/support/misc/tcpwrapper.c
+++ b/support/misc/tcpwrapper.c
@@ -34,7 +34,9 @@
 #ifdef HAVE_CONFIG_H
 #include <config.h>
 #endif
+
 #ifdef HAVE_LIBWRAP
+
 #include <tcpwrapper.h>
 #include <unistd.h>
 #include <string.h>
@@ -50,13 +52,12 @@
 #include <tcpd.h>
 
 #include "xlog.h"
+#include "compaddr.h"
 
 #ifdef SYSV40
 #include <netinet/in.h>
 #include <rpc/rpcent.h>
-#endif
-
-static int check_files(void);
+#endif	/* SYSV40 */
 
 /*
  * These need to exist since they are externed 
@@ -69,10 +70,42 @@ int     deny_severity = LOG_WARNING;
 #define ALLOW 1
 #define DENY 0
 
+#ifdef IPV6_SUPPORTED
+static void
+present_address(const struct sockaddr *sap, char *buf, const size_t buflen)
+{
+	const struct sockaddr_in *sin = (const struct sockaddr_in *)sap;
+	const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sap;
+
+	switch (sap->sa_family) {
+	case AF_INET:
+		if (inet_ntop(AF_INET, &sin->sin_addr, buf, buflen))
+			return;
+	case AF_INET6:
+		if (inet_ntop(AF_INET6, &sin6->sin6_addr, buf, buflen))
+			return;
+	}
+
+	strncpy(buf, "unrecognized address", buflen);
+}
+#else	/* !IPV6_SUPPORTED */
+static void
+present_address(const struct sockaddr *sap, char *buf, const size_t buflen)
+{
+	const struct sockaddr_in *sin = (const struct sockaddr_in *)sap;
+
+	if (sap->sa_family == AF_INET)
+		if (inet_ntop(AF_INET, &sin->sin_addr, buf, buflen))
+			return;
+
+	strncpy(buf, "unrecognized address", buflen);
+}
+#endif	/* !IPV6_SUPPORTED */
+
 typedef struct _haccess_t {
 	TAILQ_ENTRY(_haccess_t) list;
 	int access;
-    struct in_addr addr;
+	struct sockaddr_storage address;
 } haccess_t;
 
 #define HASH_TABLE_SIZE 1021
@@ -80,10 +113,9 @@ typedef struct _hash_head {
 	TAILQ_HEAD(host_list, _haccess_t) h_head;
 } hash_head;
 hash_head haccess_tbl[HASH_TABLE_SIZE];
-static haccess_t *haccess_lookup(struct sockaddr_in *addr, u_long);
-static void haccess_add(struct sockaddr_in *addr, u_long, int);
 
-inline unsigned int strtoint(char *str)
+static unsigned int
+strtoint(const char *str)
 {
 	unsigned int n = 0;
 	int len = strlen(str);
@@ -94,68 +126,70 @@ inline unsigned int strtoint(char *str)
 
 	return n;
 }
-static inline int hashint(unsigned int num)
+
+static unsigned int
+hashint(const unsigned int num)
 {
 	return num % HASH_TABLE_SIZE;
 }
 #define HASH(_addr, _prog) \
 	hashint((strtoint((_addr))+(_prog)))
 
-void haccess_add(struct sockaddr_in *addr, u_long prog, int access)
+static void
+haccess_add(const struct sockaddr *sap, const char *address,
+		const rpcprog_t program, const int access)
 {
 	hash_head *head;
  	haccess_t *hptr;
-	int hash;
+	unsigned int hash;
 
 	hptr = (haccess_t *)malloc(sizeof(haccess_t));
 	if (hptr == NULL)
 		return;
 
-	hash = HASH(inet_ntoa(addr->sin_addr), prog);
+	hash = HASH(address, program);
 	head = &(haccess_tbl[hash]);
 
 	hptr->access = access;
-	hptr->addr.s_addr = addr->sin_addr.s_addr;
+	memcpy(&hptr->address, sap, sockaddr_length(sap));
 
 	if (TAILQ_EMPTY(&head->h_head))
 		TAILQ_INSERT_HEAD(&head->h_head, hptr, list);
 	else
 		TAILQ_INSERT_TAIL(&head->h_head, hptr, list);
 }
-haccess_t *haccess_lookup(struct sockaddr_in *addr, u_long prog)
+
+static haccess_t *
+haccess_lookup(const struct sockaddr *sap, const char *address,
+		const rpcprog_t program)
 {
 	hash_head *head;
  	haccess_t *hptr;
-	int hash;
+	unsigned int hash;
 
-	hash = HASH(inet_ntoa(addr->sin_addr), prog);
+	hash = HASH(address, program);
 	head = &(haccess_tbl[hash]);
 
 	TAILQ_FOREACH(hptr, &head->h_head, list) {
-		if (hptr->addr.s_addr == addr->sin_addr.s_addr)
+		if (compare_sockaddr((struct sockaddr *)&hptr->address, sap))
 			return hptr;
 	}
 	return NULL;
 }
 
 static void
-logit(const struct sockaddr_in *sin)
+logit(const char *address)
 {
-	char buf[INET_ADDRSTRLEN];
-
-	inet_ntop(AF_INET, &sin->sin_addr, buf, sizeof(buf));
 	xlog_warn("connect from %s denied: request from unauthorized host",
-		buf);
+		address);
 }
 
-int
-good_client(daemon, addr)
-char *daemon;
-struct sockaddr_in *addr;
+static int
+good_client(char *daemon, struct sockaddr *sap)
 {
 	struct request_info req;
 
-	request_init(&req, RQ_DAEMON, daemon, RQ_CLIENT_SIN, addr, 0);
+	request_init(&req, RQ_DAEMON, daemon, RQ_CLIENT_SIN, sap, 0);
 	sock_methods(&req);
 
 	if (hosts_access(&req)) 
@@ -164,9 +198,8 @@ struct sockaddr_in *addr;
 	return DENY;
 }
 
-/* check_files - check to see if either access files have changed */
-
-static int check_files()
+static int
+check_files(void)
 {
 	static time_t allow_mtime, deny_mtime;
 	struct stat astat, dstat;
@@ -195,35 +228,43 @@ static int check_files()
  * check_default - additional checks for NULL, DUMP, GETPORT and unknown
  * @daemon: pointer to '\0'-terminated ASCII string containing name of the
  *		daemon requesting the access check
- * @addr: pointer to socket address containing address of caller
- * @prog: RPC program number caller is trying to access
+ * @sap: pointer to socket address containing address of caller
+ * @program: RPC program number caller is trying to access
  *
  * Returns TRUE if the caller is allowed access; otherwise FALSE is returned.
  */
 int
-check_default(char *daemon, struct sockaddr_in *addr, u_long prog)
+check_default(char *daemon, struct sockaddr *sap, const u_long program)
 {
 	haccess_t *acc = NULL;
 	int changed = check_files();
+	char buf[INET6_ADDRSTRLEN];
+
+	present_address(sap, buf, sizeof(buf));
 
-	acc = haccess_lookup(addr, prog);
-	if (acc && changed == 0)
-		return (acc->access);
+	acc = haccess_lookup(sap, buf, program);
+	if (acc && !changed) {
+		xlog(D_GENERAL, "%s: access by %s %s (cached)", __func__,
+			buf, acc->access == ALLOW ? "ALLOWED" : "DENIED");
+		return acc->access;
+	}
 
-	if (!(from_local((struct sockaddr *)addr) || good_client(daemon, addr))) {
-		logit(addr);
+	if (!(from_local(sap) || good_client(daemon, sap))) {
+		logit(buf);
 		if (acc)
 			acc->access = FALSE;
 		else 
-			haccess_add(addr, prog, FALSE);
-		return (FALSE);
+			haccess_add(sap, buf, program, FALSE);
+		xlog(D_GENERAL, "%s: access by %s DENIED", __func__, buf);
+		return FALSE;
 	}
 
 	if (acc)
 		acc->access = TRUE;
 	else 
-		haccess_add(addr, prog, TRUE);
-
-    return (TRUE);
+		haccess_add(sap, buf, program, TRUE);
+	xlog(D_GENERAL, "%s: access by %s ALLOWED", __func__, buf);
+	return TRUE;
 }
-#endif
+
+#endif	/* HAVE_LIBWRAP */
diff --git a/utils/mountd/mount_dispatch.c b/utils/mountd/mount_dispatch.c
index d2802ef..ba6981d 100644
--- a/utils/mountd/mount_dispatch.c
+++ b/utils/mountd/mount_dispatch.c
@@ -70,12 +70,10 @@ mount_dispatch(struct svc_req *rqstp, SVCXPRT *transp)
 {
 	union mountd_arguments 	argument;
 	union mountd_results	result;
-#ifdef HAVE_TCP_WRAPPER
-	struct sockaddr_in *sin = nfs_getrpccaller_in(transp);
 
+#ifdef HAVE_TCP_WRAPPER
 	/* remote host authorization check */
-	if (sin->sin_family == AF_INET &&
-	    !check_default("mountd", sin, MOUNTPROG)) {
+	if (!check_default("mountd", nfs_getrpccaller(transp), MOUNTPROG)) {
 		svcerr_auth (transp, AUTH_FAILED);
 		return;
 	}
diff --git a/utils/statd/statd.c b/utils/statd/statd.c
index fa3c6d5..01fdb41 100644
--- a/utils/statd/statd.c
+++ b/utils/statd/statd.c
@@ -75,11 +75,8 @@ extern void simulator (int, char **);
 static void 
 sm_prog_1_wrapper (struct svc_req *rqstp, register SVCXPRT *transp)
 {
-	struct sockaddr_in *sin = nfs_getrpccaller_in(transp);
-
 	/* remote host authorization check */
-	if (sin->sin_family == AF_INET &&
-	    !check_default("statd", sin, SM_PROG)) {
+	if (!check_default("statd", nfs_getrpccaller(transp), SM_PROG)) {
 		svcerr_auth (transp, AUTH_FAILED);
 		return;
 	}
diff --git a/utils/statd/statd.man b/utils/statd/statd.man
index 9310f0e..c0a8e9a 100644
--- a/utils/statd/statd.man
+++ b/utils/statd/statd.man
@@ -272,9 +272,6 @@ listeners using the
 .B tcp_wrapper
 library or
 .BR iptables (8).
-Note that the
-.B tcp_wrapper
-library supports only IPv4 networking.
 To use the
 .B tcp_wrapper
 library, add the hostnames of peers that should be allowed access to
-------------------------------------------------------------------------------
libnfs-a-provide-shared-helper
-------------------------------------------------------------------------------
libnfs.a: Provide shared helpers for managing netids

From: Chuck Lever <chuck.lever@oracle.com>

Introduce a couple of shared functions that can convert netids to
protocol numbers and address families, and back.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 support/include/nfsrpc.h |   12 ++++++
 support/nfs/getport.c    |   89 ++++++++++++++++++++++++++++++++++++++++++----
 2 files changed, 94 insertions(+), 7 deletions(-)


diff --git a/support/include/nfsrpc.h b/support/include/nfsrpc.h
index dff6af7..59ee093 100644
--- a/support/include/nfsrpc.h
+++ b/support/include/nfsrpc.h
@@ -90,6 +90,18 @@ extern CLIENT		*nfs_get_priv_rpcclient( const struct sockaddr *,
 				struct timeval *);
 
 /*
+ * Convert a netid to a protocol number and protocol family
+ */
+extern int		nfs_get_proto(const char *netid,
+				sa_family_t *family, unsigned long *protocol);
+
+/*
+ * Convert a protocol family and protocol name to a netid
+ */
+extern char		*nfs_get_netid(const sa_family_t family,
+				const int protocol);
+
+/*
  * Convert a socket address to a universal address
  */
 extern char		*nfs_sockaddr2universal(const struct sockaddr *);
diff --git a/support/nfs/getport.c b/support/nfs/getport.c
index 4bdf556..87fa7d1 100644
--- a/support/nfs/getport.c
+++ b/support/nfs/getport.c
@@ -199,7 +199,60 @@ static CLIENT *nfs_gp_get_rpcbclient(struct sockaddr *sap,
 	return clnt;
 }
 
-/*
+/**
+ * nfs_get_proto - Convert a netid to a protocol number and protocol family
+ * @netid: C string containing a netid
+ * @family: OUT: protocol family number
+ * @protocol: OUT: IPPROTO_ 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)
+		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;
+
+	*protocol = 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 = 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: IPPROTO_ value
+ *
  * 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 +266,12 @@ static CLIENT *nfs_gp_get_rpcbclient(struct sockaddr *sap,
  * first entry that matches @family and @protocol and whose netid string
  * fits in the provided buffer.
  *
- * Returns a '\0'-terminated string if successful; otherwise NULL.
+ * Returns a '\0'-terminated string if successful.  Caller must
+ * free the returned string.  Otherwise NULL is returned, and
  * rpc_createerr.cf_stat is set to reflect the error.
  */
 #ifdef HAVE_LIBTIRPC
-
-static char *nfs_gp_get_netid(const sa_family_t family,
-			      const unsigned short protocol)
+char *nfs_get_netid(const sa_family_t family, const int protocol)
 {
 	char *nc_protofmly, *nc_proto, *nc_netid;
 	struct netconfig *nconf;
@@ -255,6 +307,9 @@ static char *nfs_gp_get_netid(const sa_family_t family,
 
 		nc_netid = strdup(nconf->nc_netid);
 		endnetconfig(handle);
+
+		if (nc_netid == NULL)
+			rpc_createerr.cf_stat = RPC_SYSTEMERROR;
 		return nc_netid;
 	}
 	endnetconfig(handle);
@@ -263,8 +318,28 @@ out:
 	rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
 	return NULL;
 }
+#else	/* !HAVE_LIBTIRPC */
+char *nfs_get_netid(const sa_family_t family, const int protocol)
+{
+	struct protoent *proto;
+	char *netid;
 
-#endif	/* HAVE_LIBTIRPC */
+	if (family != AF_INET)
+		goto out;
+	proto = getprotobynumber(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 +496,7 @@ static int nfs_gp_init_rpcb_parms(const struct sockaddr *sap,
 {
 	char *netid, *addr;
 
-	netid = nfs_gp_get_netid(sap->sa_family, protocol);
+	netid = nfs_get_netid(sap->sa_family, protocol);
 	if (netid == NULL)
 		return 0;
 
-------------------------------------------------------------------------------
mount-nfs-support-netids-in-nf
-------------------------------------------------------------------------------
mount.nfs: support netids in nfs_options2pmap()

From: Chuck Lever <chuck.lever@oracle.com>

When parsing mount options in nfs_options2pmap(), treat the value of
proto= (and mountproto=) as a netid by looking it up in local
netconfig and protocol databases to convert it to a protocol number.
If TI-RPC is not available, the traditional behavior is preserved.

The meaning of the "udp" and "tcp" mount options is not affected by
this change.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 utils/mount/network.c |   28 ++++++----------------------
 1 files changed, 6 insertions(+), 22 deletions(-)


diff --git a/utils/mount/network.c b/utils/mount/network.c
index 7b1152a..554202e 100644
--- a/utils/mount/network.c
+++ b/utils/mount/network.c
@@ -1289,6 +1289,7 @@ nfs_nfs_version(struct mount_options *options, unsigned long *version)
 int
 nfs_nfs_protocol(struct mount_options *options, unsigned long *protocol)
 {
+	sa_family_t family;
 	char *option;
 
 	switch (po_rightmost(options, nfs_transport_opttbl)) {
@@ -1300,17 +1301,8 @@ nfs_nfs_protocol(struct mount_options *options, unsigned long *protocol)
 		return 1;
 	case 2: /* proto */
 		option = po_get(options, "proto");
-		if (option) {
-			if (strcmp(option, "tcp") == 0) {
-				*protocol = IPPROTO_TCP;
-				return 1;
-			}
-			if (strcmp(option, "udp") == 0) {
-				*protocol = IPPROTO_UDP;
-				return 1;
-			}
-			return 0;
-		}
+		if (option)
+			return nfs_get_proto(option, &family, protocol);
 	}
 
 	/*
@@ -1419,20 +1411,12 @@ nfs_mount_version(struct mount_options *options, unsigned long *version)
 static int
 nfs_mount_protocol(struct mount_options *options, unsigned long *protocol)
 {
+	sa_family_t family;
 	char *option;
 
 	option = po_get(options, "mountproto");
-	if (option) {
-		if (strcmp(option, "tcp") == 0) {
-			*protocol = IPPROTO_TCP;
-			return 1;
-		}
-		if (strcmp(option, "udp") == 0) {
-			*protocol = IPPROTO_UDP;
-			return 1;
-		}
-		return 0;
-	}
+	if (option)
+		return nfs_get_proto(option, &family, protocol);
 
 	/*
 	 * MNT transport protocol wasn't specified.  If the NFS
-------------------------------------------------------------------------------
mount-nfs-support-netids-in-v2
-------------------------------------------------------------------------------
mount.nfs: support netids in v2/v3 version/transport negotiation

From: Chuck Lever <chuck.lever@oracle.com>

When rewriting mount options during v2/v3 negotiation, restore the
correct netids, rather than protocol names, in the rewritten protocol
options.  If TI-RPC is not available, the traditional behavior is
preserved.

This patch assumes the kernel can recognize a netid, instead of a
protocol name, as the value of the proto= options.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 utils/mount/stropts.c |   51 +++++++++++++++++++++----------------------------
 1 files changed, 22 insertions(+), 29 deletions(-)


diff --git a/utils/mount/stropts.c b/utils/mount/stropts.c
index a0b9e7f..95d4f1e 100644
--- a/utils/mount/stropts.c
+++ b/utils/mount/stropts.c
@@ -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"
@@ -371,10 +372,13 @@ static int nfs_extract_server_addresses(struct mount_options *options,
 }
 
 static int nfs_construct_new_options(struct mount_options *options,
+				     struct sockaddr *nfs_saddr,
 				     struct pmap *nfs_pmap,
+				     struct sockaddr *mnt_saddr,
 				     struct pmap *mnt_pmap)
 {
 	char new_option[64];
+	char *netid;
 
 	po_remove_all(options, "nfsprog");
 	po_remove_all(options, "mountprog");
@@ -391,20 +395,14 @@ static int nfs_construct_new_options(struct mount_options *options,
 	po_remove_all(options, "proto");
 	po_remove_all(options, "udp");
 	po_remove_all(options, "tcp");
-	switch (nfs_pmap->pm_prot) {
-	case IPPROTO_TCP:
-		snprintf(new_option, sizeof(new_option) - 1,
-			 "proto=tcp");
-		if (po_append(options, new_option) == PO_FAILED)
-			return 0;
-		break;
-	case IPPROTO_UDP:
-		snprintf(new_option, sizeof(new_option) - 1,
-			 "proto=udp");
-		if (po_append(options, new_option) == PO_FAILED)
-			return 0;
-		break;
-	}
+	netid = nfs_get_netid(nfs_saddr->sa_family, nfs_pmap->pm_prot);
+	if (netid == NULL)
+		return 0;
+	snprintf(new_option, sizeof(new_option) - 1,
+			 "proto=%s", netid);
+	if (po_append(options, new_option) == PO_FAILED)
+		return 0;
+	free(netid);
 
 	po_remove_all(options, "port");
 	if (nfs_pmap->pm_port != NFS_PORT) {
@@ -421,20 +419,14 @@ static int nfs_construct_new_options(struct mount_options *options,
 		return 0;
 
 	po_remove_all(options, "mountproto");
-	switch (mnt_pmap->pm_prot) {
-	case IPPROTO_TCP:
-		snprintf(new_option, sizeof(new_option) - 1,
-			 "mountproto=tcp");
-		if (po_append(options, new_option) == PO_FAILED)
-			return 0;
-		break;
-	case IPPROTO_UDP:
-		snprintf(new_option, sizeof(new_option) - 1,
-			 "mountproto=udp");
-		if (po_append(options, new_option) == PO_FAILED)
-			return 0;
-		break;
-	}
+	netid = nfs_get_netid(mnt_saddr->sa_family, mnt_pmap->pm_prot);
+	if (netid == NULL)
+		return 0;
+	snprintf(new_option, sizeof(new_option) - 1,
+			 "mountproto=%s", netid);
+	if (po_append(options, new_option) == PO_FAILED)
+		return 0;
+	free(netid);
 
 	po_remove_all(options, "mountport");
 	snprintf(new_option, sizeof(new_option) - 1,
@@ -510,7 +502,8 @@ nfs_rewrite_pmap_mount_options(struct mount_options *options)
 		return 0;
 	}
 
-	if (!nfs_construct_new_options(options, &nfs_pmap, &mnt_pmap)) {
+	if (!nfs_construct_new_options(options, nfs_saddr, &nfs_pmap,
+					mnt_saddr, &mnt_pmap)) {
 		errno = EINVAL;
 		return 0;
 	}
-------------------------------------------------------------------------------
mount-nfs-make-nfs_lookup-glob
-------------------------------------------------------------------------------
mount.nfs: make nfs_lookup() global

From: Chuck Lever <chuck.lever@oracle.com>

Expose a DNS query API that allows callers to request DNS results from
a specific address family.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 utils/mount/network.c |   14 ++++++++++++--
 utils/mount/network.h |    2 ++
 2 files changed, 14 insertions(+), 2 deletions(-)


diff --git a/utils/mount/network.c b/utils/mount/network.c
index 554202e..1c99faf 100644
--- a/utils/mount/network.c
+++ b/utils/mount/network.c
@@ -193,8 +193,18 @@ static const unsigned int *nfs_default_proto()
 }
 #endif /* MOUNT_CONFIG */
 
-static int nfs_lookup(const char *hostname, const sa_family_t family,
-		      struct sockaddr *sap, socklen_t *salen)
+/**
+ * nfs_lookup - resolve hostname to an IPv4 or IPv6 socket address
+ * @hostname: pointer to C string containing DNS hostname to resolve
+ * @family: address family hint
+ * @sap: pointer to buffer to fill with socket address
+ * @len: IN: size of buffer to fill; OUT: size of socket address
+ *
+ * Returns 1 and places a socket address at @sap if successful;
+ * otherwise zero.
+ */
+int nfs_lookup(const char *hostname, const sa_family_t family,
+		struct sockaddr *sap, socklen_t *salen)
 {
 	struct addrinfo *gai_results;
 	struct addrinfo gai_hint = {
diff --git a/utils/mount/network.h b/utils/mount/network.h
index 7eb89b0..2cdf02e 100644
--- a/utils/mount/network.h
+++ b/utils/mount/network.h
@@ -45,6 +45,8 @@ int nfs_probe_bothports(const struct sockaddr *, const socklen_t,
 			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);
-------------------------------------------------------------------------------
mount-nfs-add-new-api-for-gett
-------------------------------------------------------------------------------
mount.nfs: Add new API for getting protocol family from netids

From: Chuck Lever <chuck.lever@oracle.com>

Introduce a couple of new functions that extract the protocol family
from the value of the proto= and mountproto= mount options.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 utils/mount/network.c |   63 +++++++++++++++++++++++++++++++++++++++++++++++++
 utils/mount/network.h |    2 ++
 2 files changed, 65 insertions(+), 0 deletions(-)


diff --git a/utils/mount/network.c b/utils/mount/network.c
index 1c99faf..9623011 100644
--- a/utils/mount/network.c
+++ b/utils/mount/network.c
@@ -1354,6 +1354,40 @@ nfs_nfs_port(struct mount_options *options, unsigned long *port)
 }
 
 /*
+ * Returns TRUE and fills in @family if a valid NFS protocol option
+ * is found, or FALSE if the option was specified with an invalid value.
+ */
+int nfs_nfs_proto_family(struct mount_options *options,
+				sa_family_t *family)
+{
+	unsigned long protocol;
+	char *option;
+
+#ifdef HAVE_LIBTIRPC
+	*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)
+			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.
  *
@@ -1466,6 +1500,35 @@ nfs_mount_port(struct mount_options *options, unsigned long *port)
 	return 1;
 }
 
+/*
+ * Returns TRUE and fills in @family if a valid MNT protocol option
+ * is found, or FALSE if the option was specified with an invalid value.
+ */
+int nfs_mount_proto_family(struct mount_options *options,
+				sa_family_t *family)
+{
+	unsigned long protocol;
+	char *option;
+
+#ifdef HAVE_LIBTIRPC
+	*family = AF_UNSPEC;
+#else
+	*family = AF_INET;
+#endif
+
+	option = po_get(options, "mountproto");
+	if (option)
+		return nfs_get_proto(option, family, &protocol);
+
+	/*
+	 * MNT transport protocol wasn't specified.  If the NFS
+	 * transport protocol was specified, derive the family
+	 * from that; otherwise, return the default family for
+	 * NFS.
+	 */
+	return nfs_nfs_proto_family(options, family);
+}
+
 /**
  * nfs_options2pmap - set up pmap structs based on mount options
  * @options: pointer to mount options
diff --git a/utils/mount/network.h b/utils/mount/network.h
index 2cdf02e..da095e3 100644
--- a/utils/mount/network.h
+++ b/utils/mount/network.h
@@ -58,6 +58,8 @@ int clnt_ping(struct sockaddr_in *, const unsigned long,
 
 struct mount_options;
 
+int nfs_nfs_proto_family(struct mount_options *options, sa_family_t *family);
+int nfs_mount_proto_family(struct mount_options *options, sa_family_t *family);
 int nfs_nfs_version(struct mount_options *options, unsigned long *version);
 int  nfs_nfs_protocol(struct mount_options *options, unsigned long *protocol);
 
-------------------------------------------------------------------------------
mount-nfs-proto-netid-forces-a
-------------------------------------------------------------------------------
mount.nfs: proto=netid forces address family when resolving server names

From: Chuck Lever <chuck.lever@oracle.com>

Using the netid settings, determine the correct address family to use
for NFS and MNT server name resolution.  Use this family when
resolving the server name for the addr= and mountaddr= options.

This patch assumes the kernel can recognize a netid, instead of a
protocol name, as the value of the proto= options.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 utils/mount/stropts.c |   32 ++++++++++++++++++++++++--------
 1 files changed, 24 insertions(+), 8 deletions(-)


diff --git a/utils/mount/stropts.c b/utils/mount/stropts.c
index 95d4f1e..84d4122 100644
--- a/utils/mount/stropts.c
+++ b/utils/mount/stropts.c
@@ -219,21 +219,34 @@ static int nfs_append_clientaddr_option(const struct sockaddr *sap,
 }
 
 /*
- * Resolve the 'mounthost=' hostname and append a new option using
- * the resulting address.
+ * Determine whether to append a 'mountaddr=' option.  The option is needed if:
+ *
+ *   1. "mounthost=" was specified, or
+ *   2. The address families for proto= and mountproto= are different.
+ *
  */
-static int nfs_fix_mounthost_option(struct mount_options *options)
+static int nfs_fix_mounthost_option(struct mount_options *options,
+		const char *nfs_hostname)
 {
 	struct sockaddr_storage dummy;
 	struct sockaddr *sap = (struct sockaddr *)&dummy;
 	socklen_t salen = sizeof(dummy);
+	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) {
+		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;
@@ -321,12 +334,15 @@ static int nfs_set_version(struct nfsmount_info *mi)
 static int nfs_validate_options(struct nfsmount_info *mi)
 {
 	struct sockaddr *sap = (struct sockaddr *)&mi->address;
+	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))
@@ -559,7 +575,7 @@ static int nfs_try_mount_v3v2(struct nfsmount_info *mi)
 		return result;
 	}
 
-	if (!nfs_fix_mounthost_option(options)) {
+	if (!nfs_fix_mounthost_option(options, mi->hostname)) {
 		errno = EINVAL;
 		goto out_fail;
 	}
-------------------------------------------------------------------------------
mount-nfs-teach-umount-nfs-to-
-------------------------------------------------------------------------------
mount.nfs: Teach umount.nfs to recognize netids in /etc/mtab

From: Chuck Lever <chuck.lever@oracle.com>

umount.nfs has to detect the correct address family to use when
looking up the server.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 utils/mount/nfsumount.c |    7 +++++--
 1 files changed, 5 insertions(+), 2 deletions(-)


diff --git a/utils/mount/nfsumount.c b/utils/mount/nfsumount.c
index c5505b1..c6fd3a0 100644
--- a/utils/mount/nfsumount.c
+++ b/utils/mount/nfsumount.c
@@ -173,6 +173,7 @@ static int nfs_umount_do_umnt(struct mount_options *options,
 	struct sockaddr *sap = (struct sockaddr *)&address;
 	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 +190,10 @@ static int nfs_umount_do_umnt(struct mount_options *options,
 		return EX_FAIL;
 	}
 
-	if (nfs_name_to_address(*hostname, sap, &salen) == 0)
-		/* nfs_name_to_address reports any errors */
+	if (!nfs_mount_proto_family(options, &family))
+		return 0;
+	if (!nfs_lookup(*hostname, family, sap, &salen))
+		/* nfs_lookup reports any errors */
 		return EX_FAIL;
 
 	if (nfs_advise_umount(sap, salen, &mnt_pmap, dirname) == 0)
-------------------------------------------------------------------------------
mount-nfs-remove-nfs_name_to_a
-------------------------------------------------------------------------------
mount.nfs: Remove nfs_name_to_address()

From: Chuck Lever <chuck.lever@oracle.com>

Clean up:  nfs_name_to_address() has no more callers.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 utils/mount/network.c |   23 ++---------------------
 utils/mount/network.h |    1 -
 2 files changed, 2 insertions(+), 22 deletions(-)


diff --git a/utils/mount/network.c b/utils/mount/network.c
index 9623011..5bb4d88 100644
--- a/utils/mount/network.c
+++ b/utils/mount/network.c
@@ -253,25 +253,6 @@ int nfs_lookup(const char *hostname, const sa_family_t family,
 }
 
 /**
- * nfs_name_to_address - resolve hostname to an IPv4 or IPv6 socket address
- * @hostname: pointer to C string containing DNS hostname to resolve
- * @sap: pointer to buffer to fill with socket address
- * @len: IN: size of buffer to fill; OUT: size of socket address
- *
- * Returns 1 and places a socket address at @sap if successful;
- * otherwise zero.
- */
-int nfs_name_to_address(const char *hostname,
-			struct sockaddr *sap, socklen_t *salen)
-{
-#ifdef IPV6_SUPPORTED
-	return nfs_lookup(hostname, AF_UNSPEC, sap, salen);
-#else	/* !IPV6_SUPPORTED */
-	return nfs_lookup(hostname, AF_INET, sap, salen);
-#endif	/* !IPV6_SUPPORTED */
-}
-
-/**
  * nfs_gethostbyname - resolve a hostname to an IPv4 address
  * @hostname: pointer to a C string containing a DNS hostname
  * @sin: returns an IPv4 address 
@@ -293,8 +274,8 @@ int nfs_gethostbyname(const char *hostname, struct sockaddr_in *sin)
  *		OUT: length of converted socket address
  *
  * Convert a presentation format address string to a socket address.
- * Similar to nfs_name_to_address(), but the DNS query is squelched,
- * and won't make any noise if the getaddrinfo() call fails.
+ * Similar to nfs_lookup(), but the DNS query is squelched, and it
+ * won't make any noise if the getaddrinfo() call fails.
  *
  * Returns 1 and fills in @sap and @salen if successful; otherwise zero.
  *
diff --git a/utils/mount/network.h b/utils/mount/network.h
index da095e3..2a3a110 100644
--- a/utils/mount/network.h
+++ b/utils/mount/network.h
@@ -44,7 +44,6 @@ int nfs_probe_bothports(const struct sockaddr *, const socklen_t,
 			struct pmap *, const struct sockaddr *,
 			const socklen_t, struct pmap *);
 int nfs_gethostbyname(const char *, struct sockaddr_in *);
-int nfs_name_to_address(const char *, struct sockaddr *, socklen_t *);
 int nfs_lookup(const char *hostname, const sa_family_t family,
 		struct sockaddr *sap, socklen_t *salen);
 int nfs_string_to_sockaddr(const char *, struct sockaddr *, socklen_t *);
-------------------------------------------------------------------------------
nfs-man-page-update-nfs-5-with
-------------------------------------------------------------------------------
NFS man page: update nfs(5) with details about IPv6 support

From: Chuck Lever <chuck.lever@oracle.com>

Add details to nfs(5) about how to specify raw IPv6 addresses when mounting an
NFS server.  Mounting via an IPv6 NFS server via hostname should work as it
does with IPv4.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 utils/mount/nfs.man |   84 ++++++++++++++++++++++++++++++++-------------------
 1 files changed, 52 insertions(+), 32 deletions(-)


diff --git a/utils/mount/nfs.man b/utils/mount/nfs.man
index 2299637..40cf733 100644
--- a/utils/mount/nfs.man
+++ b/utils/mount/nfs.man
@@ -58,9 +58,17 @@ The server's hostname and export pathname
 are separated by a colon, while
 the mount options are separated by commas. The remaining fields
 are separated by blanks or tabs.
+.P
 The server's hostname can be an unqualified hostname,
 a fully qualified domain name,
-or a dotted quad IPv4 address.
+a dotted quad IPv4 address, or
+an IPv6 address enclosed in square brackets.
+Link-local and site-local IPv6 addresses must be accompanied by an
+interface identifier.
+See
+.BR ipv6 (7)
+for details on specifying raw IPv6 addresses.
+.P
 The
 .I fstype
 field contains either "nfs" (for version 2 or version 3 NFS mounts)
@@ -470,32 +478,31 @@ 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
+.I netid
+is a valid netid listed in
+.IR /etc/netconfig .
+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
+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 +555,14 @@ or the server's mountd service is not available on the advertised port.
 This option can be used when mounting an NFS server
 through a firewall that blocks the rpcbind protocol.
 .TP 1.5i
-.BI mountproto= transport
-The transport the NFS client uses
+.BI mountproto= netid
+The transport protocol name and protocol family the NFS client uses
 to transmit requests to the NFS server's mountd service when performing
 this mount request, and when later unmounting this mount point.
-.I transport
-can be either
-.B udp
-or
-.BR tcp .
+.I netid
+is a valid netid listed in
+.IR /etc/netconfig .
+.IP
 .IP
 This option can be used when mounting an NFS server
 through a firewall that blocks a particular transport.
@@ -566,6 +572,7 @@ option, different transports for mountd requests and NFS requests
 can be specified.
 If the server's mountd service is not available via the specified
 transport, the mount request fails.
+.IP
 Refer to the TRANSPORT METHODS section for more on how the
 .B mountproto
 mount option interacts with the
@@ -709,17 +716,19 @@ 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 .
+.I netid
+is a valid netid listed in
+.IR /etc/netconfig .
+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
 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 +788,8 @@ The DATA AND METADATA COHERENCE section discusses
 the behavior of this option in more detail.
 .TP 1.5i
 .BI clientaddr= n.n.n.n
-Specifies  a  single  IPv4  address  (in dotted-quad form)
+Specifies a single IPv4 address (in dotted-quad form),
+or a non-link-local IPv6 address,
 that the NFS client advertises to allow servers
 to perform NFS version 4 callback requests against
 files on this mount point. If  the  server is unable to
@@ -855,6 +865,14 @@ This example can be used to mount /usr over NFS.
 .TA 2.5i +0.7i +0.7i +.7i
 	server:/export	/usr	nfs	ro,nolock,nocto,actimeo=3600	0 0
 .FI
+.P
+This example shows how to mount an NFS server
+using a raw IPv6 link-local address.
+.P
+.NF
+.TA 2.5i +0.7i +0.7i +.7i
+	[fe80::215:c5ff:fb3e:e2b1%eth0]:/export	/mnt	nfs	defaults	0 0
+.FI
 .SH "TRANSPORT METHODS"
 NFS clients send requests to NFS servers via
 Remote Procedure Calls, or
@@ -1498,6 +1516,8 @@ such as security negotiation, server referrals, and named attributes.
 .BR mount.nfs (5),
 .BR umount.nfs (5),
 .BR exports (5),
+.BR netconfig (5),
+.BR ipv6 (7),
 .BR nfsd (8),
 .BR sm-notify (8),
 .BR rpc.statd (8),
-------------------------------------------------------------------------------
mount-retry-when-server-can-t-
-------------------------------------------------------------------------------
mount: Retry when server can't be reached

From: Chuck Lever <chuck.lever@oracle.com>

We want new default behavior from mount.nfs when the server refuses a
connection.  Since connection refusal can be spurious (for example,
if the server is rebooting), mount.nfs should retry.

NFS shares that are automatically mounted by /etc/fstab at boot
time may be problematic.  The new behavior can be disabled by
specifying the "retry=0" mount option, or these mounts can be changed
to background mounts by specifying the "bg" option.

A kernel code change is still required for the mount(2) system call to
return ECONNREFUSED for NFSv4 mounts.  For v2/v3, the version and
transport negotiation logic in mount.nfs should drive a retry if the
server's rpcbind can't be reached.

Note that if a v2/v3 mount request encounters an unregistered NFS
service, it will still fail immediately.  That wouldn't be too hard
to change as well, but there are many more corner cases there where
failing immediately is appropriate.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 utils/mount/nfs.man   |    6 +++++-
 utils/mount/stropts.c |    4 ++++
 2 files changed, 9 insertions(+), 1 deletions(-)


diff --git a/utils/mount/nfs.man b/utils/mount/nfs.man
index 40cf733..e660137 100644
--- a/utils/mount/nfs.man
+++ b/utils/mount/nfs.man
@@ -363,7 +363,11 @@ The number of minutes that the
 command retries an NFS mount operation
 in the foreground or background before giving up.
 If this option is not specified, the default value for foreground mounts
-is 2 minutes, and the default value for background mounts is 10000 minutes (80 minutes shy of one week).
+is 2 minutes, and the default value for background mounts is 10000 minutes
+(80 minutes shy of one week).
+If a value of zero is specified, the
+.BR mount (8)
+command exits immediately after the first failure.
 .TP 1.5i
 .BI sec= mode
 The RPCGSS security flavor to use for accessing files on this mount point.
diff --git a/utils/mount/stropts.c b/utils/mount/stropts.c
index 84d4122..d64fd8e 100644
--- a/utils/mount/stropts.c
+++ b/utils/mount/stropts.c
@@ -515,6 +515,10 @@ nfs_rewrite_pmap_mount_options(struct mount_options *options)
 	if (!nfs_probe_bothports(mnt_saddr, mnt_salen, &mnt_pmap,
 				 nfs_saddr, nfs_salen, &nfs_pmap)) {
 		errno = ESPIPE;
+		if (rpc_createerr.cf_stat == RPC_PROGNOTREGISTERED)
+			errno = EOPNOTSUPP;
+		else if (rpc_createerr.cf_error.re_errno)
+			errno = rpc_createerr.cf_error.re_errno;
 		return 0;
 	}